From c5fa4aae62dd1def6342330bc8c28e97ca8a2758 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 22 Aug 2022 19:41:23 +0300 Subject: [PATCH] Support reaction dropdown based on EmojiListWidget. --- .../chat_helpers/chat_helpers.style | 95 +++++--- .../chat_helpers/emoji_list_widget.cpp | 138 +++++++---- .../chat_helpers/emoji_list_widget.h | 18 +- .../chat_helpers/gifs_list_widget.cpp | 9 +- .../chat_helpers/stickers_list_footer.cpp | 60 ++--- .../chat_helpers/stickers_list_footer.h | 5 + .../chat_helpers/stickers_list_widget.cpp | 34 +-- .../chat_helpers/stickers_list_widget.h | 6 +- .../chat_helpers/tabbed_selector.cpp | 37 ++- .../chat_helpers/tabbed_selector.h | 18 +- .../data/stickers/data_custom_emoji.cpp | 7 + .../data/stickers/data_custom_emoji.h | 1 + .../history_view_reactions_button.cpp | 1 + .../history_view_reactions_selector.cpp | 220 +++++++++++++++++- .../history_view_reactions_selector.h | 11 +- .../history_view_reactions_strip.cpp | 12 +- .../reactions/history_view_reactions_strip.h | 2 +- .../inline_bot_layout_internal.cpp | 10 +- .../SourceFiles/ui/cached_round_corners.cpp | 1 - .../SourceFiles/ui/cached_round_corners.h | 1 - Telegram/SourceFiles/window/window.style | 1 + Telegram/lib_ui | 2 +- 22 files changed, 529 insertions(+), 160 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index c12f2150f..7d673c0f6 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -10,6 +10,25 @@ using "ui/basic.style"; using "boxes/boxes.style"; using "ui/widgets/widgets.style"; +EmojiPan { + margin: margins; + padding: margins; + desiredSize: pixels; + verticalSizeSub: pixels; + header: pixels; + headerLeft: pixels; + headerLockLeft: pixels; + headerLockedLeft: pixels; + headerTop: pixels; + footer: pixels; + iconSkip: pixels; + iconWidth: pixels; + iconArea: pixels; + overBg: color; + fadeLeft: icon; + fadeRight: icon; +} + switchPmButton: RoundButton(defaultBoxButton) { width: 320px; height: 34px; @@ -147,23 +166,7 @@ emojiObjectsActive: icon {{ "emoji/emoji_objects", emojiSubIconFgActive }}; emojiSymbols: icon {{ "emoji/emoji_symbols", emojiIconFg }}; emojiSymbolsActive: icon {{ "emoji/emoji_symbols", emojiSubIconFgActive }}; -emojiFooterHeight: 46px; -emojiCategorySkip: 4px; -emojiCategory: IconButton { - width: 42px; - height: emojiFooterHeight; - - iconPosition: point(-1px, 6px); -} -emojiCategoryRecent: IconButton(emojiCategory) { icon: emojiRecent; } -emojiCategoryPeople: IconButton(emojiCategory) { icon: emojiPeople; } -emojiCategoryNature: IconButton(emojiCategory) { icon: emojiNature; } -emojiCategoryFood: IconButton(emojiCategory) { icon: emojiFood; } -emojiCategoryActivity: IconButton(emojiCategory) { icon: emojiActivity; } -emojiCategoryTravel: IconButton(emojiCategory) { icon: emojiTravel; } -emojiCategoryObjects: IconButton(emojiCategory) { icon: emojiObjects; } -emojiCategorySymbols: IconButton(emojiCategory) { icon: emojiSymbols; } - +emojiCategoryIconTop: 6px; emojiPanAnimation: PanelAnimation(defaultPanelAnimation) { fadeBg: emojiPanBg; } @@ -176,21 +179,34 @@ emojiPanShowDuration: 200; emojiPanDuration: 200; emojiPanHover: windowBgOver; emojiPanSlideDuration: 200; -emojiPanDesiredSize: 39px; emojiPanArea: size(34px, 32px); emojiPanLeft: 13px; emojiPanRight: 17px; emojiPanRadius: 8px; +defaultEmojiPan: EmojiPan { + margin: margins(roundRadiusSmall, 0px, 14px, 0px); + padding: margins(13px, 12px, 17px, 12px); + desiredSize: 39px; + verticalSizeSub: 2px; + header: 40px; + headerLeft: 23px; + headerLockLeft: 17px; + headerLockedLeft: 36px; + headerTop: 12px; + footer: 46px; + iconSkip: 4px; + iconWidth: 35px; + iconArea: 32px; + overBg: emojiPanHover; + fadeLeft: icon {{ "fade_horizontal-flip_horizontal", emojiPanCategories }}; + fadeRight: icon {{ "fade_horizontal", emojiPanCategories }}; +} + inlineResultsMinHeight: 278px; inlineResultsMaxHeight: 640px; -emojiPanHeader: 40px; emojiPanHeaderFont: semiboldFont; -emojiPanHeaderLeft: 23px; -emojiPanHeaderLockLeft: 17px; -emojiPanHeaderLockedLeft: 36px; -emojiPanHeaderTop: 12px; emojiPanRemoveSkip: 10px; emojiColorsPadding: 5px; @@ -233,7 +249,6 @@ stickerPanDeleteOpacityFg: 0.8; stickerPanDeleteOpacityFgOver: 1.; stickerPanRemoveSet: hashtagClose; stickerIconWidth: 42px; -stickerIconHeight: emojiFooterHeight; stickerIconPadding: 5px; stickerIconOpacity: 0.7; stickerIconSel: 2px; @@ -242,9 +257,6 @@ stickerIconMove: 400; stickerPreviewDuration: 150; stickerPreviewMin: 0.1; -emojiIconWidth: 35px; -emojiIconArea: 32px; - stickerGroupCategorySize: 28px; stickerGroupCategoryAbout: defaultTextStyle; stickerGroupCategoryAddMargin: margins(0px, 10px, 0px, 5px); @@ -315,9 +327,38 @@ stickersPremiumLock: icon{{ "emoji/premium_lock", premiumButtonFg }}; reactStripExtend: margins(21px, 49px, 39px, 0px); reactStripHeight: 40px; reactStripSize: 32px; +reactStripImage: 26px; reactStripSkip: 7px; reactStripBubble: icon{ { "chat/reactions_bubble_shadow", windowShadowFg }, { "chat/reactions_bubble", windowBg }, }; reactStripBubbleRight: 20px; +reactPanelEmojiPan: EmojiPan(defaultEmojiPan) { + margin: margins(reactStripSkip, 0px, reactStripSkip, 0px); + padding: margins(reactStripSkip, 0px, reactStripSkip, reactStripSkip); + desiredSize: reactStripSize; + verticalSizeSub: 0px; + headerLeft: 13px; + headerLockLeft: 7px; + headerLockedLeft: 26px; + footer: 42px; + iconSkip: 6px; + iconWidth: 33px; + iconArea: 30px; + overBg: transparent; + fadeLeft: icon {{ "fade_horizontal-flip_horizontal", windowBg }}; + fadeRight: icon {{ "fade_horizontal", windowBg }}; +} +reactPanelScroll: ScrollArea(defaultSolidScroll) { + deltat: 3px; + deltab: 3px; + round: 1px; + width: 7px; + deltax: 2px; + hiding: 0; +} + +emojiSuggestionsFadeLeft: icon {{ "fade_horizontal-flip_horizontal", boxBg }}; +emojiSuggestionsFadeRight: icon {{ "fade_horizontal", boxBg }}; + diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 755400be8..2d2ddd5f6 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -46,7 +46,7 @@ using Core::RecentEmojiDocument; } // namespace -class EmojiColorPicker : public Ui::RpWidget { +class EmojiColorPicker final : public Ui::RpWidget { public: EmojiColorPicker(QWidget *parent); @@ -89,6 +89,7 @@ private: QSize _singleSize; QPoint _areaPosition; QPoint _innerPosition; + Ui::RoundRect _overBg; bool _hiding = false; QPixmap _cache; @@ -110,7 +111,8 @@ struct EmojiListWidget::RecentOne { }; EmojiColorPicker::EmojiColorPicker(QWidget *parent) -: RpWidget(parent) { +: RpWidget(parent) +, _overBg(st::emojiPanRadius, st::emojiPanHover) { setMouseTracking(true); } @@ -347,11 +349,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { QPoint tl(w); if (rtl()) tl.setX(width() - tl.x() - st::emojiPanArea.width()); - Ui::FillRoundRect( - p, - QRect(tl, st::emojiPanArea), - st::emojiPanHover, - Ui::EmojiHoverCorners); + _overBg.paint(p, QRect(tl, st::emojiPanArea)); } Ui::Emoji::Draw( p, @@ -377,7 +375,11 @@ EmojiListWidget::EmojiListWidget( EmojiListWidget::EmojiListWidget( QWidget *parent, EmojiListDescriptor &&descriptor) -: Inner(parent, descriptor.session, std::move(descriptor.paused)) +: Inner( + parent, + descriptor.st ? *descriptor.st : st::defaultEmojiPan, + descriptor.session, + std::move(descriptor.paused)) , _controller(descriptor.controller) , _mode(descriptor.mode) , _staticCount(_mode == Mode::Full ? kEmojiSectionCount : 1) @@ -386,6 +388,8 @@ EmojiListWidget::EmojiListWidget( : nullptr) , _localSetsManager( std::make_unique(&session())) +, _customRecentFactory(std::move(descriptor.customRecentFactory)) +, _overBg(st::emojiPanRadius, st().overBg) , _collapsedBg(st::emojiPanExpand.height / 2, st::emojiPanHeaderFg) , _picker(this) , _showPickerTimer([=] { showPicker(); }) { @@ -433,6 +437,10 @@ EmojiListWidget::EmojiListWidget( initButton(_unlock, tr::lng_emoji_featured_unlock(tr::now), true); initButton(_restore, tr::lng_emoji_premium_restore(tr::now), true); }, lifetime()); + + if (!descriptor.customRecentList.empty()) { + fillRecentFrom(descriptor.customRecentList); + } } EmojiListWidget::~EmojiListWidget() { @@ -535,6 +543,7 @@ object_ptr EmojiListWidget::createFooter() { .session = &session(), .paused = pausedMethod(), .parent = this, + .st = &st(), }); _footer = result; @@ -557,7 +566,7 @@ bool EmojiListWidget::enumerateSections(Callback callback) const { ? kCollapsedRows : (info.count + _columnCount - 1) / _columnCount; info.rowsTop = info.top - + (i == 0 ? st::emojiPanPadding : st::emojiPanHeader); + + (i == 0 ? st().padding.top() : st().header); info.rowsBottom = info.rowsTop + (info.rowsCount * _singleSize.height()) + st::roundRadiusSmall; @@ -637,18 +646,17 @@ void EmojiListWidget::setSingleSize(QSize size) { } int EmojiListWidget::countDesiredHeight(int newWidth) { - const auto left = st::emojiPanLeft; - const auto right = st::emojiPanRight; - const auto fullWidth = st::roundRadiusSmall + const auto fullWidth = st().margin.left() + newWidth - + st::emojiScroll.width; - const auto innerWidth = fullWidth - left - right; - _columnCount = std::max(innerWidth / st::emojiPanDesiredSize, 1); + + st().margin.right(); + const auto padding = st().padding; + const auto innerWidth = fullWidth - padding.left() - padding.right(); + _columnCount = std::max(innerWidth / st().desiredSize, 1); const auto singleWidth = innerWidth / _columnCount; - _rowsLeft = left + _rowsLeft = padding.left() + (innerWidth - _columnCount * singleWidth) / 2 - - st::roundRadiusSmall; - setSingleSize({ singleWidth, singleWidth - 4 * st::lineWidth }); + - st().margin.left(); + setSingleSize({ singleWidth, singleWidth - 2 * st().verticalSizeSub }); auto visibleHeight = minimalHeight(); auto minimalHeight = (visibleHeight - st::stickerPanPadding); @@ -659,7 +667,13 @@ int EmojiListWidget::countDesiredHeight(int newWidth) { }; const auto minimalLastHeight = minimalHeight; return qMax(minimalHeight, countResult(minimalLastHeight)) - + st::emojiPanPadding; + + padding.bottom(); +} + +int EmojiListWidget::defaultMinimalHeight() const { + return (_mode != Mode::Full) + ? st::emojiPanArea.height() + : Inner::defaultMinimalHeight(); } void EmojiListWidget::ensureLoaded(int section) { @@ -688,6 +702,9 @@ void EmojiListWidget::ensureLoaded(int section) { } void EmojiListWidget::fillRecent() { + if (_mode != Mode::Full && _mode != Mode::EmojiStatus) { + return; // #TODO emoji_status + } _recent.clear(); _recentCustomIds.clear(); @@ -718,6 +735,21 @@ void EmojiListWidget::fillRecent() { } } +void EmojiListWidget::fillRecentFrom(const std::vector &list) { + Expects(_recent.empty()); + + const auto test = session().isTestMode(); + + _recent.reserve(list.size()); + for (const auto &id : list) { + _recent.push_back({ + .custom = resolveCustomEmoji(id), + .id = { RecentEmojiDocument{.id = id, .test = test } }, + }); + _recentCustomIds.emplace(id); + } +} + void EmojiListWidget::paintEvent(QPaintEvent *e) { Painter p(this); @@ -752,7 +784,7 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { ? (selectedButton->section == info.section) : false; const auto widthForTitle = emojiRight() - - (st::emojiPanHeaderLeft - st::roundRadiusSmall) + - (st().headerLeft - st().margin.left()) - paintButtonGetWidth(p, info, buttonSelected, r); if (info.section > 0 && r.top() < info.rowsTop) { p.setFont(st::emojiPanHeaderFont); @@ -766,13 +798,13 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { titleWidth = st::emojiPanHeaderFont->width(titleText); } const auto left = (info.premiumRequired - ? st::emojiPanHeaderLockedLeft - : st::emojiPanHeaderLeft) - st::roundRadiusSmall; - const auto top = info.top + st::emojiPanHeaderTop; + ? st().headerLockedLeft + : st().headerLeft) - st().margin.left(); + const auto top = info.top + st().headerTop; if (info.premiumRequired) { st::emojiPremiumRequired.paint( p, - st::emojiPanHeaderLockLeft - st::roundRadiusSmall, + st().headerLockLeft - st().margin.left(), top, width()); } @@ -807,16 +839,12 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { drawCollapsedBadge(p, w - _areaPosition, info.count); continue; } - if (selected) { + if (selected && st().overBg->c.alpha() > 0) { auto tl = w; if (rtl()) { tl.setX(width() - tl.x() - st::emojiPanArea.width()); } - Ui::FillRoundRect( - p, - QRect(tl, st::emojiPanArea), - st::emojiPanHover, - Ui::EmojiHoverCorners); + _overBg.paint(p, QRect(tl, st::emojiPanArea)); } if (info.section == int(Section::Recent)) { drawRecent(p, w, now, paused, index); @@ -1070,10 +1098,13 @@ void EmojiListWidget::selectCustom(not_null document) { _premiumChosen.fire_copy(document); return; } - Core::App().settings().incrementRecentEmoji({ RecentEmojiDocument{ - document->id, - document->session().isTestMode(), - } }); + auto &settings = Core::App().settings(); + if (_mode == Mode::Full) { + settings.incrementRecentEmoji({ RecentEmojiDocument{ + document->id, + document->session().isTestMode(), + } }); + } _customChosen.fire({ .document = document }); } @@ -1089,7 +1120,7 @@ void EmojiListWidget::showPicker() { auto y = emojiRect(over->section, over->index).y(); y -= _picker->height() - st::roundRadiusSmall + getVisibleTop(); - if (y < st::emojiPanHeader) { + if (y < st().header) { y += _picker->height() - st::roundRadiusSmall + _singleSize.height() - st::roundRadiusSmall; } auto xmax = width() - _picker->width(); @@ -1124,11 +1155,14 @@ QRect EmojiListWidget::removeButtonRect(int index) const { } QRect EmojiListWidget::removeButtonRect(const SectionInfo &info) const { + if (_mode != Mode::Full) { + return QRect(); + } const auto buttonw = st::stickerPanRemoveSet.rippleAreaPosition.x() + st::stickerPanRemoveSet.rippleAreaSize; const auto buttonh = st::stickerPanRemoveSet.height; const auto buttonx = emojiRight() - st::emojiPanRemoveSkip - buttonw; - const auto buttony = info.top + (st::emojiPanHeader - buttonh) / 2; + const auto buttony = info.top + (st().header - buttonh) / 2; return QRect(buttonx, buttony, buttonw, buttonh); } @@ -1322,12 +1356,18 @@ void EmojiListWidget::processPanelHideFinished() { } void EmojiListWidget::refreshRecent() { + if (_mode != Mode::Full && _mode != Mode::EmojiStatus) { + return; // #TODO emoji_status + } clearSelection(); fillRecent(); resizeToWidth(width()); } void EmojiListWidget::refreshCustom() { + if (_mode == Mode::RecentReactions) { + return; + } auto old = base::take(_custom); const auto session = &this->session(); const auto premiumPossible = session->premiumPossible(); @@ -1486,15 +1526,19 @@ not_null EmojiListWidget::resolveCustomEmoji( if (i != end(_customEmoji)) { return i->second.emoji.get(); } + auto repaint = repaintCallback(documentId, RecentEmojiSectionSetId()); + auto custom = _customRecentFactory + ? _customRecentFactory(documentId, repaint) + : nullptr; + if (!custom) { + custom = session().data().customEmojiManager().create( + documentId, + std::move(repaint), + Data::CustomEmojiManager::SizeTag::Large); + } return _customEmoji.emplace( documentId, - CustomEmojiInstance{ - .emoji = session().data().customEmojiManager().create( - documentId, - repaintCallback(documentId, RecentEmojiSectionSetId()), - Data::CustomEmojiManager::SizeTag::Large), - .recentOnly = true, - } + CustomEmojiInstance{ .emoji = std::move(custom), .recentOnly = true } ).first->second.emoji.get(); } @@ -1503,7 +1547,7 @@ std::vector EmojiListWidget::fillIcons() { result.reserve(2 + _custom.size()); result.emplace_back(RecentEmojiSectionSetId()); - if (_mode == Mode::EmojiStatus) { + if (_mode != Mode::Full) { } else if (_custom.empty()) { using Section = Ui::Emoji::Section; for (auto i = int(Section::People); i <= int(Section::Symbols); ++i) { @@ -1534,7 +1578,9 @@ int EmojiListWidget::paintButtonGetWidth( auto &custom = _custom[info.section - _staticCount]; if (hasRemoveButton(info.section)) { const auto remove = removeButtonRect(info); - if (remove.intersects(clip)) { + if (remove.isEmpty()) { + return 0; + } else if (remove.intersects(clip)) { if (custom.ripple) { custom.ripple->paint( p, @@ -1603,7 +1649,7 @@ void EmojiListWidget::updateSelected() { if (hasButton(section) && myrtlrect(buttonRect(section)).contains(p.x(), p.y())) { newSelected = OverButton{ section }; - } else if (section >= _staticCount) { + } else if (section >= _staticCount && _mode == Mode::Full) { newSelected = OverSet{ section }; } } else if (p.y() >= info.rowsTop && p.y() < info.rowsBottom) { diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index 67b4c4ccb..c7a86be04 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/round_rect.h" #include "base/timer.h" +namespace style { +struct EmojiPan; +} // namespace style + namespace Core { struct RecentEmojiId; } // namespace Core @@ -56,7 +60,8 @@ class LocalStickersManager; enum class EmojiListMode { Full, EmojiStatus, - Reactions, + FullReactions, + RecentReactions, }; struct EmojiListDescriptor { @@ -64,6 +69,11 @@ struct EmojiListDescriptor { EmojiListMode mode = EmojiListMode::Full; Window::SessionController *controller = nullptr; Fn paused; + std::vector customRecentList; + Fn( + DocumentId, + Fn)> customRecentFactory; + const style::EmojiPan *st = nullptr; }; class EmojiListWidget @@ -120,6 +130,7 @@ protected: void processHideFinished() override; void processPanelHideFinished() override; int countDesiredHeight(int newWidth) override; + int defaultMinimalHeight() const override; private: struct SectionInfo { @@ -272,6 +283,7 @@ private: void repaintCustom(uint64 setId); void fillRecent(); + void fillRecentFrom(const std::vector &list); [[nodiscard]] not_null resolveCustomEmoji( not_null document, uint64 setId); @@ -289,6 +301,9 @@ private: StickersListFooter *_footer = nullptr; std::unique_ptr _premiumIcon; std::unique_ptr _localSetsManager; + Fn( + DocumentId, + Fn)> _customRecentFactory; int _counts[kEmojiSectionCount]; std::vector _recent; @@ -299,6 +314,7 @@ private: std::vector _custom; base::flat_map _customEmoji; bool _allowWithoutPremium = false; + Ui::RoundRect _overBg; int _rowsLeft = 0; int _columnCount = 1; diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 03616fb9b..56a83763e 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -105,7 +105,8 @@ private: }; -GifsListWidget::Footer::Footer(not_null parent) : InnerFooter(parent) +GifsListWidget::Footer::Footer(not_null parent) +: InnerFooter(parent, st::defaultEmojiPan) , _pan(parent) , _field(this, st::gifsSearchField, tr::lng_gifs_search()) , _cancel(this, st::gifsSearchCancel) { @@ -170,7 +171,11 @@ GifsListWidget::GifsListWidget( QWidget *parent, not_null controller, Window::GifPauseReason level) -: Inner(parent, &controller->session(), Window::PausedIn(controller, level)) +: Inner( + parent, + st::defaultEmojiPan, + &controller->session(), + Window::PausedIn(controller, level)) , _controller(controller) , _api(&session().mtp()) , _section(Section::Gifs) diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp index 2534a3892..65ae21ad8 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp @@ -181,7 +181,9 @@ void GradientPremiumStar::renderOnDemand() const { } StickersListFooter::StickersListFooter(Descriptor &&descriptor) -: InnerFooter(descriptor.parent) +: InnerFooter( + descriptor.parent, + descriptor.st ? *descriptor.st : st::defaultEmojiPan) , _session(descriptor.session) , _paused(descriptor.paused) , _searchButtonVisible(descriptor.searchButtonVisible) @@ -189,14 +191,14 @@ StickersListFooter::StickersListFooter(Descriptor &&descriptor) , _iconState([=] { update(); }) , _subiconState([=] { update(); }) , _selectionBg(st::roundRadiusLarge, st::windowBgRipple) -, _subselectionBg(st::emojiIconArea / 2, st::windowBgRipple) +, _subselectionBg(st().iconArea / 2, st::windowBgRipple) , _barSelection(descriptor.barSelection) { setMouseTracking(true); - _iconsLeft = st::emojiCategorySkip + (_searchButtonVisible + _iconsLeft = st().iconSkip + (_searchButtonVisible ? st::stickerIconWidth : 0); - _iconsRight = st::emojiCategorySkip + (_settingsButtonVisible + _iconsRight = st().iconSkip + (_settingsButtonVisible ? st::stickerIconWidth : 0); @@ -552,7 +554,7 @@ void StickersListFooter::paintEvent(QPaintEvent *e) { _iconsLeft, _iconsTop, width() - _iconsLeft - _iconsRight, - st::emojiFooterHeight); + st().footer); if (rtl()) { clip.moveLeft(width() - _iconsLeft - clip.width()); } @@ -582,7 +584,7 @@ void StickersListFooter::paintSelectionBg(Painter &p) const { selx = width() - selx - selw; } const auto sely = _iconsTop; - const auto area = st::emojiIconArea; + const auto area = st().iconArea; const auto rect = QRect( QPoint(selx, sely) + _areaPosition, QSize(selw - 2 * _areaPosition.x(), area)); @@ -613,7 +615,7 @@ void StickersListFooter::paintSelectionBar(Painter &p) const { } p.fillRect( selx, - _iconsTop + st::emojiFooterHeight - st::stickerIconPadding, + _iconsTop + st().footer - st::stickerIconPadding, selw, st::stickerIconSel, st::stickerIconSelColor); @@ -621,27 +623,27 @@ void StickersListFooter::paintSelectionBar(Painter &p) const { void StickersListFooter::paintLeftRightFading(Painter &p) const { auto o_left = std::clamp( - _iconState.x.current() / st::stickerIconLeft.width(), + _iconState.x.current() / st().fadeLeft.width(), 0., 1.); if (o_left > 0) { p.setOpacity(o_left); - st::stickerIconLeft.fill(p, style::rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiFooterHeight, width())); + st().fadeLeft.fill(p, style::rtlrect(_iconsLeft, _iconsTop, st().fadeLeft.width(), st().footer, width())); p.setOpacity(1.); } auto o_right = std::clamp( - (_iconState.max - _iconState.x.current()) / st::stickerIconRight.width(), + (_iconState.max - _iconState.x.current()) / st().fadeRight.width(), 0., 1.); if (o_right > 0) { p.setOpacity(o_right); - st::stickerIconRight.fill( + st().fadeRight.fill( p, style::rtlrect( - width() - _iconsRight - st::stickerIconRight.width(), + width() - _iconsRight - st().fadeRight.width(), _iconsTop, - st::stickerIconRight.width(), - st::emojiFooterHeight, width())); + st().fadeRight.width(), + st().footer, width())); p.setOpacity(1.); } } @@ -880,19 +882,19 @@ void StickersListFooter::updateSelected() { && x >= searchLeft && x < searchLeft + _singleWidth && y >= _iconsTop - && y < _iconsTop + st::emojiFooterHeight) { + && y < _iconsTop + st().footer) { newOver = SpecialOver::Search; } else if (_settingsButtonVisible && x >= settingsLeft && x < settingsLeft + _singleWidth && y >= _iconsTop - && y < _iconsTop + st::emojiFooterHeight) { + && y < _iconsTop + st().footer) { if (!_icons.empty() && !hasOnlyFeaturedSets()) { newOver = SpecialOver::Settings; } } else if (!_icons.empty()) { if (y >= _iconsTop - && y < _iconsTop + st::emojiFooterHeight + && y < _iconsTop + st().footer && x >= _iconsLeft && x < width() - _iconsRight) { enumerateIcons([&](const IconInfo &info) { @@ -994,11 +996,11 @@ void StickersListFooter::refreshIconsGeometry( && _icons[1].setId == EmojiSectionSetId(EmojiSection::People)) { _singleWidth = (width() - _iconsLeft - _iconsRight) / _icons.size(); } else { - _singleWidth = st::emojiIconWidth; + _singleWidth = st().iconWidth; } _areaPosition = QPoint( - (_singleWidth - st::emojiIconArea) / 2, - (st::emojiFooterHeight - st::emojiIconArea) / 2); + (_singleWidth - st().iconArea) / 2, + (st().footer - st().iconArea) / 2); refreshScrollableDimensions(); refreshSubiconsGeometry(); _iconState.selected = _subiconState.selected = -1; @@ -1047,7 +1049,7 @@ void StickersListFooter::paintStickerSettingsIcon(Painter &p) const { p, settingsLeft + (_singleWidth - st::stickersSettings.width()) / 2, - _iconsTop + st::emojiCategory.iconPosition.y(), + _iconsTop + st::emojiCategoryIconTop, width()); } @@ -1056,7 +1058,7 @@ void StickersListFooter::paintSearchIcon(Painter &p) const { st::stickersSearch.paint( p, searchLeft + (_singleWidth - st::stickersSearch.width()) / 2, - _iconsTop + st::emojiCategory.iconPosition.y(), + _iconsTop + st::emojiCategoryIconTop, width()); } @@ -1145,7 +1147,7 @@ void StickersListFooter::updateSetIcon(uint64 setId) { } void StickersListFooter::updateSetIconAt(int left) { - update(left, _iconsTop, _singleWidth, st::emojiFooterHeight); + update(left, _iconsTop, _singleWidth, st().footer); } void StickersListFooter::paintSetIcon( @@ -1164,7 +1166,7 @@ void StickersListFooter::paintSetIcon( ? icon.stickerMedia->thumbnail() : nullptr; const auto x = info.adjustedLeft + (_singleWidth - icon.pixw) / 2; - const auto y = _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2; + const auto y = _iconsTop + (st().footer - icon.pixh) / 2; if (icon.custom) { icon.custom->paint(p, x, y, now, st::emojiIconFg->c, paused); } else if (icon.lottie && icon.lottie->ready()) { @@ -1177,7 +1179,7 @@ void StickersListFooter::paintSetIcon( p.drawImage( QRect( (info.adjustedLeft + (_singleWidth - size.width()) / 2), - _iconsTop + (st::emojiFooterHeight - size.height()) / 2, + _iconsTop + (st().footer - size.height()) / 2, size.width(), size.height()), frame); @@ -1212,14 +1214,14 @@ void StickersListFooter::paintSetIcon( p, icon.megagroupUserpic, info.adjustedLeft + (_singleWidth - size) / 2, - _iconsTop + (st::emojiFooterHeight - size) / 2, + _iconsTop + (st().footer - size) / 2, width(), st::stickerGroupCategorySize); } else if (icon.setId == Data::Stickers::PremiumSetId) { const auto size = st::stickersPremium.size(); p.drawImage( info.adjustedLeft + (_singleWidth - size.width()) / 2, - _iconsTop + (st::emojiFooterHeight - size.height()) / 2, + _iconsTop + (st().footer - size.height()) / 2, _premiumIcon.image()); } else { using Section = Ui::Emoji::Section; @@ -1252,7 +1254,7 @@ void StickersListFooter::paintSetIcon( icon->paint( p, left + (_singleWidth - icon->width()) / 2, - _iconsTop + (st::emojiFooterHeight - icon->height()) / 2, + _iconsTop + (st().footer - icon->height()) / 2, width()); }; if (_icons[info.index].setId == AllEmojiSectionSetId() @@ -1263,7 +1265,7 @@ void StickersListFooter::paintSetIcon( left + skip, _iconsTop, info.width - 2 * skip, - st::emojiFooterHeight, + st().footer, Qt::IntersectClip); enumerateSubicons([&](const IconInfo &info) { if (info.visible) { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.h b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.h index b13b5ad79..e1f8bb75c 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.h @@ -33,6 +33,10 @@ namespace Window { class SessionController; } // namespace Window +namespace style { +struct EmojiPan; +} // namespace style + namespace ChatHelpers { enum class ValidateIconAnimations { @@ -98,6 +102,7 @@ public: bool searchButtonVisible = false; bool settingsButtonVisible = false; bool barSelection = false; + const style::EmojiPan *st = nullptr; }; explicit StickersListFooter(Descriptor &&descriptor); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 4cc13f3ff..2e0b1cd65 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -164,7 +164,11 @@ StickersListWidget::StickersListWidget( not_null controller, Window::GifPauseReason level, bool masks) -: Inner(parent, &controller->session(), Window::PausedIn(controller, level)) +: Inner( + parent, + st::defaultEmojiPan, + &controller->session(), + Window::PausedIn(controller, level)) , _controller(controller) , _api(&session().mtp()) , _localSetsManager(std::make_unique(&session())) @@ -176,7 +180,7 @@ StickersListWidget::StickersListWidget( st::windowBgRipple, st::windowBgOver, [=] { update(); })) -, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st::emojiPanHeaderLeft) +, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st().headerLeft) , _addText(tr::lng_stickers_featured_add(tr::now).toUpper()) , _addWidth(st::stickersTrendingAdd.font->width(_addText)) , _settings(this, tr::lng_stickers_you_have(tr::now)) @@ -411,7 +415,7 @@ bool StickersListWidget::enumerateSections(Callback callback) const { const auto titleSkip = set.externalLayout ? st::stickersTrendingHeader : setHasTitle(set) - ? st::emojiPanHeader + ? st().header : st::stickerPanPadding; info.rowsTop = info.top + titleSkip; if (set.externalLayout) { @@ -462,16 +466,16 @@ int StickersListWidget::countDesiredHeight(int newWidth) { if (newWidth <= st::stickerPanWidthMin) { return 0; } - auto availableWidth = newWidth - (st::stickerPanPadding - st::roundRadiusSmall); + auto availableWidth = newWidth - (st::stickerPanPadding - st().margin.left()); auto columnCount = availableWidth / st::stickerPanWidthMin; auto singleWidth = availableWidth / columnCount; - auto fullWidth = (st::roundRadiusSmall + newWidth + st::emojiScroll.width); + auto fullWidth = (st().margin.left() + newWidth + st::emojiScroll.width); auto rowsRight = (fullWidth - columnCount * singleWidth) / 2; accumulate_max(rowsRight, st::emojiScroll.width); _rowsLeft = fullWidth - columnCount * singleWidth - rowsRight - - st::roundRadiusSmall; + - st().margin.left(); _singleSize = QSize(singleWidth, singleWidth); setColumnCount(columnCount); @@ -831,7 +835,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { ? set.count : loadedCount; - auto widthForTitle = stickersRight() - (st::emojiPanHeaderLeft - st::roundRadiusSmall); + auto widthForTitle = stickersRight() - (st().headerLeft - st().margin.left()); if (featuredHasAddButton(info.section)) { auto add = featuredAddRect(info); auto selected = selectedButton ? (selectedButton->section == info.section) : false; @@ -867,7 +871,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { } p.setFont(st::stickersTrendingHeaderFont); p.setPen(st::stickersTrendingHeaderFg); - p.drawTextLeft(st::emojiPanHeaderLeft - st::roundRadiusSmall, info.top + st::stickersTrendingHeaderTop, width(), titleText, titleWidth); + p.drawTextLeft(st().headerLeft - st().margin.left(), info.top + st::stickersTrendingHeaderTop, width(), titleText, titleWidth); if (set.flags & SetFlag::Unread) { p.setPen(Qt::NoPen); @@ -875,14 +879,14 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { { PainterHighQualityEnabler hq(p); - p.drawEllipse(style::rtlrect(st::emojiPanHeaderLeft - st::roundRadiusSmall + titleWidth + st::stickersFeaturedUnreadSkip, info.top + st::stickersTrendingHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); + p.drawEllipse(style::rtlrect(st().headerLeft - st().margin.left() + titleWidth + st::stickersFeaturedUnreadSkip, info.top + st::stickersTrendingHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); } } auto statusText = (count > 0) ? tr::lng_stickers_count(tr::now, lt_count, count) : tr::lng_contacts_loading(tr::now); p.setFont(st::stickersTrendingSubheaderFont); p.setPen(st::stickersTrendingSubheaderFg); - p.drawTextLeft(st::emojiPanHeaderLeft - st::roundRadiusSmall, info.top + st::stickersTrendingSubheaderTop, width(), statusText); + p.drawTextLeft(st().headerLeft - st().margin.left(), info.top + st::stickersTrendingSubheaderTop, width(), statusText); if (info.rowsTop >= clip.y() + clip.height()) { return true; @@ -904,7 +908,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { if (setHasTitle(set) && clip.top() < info.rowsTop) { auto titleText = set.title; auto titleWidth = st::stickersTrendingHeaderFont->width(titleText); - auto widthForTitle = stickersRight() - (st::emojiPanHeaderLeft - st::roundRadiusSmall); + auto widthForTitle = stickersRight() - (st().headerLeft - st().margin.left()); if (hasRemoveButton(info.section)) { auto remove = removeButtonRect(info); auto selected = selectedButton ? (selectedButton->section == info.section) : false; @@ -924,7 +928,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { } p.setFont(st::emojiPanHeaderFont); p.setPen(st::emojiPanHeaderFg); - p.drawTextLeft(st::emojiPanHeaderLeft - st::roundRadiusSmall, info.top + st::emojiPanHeaderTop, width(), titleText, titleWidth); + p.drawTextLeft(st().headerLeft - st().margin.left(), info.top + st().headerTop, width(), titleText, titleWidth); } if (clip.top() + clip.height() <= info.rowsTop) { return true; @@ -1054,7 +1058,7 @@ void StickersListWidget::paintEmptySearchResults(Painter &p) { } int StickersListWidget::megagroupSetInfoLeft() const { - return st::emojiPanHeaderLeft - st::roundRadiusSmall; + return st().headerLeft - st().margin.left(); } void StickersListWidget::paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected) { @@ -1440,7 +1444,7 @@ QRect StickersListWidget::removeButtonRect(const SectionInfo &info) const { auto buttonw = st::stickerPanRemoveSet.width; auto buttonh = st::stickerPanRemoveSet.height; auto buttonx = stickersRight() - buttonw; - auto buttony = info.top + (st::emojiPanHeader - buttonh) / 2; + auto buttony = info.top + (st().header - buttonh) / 2; return QRect(buttonx, buttony, buttonw, buttonh); } @@ -2322,7 +2326,7 @@ std::vector StickersListWidget::fillIcons() { Assert(set != nullptr); const auto s = _mySets[i].thumbnailDocument; const auto availw = st::stickerIconWidth - 2 * st::stickerIconPadding; - const auto availh = st::emojiFooterHeight - 2 * st::stickerIconPadding; + const auto availh = st().footer - 2 * st::stickerIconPadding; const auto size = set->hasThumbnail() ? QSize( set->thumbnailLocation().width(), diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 55fccdf28..8931c2c2b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -46,6 +46,10 @@ class ReaderPointer; enum class Notification; } // namespace Media::Clip +namespace style { +struct EmojiPan; +} // namespace style + namespace ChatHelpers { struct StickerIcon; @@ -320,8 +324,6 @@ private: void addSearchRow(not_null set); void showPreview(); - void validatePremiumLock(Set &set, int index, const QImage &frame); - void validatePremiumStar(); Ui::MessageSendingAnimationFrom messageSentAnimationInfo( int section, diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 410adc5c2..127995814 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -107,7 +107,7 @@ void TabbedSelector::SlideAnimation::setFinalImages(Direction direction, QImage _painterInnerBottom = _innerBottom / cIntRetinaFactor(); _painterInnerWidth = _innerWidth / cIntRetinaFactor(); _painterInnerHeight = _innerHeight / cIntRetinaFactor(); - _painterCategoriesTop = _painterInnerBottom - st::emojiFooterHeight; + _painterCategoriesTop = _painterInnerBottom - st::defaultEmojiPan.footer; _wasSectionIcons = wasSectionIcons; } @@ -294,6 +294,7 @@ TabbedSelector::TabbedSelector( Window::GifPauseReason level, Mode mode) : RpWidget(parent) +, _st(st::defaultEmojiPan) , _controller(controller) , _level(level) , _mode(mode) @@ -605,7 +606,7 @@ void TabbedSelector::updateScrollGeometry(QSize oldSize) { } void TabbedSelector::updateFooterGeometry() { - _footerTop = _dropDown ? 0 : (height() - st::emojiFooterHeight); + _footerTop = _dropDown ? 0 : (height() - _st.footer); for (auto &tab : _tabs) { tab.footer()->resizeToWidth(width()); tab.footer()->moveToLeft(0, _footerTop); @@ -684,7 +685,7 @@ void TabbedSelector::paintContent(Painter &p) { 0, _footerTop - (_dropDown ? 0 : _roundRadius), width(), - st::emojiFooterHeight + _roundRadius); + _st.footer + _roundRadius); Ui::FillRoundRect( p, footerPart, @@ -696,7 +697,7 @@ void TabbedSelector::paintContent(Painter &p) { if (_tabsSlider) { p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg); } - p.fillRect(0, _footerTop, width(), st::emojiFooterHeight, footerBg); + p.fillRect(0, _footerTop, width(), _st.footer, footerBg); } auto sidesTop = marginTop(); @@ -719,18 +720,18 @@ void TabbedSelector::paintContent(Painter &p) { int TabbedSelector::marginTop() const { return _dropDown - ? st::emojiFooterHeight + ? _st.footer : _tabsSlider ? (_tabsSlider->height() - st::lineWidth) : _roundRadius; } int TabbedSelector::scrollTop() const { - return tabbed() ? marginTop() : _dropDown ? st::emojiFooterHeight : 0; + return tabbed() ? marginTop() : _dropDown ? _st.footer : 0; } int TabbedSelector::marginBottom() const { - return _dropDown ? _roundRadius : st::emojiFooterHeight; + return _dropDown ? _roundRadius : _st.footer; } int TabbedSelector::scrollBottom() const { @@ -1200,15 +1201,18 @@ TabbedSelector::Inner::Inner( Window::GifPauseReason level) : Inner( parent, + st::defaultEmojiPan, &controller->session(), Window::PausedIn(controller, level)) { } TabbedSelector::Inner::Inner( QWidget *parent, + const style::EmojiPan &st, not_null session, Fn paused) : RpWidget(parent) +, _st(st) , _session(session) , _paused(paused) { } @@ -1271,7 +1275,11 @@ int TabbedSelector::Inner::resizeGetHeight(int newWidth) { int TabbedSelector::Inner::minimalHeight() const { return (_minimalHeight > 0) ? _minimalHeight - : (st::emojiPanMaxHeight - st::emojiFooterHeight); + : defaultMinimalHeight(); +} + +int TabbedSelector::Inner::defaultMinimalHeight() const { + return st::emojiPanMaxHeight - _st.footer; } void TabbedSelector::Inner::hideFinished() { @@ -1289,9 +1297,16 @@ void TabbedSelector::Inner::panelHideFinished() { } } -TabbedSelector::InnerFooter::InnerFooter(QWidget *parent) -: RpWidget(parent) { - resize(st::emojiPanWidth, st::emojiFooterHeight); +TabbedSelector::InnerFooter::InnerFooter( + QWidget *parent, + const style::EmojiPan &st) +: RpWidget(parent) +, _st(st) { + resize(st::emojiPanWidth, _st.footer); +} + +const style::EmojiPan &TabbedSelector::InnerFooter::st() const { + return _st; } } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index e495ab563..f93509c43 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -41,6 +41,10 @@ namespace SendMenu { enum class Type; } // namespace SendMenu +namespace style { +struct EmojiPan; +} // namespace style + namespace ChatHelpers { enum class SelectorTab { @@ -233,6 +237,7 @@ private: not_null gifs() const; not_null masks() const; + const style::EmojiPan &_st; const not_null _controller; const Window::GifPauseReason _level = {}; @@ -278,12 +283,16 @@ public: Window::GifPauseReason level); Inner( QWidget *parent, + const style::EmojiPan &st, not_null session, Fn paused); [[nodiscard]] Main::Session &session() const { return *_session; } + [[nodiscard]] const style::EmojiPan &st() const { + return _st; + } [[nodiscard]] Fn pausedMethod() const { return _paused; } @@ -332,6 +341,7 @@ protected: int visibleTop, int visibleBottom) override; int minimalHeight() const; + virtual int defaultMinimalHeight() const; int resizeGetHeight(int newWidth) override final; virtual int countDesiredHeight(int newWidth) = 0; @@ -347,6 +357,7 @@ protected: void checkHideWithBox(QPointer box); private: + const style::EmojiPan &_st; const not_null _session; const Fn _paused; @@ -364,7 +375,9 @@ private: class TabbedSelector::InnerFooter : public Ui::RpWidget { public: - InnerFooter(QWidget *parent); + InnerFooter(QWidget *parent, const style::EmojiPan &st); + + [[nodiscard]] const style::EmojiPan &st() const; protected: virtual void processHideFinished() { @@ -373,6 +386,9 @@ protected: } friend class Inner; +private: + const style::EmojiPan &_st; + }; } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index 0c9b92daf..9fa351267 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "apiwrap.h" #include "styles/style_chat.h" +#include "styles/style_chat_helpers.h" #include "data/stickers/data_stickers.h" #include "ui/widgets/input_fields.h" @@ -41,6 +42,7 @@ using SizeTag = CustomEmojiManager::SizeTag; case SizeTag::Normal: return LottieSize::EmojiInteraction; case SizeTag::Large: return LottieSize::EmojiInteractionReserved1; case SizeTag::Isolated: return LottieSize::EmojiInteractionReserved2; + case SizeTag::ReactionFake: return LottieSize::EmojiInteractionReserved3; } Unexpected("SizeTag value in CustomEmojiManager-LottieSizeFromTag."); } @@ -52,6 +54,8 @@ using SizeTag = CustomEmojiManager::SizeTag; case SizeTag::Isolated: return (st::largeEmojiSize + 2 * st::largeEmojiOutline) * style::DevicePixelRatio(); + case SizeTag::ReactionFake: + return st::reactStripImage * style::DevicePixelRatio(); } Unexpected("SizeTag value in CustomEmojiManager-SizeFromTag."); } @@ -663,6 +667,9 @@ Session &CustomEmojiManager::owner() const { int FrameSizeFromTag(SizeTag tag) { const auto emoji = EmojiSizeFromTag(tag); + if (tag == SizeTag::ReactionFake) { + return emoji; + } const auto factor = style::DevicePixelRatio(); return Ui::Text::AdjustCustomEmojiSize(emoji / factor) * factor; } diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h index 3ec7c2b9a..c772226a0 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h @@ -33,6 +33,7 @@ public: Normal, Large, Isolated, + ReactionFake, kCount, }; diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_button.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_button.cpp index 133c6cd62..dda4703e9 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_button.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_button.cpp @@ -318,6 +318,7 @@ Manager::Manager( , _inner(QRect({}, st::reactionCornerSize)) , _strip( _inner, + st::reactionCornerImage, crl::guard(this, [=] { updateCurrentButton(); }), std::move(iconFactory)) , _cachedRound( diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp index e5b6acd9a..9d9fe7163 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -7,13 +7,78 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/view/reactions/history_view_reactions_selector.h" +#include "ui/widgets/scroll_area.h" #include "ui/widgets/popup_menu.h" +#include "ui/widgets/shadow.h" #include "history/history_item.h" +#include "data/data_document.h" +#include "data/data_session.h" +#include "data/stickers/data_custom_emoji.h" +#include "main/main_session.h" +#include "chat_helpers/emoji_list_widget.h" +#include "chat_helpers/stickers_list_footer.h" #include "window/window_session_controller.h" #include "styles/style_chat_helpers.h" #include "styles/style_chat.h" namespace HistoryView::Reactions { +namespace { + +class ShiftedEmoji final : public Ui::Text::CustomEmoji { +public: + ShiftedEmoji( + not_null manager, + DocumentId id, + Fn repaint, + QPoint shift); + + QString entityData() override; + void paint( + QPainter &p, + int x, + int y, + crl::time now, + const QColor &preview, + bool paused) override; + void unload() override; + +private: + std::unique_ptr _real; + QPoint _shift; + +}; + +ShiftedEmoji::ShiftedEmoji( + not_null manager, + DocumentId id, + Fn repaint, + QPoint shift) +: _real(manager->create( + id, + std::move(repaint), + Data::CustomEmojiManager::SizeTag::ReactionFake)) +, _shift(shift) { +} + +QString ShiftedEmoji::entityData() { + return _real->entityData(); +} + +void ShiftedEmoji::paint( + QPainter &p, + int x, + int y, + crl::time now, + const QColor &preview, + bool paused) { + _real->paint(p, x + _shift.x(), y + _shift.y(), now, preview, paused); +} + +void ShiftedEmoji::unload() { + _real->unload(); +} + +} // namespace Selector::Selector( not_null parent, @@ -29,6 +94,7 @@ Selector::Selector( st::reactStripHeight) , _strip( QRect(0, 0, st::reactStripSize, st::reactStripSize), + st::reactStripImage, crl::guard(this, [=] { update(_inner); }), std::move(iconFactory)) , _size(st::reactStripSize) @@ -70,7 +136,7 @@ QMargins Selector::extentsForShadow() const { } int Selector::extendTopForCategories() const { - return _reactions.customAllowed ? st::emojiFooterHeight : 0; + return _reactions.customAllowed ? st::reactPanelEmojiPan.footer : 0; } int Selector::desiredHeight() const { @@ -275,6 +341,10 @@ void Selector::finishExpand() { if (!fill.isEmpty()) { q.fillRect(fill, st::defaultPopupMenu.menu.itemBg); } + if (_footer) { + _footer->show(); + } + _scroll->show(); } void Selector::paintBubble(QPainter &p, int innerWidth) { @@ -348,9 +418,8 @@ void Selector::mouseReleaseEvent(QMouseEvent *e) { _premiumPromoChosen.fire({}); } else if (selected == Strip::AddedButton::Expand) { expand(); - } else { - const auto id = std::get_if(&selected); - if (id && !id->empty()) { + } else if (const auto id = std::get_if(&selected)) { + if (!id->empty()) { _chosen.fire({ .id = *id }); } } @@ -360,13 +429,23 @@ void Selector::expand() { const auto parent = parentWidget()->geometry(); const auto additionalBottom = parent.height() - y() - height(); const auto additional = _specialExpandTopSkip + additionalBottom; - if (additionalBottom < 0 || additional <= 0) { + const auto strong = _parentController.get(); + if (additionalBottom < 0 || additional <= 0 || !strong) { return; - } - if (additionalBottom > 0) { + } else if (additionalBottom > 0) { resize(width(), height() + additionalBottom); raise(); } + + createList(strong); + cacheExpandIcon(); + + _paintBuffer = _cachedRound.PrepareImage(size()); + _expanded = true; + _expanding.start([=] { update(); }, 0., 1., st::slideDuration); +} + +void Selector::cacheExpandIcon() { _expandIconCache = _cachedRound.PrepareImage({ _size, _size }); _expandIconCache.fill(Qt::transparent); auto q = QPainter(&_expandIconCache); @@ -378,9 +457,130 @@ void Selector::expand() { QRect(-(count - 1) * _size, 0, count * _size, _size), 1., false); - _paintBuffer = _cachedRound.PrepareImage(size()); - _expanded = true; - _expanding.start([=] { update(); }, 0., 1., st::slideDuration); +} + +void Selector::createList(not_null controller) { + using namespace ChatHelpers; + auto recent = std::vector(); + auto defaultReactionIds = base::flat_map(); + recent.reserve(_reactions.recent.size()); + for (const auto &reaction : _reactions.recent) { + if (const auto id = reaction->id.custom()) { + recent.push_back(id); + } else { + recent.push_back(reaction->selectAnimation->id); + defaultReactionIds.emplace(recent.back(), reaction->id.emoji()); + } + }; + const auto manager = &controller->session().data().customEmojiManager(); + const auto shift = [&] { + // See EmojiListWidget custom emoji position resolving. + const auto area = st::emojiPanArea; + const auto areaPosition = QPoint( + (_size - area.width()) / 2, + (_size - area.height()) / 2); + const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio(); + const auto innerPosition = QPoint( + (area.width() - esize) / 2, + (area.height() - esize) / 2); + const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize); + const auto customSkip = (esize - customSize) / 2; + const auto customPosition = QPoint(customSkip, customSkip); + return QPoint( + (_size - st::reactStripImage) / 2, + (_size - st::reactStripImage) / 2 + ) - areaPosition - innerPosition - customPosition; + }(); + auto factory = [=](DocumentId id, Fn repaint) { + return defaultReactionIds.contains(id) + ? std::make_unique( + manager, + id, + std::move(repaint), + shift) + : nullptr; + }; + _scroll = Ui::CreateChild(this, st::reactPanelScroll); + _scroll->hide(); + + const auto st = lifetime().make_state( + st::reactPanelEmojiPan); + st->padding.setTop(_skipy); + _list = _scroll->setOwnedWidget( + object_ptr(_scroll, EmojiListDescriptor{ + .session = &controller->session(), + .mode = (_reactions.customAllowed + ? EmojiListMode::FullReactions + : EmojiListMode::RecentReactions), + .controller = controller, + .paused = [] { return false; }, + .customRecentList = std::move(recent), + .customRecentFactory = std::move(factory), + .st = st, + }) + ).data(); + + _list->customChosen( + ) | rpl::start_with_next([=](const TabbedSelector::FileChosen &chosen) { + const auto id = DocumentId{ chosen.document->id }; + const auto i = defaultReactionIds.find(id); + if (i != end(defaultReactionIds)) { + _chosen.fire({ .id = { i->second } }); + } else { + _chosen.fire({ .id = { id } }); + } + }, _list->lifetime()); + + const auto inner = rect().marginsRemoved(extentsForShadow()); + const auto footer = _reactions.customAllowed + ? _list->createFooter().data() + : nullptr; + if ((_footer = static_cast(footer))) { + _footer->setParent(this); + _footer->hide(); + _footer->setGeometry( + inner.x(), + inner.y(), + inner.width(), + _footer->height()); + const auto shadow = Ui::CreateChild(this); + _footer->geometryValue() | rpl::start_with_next([=](QRect geometry) { + shadow->setGeometry( + geometry.x(), + geometry.y() + geometry.height(), + geometry.width(), + st::lineWidth); + }, shadow->lifetime()); + shadow->show(); + } + const auto geometry = inner.marginsRemoved( + st::reactPanelEmojiPan.margin); + _list->move(0, 0); + _list->refreshEmoji(); + _list->resizeToWidth(geometry.width()); + _list->show(); + + const auto updateVisibleTopBottom = [=] { + const auto scrollTop = _scroll->scrollTop(); + const auto scrollBottom = scrollTop + _scroll->height(); + _list->setVisibleTopBottom(scrollTop, scrollBottom); + }; + _scroll->scrollTopChanges( + ) | rpl::start_with_next(updateVisibleTopBottom, _list->lifetime()); + + _list->scrollToRequests( + ) | rpl::start_with_next([=](int y) { + _scroll->scrollToY(y); + }, _list->lifetime()); + + _scroll->setGeometry(inner.marginsRemoved({ + st::reactPanelEmojiPan.margin.left(), + _footer ? _footer->height() : 0, + 0, + 0, + })); + + updateVisibleTopBottom(); } bool AdjustMenuGeometryForSelector( diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h index ee6b97db2..7ea4cd965 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h @@ -20,6 +20,8 @@ struct ReactionId; namespace ChatHelpers { class TabbedPanel; +class EmojiListWidget; +class StickersListFooter; } // namespace ChatHelpers namespace Window { @@ -28,6 +30,7 @@ class SessionController; namespace Ui { class PopupMenu; +class ScrollArea; } // namespace Ui namespace HistoryView::Reactions { @@ -78,12 +81,14 @@ private: void paintExpanded(QPainter &p); void paintBubble(QPainter &p, int innerWidth); void paintBackgroundToBuffer(); - void finishExpand(); [[nodiscard]] int lookupSelectedIndex(QPoint position) const; void setSelected(int index); void expand(); + void cacheExpandIcon(); + void createList(not_null controller); + void finishExpand(); const base::weak_ptr _parentController; const Data::PossibleItemReactions _reactions; @@ -93,6 +98,10 @@ private: rpl::event_stream _chosen; rpl::event_stream<> _premiumPromoChosen; + Ui::ScrollArea *_scroll = nullptr; + ChatHelpers::EmojiListWidget *_list = nullptr; + ChatHelpers::StickersListFooter *_footer = nullptr; + QImage _paintBuffer; Ui::Animations::Simple _expanding; float64 _appearProgress = 0.; diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.cpp index fb0dffb42..cced7a9a4 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.cpp @@ -45,11 +45,12 @@ constexpr auto kHoverScale = 1.24; Strip::Strip( QRect inner, + int size, Fn update, IconFactory iconFactory) : _iconFactory(std::move(iconFactory)) , _inner(inner) -, _finalSize(st::reactionCornerImage) +, _finalSize(size) , _update(std::move(update)) { } @@ -98,8 +99,7 @@ void Strip::paint( const auto animationRect = clip.marginsRemoved({ 0, skip, 0, skip }); PainterHighQualityEnabler hq(p); - const auto finalSize = st::reactionCornerImage; - const auto hoveredSize = int(base::SafeRound(finalSize * kHoverScale)); + 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( @@ -109,7 +109,7 @@ void Strip::paint( size )).marginsRemoved({ remove, remove, remove, remove }); }; - const auto basicTarget = basicTargetForScale(finalSize, scale); + const auto basicTarget = basicTargetForScale(_finalSize, scale); const auto countTarget = [&](const ReactionIcons &icon) { const auto selectScale = icon.selectedScale.value( icon.selected ? kHoverScale : 1.); @@ -118,7 +118,7 @@ void Strip::paint( } const auto finalScale = scale * selectScale; return (finalScale <= 1.) - ? basicTargetForScale(finalSize, finalScale) + ? basicTargetForScale(_finalSize, finalScale) : basicTargetForScale(hoveredSize, finalScale / kHoverScale); }; for (auto &icon : _icons) { @@ -288,7 +288,7 @@ auto Strip::selected() const -> std::variant { } int Strip::computeOverSize() const { - return int(base::SafeRound(st::reactionCornerImage * kHoverScale)); + return int(base::SafeRound(_finalSize * kHoverScale)); } void Strip::clearAppearAnimations(bool mainAppeared) { diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.h index 064e3fa4c..22724312e 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.h @@ -43,7 +43,7 @@ class Strip final { public: using ReactionId = Data::ReactionId; - Strip(QRect inner, Fn update, IconFactory iconFactory); + Strip(QRect inner, int size, Fn update, IconFactory iconFactory); enum class AddedButton : uchar { None, diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index ebafdf831..93fe573c8 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -1183,7 +1183,7 @@ void Contact::initDimensions() { } void Contact::paint(Painter &p, const QRect &clip, const PaintContext *context) const { - int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; + int32 left = st::defaultEmojiPan.headerLeft - st::inlineResultsLeft; left = st::inlineFileSize + st::inlineThumbSkip; prepareThumbnail(st::inlineFileSize, st::inlineFileSize); @@ -1272,7 +1272,7 @@ Article::Article( void Article::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; - int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::emojiPanHeaderLeft - st::inlineResultsLeft)); + int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::defaultEmojiPan.headerLeft - st::inlineResultsLeft)); TextParseOptions titleOpts = { 0, textWidth, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(textWidth), 2 * st::semiboldFont->height); @@ -1294,7 +1294,7 @@ int32 Article::resizeGetHeight(int32 width) { if (_url) { _urlText = getResultUrl(); _urlWidth = st::normalFont->width(_urlText); - int32 textWidth = _width - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::emojiPanHeaderLeft - st::inlineResultsLeft)); + int32 textWidth = _width - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::defaultEmojiPan.headerLeft - st::inlineResultsLeft)); if (_urlWidth > textWidth) { _urlText = st::normalFont->elided(_urlText, textWidth); _urlWidth = st::normalFont->width(_urlText); @@ -1305,7 +1305,7 @@ int32 Article::resizeGetHeight(int32 width) { } void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) const { - int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; + int32 left = st::defaultEmojiPan.headerLeft - st::inlineResultsLeft; if (_withThumb) { left = st::inlineThumbSize + st::inlineThumbSkip; prepareThumbnail(st::inlineThumbSize, st::inlineThumbSize); @@ -1470,7 +1470,7 @@ void Game::setPosition(int32 position) { } void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) const { - int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; + int32 left = st::defaultEmojiPan.headerLeft - st::inlineResultsLeft; left = st::inlineThumbSize + st::inlineThumbSkip; auto rthumb = style::rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width); diff --git a/Telegram/SourceFiles/ui/cached_round_corners.cpp b/Telegram/SourceFiles/ui/cached_round_corners.cpp index 056484388..1c09eb12e 100644 --- a/Telegram/SourceFiles/ui/cached_round_corners.cpp +++ b/Telegram/SourceFiles/ui/cached_round_corners.cpp @@ -80,7 +80,6 @@ void CreatePaletteCorners() { PrepareCorners(OverviewVideoSelectedCorners, st::overviewVideoStatusRadius, st::msgDateImgBgSelected); PrepareCorners(ForwardCorners, st::historyMessageRadius, st::historyForwardChooseBg); PrepareCorners(MediaviewSaveCorners, st::mediaviewControllerRadius, st::mediaviewSaveMsgBg); - PrepareCorners(EmojiHoverCorners, st::emojiPanRadius, st::emojiPanHover); PrepareCorners(StickerHoverCorners, st::roundRadiusSmall, st::emojiPanHover); PrepareCorners(BotKeyboardCorners, st::roundRadiusSmall, st::botKbBg); diff --git a/Telegram/SourceFiles/ui/cached_round_corners.h b/Telegram/SourceFiles/ui/cached_round_corners.h index 17ee285ad..c227654cc 100644 --- a/Telegram/SourceFiles/ui/cached_round_corners.h +++ b/Telegram/SourceFiles/ui/cached_round_corners.h @@ -27,7 +27,6 @@ enum CachedRoundCorners : int { OverviewVideoSelectedCorners, ForwardCorners, MediaviewSaveCorners, - EmojiHoverCorners, StickerHoverCorners, BotKeyboardCorners, diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 9bd565679..5326c5280 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -84,6 +84,7 @@ notifySendReply: IconButton { iconOver: historySendIconOver; iconPosition: point(6px, 6px); } +notifyFadeRight: icon {{ "fade_horizontal", notificationBg }}; titleUnreadCounterTop: 6px; titleUnreadCounterRight: 35px; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index a1ec2c9ad..8162619cb 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit a1ec2c9ade2b050bd307ecf669a2ae5019d70df4 +Subproject commit 8162619cb17456f31d1be378a7ed72dc928e0831