diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 347d39395c..379b761abf 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -57,6 +57,7 @@ callAnswer: CallButton { } } bg: callAnswerBg; + angle: 135.; } callHangup: CallButton { button: IconButton(callButton) { @@ -188,4 +189,4 @@ callRatingCommentTop: 2px; callDebugLabel: FlatLabel(defaultFlatLabel) { margin: margins(24px, 0px, 24px, 0px); } -callPanelDuration: 200; +callPanelDuration: 150; diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index c8619d30fa..86cbedbd87 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -25,8 +25,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #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 "ui/effects/ripple_animation.h" +#include "ui/effects/widget_fade_wrap.h" #include "messenger.h" #include "lang.h" #include "auth_session.h" @@ -44,7 +45,9 @@ constexpr auto kTooltipShowTimeoutMs = 1000; class Panel::Button : public Ui::RippleButton { public: - Button(QWidget *parent, const style::CallButton &st); + Button(QWidget *parent, const style::CallButton &stFrom, const style::CallButton *stTo = nullptr); + + void setProgress(float64 progress); protected: void paintEvent(QPaintEvent *e) override; @@ -55,35 +58,132 @@ protected: QPoint prepareRippleStartPosition() const override; private: - const style::CallButton &_st; - QPixmap _bg; + QPoint iconPosition(gsl::not_null st) const; + void mixIconMasks(); + + gsl::not_null _stFrom; + const style::CallButton *_stTo = nullptr; + float64 _progress = 0.; + + QImage _bgMask, _bg; + QPixmap _bgFrom, _bgTo; + QImage _iconMixedMask, _iconFrom, _iconTo, _iconMixed; }; -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)); +Panel::Button::Button(QWidget *parent, const style::CallButton &stFrom, const style::CallButton *stTo) : Ui::RippleButton(parent, stFrom.button.ripple) +, _stFrom(&stFrom) +, _stTo(stTo) { + resize(_stFrom->button.width, _stFrom->button.height); + + _bgMask = prepareRippleMask(); + _bgFrom = App::pixmapFromImageInPlace(style::colorizeImage(_bgMask, _stFrom->bg)); + if (_stTo) { + t_assert(_stFrom->button.width == _stTo->button.width); + t_assert(_stFrom->button.height == _stTo->button.height); + t_assert(_stFrom->button.rippleAreaPosition == _stTo->button.rippleAreaPosition); + t_assert(_stFrom->button.rippleAreaSize == _stTo->button.rippleAreaSize); + + _bg = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied); + _bg.setDevicePixelRatio(cRetinaFactor()); + _bgTo = App::pixmapFromImageInPlace(style::colorizeImage(_bgMask, _stTo->bg)); + _iconMixedMask = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied); + _iconMixedMask.setDevicePixelRatio(cRetinaFactor()); + _iconFrom = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied); + _iconFrom.setDevicePixelRatio(cRetinaFactor()); + _iconFrom.fill(Qt::black); + { + Painter p(&_iconFrom); + p.drawImage((_stFrom->button.rippleAreaSize - _stFrom->button.icon.width()) / 2, (_stFrom->button.rippleAreaSize - _stFrom->button.icon.height()) / 2, _stFrom->button.icon.instance(Qt::white)); + } + _iconTo = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied); + _iconTo.setDevicePixelRatio(cRetinaFactor()); + _iconTo.fill(Qt::black); + { + Painter p(&_iconTo); + p.drawImage((_stTo->button.rippleAreaSize - _stTo->button.icon.width()) / 2, (_stTo->button.rippleAreaSize - _stTo->button.icon.height()) / 2, _stTo->button.icon.instance(Qt::white)); + } + _iconMixed = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied); + _iconMixed.setDevicePixelRatio(cRetinaFactor()); + } +} + +void Panel::Button::setProgress(float64 progress) { + _progress = progress; + update(); } void Panel::Button::paintEvent(QPaintEvent *e) { Painter p(this); - p.drawPixmap(myrtlpoint(_st.button.rippleAreaPosition), _bg); + auto paintFrom = (_progress == 0.) || !_stTo; + auto paintTo = !paintFrom && (_progress == 1.); + auto bgPosition = myrtlpoint(_stFrom->button.rippleAreaPosition); + if (paintFrom) { + p.drawPixmap(bgPosition, _bgFrom); + } else if (paintTo) { + p.drawPixmap(bgPosition, _bgTo); + } else { + style::colorizeImage(_bgMask, anim::color(_stFrom->bg, _stTo->bg, _progress), &_bg); + p.drawImage(bgPosition, _bg); + } auto ms = getms(); - paintRipple(p, _st.button.rippleAreaPosition.x(), _st.button.rippleAreaPosition.y(), ms); + auto rippleColorInterpolated = QColor(); + auto rippleColorOverride = &rippleColorInterpolated; + if (paintFrom) { + rippleColorOverride = nullptr; + } else if (paintTo) { + rippleColorOverride = &_stTo->button.ripple.color->c; + } else { + rippleColorInterpolated = anim::color(_stFrom->button.ripple.color, _stTo->button.ripple.color, _progress); + } + paintRipple(p, _stFrom->button.rippleAreaPosition.x(), _stFrom->button.rippleAreaPosition.y(), ms, rippleColorOverride); - auto down = isDown(); - auto position = _st.button.iconPosition; - if (position.x() < 0) { - position.setX((width() - _st.button.icon.width()) / 2); + auto positionFrom = iconPosition(_stFrom); + if (paintFrom) { + _stFrom->button.icon.paint(p, positionFrom, width()); + } else { + auto positionTo = iconPosition(_stTo); + if (paintTo) { + _stTo->button.icon.paint(p, positionTo, width()); + } else { + mixIconMasks(); + style::colorizeImage(_iconMixedMask, st::callIconFg->c, &_iconMixed); + p.drawImage(myrtlpoint(_stFrom->button.rippleAreaPosition), _iconMixed); + } } - if (position.y() < 0) { - position.setY((height() - _st.button.icon.height()) / 2); +} + +QPoint Panel::Button::iconPosition(gsl::not_null st) const { + auto result = st->button.iconPosition; + if (result.x() < 0) { + result.setX((width() - st->button.icon.width()) / 2); } - _st.button.icon.paint(p, position, width()); + if (result.y() < 0) { + result.setY((height() - st->button.icon.height()) / 2); + } + return result; +} + +void Panel::Button::mixIconMasks() { + _iconMixedMask.fill(Qt::black); + + Painter p(&_iconMixedMask); + PainterHighQualityEnabler hq(p); + auto paintIconMask = [this, &p](const QImage &mask, float64 angle) { + auto skipFrom = _stFrom->button.rippleAreaSize / 2; + p.translate(skipFrom, skipFrom); + p.rotate(angle); + p.translate(-skipFrom, -skipFrom); + p.drawImage(0, 0, mask); + }; + p.save(); + paintIconMask(_iconFrom, (_stFrom->angle - _stTo->angle) * _progress); + p.restore(); + p.setOpacity(_progress); + paintIconMask(_iconTo, (_stTo->angle - _stFrom->angle) * (1. - _progress)); } void Panel::Button::onStateChanged(State was, StateChangeSource source) { @@ -97,16 +197,19 @@ void Panel::Button::onStateChanged(State was, StateChangeSource source) { } QPoint Panel::Button::prepareRippleStartPosition() const { - return mapFromGlobal(QCursor::pos()) - _st.button.rippleAreaPosition; + return mapFromGlobal(QCursor::pos()) - _stFrom->button.rippleAreaPosition; } QImage Panel::Button::prepareRippleMask() const { - return Ui::RippleAnimation::ellipseMask(QSize(_st.button.rippleAreaSize, _st.button.rippleAreaSize)); + return Ui::RippleAnimation::ellipseMask(QSize(_stFrom->button.rippleAreaSize, _stFrom->button.rippleAreaSize)); } Panel::Panel(gsl::not_null call) : _call(call) , _user(call->user()) +, _answerHangupRedial(this, st::callAnswer, &st::callHangup) +, _decline(this, object_ptr