From cfc2a959cff5cd0ea4c28d30b14bd9a41f5e314c Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 7 Jan 2022 15:15:10 +0300 Subject: [PATCH] Implement nice dropdown collapse animation. --- .../view/history_view_react_button.cpp | 52 ++++++++++++++++--- .../history/view/history_view_react_button.h | 10 ++++ Telegram/SourceFiles/ui/chat/chat.style | 2 + 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_react_button.cpp b/Telegram/SourceFiles/history/view/history_view_react_button.cpp index 188022bfb..08dd0309e 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_button.cpp +++ b/Telegram/SourceFiles/history/view/history_view_react_button.cpp @@ -116,6 +116,18 @@ int Button::scrollMax() const { return _expandedInnerHeight - _expandedHeight; } +float64 Button::expandAnimationOpacity(float64 expandRatio) const { + return (_collapseType == CollapseType::Fade) + ? expandRatio + : 1.; +} + +int Button::expandAnimationScroll(float64 expandRatio) const { + return (_collapseType == CollapseType::Scroll && expandRatio < 1.) + ? std::clamp(int(base::SafeRound(expandRatio * _scroll)), 0, _scroll) + : _scroll; +} + bool Button::expandUp() const { return (_expandDirection == ExpandDirection::Up); } @@ -247,6 +259,11 @@ void Button::applyState(State state, Fn update) { if (state == State::Hidden) { _heightAnimation.stop(); } else { + if (!_heightAnimation.animating()) { + _collapseType = (_scroll < st::reactionCollapseFadeThreshold) + ? CollapseType::Scroll + : CollapseType::Fade; + } _heightAnimation.start( [=] { updateGeometry(_update); }, _finalHeight, @@ -736,7 +753,10 @@ void Manager::paintButton( const auto current = (button == _button.get()); const auto expandRatio = expanded - ? (float64(expanded) / (button->expandedHeight() - _outer.height())) + ? std::clamp( + float64(expanded) / (button->expandedHeight() - _outer.height()), + 0., + 1.) : 0.; const auto expandedSkip = int(base::SafeRound( expandRatio * st::reactionExpandedSkip)); @@ -745,17 +765,36 @@ void Manager::paintButton( : button->expandUp() ? QPoint(0, expanded - expandedSkip) : QPoint(0, expandedSkip); + const auto source = validateEmoji(frameIndex, scale); if (expanded || (current && !onlyMainEmojiVisible())) { const auto origin = expanded ? QPoint() : position; - paintAllEmoji(*q, button, scale, origin, mainEmojiPosition); + const auto scroll = button->expandAnimationScroll(expandRatio); + const auto opacity = button->expandAnimationOpacity(expandRatio); + if (opacity != 1.) { + q->setOpacity(opacity); + } + paintAllEmoji(*q, button, scroll, scale, origin, mainEmojiPosition); + if (opacity != 1.) { + q->setOpacity(1.); + } if (current && expanded) { _showingAll = true; } if (expanded) { - paintInnerGradients(*q, background, button, expandRatio); + paintInnerGradients(*q, background, button, scroll, expandRatio); + } + if (opacity != 1.) { + const auto appearShift = st::reactionMainAppearShift * opacity; + const auto appearPosition = !expanded + ? position + : button->expandUp() + ? QPoint(0, expanded - appearShift) + : QPoint(0, appearShift); + q->setOpacity(1. - opacity); + q->drawImage(appearPosition, _cacheParts, source); + q->setOpacity(1.); } } else { - const auto source = validateEmoji(frameIndex, scale); p.drawImage(mainEmojiPosition, _cacheParts, source); } if (current && !expanded) { @@ -779,8 +818,8 @@ void Manager::paintInnerGradients( Painter &p, const QColor &background, not_null button, + int scroll, float64 expandRatio) { - const auto scroll = button->scroll(); const auto endScroll = button->scrollMax() - scroll; const auto size = st::reactionGradientSize; const auto ensureGradient = [&](QImage &gradient, bool top) { @@ -970,6 +1009,7 @@ void Manager::paintLongImage( void Manager::paintAllEmoji( Painter &p, not_null button, + int scroll, float64 scale, QPoint position, QPoint mainEmojiPosition) { @@ -1013,7 +1053,7 @@ void Manager::paintAllEmoji( const auto expandUp = button->expandUp(); const auto shift = QPoint(0, oneHeight * (expandUp ? -1 : 1)); auto emojiPosition = mainEmojiPosition - + QPoint(0, button->scroll() * (expandUp ? 1 : -1)); + + QPoint(0, scroll * (expandUp ? 1 : -1)); const auto update = [=] { updateCurrentButton(); }; diff --git a/Telegram/SourceFiles/history/view/history_view_react_button.h b/Telegram/SourceFiles/history/view/history_view_react_button.h index ef2f98345..698ea1e74 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_button.h +++ b/Telegram/SourceFiles/history/view/history_view_react_button.h @@ -81,12 +81,19 @@ public: [[nodiscard]] int scrollMax() const; [[nodiscard]] float64 currentScale() const; [[nodiscard]] float64 currentOpacity() const; + [[nodiscard]] float64 expandAnimationOpacity(float64 expandRatio) const; + [[nodiscard]] int expandAnimationScroll(float64 expandRatio) const; [[nodiscard]] bool consumeWheelEvent(not_null e); [[nodiscard]] static float64 ScaleForState(State state); [[nodiscard]] static float64 OpacityForScale(float64 scale); private: + enum class CollapseType { + Scroll, + Fade, + }; + void updateGeometry(Fn update); void applyState(State satte, Fn update); void applyParameters( @@ -108,6 +115,7 @@ private: int _finalHeight = 0; int _scroll = 0; ExpandDirection _expandDirection = ExpandDirection::Up; + CollapseType _collapseType = CollapseType::Scroll; base::Timer _expandTimer; base::Timer _hideTimer; @@ -174,6 +182,7 @@ private: void paintAllEmoji( Painter &p, not_null button, + int scroll, float64 scale, QPoint position, QPoint mainEmojiPosition); @@ -181,6 +190,7 @@ private: Painter &p, const QColor &background, not_null button, + int scroll, float64 expandRatio); void overlayExpandedBorder( Painter &p, diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index e5c9bf7d6..e59fe7e94 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -987,3 +987,5 @@ reactionGradientStart: 8px; reactionGradientSize: 24px; reactionGradientFadeSize: 24px; reactionAppearStartSkip: 2px; +reactionMainAppearShift: 20px; +reactionCollapseFadeThreshold: 40px;