Use Ui::Window for the Calls::Panel.

This commit is contained in:
John Preston 2020-08-12 17:35:31 +04:00
parent 79feb0c6d9
commit 38b9111bf5
8 changed files with 230 additions and 342 deletions

View file

@ -259,3 +259,80 @@ callBarSignalBars: CallSignalBars(callPanelSignalBars) {
} }
callSignalMargin: 8px; callSignalMargin: 8px;
callSignalPadding: 4px; callSignalPadding: 4px;
callTitle: WindowTitle(defaultWindowTitle) {
bg: callBgOpaque;
bgActive: callBgOpaque;
fg: transparent;
fgActive: transparent;
minimize: IconButton(windowTitleButton) {
icon: icon {
{ size(24px, 21px), callBgOpaque },
{ "title_button_minimize", callStatusFg, point(4px, 4px) },
};
iconOver: icon {
{ size(24px, 21px), callMuteRipple },
{ "title_button_minimize", callStatusFg, point(4px, 4px) },
};
}
minimizeIconActive: icon {
{ size(24px, 21px), callBgOpaque },
{ "title_button_minimize", callStatusFg, point(4px, 4px) },
};
minimizeIconActiveOver: icon {
{ size(24px, 21px), callMuteRipple },
{ "title_button_minimize", callStatusFg, point(4px, 4px) },
};
maximize: IconButton(windowTitleButton) {
icon: icon {
{ size(24px, 21px), callBgOpaque },
{ "title_button_maximize", callStatusFg, point(4px, 4px) },
};
iconOver: icon {
{ size(24px, 21px), callMuteRipple },
{ "title_button_maximize", callStatusFg, point(4px, 4px) },
};
}
maximizeIconActive: icon {
{ size(24px, 21px), callBgOpaque },
{ "title_button_maximize", callStatusFg, point(4px, 4px) },
};
maximizeIconActiveOver: icon {
{ size(24px, 21px), callMuteRipple },
{ "title_button_maximize", callStatusFg, point(4px, 4px) },
};
restoreIcon: icon {
{ size(24px, 21px), callBgOpaque },
{ "title_button_restore", callStatusFg, point(4px, 4px) },
};
restoreIconOver: icon {
{ size(24px, 21px), callMuteRipple },
{ "title_button_restore", callStatusFg, point(4px, 4px) },
};
restoreIconActive: icon {
{ size(24px, 21px), callBgOpaque },
{ "title_button_restore", callStatusFg, point(4px, 4px) },
};
restoreIconActiveOver: icon {
{ size(24px, 21px), callMuteRipple },
{ "title_button_restore", callStatusFg, point(4px, 4px) },
};
close: IconButton(windowTitleButtonClose) {
icon: icon {
{ size(25px, 21px), callBgOpaque },
{ "title_button_close", callStatusFg, point(5px, 4px) },
};
iconOver: icon {
{ size(25px, 21px), titleButtonCloseBgOver },
{ "title_button_close", titleButtonCloseFgOver, point(5px, 4px) },
};
}
closeIconActive: icon {
{ size(25px, 21px), callBgOpaque },
{ "title_button_close", callStatusFg, point(5px, 4px) },
};
closeIconActiveOver: icon {
{ size(25px, 21px), titleButtonCloseBgActiveOver },
{ "title_button_close", titleButtonCloseFgActiveOver, point(5px, 4px) },
};
}

View file

