diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 1384a7eb63..9e15e4b62b 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -292,7 +292,6 @@ void RenameBox(not_null box) { return std::make_unique(Lottie::IconDescriptor{ .path = u":/icons/settings/devices/"_q + path + u".lottie"_q, .sizeOverride = QSize(size, size), - .frame = 1, }); } @@ -358,7 +357,7 @@ void RenameBox(not_null box) { state->lottie->animate( [=] { result->update(); }, 0, - state->lottie->framesCount()); + state->lottie->framesCount() - 1); }, result->lifetime()); } diff --git a/Telegram/SourceFiles/history/view/history_view_react_button.cpp b/Telegram/SourceFiles/history/view/history_view_react_button.cpp index 9eeab7e26c..1699d67819 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_button.cpp +++ b/Telegram/SourceFiles/history/view/history_view_react_button.cpp @@ -25,6 +25,7 @@ constexpr auto kToggleDuration = crl::time(120); constexpr auto kActivateDuration = crl::time(150); constexpr auto kExpandDuration = crl::time(300); constexpr auto kCollapseDuration = crl::time(250); +constexpr auto kHoverScaleDuration = crl::time(120); constexpr auto kBgCacheIndex = 0; constexpr auto kShadowCacheIndex = 0; constexpr auto kEmojiCacheIndex = 1; @@ -35,7 +36,7 @@ constexpr auto kButtonExpandDelay = crl::time(25); constexpr auto kButtonHideDelay = crl::time(300); constexpr auto kButtonExpandedHideDelay = crl::time(0); constexpr auto kSizeForDownscale = 96; -constexpr auto kHoverScale = 1.2; +constexpr auto kHoverScale = 1.24; [[nodiscard]] QPoint LocalPosition(not_null e) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -348,10 +349,12 @@ Manager::Manager( void Manager::stealWheelEvents(not_null target) { base::install_event_filter(target, [=](not_null e) { - return (e->type() == QEvent::Wheel - && consumeWheelEvent(static_cast(e.get()))) - ? base::EventFilterResult::Cancel - : base::EventFilterResult::Continue; + if (e->type() != QEvent::Wheel + || !consumeWheelEvent(static_cast(e.get()))) { + return base::EventFilterResult::Continue; + } + Ui::SendSynteticMouseEvent(target, QEvent::MouseMove, Qt::NoButton); + return base::EventFilterResult::Cancel; }); } @@ -417,6 +420,7 @@ void Manager::applyList(std::vector list) { if (_list.empty()) { _mainReactionMedia = nullptr; _mainReactionLifetime.destroy(); + setSelectedIcon(-1); _icons.clear(); return; } @@ -511,11 +515,12 @@ void Manager::loadIcons() { } return entry.icon; }; + // #TODO reactions rebuild list better _icons.clear(); auto main = true; for (const auto &reaction : _list) { _icons.push_back({ - .appear = load(reaction.appearAnimation, main ? -1 : 1), + .appear = load(reaction.appearAnimation, main ? -1 : 0), .select = load(reaction.selectAnimation, -1), .appearAnimated = main, }); @@ -554,6 +559,7 @@ void Manager::paintButtons(Painter &p, const PaintContext &context) { ClickHandlerPtr Manager::computeButtonLink(QPoint position) const { if (_list.empty()) { + setSelectedIcon(-1); return nullptr; } const auto inner = buttonInner(); @@ -571,9 +577,34 @@ ClickHandlerPtr Manager::computeButtonLink(QPoint position) const { if (!result) { result = resolveButtonLink(_list[index]); } + setSelectedIcon(index); return result; } +void Manager::setSelectedIcon(int index) const { + const auto setSelected = [&](int index, bool selected) { + if (index < 0 || index >= _icons.size()) { + return; + } + auto &icon = _icons[index]; + if (icon.selected == selected) { + return; + } + icon.selected = selected; + icon.selectedScale.start( + [=] { if (_button) _buttonUpdate(_button->geometry()); }, + selected ? 1. : kHoverScale, + selected ? kHoverScale : 1., + kHoverScaleDuration, + anim::sineInOut); + }; + if (_selectedIcon != index) { + setSelected(_selectedIcon, false); + _selectedIcon = index; + } + setSelected(index, true); +} + ClickHandlerPtr Manager::resolveButtonLink( const Data::Reaction &reaction) const { const auto emoji = reaction.emoji; @@ -593,6 +624,8 @@ TextState Manager::buttonTextState(QPoint position) const { auto result = TextState(nullptr, computeButtonLink(position)); result.itemId = _buttonContext; return result; + } else { + setSelectedIcon(-1); } return {}; } @@ -661,7 +694,8 @@ void Manager::paintButton( const auto mainEmojiPosition = position + (button->expandUp() ? QPoint(0, size.height() - _outer.height()) : QPoint()); - if (size.height() > _outer.height()) { + if (size.height() > _outer.height() + || (!_icons.empty() && _icons.front().selected)) { p.save(); paintAllEmoji(p, button, scale, mainEmojiPosition); p.restore(); @@ -750,13 +784,28 @@ void Manager::paintAllEmoji( const auto between = st::reactionCornerSkip; const auto oneHeight = st::reactionCornerSize.height() + between; const auto finalSize = CornerImageSize(1.); - const auto remove = finalSize * (1. - scale) / 2.; - const auto basicTarget = QRectF(QRect( - _inner.x() + (_inner.width() - finalSize) / 2, - _inner.y() + (_inner.height() - finalSize) / 2, - finalSize, - finalSize - )).marginsRemoved({ remove, remove, remove, remove }); + const auto hoveredSize = int(base::SafeRound(finalSize * kHoverScale)); + const auto basicTargetForScale = [&](int size, float64 scale) { + const auto remove = size * (1. - scale) / 2.; + return QRectF(QRect( + _inner.x() + (_inner.width() - size) / 2, + _inner.y() + (_inner.height() - size) / 2, + size, + size + )).marginsRemoved({ remove, remove, remove, remove }); + }; + const auto basicTarget = basicTargetForScale(finalSize, scale); + const auto countTarget = [&](const ReactionIcons &icon) { + const auto selectScale = icon.selectedScale.value( + icon.selected ? kHoverScale : 1.); + if (selectScale == 1.) { + return basicTarget; + } + const auto finalScale = scale * selectScale; + return (finalScale <= 1.) + ? basicTargetForScale(finalSize, finalScale) + : basicTargetForScale(hoveredSize, finalScale / kHoverScale); + }; const auto expandUp = button->expandUp(); const auto shift = QPoint(0, oneHeight * (expandUp ? -1 : 1)); auto emojiPosition = mainEmojiPosition @@ -765,7 +814,7 @@ void Manager::paintAllEmoji( if (_button) _buttonUpdate(_button->geometry()); }; for (auto &icon : _icons) { - const auto target = basicTarget.translated(emojiPosition); + const auto target = countTarget(icon).translated(emojiPosition); emojiPosition += shift; if (!target.intersects(clip)) { @@ -775,16 +824,15 @@ void Manager::paintAllEmoji( } icon.appearAnimated = false; } - continue; - } else if (icon.appear) { + } else if (const auto appear = icon.appear.get()) { if (current && !icon.appearAnimated && target.intersects(animationRect)) { icon.appearAnimated = true; - icon.appear->animate(update, 0, icon.appear->framesCount()); + appear->animate(update, 0, appear->framesCount() - 1); } const auto size = int(base::SafeRound(target.width())); - const auto frame = icon.appear->frame({ size, size }, update); + const auto frame = appear->frame({ size, size }, update); p.drawImage(target, frame.image); } } diff --git a/Telegram/SourceFiles/history/view/history_view_react_button.h b/Telegram/SourceFiles/history/view/history_view_react_button.h index 56f63190d3..0d1ed305be 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_button.h +++ b/Telegram/SourceFiles/history/view/history_view_react_button.h @@ -147,7 +147,9 @@ private: struct ReactionIcons { std::shared_ptr appear; std::shared_ptr select; + mutable Ui::Animations::Simple selectedScale; bool appearAnimated = false; + mutable bool selected = false; }; static constexpr auto kFramesCount = 30; @@ -193,6 +195,8 @@ private: const QColor &background, const QColor &shadow); + void setSelectedIcon(int index) const; + [[nodiscard]] QMargins innerMargins() const; [[nodiscard]] QRect buttonInner() const; [[nodiscard]] QRect buttonInner(not_null button) const; @@ -227,6 +231,7 @@ private: std::vector _icons; rpl::lifetime _loadCacheLifetime; bool _showingAll = false; + mutable int _selectedIcon = -1; std::optional _scheduledParameters; base::Timer _buttonShowTimer; diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp index d47b7d684c..fefd886238 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp @@ -676,10 +676,10 @@ CallMuteButton::IconState CallMuteButton::randomWavingState() { int to = 0; }; static const auto kAnimations = std::vector{ - { 0, 120 }, - { 120, 240 }, - { 240, 420 }, - { 420, 540 }, + { 0, 119 }, + { 120, 239 }, + { 240, 419 }, + { 420, 539 }, }; const auto index = base::RandomIndex(kAnimations.size()); return { 1, kAnimations[index].from, kAnimations[index].to }; diff --git a/Telegram/lib_lottie b/Telegram/lib_lottie index ab022b57a0..513fd602e8 160000 --- a/Telegram/lib_lottie +++ b/Telegram/lib_lottie @@ -1 +1 @@ -Subproject commit ab022b57a0a970a9a3ba73bc7fff7ea2cffc046b +Subproject commit 513fd602e8e26a3743abb7c76d25d98895a1d6a2