From d102d256a9ae9782ae1ae84a84359be585094c19 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 13 May 2024 15:00:46 +0400 Subject: [PATCH] Implement effects search. --- .../chat_helpers/emoji_list_widget.cpp | 10 +- .../chat_helpers/emoji_list_widget.h | 4 + .../chat_helpers/stickers_list_widget.cpp | 108 ++++++++++++------ .../chat_helpers/stickers_list_widget.h | 16 ++- .../chat_helpers/tabbed_selector.cpp | 4 +- .../chat_helpers/tabbed_selector.h | 2 +- .../history_view_reactions_selector.cpp | 25 ++-- 7 files changed, 115 insertions(+), 54 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 8482d8b81..4962a42a9 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -583,9 +583,14 @@ void EmojiListWidget::setupSearch() { InvokeQueued(this, [=] { applyNextSearchQuery(); }); + _searchQueries.fire_copy(_nextSearchQuery); }, session, type); } +rpl::producer> EmojiListWidget::searchQueries() const { + return _searchQueries.events(); +} + void EmojiListWidget::applyNextSearchQuery() { if (_searchQuery == _nextSearchQuery) { return; @@ -834,7 +839,8 @@ void EmojiListWidget::unloadCustomIn(const SectionInfo &info) { object_ptr EmojiListWidget::createFooter() { Expects(_footer == nullptr); - if (_mode == EmojiListMode::RecentReactions) { + if (_mode == EmojiListMode::RecentReactions + || _mode == EmojiListMode::MessageEffects) { return { nullptr }; } @@ -2131,7 +2137,7 @@ void EmojiListWidget::refreshRecent() { } void EmojiListWidget::refreshCustom() { - if (_mode == Mode::RecentReactions) { + if (_mode == Mode::RecentReactions || _mode == Mode::MessageEffects) { return; } auto old = base::take(_custom); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index 219add05b..59bfbfce6 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -77,6 +77,7 @@ enum class EmojiListMode { UserpicBuilder, BackgroundEmoji, PeerTitle, + MessageEffects, }; struct EmojiListDescriptor { @@ -146,6 +147,8 @@ public: base::unique_qptr fillContextMenu( const SendMenu::Details &details) override; + [[nodiscard]] rpl::producer> searchQueries() const; + protected: void visibleTopBottomUpdated( int visibleTop, @@ -418,6 +421,7 @@ private: bool _colorAllRippleForced = false; rpl::lifetime _colorAllRippleForcedLifetime; + rpl::event_stream> _searchQueries; std::vector _nextSearchQuery; std::vector _searchQuery; base::flat_set _searchEmoji; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index c2ef3f7f2..0c46508e6 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -547,7 +547,7 @@ int StickersListWidget::countDesiredHeight(int newWidth) { } void StickersListWidget::sendSearchRequest() { - if (_searchRequestId || _searchNextQuery.isEmpty()) { + if (_searchRequestId || _searchNextQuery.isEmpty() || _isEffects) { return; } @@ -556,14 +556,12 @@ void StickersListWidget::sendSearchRequest() { auto it = _searchCache.find(_searchQuery); if (it != _searchCache.cend()) { - _search->setLoading(false); + toggleSearchLoading(false); return; } - - _search->setLoading(true); - + toggleSearchLoading(true); if (_searchQuery == Ui::PremiumGroupFakeEmoticon()) { - _search->setLoading(false); + toggleSearchLoading(false); _searchRequestId = 0; _searchCache.emplace(_searchQuery, std::vector()); showSearchResults(); @@ -579,7 +577,7 @@ void StickersListWidget::sendSearchRequest() { searchResultsDone(result); }).fail([=] { // show error? - _search->setLoading(false); + toggleSearchLoading(false); _searchRequestId = 0; }).handleAllErrors().send(); } @@ -593,7 +591,10 @@ void StickersListWidget::searchForSets( return; } - if (query == Ui::PremiumGroupFakeEmoticon()) { + _filterStickersCornerEmoji.clear(); + if (_isEffects) { + filterEffectsByEmoji(std::move(emoji)); + } else if (query == Ui::PremiumGroupFakeEmoticon()) { _filteredStickers = session().data().stickers().getPremiumList(0); } else { _filteredStickers = session().data().stickers().getListByEmoji( @@ -602,7 +603,7 @@ void StickersListWidget::searchForSets( true); } if (_searchQuery != cleaned) { - _search->setLoading(false); + toggleSearchLoading(false); if (const auto requestId = base::take(_searchRequestId)) { _api.request(requestId).cancel(); } @@ -618,13 +619,14 @@ void StickersListWidget::searchForSets( } void StickersListWidget::cancelSetsSearch() { - _search->setLoading(false); + toggleSearchLoading(false); if (const auto requestId = base::take(_searchRequestId)) { _api.request(requestId).cancel(); } _searchRequestTimer.cancel(); _searchQuery = _searchNextQuery = QString(); _filteredStickers.clear(); + _filterStickersCornerEmoji.clear(); _searchCache.clear(); refreshSearchRows(nullptr); } @@ -655,8 +657,9 @@ void StickersListWidget::refreshSearchRows( }); fillFilteredStickersRow(); - fillLocalSearchRows(_searchNextQuery); - + if (!_isEffects) { + fillLocalSearchRows(_searchNextQuery); + } if (!cloudSets && _searchNextQuery.isEmpty()) { showStickerSet(!_mySets.empty() ? _mySets[0].id @@ -665,11 +668,10 @@ void StickersListWidget::refreshSearchRows( } setSection(Section::Search); - if (cloudSets) { + if (!_isEffects && cloudSets) { fillCloudSearchRows(*cloudSets); } refreshIcons(ValidateIconAnimations::Scroll); - _lastMousePosition = QCursor::pos(); resizeToWidth(width()); @@ -735,7 +737,7 @@ void StickersListWidget::fillFilteredStickersRow() { SearchEmojiSectionSetId(), nullptr, Data::StickersSetFlag::Special, - QString(), // title + _isEffects ? tr::lng_effect_stickers_title(tr::now) : QString(), QString(), // shortName _filteredStickers.size(), false, // externalLayout @@ -758,6 +760,12 @@ void StickersListWidget::addSearchRow(not_null set) { std::move(elements)); } +void StickersListWidget::toggleSearchLoading(bool loading) { + if (_search) { + _search->setLoading(loading); + } +} + void StickersListWidget::takeHeavyData( std::vector &to, std::vector &from) { @@ -839,7 +847,7 @@ auto StickersListWidget::shownSets() -> std::vector & { void StickersListWidget::searchResultsDone( const MTPmessages_FoundStickerSets &result) { - _search->setLoading(false); + toggleSearchLoading(false); _searchRequestId = 0; if (result.type() == mtpc_messages_foundStickerSetsNotModified) { @@ -1476,9 +1484,14 @@ void StickersListWidget::paintSticker( } auto cornerPainted = false; - if (set.id == Data::Stickers::RecentSetId && !_cornerEmoji.empty()) { - Assert(index < _cornerEmoji.size()); - if (const auto emoji = _cornerEmoji[index]) { + const auto corner = (set.id == Data::Stickers::RecentSetId) + ? &_cornerEmoji + : (set.id == SearchEmojiSectionSetId()) + ? &_filterStickersCornerEmoji + : nullptr; + if (corner && !corner->empty()) { + Assert(index < corner->size()); + if (const auto emoji = (*corner)[index]) { const auto size = Ui::Emoji::GetSizeNormal(); const auto ratio = style::DevicePixelRatio(); const auto radius = st::roundRadiusSmall; @@ -2492,14 +2505,14 @@ void StickersListWidget::updateSelected() { } bool StickersListWidget::setHasTitle(const Set &set) const { - if (set.id == Data::Stickers::FavedSetId + if (_isEffects) { + return true; + } else if (set.id == Data::Stickers::FavedSetId || set.id == SearchEmojiSectionSetId()) { return false; } else if (set.id == Data::Stickers::RecentSetId) { return !_mySets.empty() - && (_isMasks - || _isEffects - || (_mySets[0].id == Data::Stickers::FavedSetId)); + && (_isMasks || (_mySets[0].id == Data::Stickers::FavedSetId)); } return true; } @@ -2581,9 +2594,10 @@ void StickersListWidget::showStickerSet(uint64 setId) { const auto guard = gsl::finally([&] { _showingSetById = false; }); clearSelection(); - if (_search - && (!_searchQuery.isEmpty() || !_searchNextQuery.isEmpty())) { - _search->cancel(); + if (!_searchQuery.isEmpty() || !_searchNextQuery.isEmpty()) { + if (_search) { + _search->cancel(); + } cancelSetsSearch(); } @@ -2686,14 +2700,18 @@ void StickersListWidget::setupSearch() { ? TabbedSearchType::Greeting : TabbedSearchType::Stickers; _search = MakeSearch(this, st(), [=](std::vector &&query) { - auto set = base::flat_set(); - auto text = ranges::accumulate(query, QString(), []( + applySearchQuery(std::move(query)); + }, session, type); +} + +void StickersListWidget::applySearchQuery(std::vector &&query) { + auto set = base::flat_set(); + auto text = ranges::accumulate(query, QString(), []( QString a, QString b) { - return a.isEmpty() ? b : (a + ' ' + b); - }); - searchForSets(std::move(text), SearchEmoji(query, set)); - }, session, type); + return a.isEmpty() ? b : (a + ' ' + b); + }); + searchForSets(std::move(text), SearchEmoji(query, set)); } void StickersListWidget::displaySet(uint64 setId) { @@ -2768,6 +2786,32 @@ bool StickersListWidget::mySetsEmpty() const { return _mySets.empty(); } +void StickersListWidget::filterEffectsByEmoji( + const std::vector &emoji) { + _filteredStickers.clear(); + _filterStickersCornerEmoji.clear(); + if (_mySets.empty() + || _mySets.front().id != Data::Stickers::RecentSetId + || _mySets.front().stickers.empty()) { + return; + } + const auto &list = _mySets.front().stickers; + auto all = base::flat_set(); + for (const auto &one : emoji) { + all.emplace(one->original()); + } + const auto count = int(list.size()); + _filteredStickers.reserve(count); + _filterStickersCornerEmoji.reserve(count); + for (auto i = 0; i != count; ++i) { + Assert(i < _cornerEmoji.size()); + if (all.contains(_cornerEmoji[i])) { + _filteredStickers.push_back(list[i].document); + _filterStickersCornerEmoji.push_back(_cornerEmoji[i]); + } + } +} + StickersListWidget::~StickersListWidget() = default; object_ptr MakeConfirmRemoveSetBox( diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 8dca5601e..1f1de231b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -127,6 +127,8 @@ public: bool mySetsEmpty() const; + void applySearchQuery(std::vector &&query); + ~StickersListWidget(); protected: @@ -261,12 +263,13 @@ private: void updateSelected(); void setSelected(OverState newSelected); void setPressed(OverState newPressed); - std::unique_ptr createButtonRipple(int section); - QPoint buttonRippleTopLeft(int section) const; + [[nodiscard]] std::unique_ptr createButtonRipple( + int section); + [[nodiscard]] QPoint buttonRippleTopLeft(int section) const; - std::vector &shownSets(); - const std::vector &shownSets() const; - int featuredRowHeight() const; + [[nodiscard]] std::vector &shownSets(); + [[nodiscard]] const std::vector &shownSets() const; + [[nodiscard]] int featuredRowHeight() const; void checkVisibleFeatured(int visibleTop, int visibleBottom); void readVisibleFeatured(int visibleTop, int visibleBottom); @@ -324,6 +327,7 @@ private: [[nodiscard]] const Data::StickersSetsOrder &defaultSetsOrder() const; [[nodiscard]] Data::StickersSetsOrder &defaultSetsOrderRef(); + void filterEffectsByEmoji(const std::vector &emoji); enum class AppendSkip { None, @@ -356,6 +360,7 @@ private: void fillLocalSearchRows(const QString &query); void fillCloudSearchRows(const std::vector &cloudSets); void addSearchRow(not_null set); + void toggleSearchLoading(bool loading); void showPreview(); @@ -431,6 +436,7 @@ private: std::unique_ptr _premiumMark; std::vector> _filteredStickers; + std::vector _filterStickersCornerEmoji; std::map> _searchCache; std::vector> _searchIndex; base::Timer _searchRequestTimer; diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 5e8967541..7ac136421 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -1460,9 +1460,7 @@ int TabbedSelector::Inner::resizeGetHeight(int newWidth) { } int TabbedSelector::Inner::minimalHeight() const { - return (_minimalHeight > 0) - ? _minimalHeight - : defaultMinimalHeight(); + return _minimalHeight.value_or(defaultMinimalHeight()); } int TabbedSelector::Inner::defaultMinimalHeight() const { diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index bb780797c..b5355dafc 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -422,7 +422,7 @@ private: int _visibleTop = 0; int _visibleBottom = 0; - int _minimalHeight = 0; + std::optional _minimalHeight; rpl::event_stream _scrollToRequests; rpl::event_stream _disableScrollRequests; 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 70b987eda..2f55b6048 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -211,7 +211,9 @@ Selector::Selector( reactions, (reactions.customAllowed ? ChatHelpers::EmojiListMode::FullReactions - : ChatHelpers::EmojiListMode::RecentReactions), + : reactions.stickers.empty() + ? ChatHelpers::EmojiListMode::RecentReactions + : ChatHelpers::EmojiListMode::MessageEffects), {}, std::move(about), iconFactory, @@ -1075,15 +1077,6 @@ void Selector::createList() { _shadow->update(); } }, _list->lifetime()); - if (_stickers) { - _stickers->scrollToRequests( - ) | rpl::start_with_next([=](int y) { - _scroll->scrollToY(_list->height() + y); - if (_shadow) { - _shadow->update(); - } - }, _stickers->lifetime()); - } _scroll->setGeometry(inner.marginsRemoved({ _st.margin.left(), @@ -1091,7 +1084,17 @@ void Selector::createList() { 0, 0, })); - _list->setMinimalHeight(geometry.width(), _scroll->height()); + if (_stickers) { + _list->setMinimalHeight(geometry.width(), 0); + _stickers->setMinimalHeight(geometry.width(), 0); + + _list->searchQueries( + ) | rpl::start_with_next([=](std::vector &&query) { + _stickers->applySearchQuery(std::move(query)); + }, _stickers->lifetime()); + } else { + _list->setMinimalHeight(geometry.width(), _scroll->height()); + } updateVisibleTopBottom(); }