From 3bd98ac5640523d2ccd241c2c0af49aba9494066 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Dec 2020 15:27:59 +0400 Subject: [PATCH] Add shake-on-error animation to CallMuteButton. --- ui/colors.palette | 2 +- ui/widgets/call_mute_button.cpp | 87 +++++++++++++++++++++++++++------ ui/widgets/call_mute_button.h | 17 ++++++- ui/widgets/widgets.style | 2 + 4 files changed, 92 insertions(+), 16 deletions(-) diff --git a/ui/colors.palette b/ui/colors.palette index 73c0858..6d1083c 100644 --- a/ui/colors.palette +++ b/ui/colors.palette @@ -564,7 +564,7 @@ groupCallMemberActiveIcon: #8deb90; // group call active member icon groupCallMemberActiveStatus: #8deb90; // group call active member status text groupCallMemberInactiveIcon: #84888f; // group call inactive member icon groupCallMemberInactiveStatus: #61c0ff; // group call inactive member status text -groupCallMemberMutedIcon: #ff4f4d; // group call muted by admin member icon +groupCallMemberMutedIcon: #ed7372; // group call muted by admin member icon groupCallMemberNotJoinedStatus: #91979e; // group call non joined member status text groupCallIconFg: #ffffff; // group call mute / settings / leave icon groupCallLive1: #0dcc39; // group call live button color1 diff --git a/ui/widgets/call_mute_button.cpp b/ui/widgets/call_mute_button.cpp index c3c7a2a..1488515 100644 --- a/ui/widgets/call_mute_button.cpp +++ b/ui/widgets/call_mute_button.cpp @@ -47,6 +47,7 @@ constexpr auto kOverrideColorBgAlpha = 76; constexpr auto kOverrideColorRippleAlpha = 50; constexpr auto kSwitchStateDuration = 120; +constexpr auto kShiftDuration = crl::time(300); auto MuteBlobs() { return std::vector{ @@ -315,12 +316,9 @@ void CallMuteButton::init() { _label->show(); rpl::combine( _content->geometryValue(), - _label->geometryValue() - ) | rpl::start_with_next([=](QRect my, QRect label) { - _label->moveToLeft( - my.x() + (my.width() - label.width()) / 2, - my.y() + my.height() - label.height(), - my.width()); + _label->sizeValue() + ) | rpl::start_with_next([=](QRect my, QSize size) { + updateLabelGeometry(my, size); }, _label->lifetime()); _label->setAttribute(Qt::WA_TransparentForMouseEvents); @@ -342,7 +340,7 @@ void CallMuteButton::init() { // State type. const auto previousType = lifetime().make_state(_state.current().type); - setEnableMouse(false); + setHandleMouseState(HandleMouseState::Disabled); const auto blobsInner = [&] { // The point of the circle at 45 degrees. @@ -379,8 +377,10 @@ void CallMuteButton::init() { const auto previous = *previousType; *previousType = type; - if (IsInactive(type) && !IsInactive(previous)) { - setEnableMouse(false); + const auto mouseState = HandleMouseStateFromType(type); + setHandleMouseState(HandleMouseState::Disabled); + if (mouseState != HandleMouseState::Enabled) { + setHandleMouseState(mouseState); } const auto crossFrom = IsMuted(previous) ? 0. : 1.; @@ -419,9 +419,7 @@ void CallMuteButton::init() { overridesColors(previous, type, value); if (value == to) { - if (!IsInactive(type) && IsInactive(previous)) { - setEnableMouse(true); - } + setHandleMouseState(mouseState); } }; @@ -463,6 +461,60 @@ void CallMuteButton::init() { }, _content->lifetime()); } +void CallMuteButton::updateLabelGeometry() { + updateLabelGeometry(_content->geometry(), _label->size()); +} + +void CallMuteButton::updateLabelGeometry(QRect my, QSize size) { + _label->moveToLeft( + my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, + my.y() + my.height() - size.height(), + my.width()); +} + +void CallMuteButton::shake() { + if (_shakeAnimation.animating()) { + return; + } + const auto update = [=] { + const auto fullProgress = _shakeAnimation.value(1.) * 6; + const auto segment = std::clamp(int(std::floor(fullProgress)), 0, 5); + const auto part = fullProgress - segment; + const auto from = (segment == 0) + ? 0. + : (segment == 1 || segment == 3 || segment == 5) + ? 1. + : -1.; + const auto to = (segment == 0 || segment == 2 || segment == 4) + ? 1. + : (segment == 1 || segment == 3) + ? -1. + : 0.; + const auto shift = from * (1. - part) + to * part; + _labelShakeShift = int(std::round(shift * st::shakeShift)); + updateLabelGeometry(); + }; + _shakeAnimation.start( + update, + 0., + 1., + kShiftDuration); +} + +CallMuteButton::HandleMouseState CallMuteButton::HandleMouseStateFromType( + CallMuteButtonType type) { + switch (type) { + case CallMuteButtonType::Active: + case CallMuteButtonType::Muted: + return HandleMouseState::Enabled; + case CallMuteButtonType::Connecting: + return HandleMouseState::Disabled; + case CallMuteButtonType::ForceMuted: + return HandleMouseState::Blocked; + } + Unexpected("Type in HandleMouseStateFromType."); +} + void CallMuteButton::setState(const CallMuteButtonState &state) { _state = state; } @@ -516,8 +568,15 @@ void CallMuteButton::lower() { _blobs->lower(); } -void CallMuteButton::setEnableMouse(bool value) { - _content->setAttribute(Qt::WA_TransparentForMouseEvents, !value); +void CallMuteButton::setHandleMouseState(HandleMouseState state) { + if (_handleMouseState == state) { + return; + } + _handleMouseState = state; + const auto handle = (_handleMouseState != HandleMouseState::Disabled); + const auto pointer = (_handleMouseState == HandleMouseState::Enabled); + _content->setAttribute(Qt::WA_TransparentForMouseEvents, !handle); + _content->setPointerCursor(pointer); } void CallMuteButton::overridesColors( diff --git a/ui/widgets/call_mute_button.h b/ui/widgets/call_mute_button.h index 83bab67..39c5c1a 100644 --- a/ui/widgets/call_mute_button.h +++ b/ui/widgets/call_mute_button.h @@ -49,6 +49,8 @@ public: [[nodiscard]] QRect innerGeometry() const; void moveInner(QPoint position); + void shake(); + void setVisible(bool visible); void show() { setVisible(true); @@ -64,31 +66,44 @@ public: [[nodiscard]] rpl::lifetime &lifetime(); private: + enum class HandleMouseState { + Enabled, + Blocked, + Disabled, + }; void init(); void overridesColors( CallMuteButtonType fromType, CallMuteButtonType toType, float64 progress); - void setEnableMouse(bool value); + void setHandleMouseState(HandleMouseState state); + void updateLabelGeometry(QRect my, QSize size); + void updateLabelGeometry(); + + [[nodiscard]] static HandleMouseState HandleMouseStateFromType( + CallMuteButtonType type); rpl::variable _state; float _level = 0.; float64 _crossLineProgress = 0.; rpl::variable _radialShowProgress = 0.; QRect _muteIconPosition; + HandleMouseState _handleMouseState = HandleMouseState::Enabled; const style::CallButton &_st; const base::unique_qptr _blobs; const base::unique_qptr _content; const base::unique_qptr _label; + int _labelShakeShift = 0; std::unique_ptr _radial; const base::flat_map> _colors; CrossLineAnimation _crossLineMuteAnimation; Animations::Simple _switchAnimation; + Animations::Simple _shakeAnimation; rpl::event_stream _colorOverrides; diff --git a/ui/widgets/widgets.style b/ui/widgets/widgets.style index 367887e..8ef228e 100644 --- a/ui/widgets/widgets.style +++ b/ui/widgets/widgets.style @@ -1468,6 +1468,8 @@ callConnectingRadial: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { size: size(100px, 100px); } +shakeShift: 4px; + // Windows specific title windowTitleButton: IconButton {