diff --git a/ui/widgets/call_mute_button.cpp b/ui/widgets/call_mute_button.cpp index 7696a67..857d65b 100644 --- a/ui/widgets/call_mute_button.cpp +++ b/ui/widgets/call_mute_button.cpp @@ -6,14 +6,147 @@ // #include "ui/widgets/call_mute_button.h" +#include "ui/paint/blobs.h" +#include "ui/painter.h" + +#include "styles/palette.h" #include "styles/style_widgets.h" namespace Ui { +namespace { + +constexpr auto kMaxLevel = 1.; + +constexpr auto kLevelDuration = 100. + 500. * 0.33; + +constexpr auto kScaleBig = 0.807 - 0.1; +constexpr auto kScaleSmall = 0.704 - 0.1; + +constexpr auto kScaleBigMin = 0.878; +constexpr auto kScaleSmallMin = 0.926; + +constexpr auto kScaleBigMax = kScaleBigMin + kScaleBig; +constexpr auto kScaleSmallMax = kScaleSmallMin + kScaleSmall; + +constexpr auto kMainRadiusFactor = 50. / 57.; + +constexpr auto kSwitchStateDuration = 120; + +constexpr auto MuteBlobs() -> std::array { + return {{ + { + .segmentsCount = 6, + .minScale = 1., + .minRadius = 57. * kMainRadiusFactor, + .maxRadius = 63. * kMainRadiusFactor, + .speedScale = .4, + .alpha = 1., + }, + { + .segmentsCount = 9, + .minScale = kScaleSmallMin / kScaleSmallMax, + .minRadius = 62 * kScaleSmallMax * kMainRadiusFactor, + .maxRadius = 72 * kScaleSmallMax * kMainRadiusFactor, + .speedScale = 1., + .alpha = (76. / 255.), + }, + { + .segmentsCount = 12, + .minScale = kScaleBigMin / kScaleBigMax, + .minRadius = 65 * kScaleBigMax * kMainRadiusFactor, + .maxRadius = 75 * kScaleBigMax * kMainRadiusFactor, + .speedScale = 1., + .alpha = (76. / 255.), + }, + }}; +} + +} // namespace + +class BlobsWidget final : public RpWidget { +public: + BlobsWidget(not_null parent); + + void setLevel(float level); + void requestPaintProgress(float64 progress); + +private: + void init(); + + Paint::Blobs _blobs; + + int _center = 0; + float64 _progress = 0; + + Animations::Basic _animation; + +}; + +BlobsWidget::BlobsWidget(not_null parent) +: RpWidget(parent) +, _blobs(MuteBlobs() | ranges::to_vector, kLevelDuration, kMaxLevel) { + init(); +} + +void BlobsWidget::init() { + setAttribute(Qt::WA_TransparentForMouseEvents); + + const auto maxBlobDiameter = _blobs.maxRadius() * 2; + resize(maxBlobDiameter, maxBlobDiameter); + + const auto gradient = anim::linear_gradient( + { st::groupCallMuted1->c, st::groupCallMuted2->c }, + { st::groupCallLive1->c, st::groupCallLive2->c }, + QPoint(0, height()), + QPoint(width(), 0)); + + sizeValue( + ) | rpl::start_with_next([=](QSize size) { + _center = size.width() / 2; + }, lifetime()); + + paintRequest( + ) | rpl::start_with_next([=] { + Painter p(this); + + PainterHighQualityEnabler hq(p); + p.translate(_center, _center); + _blobs.paint(p, QBrush(gradient.gradient(_progress))); + }, lifetime()); + + _animation.init([=](crl::time now) { + const auto dt = now - _animation.started(); + update(); + return true; + }); + shownValue( + ) | rpl::start_with_next([=](bool shown) { + if (shown) { + _animation.start(); + } else { + _animation.stop(); + } + }, lifetime()); +} + +void BlobsWidget::setLevel(float level) { + _blobs.setLevel(level); +} + +void BlobsWidget::requestPaintProgress(float64 progress) { + if (progress == _progress) { + return; + } + _progress = progress; + update(); +} + CallMuteButton::CallMuteButton( - not_null parent, + not_null parent, CallMuteButtonState initial) : _state(initial) +, _blobs(base::make_unique_q(parent)) , _content(parent, st::callMuteButtonActive, &st::callMuteButtonMuted) , _connecting(parent, st::callMuteButtonConnecting) { if (_state.type == CallMuteButtonType::Connecting @@ -45,12 +178,25 @@ void CallMuteButton::setState(const CallMuteButtonState &state) { if (_state.text != state.text) { _content.setText(rpl::single(state.text)); } + + const auto isWasActive = (_state.type == CallMuteButtonType::Active); + const auto isActive = (state.type == CallMuteButtonType::Active); + { + const auto from = _switchAnimation.value(isWasActive ? 1. : 0.); + const auto to = isActive ? 1. : 0.; + _switchAnimation.start( + [=](auto value) { _blobs->requestPaintProgress(value); }, + from, + to, + kSwitchStateDuration); + } + _content.setProgress((state.type == CallMuteButtonType::Muted) ? 1. : 0.); if (!_connecting.isHidden() || !_content.isHidden()) { _content.show(); } _connecting.hide(); - if (state.type == CallMuteButtonType::Active) { + if (isActive) { _content.setOuterValue(_level); } else { _content.setOuterValue(0.); @@ -61,6 +207,7 @@ void CallMuteButton::setState(const CallMuteButtonState &state) { void CallMuteButton::setLevel(float level) { _level = level; + _blobs->setLevel(level); if (_state.type == CallMuteButtonType::Active) { _content.setOuterValue(level); } @@ -87,6 +234,13 @@ void CallMuteButton::moveInner(QPoint position) { const auto skip = st::callMuteButtonActive.outerRadius; _content.move(position - QPoint(skip, skip)); _connecting.move(_content.pos()); + + { + const auto offset = QPoint( + (_blobs->width() - _content.width()) / 2, + (_blobs->height() - _content.width()) / 2); + _blobs->move(_content.pos() - offset); + } } void CallMuteButton::setVisible(bool visible) { @@ -102,6 +256,7 @@ void CallMuteButton::setVisible(bool visible) { } void CallMuteButton::raise() { + _blobs->raise(); _content.raise(); _connecting.raise(); } @@ -109,6 +264,7 @@ void CallMuteButton::raise() { void CallMuteButton::lower() { _content.lower(); _connecting.lower(); + _blobs->lower(); } rpl::lifetime &CallMuteButton::lifetime() { diff --git a/ui/widgets/call_mute_button.h b/ui/widgets/call_mute_button.h index 31cc78e..45d1239 100644 --- a/ui/widgets/call_mute_button.h +++ b/ui/widgets/call_mute_button.h @@ -6,10 +6,14 @@ // #pragma once +#include "base/unique_qptr.h" +#include "ui/effects/animations.h" #include "ui/widgets/call_button.h" namespace Ui { +class BlobsWidget; + enum class CallMuteButtonType { Connecting, Active, @@ -25,7 +29,7 @@ struct CallMuteButtonState { class CallMuteButton final { public: explicit CallMuteButton( - not_null parent, + not_null parent, CallMuteButtonState initial = CallMuteButtonState()); void setState(const CallMuteButtonState &state); @@ -52,9 +56,12 @@ private: CallMuteButtonState _state; float _level = 0.; + const base::unique_qptr _blobs; CallButton _content; CallButton _connecting; + Animations::Simple _switchAnimation; + }; } // namespace Ui