2017-04-19 23:25:48 +03:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
|
|
*/
|
|
|
|
#include "calls/calls_panel.h"
|
|
|
|
|
2017-04-25 19:45:41 +03:00
|
|
|
#include "calls/calls_emoji_fingerprint.h"
|
2017-04-19 23:25:48 +03:00
|
|
|
#include "styles/style_calls.h"
|
|
|
|
#include "styles/style_history.h"
|
|
|
|
#include "ui/widgets/buttons.h"
|
|
|
|
#include "ui/widgets/labels.h"
|
|
|
|
#include "ui/effects/ripple_animation.h"
|
|
|
|
#include "ui/widgets/shadow.h"
|
|
|
|
#include "messenger.h"
|
2017-04-25 19:45:41 +03:00
|
|
|
#include "lang.h"
|
2017-04-19 23:25:48 +03:00
|
|
|
#include "auth_session.h"
|
|
|
|
#include "apiwrap.h"
|
2017-04-25 19:45:41 +03:00
|
|
|
#include "observer_peer.h"
|
2017-04-19 23:25:48 +03:00
|
|
|
#include "platform/platform_specific.h"
|
|
|
|
|
|
|
|
namespace Calls {
|
2017-04-29 21:41:41 +03:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr auto kTooltipShowTimeoutMs = 1000;
|
|
|
|
|
|
|
|
} // namespace
|
2017-04-19 23:25:48 +03:00
|
|
|
|
|
|
|
class Panel::Button : public Ui::RippleButton {
|
|
|
|
public:
|
|
|
|
Button(QWidget *parent, const style::CallButton &st);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void paintEvent(QPaintEvent *e) override;
|
|
|
|
|
|
|
|
void onStateChanged(State was, StateChangeSource source) override;
|
|
|
|
|
|
|
|
QImage prepareRippleMask() const override;
|
|
|
|
QPoint prepareRippleStartPosition() const override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
const style::CallButton &_st;
|
|
|
|
QPixmap _bg;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
Panel::Button::Button(QWidget *parent, const style::CallButton &st) : Ui::RippleButton(parent, st.button.ripple)
|
|
|
|
, _st(st) {
|
|
|
|
resize(_st.button.width, _st.button.height);
|
|
|
|
_bg = App::pixmapFromImageInPlace(style::colorizeImage(prepareRippleMask(), _st.bg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::Button::paintEvent(QPaintEvent *e) {
|
|
|
|
Painter p(this);
|
|
|
|
|
|
|
|
p.drawPixmap(myrtlpoint(_st.button.rippleAreaPosition), _bg);
|
|
|
|
|
|
|
|
auto ms = getms();
|
|
|
|
|
|
|
|
paintRipple(p, _st.button.rippleAreaPosition.x(), _st.button.rippleAreaPosition.y(), ms);
|
|
|
|
|
|
|
|
auto down = isDown();
|
|
|
|
auto position = _st.button.iconPosition;
|
2017-04-29 21:00:27 +03:00
|
|
|
if (position.x() < 0) {
|
|
|
|
position.setX((width() - _st.button.icon.width()) / 2);
|
|
|
|
}
|
|
|
|
if (position.y() < 0) {
|
|
|
|
position.setY((height() - _st.button.icon.height()) / 2);
|
|
|
|
}
|
2017-04-19 23:25:48 +03:00
|
|
|
_st.button.icon.paint(p, position, width());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::Button::onStateChanged(State was, StateChangeSource source) {
|
|
|
|
RippleButton::onStateChanged(was, source);
|
|
|
|
|
|
|
|
auto over = isOver();
|
|
|
|
auto wasOver = static_cast<bool>(was & StateFlag::Over);
|
|
|
|
if (over != wasOver) {
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint Panel::Button::prepareRippleStartPosition() const {
|
|
|
|
return mapFromGlobal(QCursor::pos()) - _st.button.rippleAreaPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
QImage Panel::Button::prepareRippleMask() const {
|
|
|
|
return Ui::RippleAnimation::ellipseMask(QSize(_st.button.rippleAreaSize, _st.button.rippleAreaSize));
|
|
|
|
}
|
|
|
|
|
|
|
|
Panel::Panel(gsl::not_null<Call*> call)
|
|
|
|
: _call(call)
|
|
|
|
, _user(call->user())
|
|
|
|
, _mute(this, st::callMuteToggle)
|
2017-04-25 19:45:41 +03:00
|
|
|
, _name(this, st::callName)
|
|
|
|
, _status(this, st::callStatus) {
|
2017-04-29 21:41:41 +03:00
|
|
|
setMouseTracking(true);
|
2017-04-19 23:25:48 +03:00
|
|
|
initControls();
|
|
|
|
initLayout();
|
2017-04-25 23:36:04 +03:00
|
|
|
showAndActivate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::showAndActivate() {
|
2017-04-19 23:25:48 +03:00
|
|
|
show();
|
2017-04-25 23:36:04 +03:00
|
|
|
raise();
|
|
|
|
setWindowState(windowState() | Qt::WindowActive);
|
|
|
|
activateWindow();
|
|
|
|
setFocus();
|
|
|
|
}
|
|
|
|
|
2017-05-04 15:29:32 +03:00
|
|
|
void Panel::replaceCall(gsl::not_null<Call*> call) {
|
|
|
|
_call = call;
|
|
|
|
_user = call->user();
|
|
|
|
reinitControls();
|
|
|
|
updateControlsGeometry();
|
|
|
|
}
|
|
|
|
|
2017-04-25 23:36:04 +03:00
|
|
|
bool Panel::event(QEvent *e) {
|
|
|
|
if (e->type() == QEvent::WindowDeactivate) {
|
|
|
|
if (_call && _call->state() == State::Established) {
|
|
|
|
hideDeactivated();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TWidget::event(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::hideDeactivated() {
|
|
|
|
hide();
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::initControls() {
|
2017-04-25 19:45:41 +03:00
|
|
|
_mute->setClickedCallback([this] {
|
|
|
|
_call->setMute(!_call->isMute());
|
|
|
|
});
|
|
|
|
subscribe(_call->muteChanged(), [this](bool mute) {
|
|
|
|
_mute->setIconOverride(mute ? &st::callUnmuteIcon : nullptr);
|
|
|
|
});
|
|
|
|
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::NameChanged, [this](const Notify::PeerUpdate &update) {
|
|
|
|
if (!_call || update.peer != _call->user()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_name->setText(App::peerName(_call->user()));
|
|
|
|
updateControlsGeometry();
|
|
|
|
}));
|
|
|
|
_updateDurationTimer.setCallback([this] {
|
|
|
|
if (_call) {
|
|
|
|
updateStatusText(_call->state());
|
|
|
|
}
|
|
|
|
});
|
2017-05-04 15:29:32 +03:00
|
|
|
|
|
|
|
reinitControls();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::reinitControls() {
|
|
|
|
unsubscribe(_stateChangedSubscription);
|
|
|
|
_stateChangedSubscription = subscribe(_call->stateChanged(), [this](State state) { stateChanged(state); });
|
|
|
|
stateChanged(_call->state());
|
|
|
|
|
|
|
|
_name->setText(App::peerName(_call->user()));
|
|
|
|
updateStatusText(_call->state());
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
|
|
|
|
2017-04-29 21:00:27 +03:00
|
|
|
void Panel::refreshCallbacks() {
|
|
|
|
auto safeSetCallback = [this](auto &&button, auto &&callback) {
|
|
|
|
if (button) {
|
|
|
|
button->setClickedCallback([this, callback] {
|
|
|
|
if (_call) {
|
|
|
|
callback(_call.get());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
safeSetCallback(_answer, [](gsl::not_null<Call*> call) { call->answer(); });
|
|
|
|
safeSetCallback(_redial, [](gsl::not_null<Call*> call) { call->redial(); });
|
|
|
|
safeSetCallback(_hangup, [](gsl::not_null<Call*> call) { call->hangup(); });
|
|
|
|
safeSetCallback(_cancel, [](gsl::not_null<Call*> call) { call->hangup(); });
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:25:48 +03:00
|
|
|
void Panel::initLayout() {
|
2017-05-02 12:08:08 +03:00
|
|
|
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Dialog);
|
2017-04-19 23:25:48 +03:00
|
|
|
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
|
|
|
|
setAttribute(Qt::WA_NoSystemBackground, true);
|
|
|
|
setAttribute(Qt::WA_TranslucentBackground, true);
|
|
|
|
|
|
|
|
initGeometry();
|
|
|
|
|
|
|
|
processUserPhoto();
|
|
|
|
subscribe(AuthSession::Current().api().fullPeerUpdated(), [this](PeerData *peer) {
|
|
|
|
if (peer == _user) {
|
|
|
|
processUserPhoto();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] {
|
|
|
|
refreshUserPhoto();
|
|
|
|
});
|
|
|
|
createDefaultCacheImage();
|
2017-05-02 10:25:20 +03:00
|
|
|
|
|
|
|
Platform::InitOnTopPanel(this);
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::processUserPhoto() {
|
|
|
|
if (!_user->userpicLoaded()) {
|
|
|
|
_user->loadUserpic(true);
|
|
|
|
}
|
|
|
|
auto photo = (_user->photoId && _user->photoId != UnknownPeerPhotoId) ? App::photo(_user->photoId) : nullptr;
|
|
|
|
if (isGoodUserPhoto(photo)) {
|
|
|
|
photo->full->load(true);
|
|
|
|
} else {
|
|
|
|
if ((_user->photoId == UnknownPeerPhotoId) || (_user->photoId && (!photo || !photo->date))) {
|
|
|
|
App::api()->requestFullPeer(_user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
refreshUserPhoto();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::refreshUserPhoto() {
|
|
|
|
auto photo = (_user->photoId && _user->photoId != UnknownPeerPhotoId) ? App::photo(_user->photoId) : nullptr;
|
|
|
|
if (isGoodUserPhoto(photo) && photo->full->loaded() && (photo->id != _userPhotoId || !_userPhotoFull)) {
|
|
|
|
_userPhotoId = photo->id;
|
|
|
|
_userPhotoFull = true;
|
|
|
|
createUserpicCache(photo->full);
|
|
|
|
} else if (_userPhoto.isNull()) {
|
|
|
|
if (auto userpic = _user->currentUserpic()) {
|
|
|
|
createUserpicCache(userpic);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::createUserpicCache(ImagePtr image) {
|
|
|
|
auto size = st::callWidth * cIntRetinaFactor();
|
2017-05-02 10:25:20 +03:00
|
|
|
auto options = _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None;
|
2017-04-19 23:25:48 +03:00
|
|
|
auto width = image->width();
|
|
|
|
auto height = image->height();
|
|
|
|
if (width > height) {
|
|
|
|
width = qMax((width * size) / height, 1);
|
|
|
|
height = size;
|
|
|
|
} else {
|
|
|
|
height = qMax((height * size) / width, 1);
|
|
|
|
width = size;
|
|
|
|
}
|
2017-05-02 10:25:20 +03:00
|
|
|
_userPhoto = image->pixNoCache(width, height, options, st::callWidth, st::callWidth);
|
2017-04-19 23:25:48 +03:00
|
|
|
if (cRetina()) _userPhoto.setDevicePixelRatio(cRetinaFactor());
|
|
|
|
|
|
|
|
refreshCacheImageUserPhoto();
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Panel::isGoodUserPhoto(PhotoData *photo) {
|
|
|
|
if (!photo || !photo->date) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto badAspect = [](int a, int b) {
|
|
|
|
return a > 10 * b;
|
|
|
|
};
|
|
|
|
auto width = photo->full->width();
|
|
|
|
auto height = photo->full->height();
|
|
|
|
return !badAspect(width, height) && !badAspect(height, width);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::initGeometry() {
|
|
|
|
auto center = Messenger::Instance().getPointForCallPanelCenter();
|
2017-05-02 12:08:08 +03:00
|
|
|
_useTransparency = Platform::TranslucentWindowsSupported(center);
|
|
|
|
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
|
|
|
|
_padding = _useTransparency ? st::callShadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth);
|
2017-04-19 23:25:48 +03:00
|
|
|
_contentTop = _padding.top() + st::callWidth;
|
|
|
|
auto screen = QApplication::desktop()->screenGeometry(center);
|
|
|
|
auto rect = QRect(0, 0, st::callWidth, st::callHeight);
|
|
|
|
setGeometry(rect.translated(center - rect.center()).marginsAdded(_padding));
|
|
|
|
createBottomImage();
|
2017-04-25 19:45:41 +03:00
|
|
|
updateControlsGeometry();
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::createBottomImage() {
|
|
|
|
if (!_useTransparency) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto bottomWidth = width();
|
|
|
|
auto bottomHeight = height() - _padding.top() - st::callWidth;
|
|
|
|
auto image = QImage(QSize(bottomWidth, bottomHeight) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
|
|
|
image.fill(Qt::transparent);
|
|
|
|
{
|
|
|
|
Painter p(&image);
|
|
|
|
Ui::Shadow::paint(p, QRect(_padding.left(), 0, st::callWidth, bottomHeight - _padding.bottom()), width(), st::callShadow, Ui::Shadow::Side::Left | Ui::Shadow::Side::Right | Ui::Shadow::Side::Bottom);
|
|
|
|
p.setCompositionMode(QPainter::CompositionMode_Source);
|
|
|
|
p.setBrush(st::callBg);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.drawRoundedRect(myrtlrect(_padding.left(), -st::historyMessageRadius, st::callWidth, bottomHeight - _padding.bottom() + st::historyMessageRadius), st::historyMessageRadius, st::historyMessageRadius);
|
|
|
|
}
|
|
|
|
_bottomCache = App::pixmapFromImageInPlace(std::move(image));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::createDefaultCacheImage() {
|
|
|
|
if (!_useTransparency || !_cache.isNull()) {
|
|
|
|
return;
|
|
|
|
}
|
2017-05-02 10:25:20 +03:00
|
|
|
auto cache = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
|
|
|
cache.setDevicePixelRatio(cRetinaFactor());
|
2017-04-19 23:25:48 +03:00
|
|
|
cache.fill(Qt::transparent);
|
|
|
|
{
|
|
|
|
Painter p(&cache);
|
|
|
|
auto inner = rect().marginsRemoved(_padding);
|
|
|
|
Ui::Shadow::paint(p, inner, width(), st::callShadow);
|
|
|
|
p.setCompositionMode(QPainter::CompositionMode_Source);
|
|
|
|
p.setBrush(st::callBg);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.drawRoundedRect(myrtlrect(inner), st::historyMessageRadius, st::historyMessageRadius);
|
|
|
|
}
|
|
|
|
_cache = App::pixmapFromImageInPlace(std::move(cache));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::refreshCacheImageUserPhoto() {
|
2017-05-02 10:25:20 +03:00
|
|
|
auto cache = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
|
|
|
cache.setDevicePixelRatio(cRetinaFactor());
|
2017-04-19 23:25:48 +03:00
|
|
|
cache.fill(Qt::transparent);
|
|
|
|
{
|
|
|
|
Painter p(&cache);
|
|
|
|
Ui::Shadow::paint(p, QRect(_padding.left(), _padding.top(), st::callWidth, st::callWidth), width(), st::callShadow, Ui::Shadow::Side::Top | Ui::Shadow::Side::Left | Ui::Shadow::Side::Right);
|
|
|
|
p.drawPixmapLeft(_padding.left(), _padding.top(), width(), _userPhoto);
|
|
|
|
p.drawPixmapLeft(0, _padding.top() + st::callWidth, width(), _bottomCache);
|
|
|
|
}
|
|
|
|
_cache = App::pixmapFromImageInPlace(std::move(cache));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::resizeEvent(QResizeEvent *e) {
|
2017-04-25 19:45:41 +03:00
|
|
|
updateControlsGeometry();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::updateControlsGeometry() {
|
|
|
|
_name->moveToLeft((width() - _name->width()) / 2, _contentTop + st::callNameTop);
|
|
|
|
updateStatusGeometry();
|
|
|
|
|
2017-04-19 23:25:48 +03:00
|
|
|
auto controlsTop = _contentTop + st::callControlsTop;
|
2017-04-29 21:00:27 +03:00
|
|
|
if (_answer || _redial) {
|
|
|
|
auto bothWidth = (_answer ? _answer : _redial)->width() + st::callControlsSkip + (_hangup ? _hangup : _cancel)->width();
|
|
|
|
if (_hangup) _hangup->moveToLeft((width() - bothWidth) / 2, controlsTop);
|
|
|
|
if (_cancel) _cancel->moveToLeft((width() - bothWidth) / 2, controlsTop);
|
|
|
|
if (_answer) _answer->moveToRight((width() - bothWidth) / 2, controlsTop);
|
|
|
|
if (_redial) _redial->moveToRight((width() - bothWidth) / 2, controlsTop);
|
2017-04-19 23:25:48 +03:00
|
|
|
} else {
|
2017-04-29 21:00:27 +03:00
|
|
|
t_assert(_hangup != nullptr);
|
2017-04-19 23:25:48 +03:00
|
|
|
_hangup->moveToLeft((width() - _hangup->width()) / 2, controlsTop);
|
|
|
|
}
|
|
|
|
_mute->moveToRight(_padding.right() + st::callMuteRight, controlsTop);
|
|
|
|
}
|
|
|
|
|
2017-04-25 19:45:41 +03:00
|
|
|
void Panel::updateStatusGeometry() {
|
|
|
|
_status->moveToLeft((width() - _status->width()) / 2, _contentTop + st::callStatusTop);
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:25:48 +03:00
|
|
|
void Panel::paintEvent(QPaintEvent *e) {
|
|
|
|
Painter p(this);
|
|
|
|
if (_useTransparency) {
|
2017-05-02 12:08:08 +03:00
|
|
|
Platform::StartTranslucentPaint(p, e);
|
2017-04-19 23:25:48 +03:00
|
|
|
p.drawPixmapLeft(0, 0, width(), _cache);
|
|
|
|
} else {
|
2017-05-02 12:08:08 +03:00
|
|
|
p.drawPixmapLeft(_padding.left(), _padding.top(), width(), _userPhoto);
|
|
|
|
auto callBgOpaque = st::callBg->c;
|
|
|
|
callBgOpaque.setAlpha(255);
|
|
|
|
auto brush = QBrush(callBgOpaque);
|
|
|
|
p.fillRect(0, 0, width(), _padding.top(), brush);
|
|
|
|
p.fillRect(myrtlrect(0, _padding.top(), _padding.left(), _contentTop - _padding.top()), brush);
|
|
|
|
p.fillRect(myrtlrect(width() - _padding.right(), _padding.top(), _padding.right(), _contentTop - _padding.top()), brush);
|
|
|
|
p.fillRect(0, _contentTop, width(), height() - _contentTop, brush);
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
2017-04-25 19:45:41 +03:00
|
|
|
|
|
|
|
if (!_fingerprint.empty()) {
|
2017-04-29 21:41:41 +03:00
|
|
|
App::roundRect(p, _fingerprintArea, st::callFingerprintBg, ImageRoundRadius::Small);
|
|
|
|
|
2017-04-25 19:45:41 +03:00
|
|
|
auto realSize = Ui::Emoji::Size(Ui::Emoji::Index() + 1);
|
|
|
|
auto size = realSize / cIntRetinaFactor();
|
2017-04-29 21:41:41 +03:00
|
|
|
auto left = _fingerprintArea.left() + st::callFingerprintPadding.left();
|
|
|
|
auto top = _fingerprintArea.top() + st::callFingerprintPadding.top();
|
2017-04-25 19:45:41 +03:00
|
|
|
for (auto emoji : _fingerprint) {
|
|
|
|
p.drawPixmap(QPoint(left, top), App::emojiLarge(), QRect(emoji->x() * realSize, emoji->y() * realSize, realSize, realSize));
|
|
|
|
left += st::callFingerprintSkip + size;
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::mousePressEvent(QMouseEvent *e) {
|
|
|
|
auto dragArea = myrtlrect(_padding.left(), _padding.top(), st::callWidth, st::callWidth);
|
2017-05-02 12:08:08 +03:00
|
|
|
if (e->button() == Qt::LeftButton) {
|
|
|
|
if (dragArea.contains(e->pos())) {
|
|
|
|
_dragging = true;
|
|
|
|
_dragStartMousePosition = e->globalPos();
|
|
|
|
_dragStartMyPosition = QPoint(x(), y());
|
|
|
|
} else if (!rect().contains(e->pos())) {
|
|
|
|
if (_call && _call->state() == State::Established) {
|
|
|
|
hideDeactivated();
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::mouseMoveEvent(QMouseEvent *e) {
|
|
|
|
if (_dragging) {
|
2017-04-29 21:41:41 +03:00
|
|
|
Ui::Tooltip::Hide();
|
2017-04-19 23:25:48 +03:00
|
|
|
if (!(e->buttons() & Qt::LeftButton)) {
|
|
|
|
_dragging = false;
|
|
|
|
} else {
|
|
|
|
move(_dragStartMyPosition + (e->globalPos() - _dragStartMousePosition));
|
|
|
|
}
|
2017-04-29 21:41:41 +03:00
|
|
|
} else if (_fingerprintArea.contains(e->pos())) {
|
|
|
|
Ui::Tooltip::Show(kTooltipShowTimeoutMs, this);
|
|
|
|
} else {
|
|
|
|
Ui::Tooltip::Hide();
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::mouseReleaseEvent(QMouseEvent *e) {
|
|
|
|
if (e->button() == Qt::LeftButton) {
|
|
|
|
_dragging = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-29 21:41:41 +03:00
|
|
|
void Panel::leaveEventHook(QEvent *e) {
|
|
|
|
Ui::Tooltip::Hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::leaveToChildEvent(QEvent *e, QWidget *child) {
|
|
|
|
Ui::Tooltip::Hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString Panel::tooltipText() const {
|
|
|
|
return lng_call_fingerprint_tooltip(lt_user, App::peerName(_user));
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint Panel::tooltipPos() const {
|
|
|
|
return QCursor::pos();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Panel::tooltipWindowActive() const {
|
|
|
|
return !isHidden();
|
|
|
|
}
|
|
|
|
|
2017-04-25 19:45:41 +03:00
|
|
|
void Panel::stateChanged(State state) {
|
|
|
|
updateStatusText(state);
|
2017-04-29 21:00:27 +03:00
|
|
|
|
|
|
|
auto buttonsUpdated = false;
|
|
|
|
auto syncButton = [this, &buttonsUpdated](auto &&button, bool exists, auto &&style) {
|
|
|
|
if (exists == (button != nullptr)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (exists) {
|
|
|
|
button.create(this, style);
|
|
|
|
button->show();
|
|
|
|
} else {
|
|
|
|
button.destroy();
|
|
|
|
}
|
|
|
|
buttonsUpdated = true;
|
|
|
|
};
|
2017-05-04 15:29:32 +03:00
|
|
|
if (_call) {
|
|
|
|
syncButton(_answer, (_call->type() == Call::Type::Incoming) && ((state == State::Starting) || (state == State::WaitingIncoming)), st::callAnswer);
|
|
|
|
syncButton(_hangup, (state != State::Busy), st::callHangup);
|
|
|
|
syncButton(_redial, (state == State::Busy), st::callAnswer);
|
|
|
|
syncButton(_cancel, (state == State::Busy), st::callCancel);
|
|
|
|
}
|
2017-04-29 21:00:27 +03:00
|
|
|
if (buttonsUpdated) {
|
|
|
|
refreshCallbacks();
|
2017-04-25 19:45:41 +03:00
|
|
|
updateControlsGeometry();
|
|
|
|
}
|
2017-04-29 21:00:27 +03:00
|
|
|
|
2017-04-25 19:45:41 +03:00
|
|
|
if (_fingerprint.empty() && _call && _call->isKeyShaForFingerprintReady()) {
|
2017-04-29 21:41:41 +03:00
|
|
|
fillFingerprint();
|
2017-04-25 19:45:41 +03:00
|
|
|
}
|
2017-05-02 12:08:08 +03:00
|
|
|
if ((state == State::Starting) || (state == State::WaitingIncoming)) {
|
|
|
|
Platform::ReInitOnTopPanel(this);
|
|
|
|
} else {
|
|
|
|
Platform::DeInitOnTopPanel(this);
|
|
|
|
}
|
|
|
|
if (state == State::Established) {
|
|
|
|
if (!isActiveWindow()) {
|
|
|
|
hideDeactivated();
|
|
|
|
}
|
2017-04-25 23:36:04 +03:00
|
|
|
}
|
2017-04-25 19:45:41 +03:00
|
|
|
}
|
|
|
|
|
2017-04-29 21:41:41 +03:00
|
|
|
void Panel::fillFingerprint() {
|
|
|
|
_fingerprint = ComputeEmojiFingerprint(_call.get());
|
|
|
|
|
|
|
|
auto realSize = Ui::Emoji::Size(Ui::Emoji::Index() + 1);
|
|
|
|
auto size = realSize / cIntRetinaFactor();
|
|
|
|
auto count = _fingerprint.size();
|
|
|
|
auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip;
|
|
|
|
auto rectHeight = size;
|
|
|
|
auto left = (width() - rectWidth) / 2;
|
|
|
|
auto top = _contentTop - st::callFingerprintBottom - st::callFingerprintPadding.bottom() - size;
|
|
|
|
_fingerprintArea = QRect(left, top, rectWidth, rectHeight).marginsAdded(st::callFingerprintPadding);
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2017-04-25 19:45:41 +03:00
|
|
|
void Panel::updateStatusText(State state) {
|
|
|
|
auto statusText = [this, state]() -> QString {
|
|
|
|
switch (state) {
|
2017-04-25 23:36:04 +03:00
|
|
|
case State::Starting:
|
2017-04-25 19:45:41 +03:00
|
|
|
case State::WaitingInit:
|
|
|
|
case State::WaitingInitAck: return lang(lng_call_status_connecting);
|
|
|
|
case State::Established: {
|
|
|
|
if (_call) {
|
|
|
|
auto durationMs = _call->getDurationMs();
|
|
|
|
auto durationSeconds = durationMs / 1000;
|
|
|
|
startDurationUpdateTimer(durationMs);
|
|
|
|
return formatDurationText(durationSeconds);
|
|
|
|
}
|
|
|
|
return lang(lng_call_status_ended);
|
|
|
|
} break;
|
|
|
|
case State::Failed: return lang(lng_call_status_failed);
|
|
|
|
case State::HangingUp: return lang(lng_call_status_hanging);
|
|
|
|
case State::Ended: return lang(lng_call_status_ended);
|
|
|
|
case State::ExchangingKeys: return lang(lng_call_status_exchanging);
|
|
|
|
case State::Waiting: return lang(lng_call_status_waiting);
|
|
|
|
case State::Requesting: return lang(lng_call_status_requesting);
|
|
|
|
case State::WaitingIncoming: return lang(lng_call_status_incoming);
|
|
|
|
case State::Ringing: return lang(lng_call_status_ringing);
|
|
|
|
case State::Busy: return lang(lng_call_status_busy);
|
|
|
|
}
|
|
|
|
Unexpected("State in stateChanged()");
|
|
|
|
};
|
|
|
|
_status->setText(statusText());
|
|
|
|
updateStatusGeometry();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::startDurationUpdateTimer(TimeMs currentDuration) {
|
|
|
|
auto msTillNextSecond = 1000 - (currentDuration % 1000);
|
|
|
|
_updateDurationTimer.callOnce(msTillNextSecond + 5);
|
2017-04-19 23:25:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Calls
|