@ -35,13 +35,7 @@ constexpr auto kServerConfigUpdateTimeoutMs = 24 * 3600 * crl::time(1000);
Instance::Instance() = default; Instance::Instance() = default;
Instance::~Instance() { Instance::~Instance() = default;
for (const auto panel : _pendingPanels) {
if (panel) {
delete panel;
}
}
}
void Instance::startOutgoingCall(not_null<UserData*> user, bool video) { void Instance::startOutgoingCall(not_null<UserData*> user, bool video) {
if (alreadyInCall()) { // Already in a call. if (alreadyInCall()) { // Already in a call.
@ -120,14 +114,10 @@ void Instance::destroyCall(not_null<Call*> call) {
} }
void Instance::destroyCurrentPanel() { void Instance::destroyCurrentPanel() {
_pendingPanels.erase( _currentCallPanel->hideBeforeDestroy();
std::remove_if(
_pendingPanels.begin(), // Always queue the destruction.
_pendingPanels.end(), crl::on_main([panel = std::move(_currentCallPanel)]{});
[](auto &&panel) { return !panel; }),
_pendingPanels.end());
_pendingPanels.emplace_back(_currentCallPanel.release());
_pendingPanels.back()->hideAndDestroy(); // Always queues the destruction.
} }
void Instance::createCall(not_null<UserData*> user, Call::Type type, bool video) { void Instance::createCall(not_null<UserData*> user, Call::Type type, bool video) {

View file

@ -85,7 +85,6 @@ private:
std::unique_ptr<Panel> _currentCallPanel; std::unique_ptr<Panel> _currentCallPanel;
base::Observable<Call*> _currentCallChanged; base::Observable<Call*> _currentCallChanged;
base::Observable<FullMsgId> _newServiceMessage; base::Observable<FullMsgId> _newServiceMessage;
std::vector<QPointer<Panel>> _pendingPanels;
std::unique_ptr<Media::Audio::Track> _callConnectingTrack; std::unique_ptr<Media::Audio::Track> _callConnectingTrack;
std::unique_ptr<Media::Audio::Track> _callEndedTrack; std::unique_ptr<Media::Audio::Track> _callEndedTrack;

View file

@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/widgets/window.h"
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/wrap/fade_wrap.h" #include "ui/wrap/fade_wrap.h"
@ -50,7 +51,7 @@ constexpr auto kTooltipShowTimeoutMs = 1000;
} // namespace } // namespace
class Panel::Button : public Ui::RippleButton { class Panel::Button final : public Ui::RippleButton {
public: public:
Button(QWidget *parent, const style::CallButton &stFrom, const style::CallButton *stTo = nullptr); Button(QWidget *parent, const style::CallButton &stFrom, const style::CallButton *stTo = nullptr);
@ -86,7 +87,8 @@ private:
}; };
Panel::Button::Button(QWidget *parent, const style::CallButton &stFrom, const style::CallButton *stTo) : Ui::RippleButton(parent, stFrom.button.ripple) Panel::Button::Button(QWidget *parent, const style::CallButton &stFrom, const style::CallButton *stTo)
: Ui::RippleButton(parent, stFrom.button.ripple)
, _stFrom(&stFrom) , _stFrom(&stFrom)
, _stTo(stTo) { , _stTo(stTo) {
resize(_stFrom->button.width, _stFrom->button.height); resize(_stFrom->button.width, _stFrom->button.height);
@ -255,22 +257,22 @@ QImage Panel::Button::prepareRippleMask() const {
} }
Panel::Panel(not_null<Call*> call) Panel::Panel(not_null<Call*> call)
: RpWidget(Core::App().getModalParent()) : _call(call)
, _call(call)
, _user(call->user()) , _user(call->user())
, _window(std::make_unique<Ui::Window>(Core::App().getModalParent()))
, _bodySt(&st::callBodyLayout) , _bodySt(&st::callBodyLayout)
, _answerHangupRedial(this, st::callAnswer, &st::callHangup) , _answerHangupRedial(widget(), st::callAnswer, &st::callHangup)
, _decline(this, object_ptr<Button>(this, st::callHangup)) , _decline(widget(), object_ptr<Button>(widget(), st::callHangup))
, _cancel(this, object_ptr<Button>(this, st::callCancel)) , _cancel(widget(), object_ptr<Button>(widget(), st::callCancel))
, _camera(this, st::callCameraToggle) , _camera(widget(), st::callCameraToggle)
, _mute(this, st::callMuteToggle) , _mute(widget(), st::callMuteToggle)
, _name(this, st::callName) , _name(widget(), st::callName)
, _status(this, st::callStatus) { , _status(widget(), st::callStatus) {
_decline->setDuration(st::callPanelDuration); _decline->setDuration(st::callPanelDuration);
_cancel->setDuration(st::callPanelDuration); _cancel->setDuration(st::callPanelDuration);
setMouseTracking(true); initWindow();
setWindowIcon(Window::CreateIcon(&_user->session())); initWidget();
initControls(); initControls();
initLayout(); initLayout();
showAndActivate(); showAndActivate();
@ -279,11 +281,10 @@ Panel::Panel(not_null<Call*> call)
Panel::~Panel() = default; Panel::~Panel() = default;
void Panel::showAndActivate() { void Panel::showAndActivate() {
toggleOpacityAnimation(true); _window->raise();
raise(); _window->setWindowState(_window->windowState() | Qt::WindowActive);
setWindowState(windowState() | Qt::WindowActive); _window->activateWindow();
activateWindow(); _window->setFocus();
setFocus();
} }
void Panel::replaceCall(not_null<Call*> call) { void Panel::replaceCall(not_null<Call*> call) {
@ -291,15 +292,47 @@ void Panel::replaceCall(not_null<Call*> call) {
updateControlsGeometry(); updateControlsGeometry();
} }
bool Panel::eventHook(QEvent *e) { void Panel::initWindow() {
if (e->type() == QEvent::WindowDeactivate) { _window->setWindowIcon(
checkForInactiveHide(); QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
} _window->setTitle(u" "_q);
return RpWidget::eventHook(e);
_window->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Close) {
handleClose();
}
}, _window->lifetime());
_window->setBodyTitleArea([=](QPoint widgetPoint) {
const auto buttonWidth = st::callCancel.button.width;
const auto buttonsWidth = buttonWidth * 4;
return !_fingerprintArea.contains(widgetPoint)
&& !QRect(
(widget()->width() - buttonsWidth) / 2,
_answerHangupRedial->y(),
buttonsWidth,
_answerHangupRedial->height()).contains(widgetPoint)
&& !_outgoingVideoBubble->geometry().contains(widgetPoint);
});
} }
void Panel::hideDeactivated() { void Panel::initWidget() {
toggleOpacityAnimation(false); widget()->setMouseTracking(true);
widget()->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
paint(clip);
}, widget()->lifetime());
widget()->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::MouseMove) {
handleMouseMove(static_cast<QMouseEvent*>(e.get()));
} else if (e->type() == QEvent::Resize) {
updateControlsGeometry();
}
}, widget()->lifetime());
} }
void Panel::initControls() { void Panel::initControls() {
@ -363,9 +396,7 @@ void Panel::setIncomingShown(bool shown) {
return; return;
} }
_incomingShown = shown; _incomingShown = shown;
if (_animationCache.isNull()) { showControls();
showControls();
}
} }
void Panel::reinitWithCall(Call *call) { void Panel::reinitWithCall(Call *call) {
@ -379,18 +410,21 @@ void Panel::reinitWithCall(Call *call) {
_user = _call->user(); _user = _call->user();
_signalBars.create( _signalBars.create(
this, widget(),
_call, _call,
st::callPanelSignalBars, st::callPanelSignalBars,
[=] { rtlupdate(signalBarsRect()); }); [=] { widget()->rtlupdate(signalBarsRect()); });
auto remoteMuted = _call->remoteAudioStateValue( auto remoteMuted = _call->remoteAudioStateValue(
) | rpl::map([=](Call::RemoteAudioState state) { ) | rpl::map([=](Call::RemoteAudioState state) {
return (state == Call::RemoteAudioState::Muted); return (state == Call::RemoteAudioState::Muted);
}); });
_userpic = std::make_unique<Userpic>(this, _user, std::move(remoteMuted)); _userpic = std::make_unique<Userpic>(
widget(),
_user,
std::move(remoteMuted));
_outgoingVideoBubble = std::make_unique<VideoBubble>( _outgoingVideoBubble = std::make_unique<VideoBubble>(
this, widget(),
_call->videoOutgoing()); _call->videoOutgoing());
_call->mutedValue( _call->mutedValue(
@ -413,14 +447,7 @@ void Panel::reinitWithCall(Call *call) {
_call->videoIncoming()->renderNextFrame( _call->videoIncoming()->renderNextFrame(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
setIncomingShown(!_call->videoIncoming()->frame({}).isNull()); setIncomingShown(!_call->videoIncoming()->frame({}).isNull());
update(); widget()->update();
}, _callLifetime);
rpl::merge(
_call->videoIncoming()->stateChanges(),
_call->videoOutgoing()->stateChanges()
) | rpl::start_with_next([=] {
checkForInactiveShow();
}, _callLifetime); }, _callLifetime);
rpl::combine( rpl::combine(
@ -441,11 +468,6 @@ void Panel::reinitWithCall(Call *call) {
} }
void Panel::initLayout() { void Panel::initLayout() {
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::NoDropShadowWindowHint | Qt::Dialog);
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
initGeometry(); initGeometry();
using UpdateFlag = Data::PeerUpdate::Flag; using UpdateFlag = Data::PeerUpdate::Flag;
@ -457,56 +479,13 @@ void Panel::initLayout() {
}) | rpl::start_with_next([=](const Data::PeerUpdate &update) { }) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
_name->setText(_call->user()->name); _name->setText(_call->user()->name);
updateControlsGeometry(); updateControlsGeometry();
}, lifetime()); }, widget()->lifetime());
createDefaultCacheImage();
Ui::Platform::InitOnTopPanel(this);
}
void Panel::toggleOpacityAnimation(bool visible) {
if (!_call || _visible == visible) {
return;
}
_visible = visible;
if (_useTransparency) {
if (_animationCache.isNull()) {
showControls();
_animationCache = Ui::GrabWidget(this);
hideChildren();
}
_opacityAnimation.start(
[this] { update(); },
_visible ? 0. : 1.,
_visible ? 1. : 0.,
st::callPanelDuration,
_visible ? anim::easeOutCirc : anim::easeInCirc);
} else if (!isHidden() && !_visible) {
hide();
}
if (isHidden() && _visible) {
show();
}
}
void Panel::finishAnimating() {
_animationCache = QPixmap();
if (_call) {
if (!_visible) {
hide();
} else {
showControls();
}
} else {
destroyDelayed();
}
} }
void Panel::showControls() { void Panel::showControls() {
Expects(_call != nullptr); Expects(_call != nullptr);
showChildren(); widget()->showChildren();
_decline->setVisible(_decline->toggled()); _decline->setVisible(_decline->toggled());
_cancel->setVisible(_cancel->toggled()); _cancel->setVisible(_cancel->toggled());
_name->setVisible(!_incomingShown); _name->setVisible(!_incomingShown);
@ -514,34 +493,18 @@ void Panel::showControls() {
_userpic->setVisible(!_incomingShown); _userpic->setVisible(!_incomingShown);
} }
void Panel::destroyDelayed() { void Panel::hideBeforeDestroy() {
hide(); _window->hide();
crl::on_main(this, [=] {
delete this;
});
}
void Panel::hideAndDestroy() {
toggleOpacityAnimation(false);
reinitWithCall(nullptr); reinitWithCall(nullptr);
if (_animationCache.isNull()) {
destroyDelayed();
}
} }
void Panel::initGeometry() { void Panel::initGeometry() {
const auto center = Core::App().getPointForCallPanelCenter(); const auto center = Core::App().getPointForCallPanelCenter();
_useTransparency = Ui::Platform::TranslucentWindowsSupported(center); const auto initRect = QRect(0, 0, st::callWidth, st::callHeight);
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); _window->setGeometry(initRect.translated(center - initRect.center()));
_padding = _useTransparency ? st::callShadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); _window->setMinimumSize({ st::callWidthMin, st::callHeightMin });
const auto rect = [&] { _window->setTitleStyle(st::callTitle);
const QRect initRect(0, 0, st::callWidth, st::callHeight); _window->show();
return initRect.translated(center - initRect.center()).marginsAdded(_padding);
}();
setGeometry(rect);
setMinimumSize(rect.size());
setMaximumSize(rect.size());
createBottomImage();
updateControlsGeometry(); updateControlsGeometry();
} }
@ -557,56 +520,32 @@ void Panel::refreshOutgoingPreviewInBody(State state) {
updateControlsGeometry(); updateControlsGeometry();
} }
void Panel::createBottomImage() { void Panel::updateFingerprintGeometry() {
if (!_useTransparency) { auto realSize = Ui::Emoji::GetSizeLarge();
return; auto size = realSize / cIntRetinaFactor();
} auto count = _fingerprint.size();
auto bottomWidth = width(); auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip;
auto bottomHeight = height(); auto rectHeight = size;
auto image = QImage(QSize(bottomWidth, bottomHeight) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); auto left = (widget()->width() - rectWidth) / 2;
const auto inner = rect().marginsRemoved(_padding); _fingerprintArea = QRect(
image.fill(Qt::transparent); left,
{ st::callFingerprintTop + st::callFingerprintPadding.top(),
Painter p(&image); rectWidth,
Ui::Shadow::paint(p, inner, width(), st::callShadow); rectHeight
p.setCompositionMode(QPainter::CompositionMode_Source); ).marginsAdded(st::callFingerprintPadding);
p.setBrush(st::callBg); _fingerprintHeight = st::callFingerprintTop + _fingerprintArea.height() + st::callFingerprintBottom;
p.setPen(Qt::NoPen);
PainterHighQualityEnabler hq(p);
p.drawRoundedRect(inner, st::callRadius, st::callRadius);
}
_bottomCache = App::pixmapFromImageInPlace(std::move(image));
}
void Panel::createDefaultCacheImage() {
if (!_useTransparency || !_cache.isNull()) {
return;
}
auto cache = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cache.setDevicePixelRatio(cRetinaFactor());
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::callRadius, st::callRadius);
}
_cache = App::pixmapFromImageInPlace(std::move(cache));
}
void Panel::resizeEvent(QResizeEvent *e) {
updateControlsGeometry();
} }
void Panel::updateControlsGeometry() { void Panel::updateControlsGeometry() {
const auto innerHeight = height() - _padding.top() - _padding.bottom(); if (widget()->width() < st::callWidthMin
const auto availableTop = _padding.top() + _fingerprintHeight; || widget()->height() < st::callHeightMin) {
const auto available = height() return;
- (st::callBottomControlsHeight + _padding.bottom()) }
updateFingerprintGeometry();
const auto innerHeight = widget()->height();
const auto availableTop = _fingerprintHeight;
const auto available = widget()->height()
- st::callBottomControlsHeight
- availableTop; - availableTop;
const auto bodyPreviewSizeMax = st::callOutgoingPreviewMin const auto bodyPreviewSizeMax = st::callOutgoingPreviewMin
+ ((st::callOutgoingPreview + ((st::callOutgoingPreview
@ -627,12 +566,12 @@ void Panel::updateControlsGeometry() {
const auto previewTop = _bodyTop + _bodySt->height + skipHeight; const auto previewTop = _bodyTop + _bodySt->height + skipHeight;
_userpic->setGeometry( _userpic->setGeometry(
(width() - _bodySt->photoSize) / 2, (widget()->width() - _bodySt->photoSize) / 2,
_bodyTop + _bodySt->photoTop, _bodyTop + _bodySt->photoTop,
_bodySt->photoSize); _bodySt->photoSize);
_name->moveToLeft( _name->moveToLeft(
(width() - _name->width()) / 2, (widget()->width() - _name->width()) / 2,
_bodyTop + _bodySt->nameTop); _bodyTop + _bodySt->nameTop);
updateStatusGeometry(); updateStatusGeometry();
@ -640,7 +579,7 @@ void Panel::updateControlsGeometry() {
_outgoingVideoBubble->updateGeometry( _outgoingVideoBubble->updateGeometry(
VideoBubble::DragMode::None, VideoBubble::DragMode::None,
QRect( QRect(
(width() - bodyPreviewSize.width()) / 2, (widget()->width() - bodyPreviewSize.width()) / 2,
previewTop, previewTop,
bodyPreviewSize.width(), bodyPreviewSize.width(),
bodyPreviewSize.height())); bodyPreviewSize.height()));
@ -649,39 +588,31 @@ void Panel::updateControlsGeometry() {
} }
auto bothWidth = _answerHangupRedial->width() + st::callCancel.button.width; auto bothWidth = _answerHangupRedial->width() + st::callCancel.button.width;
_decline->moveToLeft((width() - bothWidth) / 2, _buttonsTop); _decline->moveToLeft((widget()->width() - bothWidth) / 2, _buttonsTop);
_cancel->moveToLeft((width() - bothWidth) / 2, _buttonsTop); _cancel->moveToLeft((widget()->width() - bothWidth) / 2, _buttonsTop);
updateHangupGeometry(); updateHangupGeometry();
const auto skip = st::callSignalMargin + st::callSignalPadding; const auto skip = st::callSignalMargin + st::callSignalPadding;
const auto delta = (_signalBars->width() - _signalBars->height()); const auto delta = (_signalBars->width() - _signalBars->height());
_signalBars->moveToLeft( _signalBars->moveToLeft(skip, skip + delta / 2);
_padding.left() + skip,
_padding.top() + skip + delta / 2);
} }
void Panel::updateOutgoingVideoBubbleGeometry() { void Panel::updateOutgoingVideoBubbleGeometry() {
Expects(!_outgoingPreviewInBody); Expects(!_outgoingPreviewInBody);
const auto size = st::callOutgoingDefaultSize; const auto size = st::callOutgoingDefaultSize;
const auto availableHeight = height() - st::callBottomControlsHeight;
const auto padding = 2 * _padding;
_outgoingVideoBubble->updateGeometry( _outgoingVideoBubble->updateGeometry(
VideoBubble::DragMode::SnapToCorners, VideoBubble::DragMode::SnapToCorners,
QRect( widget()->rect(),
padding.left(),
padding.top(),
width() - padding.left() - padding.right(),
height() - padding.left() - padding.bottom()),
size); size);
} }
void Panel::updateHangupGeometry() { void Panel::updateHangupGeometry() {
auto singleWidth = _answerHangupRedial->width(); auto singleWidth = _answerHangupRedial->width();
auto bothWidth = singleWidth + st::callCancel.button.width; auto bothWidth = singleWidth + st::callCancel.button.width;
auto rightFrom = (width() - bothWidth) / 2; auto rightFrom = (widget()->width() - bothWidth) / 2;
auto rightTo = (width() - singleWidth) / 2; auto rightTo = (widget()->width() - singleWidth) / 2;
auto hangupProgress = _hangupShownProgress.value(_hangupShown ? 1. : 0.); auto hangupProgress = _hangupShownProgress.value(_hangupShown ? 1. : 0.);
auto hangupRight = anim::interpolate(rightFrom, rightTo, hangupProgress); auto hangupRight = anim::interpolate(rightFrom, rightTo, hangupProgress);
_answerHangupRedial->moveToRight(hangupRight, _buttonsTop); _answerHangupRedial->moveToRight(hangupRight, _buttonsTop);
@ -691,41 +622,21 @@ void Panel::updateHangupGeometry() {
} }
void Panel::updateStatusGeometry() { void Panel::updateStatusGeometry() {
_status->moveToLeft((width() - _status->width()) / 2, _bodyTop + _bodySt->statusTop); _status->moveToLeft(
(widget()->width() - _status->width()) / 2,
_bodyTop + _bodySt->statusTop);
} }
void Panel::paintEvent(QPaintEvent *e) { void Panel::paint(QRect clip) {
Painter p(this); Painter p(widget());
if (!_animationCache.isNull()) {
auto opacity = _opacityAnimation.value(_call ? 1. : 0.);
if (!_opacityAnimation.animating()) {
finishAnimating();
if (!_call || isHidden()) return;
} else {
p.setOpacity(opacity);
PainterHighQualityEnabler hq(p); p.fillRect(clip, st::callBgOpaque);
auto marginRatio = (1. - opacity) / 5;
auto marginWidth = qRound(width() * marginRatio);
auto marginHeight = qRound(height() * marginRatio);
p.drawPixmap(rect().marginsRemoved(QMargins(marginWidth, marginHeight, marginWidth, marginHeight)), _animationCache, QRect(QPoint(0, 0), _animationCache.size()));
return;
}
}
if (_useTransparency) {
p.drawPixmapLeft(0, 0, width(), _cache);
} else {
auto callBgOpaque = st::callBg->c;
callBgOpaque.setAlpha(255);
p.fillRect(rect(), QBrush(callBgOpaque));
}
const auto incomingFrame = _call const auto incomingFrame = _call
? _call->videoIncoming()->frame(Webrtc::FrameRequest()) ? _call->videoIncoming()->frame(Webrtc::FrameRequest())
: QImage(); : QImage();
if (!incomingFrame.isNull()) { if (!incomingFrame.isNull()) {
const auto to = rect().marginsRemoved(_padding); const auto to = widget()->rect();
p.save(); p.save();
p.setClipRect(to); p.setClipRect(to);
const auto big = incomingFrame.size().scaled(to.size(), Qt::KeepAspectRatio); const auto big = incomingFrame.size().scaled(to.size(), Qt::KeepAspectRatio);
@ -759,8 +670,8 @@ void Panel::paintEvent(QPaintEvent *e) {
QRect Panel::signalBarsRect() const { QRect Panel::signalBarsRect() const {
const auto size = 2 * st::callSignalPadding + _signalBars->width(); const auto size = 2 * st::callSignalPadding + _signalBars->width();
return QRect( return QRect(
_padding.left() + st::callSignalMargin, st::callSignalMargin,
_padding.top() + st::callSignalMargin, st::callSignalMargin,
size, size,
size); size);
} }
@ -773,50 +684,22 @@ void Panel::paintSignalBarsBg(Painter &p) {
ImageRoundRadius::Small); ImageRoundRadius::Small);
} }
void Panel::closeEvent(QCloseEvent *e) { void Panel::handleClose() {
if (_call) { if (_call) {
_call->hangup(); _call->hangup();
} }
} }
void Panel::mousePressEvent(QMouseEvent *e) { void Panel::handleMouseMove(not_null<QMouseEvent*> e) {
auto dragArea = myrtlrect(_padding.left(), _padding.top(), st::callWidth, st::callWidth); if (_fingerprintArea.contains(e->pos())) {
if (e->button() == Qt::LeftButton) {
if (dragArea.contains(e->pos())) {
_dragging = true;
_dragStartMousePosition = e->globalPos();
_dragStartMyPosition = QPoint(x(), y());
}
}
}
void Panel::mouseMoveEvent(QMouseEvent *e) {
if (_dragging) {
Ui::Tooltip::Hide();
if (!(e->buttons() & Qt::LeftButton)) {
_dragging = false;
} else {
move(_dragStartMyPosition + (e->globalPos() - _dragStartMousePosition));
}
} else if (_fingerprintArea.contains(e->pos())) {
Ui::Tooltip::Show(kTooltipShowTimeoutMs, this); Ui::Tooltip::Show(kTooltipShowTimeoutMs, this);
} else { } else {
Ui::Tooltip::Hide(); Ui::Tooltip::Hide();
} }
} }
void Panel::mouseReleaseEvent(QMouseEvent *e) { not_null<Ui::RpWidget*> Panel::widget() const {
if (e->button() == Qt::LeftButton) { return _window->body();
_dragging = false;
}
}
void Panel::leaveEventHook(QEvent *e) {
Ui::Tooltip::Hide();
}
void Panel::leaveToChildEvent(QEvent *e, QWidget *child) {
Ui::Tooltip::Hide();
} }
QString Panel::tooltipText() const { QString Panel::tooltipText() const {
@ -828,7 +711,7 @@ QPoint Panel::tooltipPos() const {
} }
bool Panel::tooltipWindowActive() const { bool Panel::tooltipWindowActive() const {
return !isHidden(); return _window->isActiveWindow();
} }
void Panel::stateChanged(State state) { void Panel::stateChanged(State state) {
@ -844,7 +727,7 @@ void Panel::stateChanged(State state) {
auto toggleButton = [this](auto &&button, bool visible) { auto toggleButton = [this](auto &&button, bool visible) {
button->toggle( button->toggle(
visible, visible,
isHidden() _window->isHidden()
? anim::type::instant ? anim::type::instant
: anim::type::normal); : anim::type::normal);
}; };
@ -864,60 +747,14 @@ void Panel::stateChanged(State state) {
fillFingerprint(); fillFingerprint();
} }
} }
if (windowHandle()) {
// First stateChanged() is called before
// the first Platform::InitOnTopPanel(this).
if ((state == State::Starting) || (state == State::WaitingIncoming)) {
Ui::Platform::ReInitOnTopPanel(this);
} else {
Ui::Platform::DeInitOnTopPanel(this);
}
checkForInactiveHide();
}
}
bool Panel::hasActiveVideo() const {
const auto inactive = Webrtc::VideoState::Inactive;
return (_call->videoIncoming()->state() != inactive)
|| (_call->videoOutgoing()->state() != inactive);
}
void Panel::checkForInactiveHide() {
if (!_call
|| (_call->state() != State::Established)
|| isActiveWindow()
|| hasActiveVideo()) {
return;
}
hideDeactivated();
}
void Panel::checkForInactiveShow() {
if (!_visible && hasActiveVideo()) {
toggleOpacityAnimation(true);
}
} }
void Panel::fillFingerprint() { void Panel::fillFingerprint() {
Expects(_call != nullptr); Expects(_call != nullptr);
_fingerprint = ComputeEmojiFingerprint(_call); _fingerprint = ComputeEmojiFingerprint(_call);
auto realSize = Ui::Emoji::GetSizeLarge();
auto size = realSize / cIntRetinaFactor();
auto count = _fingerprint.size();
auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip;
auto rectHeight = size;
auto left = (width() - rectWidth) / 2;
_fingerprintArea = QRect(
left,
_padding.top() + st::callFingerprintTop + st::callFingerprintPadding.top(),
rectWidth,
rectHeight
).marginsAdded(st::callFingerprintPadding);
_fingerprintHeight = st::callFingerprintTop + _fingerprintArea.height() + st::callFingerprintBottom;
updateControlsGeometry(); updateControlsGeometry();
widget()->update();
update();
} }
void Panel::updateStatusText(State state) { void Panel::updateStatusText(State state) {

View file

@ -26,6 +26,7 @@ class IconButton;
class FlatLabel; class FlatLabel;
template <typename Widget> template <typename Widget>
class FadeWrap; class FadeWrap;
class Window;
} // namespace Ui } // namespace Ui
namespace style { namespace style {
@ -39,48 +40,44 @@ class Userpic;
class SignalBars; class SignalBars;
class VideoBubble; class VideoBubble;
class Panel final : public Ui::RpWidget, private Ui::AbstractTooltipShower { class Panel final : private Ui::AbstractTooltipShower {
public: public:
Panel(not_null<Call*> call); Panel(not_null<Call*> call);
~Panel(); ~Panel();
void showAndActivate(); void showAndActivate();
void replaceCall(not_null<Call*> call); void replaceCall(not_null<Call*> call);
void hideAndDestroy(); void hideBeforeDestroy();
protected:
void paintEvent(QPaintEvent *e) override;
void closeEvent(QCloseEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void leaveEventHook(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
bool eventHook(QEvent *e) override;
private: private:
class Content;
class Button; class Button;
using State = Call::State; using State = Call::State;
using Type = Call::Type; using Type = Call::Type;
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
// AbstractTooltipShower interface // AbstractTooltipShower interface
QString tooltipText() const override; QString tooltipText() const override;
QPoint tooltipPos() const override; QPoint tooltipPos() const override;
bool tooltipWindowActive() const override; bool tooltipWindowActive() const override;
void paint(QRect clip);
void initWindow();
void initWidget();
void initControls(); void initControls();
void reinitWithCall(Call *call); void reinitWithCall(Call *call);
void initLayout(); void initLayout();
void initGeometry(); void initGeometry();
void hideDeactivated();
void createBottomImage(); void handleClose();
void createDefaultCacheImage(); void handleMouseMove(not_null<QMouseEvent*> e);
QRect signalBarsRect() const; QRect signalBarsRect() const;
void paintSignalBarsBg(Painter &p); void paintSignalBarsBg(Painter &p);
void updateFingerprintGeometry();
void updateControlsGeometry(); void updateControlsGeometry();
void updateHangupGeometry(); void updateHangupGeometry();
void updateStatusGeometry(); void updateStatusGeometry();
@ -90,26 +87,16 @@ private:
void updateStatusText(State state); void updateStatusText(State state);
void startDurationUpdateTimer(crl::time currentDuration); void startDurationUpdateTimer(crl::time currentDuration);
void fillFingerprint(); void fillFingerprint();
void toggleOpacityAnimation(bool visible);
void finishAnimating();
void destroyDelayed();
void setIncomingShown(bool shown); void setIncomingShown(bool shown);
[[nodiscard]] bool hasActiveVideo() const;
void checkForInactiveHide();
void checkForInactiveShow();
void refreshOutgoingPreviewInBody(State state); void refreshOutgoingPreviewInBody(State state);
Call *_call = nullptr; Call *_call = nullptr;
not_null<UserData*> _user; not_null<UserData*> _user;
bool _useTransparency = true; const std::unique_ptr<Ui::Window> _window;
bool _incomingShown = false;
style::margins _padding;
bool _dragging = false; bool _incomingShown = false;
QPoint _dragStartMousePosition;
QPoint _dragStartMyPosition;
rpl::lifetime _callLifetime; rpl::lifetime _callLifetime;
@ -136,13 +123,6 @@ private:
base::Timer _updateDurationTimer; base::Timer _updateDurationTimer;
base::Timer _updateOuterRippleTimer; base::Timer _updateOuterRippleTimer;
bool _visible = false;
Ui::Animations::Simple _opacityAnimation;
QPixmap _animationCache;
QPixmap _bottomCache;
QPixmap _cache;
}; };
} // namespace Calls } // namespace Calls

View file

@ -80,6 +80,10 @@ void VideoBubble::updateGeometry(
} }
} }
QRect VideoBubble::geometry() const {
return _content.isHidden() ? QRect() : _content.geometry();
}
void VideoBubble::applyBoundingRect(QRect rect) { void VideoBubble::applyBoundingRect(QRect rect) {
_boundingRect = rect; _boundingRect = rect;
_geometryDirty = true; _geometryDirty = true;

View file

@ -31,6 +31,7 @@ public:
QRect boundingRect, QRect boundingRect,
QSize sizeMin = QSize(), QSize sizeMin = QSize(),
QSize sizeMax = QSize()); QSize sizeMax = QSize());
[[nodiscard]] QRect geometry() const;
[[nodiscard]] rpl::lifetime &lifetime() { [[nodiscard]] rpl::lifetime &lifetime() {
return _content.lifetime(); return _content.lifetime();

@ -1 +1 @@
Subproject commit f7bcb15bad3879b448a9b5bd3f9827009151b2f3 Subproject commit 608b25bd32d01e233df0d78ccb5fd97028a82ae9