From 7b720b2a3f2273298fb74d1cdbbbe28a96bbd93b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 13 Apr 2026 21:42:43 +0300 Subject: [PATCH] Added expand/collapse animation to emoji picker overlay. --- .../SourceFiles/boxes/sticker_creator_box.cpp | 21 +++-- .../chat_helpers/emoji_picker_overlay.cpp | 78 +++++++++++++------ .../chat_helpers/emoji_picker_overlay.h | 10 ++- 3 files changed, 73 insertions(+), 36 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sticker_creator_box.cpp b/Telegram/SourceFiles/boxes/sticker_creator_box.cpp index bd782e1f01..80b891f46c 100644 --- a/Telegram/SourceFiles/boxes/sticker_creator_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_creator_box.cpp @@ -212,10 +212,15 @@ void CreateStickerBox( .maxSelected = kMaxEmojis, .allowExpand = true, }; - const auto overlayExpanded = [&] { + const auto pickerCollapsed = [&] { + auto probe = ChatHelpers::EmojiPickerOverlay(nullptr, pickerDescriptor); + return probe.collapsedHeight(); + }(); + const auto pickerExpanded = [&] { auto probe = ChatHelpers::EmojiPickerOverlay(nullptr, pickerDescriptor); return probe.expandedHeight(); }(); + const auto pickerExtra = pickerExpanded - pickerCollapsed; const auto previewHolder = inner->add( object_ptr(inner), @@ -223,7 +228,7 @@ void CreateStickerBox( style::al_top); previewHolder->resize( st::boxWideWidth, - kPreviewSide + overlayExpanded / 2); + kPreviewSide + pickerExtra); const auto preview = Ui::CreateChild( previewHolder, image); @@ -236,12 +241,9 @@ void CreateStickerBox( const auto w = std::min( previewHolder->width() - 2 * st::boxRowPadding.left(), int(kPreviewSide * 1.1)); - const auto h = picker->expanded() - ? picker->expandedHeight() - : picker->collapsedHeight(); const auto x = (previewHolder->width() - w) / 2; - const auto y = kPreviewSide - h; - picker->setGeometry(x, y, w, h); + const auto y = kPreviewSide - pickerCollapsed; + picker->setGeometry(x, y, w, pickerExpanded); picker->raise(); }; @@ -251,11 +253,6 @@ void CreateStickerBox( layoutOverlay(); }, preview->lifetime()); - picker->expandedValue( - ) | rpl::on_next([=](bool) { - layoutOverlay(); - }, picker->lifetime()); - Ui::AddSkip(inner); const auto startUpload = [=, set = std::move(set), done = std::move(done)]( diff --git a/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.cpp b/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.cpp index 6f02547624..10950c523a 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.cpp @@ -240,7 +240,7 @@ void EmojiPickerOverlay::Strip::mouseReleaseEvent(QMouseEvent *e) { const auto index = _pressed; _pressed = -1; if (released == index && index >= 0 && index < int(_cells.size())) { - _owner->toggleEmoji(_cells[index].emoji); + _owner->toggleEmoji(_cells[index].emoji, false); } } @@ -346,7 +346,7 @@ void EmojiPickerOverlay::Grid::mouseReleaseEvent(QMouseEvent *e) { const auto index = _pressed; _pressed = -1; if (released == index && index >= 0 && index < int(_cells.size())) { - _owner->toggleEmoji(_cells[index].emoji); + _owner->toggleEmoji(_cells[index].emoji, true); } } @@ -402,22 +402,10 @@ EmojiPickerOverlay::EmojiPickerOverlay( object_ptr(_scroll.get(), this)); _grid = gridPtr.data(); _grid->setEmojis(_allForGrid); - - _expanded.value( - ) | rpl::on_next([=](bool value) { - if (_scroll) { - _scroll->setVisible(value); - } - if (_expandButton) { - _expandButton->update(); - } - resize(width(), value ? expandedHeight() : collapsedHeight()); - relayout(); - }, lifetime()); } _selectedVar = _selectedList; - resize(width(), collapsedHeight()); + resize(width(), expandedHeight()); } EmojiPickerOverlay::~EmojiPickerOverlay() = default; @@ -432,10 +420,49 @@ EmojiPickerOverlay::selectedValue() const { } void EmojiPickerOverlay::setExpanded(bool expanded) { - if (!_allowExpand) { + if (!_allowExpand || _expanded.current() == expanded) { return; } + startExpandAnimation(expanded); _expanded = expanded; + if (_expandButton) { + _expandButton->update(); + } +} + +void EmojiPickerOverlay::startExpandAnimation(bool expanded) { + const auto from = _expandAnim.value(expanded ? 0. : 1.); + const auto to = expanded ? 1. : 0.; + _expandAnim.start( + [=] { applyExpandProgress(); }, + from, + to, + st::slideWrapDuration, + anim::easeOutCirc); + applyExpandProgress(); +} + +float64 EmojiPickerOverlay::currentExpandValue() const { + return _expandAnim.value(_expanded.current() ? 1. : 0.); +} + +int EmojiPickerOverlay::currentShownHeight() const { + const auto progress = currentExpandValue(); + return anim::interpolate( + collapsedHeight(), + expandedHeight(), + progress); +} + +void EmojiPickerOverlay::applyExpandProgress() { + const auto h = currentShownHeight(); + setMask(QRegion(0, 0, width(), h)); + if (_scroll) { + const auto progress = currentExpandValue(); + _scroll->setVisible(progress > 0.); + } + relayout(); + update(); } bool EmojiPickerOverlay::expanded() const { @@ -465,13 +492,21 @@ void EmojiPickerOverlay::paintEvent(QPaintEvent *e) { p.setPen(Qt::NoPen); p.setBrush(st::stickersEmojiPickerBg); const auto radius = st::stickersEmojiPickerRadius; - p.drawRoundedRect(rect(), radius, radius); + const auto h = currentShownHeight(); + p.drawRoundedRect(QRect(0, 0, width(), h), radius, radius); } void EmojiPickerOverlay::resizeEvent(QResizeEvent *e) { + setMask(QRegion(0, 0, width(), currentShownHeight())); relayout(); } +void EmojiPickerOverlay::mousePressEvent(QMouseEvent *e) { + if (e->pos().y() > currentShownHeight()) { + e->ignore(); + } +} + void EmojiPickerOverlay::relayout() { const auto &pad = st::stickersEmojiPickerPadding; if (_about) { @@ -520,7 +555,7 @@ void EmojiPickerOverlay::relayout() { } } -void EmojiPickerOverlay::toggleEmoji(EmojiPtr emoji) { +void EmojiPickerOverlay::toggleEmoji(EmojiPtr emoji, bool fromGrid) { if (!emoji) { return; } @@ -537,6 +572,9 @@ void EmojiPickerOverlay::toggleEmoji(EmojiPtr emoji) { _selectedList.push_back(emoji); } notifySelectionChanged(); + if (fromGrid) { + setExpanded(false); + } } void EmojiPickerOverlay::notifySelectionChanged() { @@ -549,8 +587,4 @@ void EmojiPickerOverlay::notifySelectionChanged() { } } -void EmojiPickerOverlay::buildSections() { - // Reserved for potential future categorised rendering in the grid. -} - } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.h b/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.h index f4eadcb91a..0919cdab80 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "ui/rp_widget.h" +#include "ui/effects/animations.h" #include "ui/emoji_config.h" namespace Ui { @@ -46,15 +47,19 @@ public: protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; private: class Strip; class Grid; - void buildSections(); void relayout(); - void toggleEmoji(EmojiPtr emoji); + void toggleEmoji(EmojiPtr emoji, bool fromGrid); void notifySelectionChanged(); + void startExpandAnimation(bool expanded); + void applyExpandProgress(); + [[nodiscard]] float64 currentExpandValue() const; + [[nodiscard]] int currentShownHeight() const; const QString _aboutText; const std::vector _recent; @@ -72,6 +77,7 @@ private: Ui::AbstractButton *_expandButton = nullptr; std::unique_ptr _scroll; Grid *_grid = nullptr; + Ui::Animations::Simple _expandAnim; };