diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 3c3ab74f6..25cf71a96 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -157,13 +157,11 @@ InnerWidget::InnerWidget( , _narrowWidth(st::defaultDialogRow.padding.left() + st::defaultDialogRow.photoSize + st::defaultDialogRow.padding.left()) -, _cancelSearchInChat(this, st::dialogsCancelSearchInPeer) , _cancelSearchFromUser(this, st::dialogsCancelSearchInPeer) , _chatPreviewTimer([=] { showChatPreview(true); }) , _childListShown(std::move(childListShown)) { setAttribute(Qt::WA_OpaquePaintEvent, true); - _cancelSearchInChat->hide(); _cancelSearchFromUser->hide(); style::PaletteChanged( @@ -509,13 +507,7 @@ int InnerWidget::searchInChatSkip() const { if (_searchTags) { result += _searchTags->height(); } - if (_searchInChat) { - result += st::searchedBarHeight + st::dialogsSearchInHeight; - } if (_searchFromShown) { - if (_searchInChat) { - result += st::lineWidth; - } result += st::dialogsSearchInHeight; } return result; @@ -856,7 +848,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) { } } - if (_searchInChat || _searchFromPeer) { + if (_searchFromShown) { paintSearchInChat(p, { .st = &st::forumTopicRow, .currentBg = currentBg(), @@ -1141,37 +1133,8 @@ void InnerWidget::paintSearchInChat( top += height; } p.setFont(st::searchedBarFont); - if (_searchInChat) { - const auto bar = st::searchedBarHeight; - p.fillRect(0, top, width(), top + bar, st::searchedBarBg); - p.setPen(st::searchedBarFg); - p.drawTextLeft(st::searchedBarPosition.x(), top + st::searchedBarPosition.y(), width(), tr::lng_dlg_search_in(tr::now)); - top += bar; - } auto fullRect = QRect(0, top, width(), height - top); p.fillRect(fullRect, currentBg()); - if (_searchInChat) { - if (_searchFromShown) { - p.fillRect(QRect(0, top + st::dialogsSearchInHeight, width(), st::lineWidth), st::shadowFg); - } - p.setPen(st::dialogsNameFg); - if (const auto topic = _searchInChat.topic()) { - paintSearchInTopic(p, context, topic, _searchInChatUserpic, top, _searchInChatText); - } else if (const auto peer = _searchInChat.peer()) { - if (peer->isSelf()) { - paintSearchInSaved(p, top, _searchInChatText); - } else if (peer->isRepliesChat()) { - paintSearchInReplies(p, top, _searchInChatText); - } else { - paintSearchInPeer(p, peer, _searchInChatUserpic, top, _searchInChatText); - } - } else if (const auto sublist = _searchInChat.sublist()) { - paintSearchInSaved(p, top, _searchInChatText); - } else { - Unexpected("Empty Key in paintSearchInChat."); - } - top += st::dialogsSearchInHeight + st::lineWidth; - } if (_searchFromShown) { p.setPen(st::dialogsTextFg); p.setTextPalette(st::dialogsSearchFromPalette); @@ -1930,12 +1893,10 @@ void InnerWidget::moveCancelSearchButtons() { const auto widthForCancelButton = qMax( width(), st::columnMinimalWidthLeft - _narrowWidth); - const auto left = widthForCancelButton - st::dialogsSearchInSkip - _cancelSearchInChat->width(); + const auto left = widthForCancelButton - st::dialogsSearchInSkip - _cancelSearchFromUser->width(); const auto top = (st::dialogsSearchInHeight - st::dialogsCancelSearchInPeer.height) / 2; const auto skip = st::searchedBarHeight + (_searchTags ? _searchTags->height() : 0); - _cancelSearchInChat->moveToLeft(left, skip + top); - const auto next = _searchInChat ? (skip + st::dialogsSearchInHeight + st::lineWidth) : 0; - _cancelSearchFromUser->moveToLeft(left, next + top); + _cancelSearchFromUser->moveToLeft(left, skip + top); } void InnerWidget::dialogRowReplaced( @@ -2334,7 +2295,7 @@ void InnerWidget::fillArchiveSearchMenu(not_null menu) { const auto folder = session().data().folderLoaded(Data::Folder::kId); if (!folder || !folder->chatsList()->fullSize().current() - || _searchInChat) { + || _searchState.inChat) { return; } const auto skip = session().settings().skipArchiveInSearch(); @@ -2491,17 +2452,26 @@ void InnerWidget::parentGeometryChanged() { } } -void InnerWidget::applyFilterUpdate(QString newFilter, bool force) { +void InnerWidget::applySearchState(SearchState state) { + if (_searchState == state) { + return; + } + auto withSameQuery = state; + withSameQuery.query = _searchState.query; + const auto otherChanged = (_searchState != withSameQuery); + + _searchState = std::move(state); + auto newFilter = _searchState.query; const auto mentionsSearch = (newFilter == u"@"_q); const auto words = mentionsSearch ? QStringList(newFilter) : TextUtilities::PrepareSearchWords(newFilter); newFilter = words.isEmpty() ? QString() : words.join(' '); - if (newFilter != _filter || force) { + if (newFilter != _filter || otherChanged) { _filter = newFilter; if (_filter.isEmpty() - && !_searchFromPeer - && _searchTagsSelected.empty()) { + && !_searchState.fromPeer + && _searchState.tags.empty()) { clearFilter(); } else { setState(WidgetState::Filtered); @@ -2521,9 +2491,7 @@ void InnerWidget::applyFilterUpdate(QString newFilter, bool force) { top += i->row->height(); } }; - if (!_searchInChat - && !_searchFromPeer - && !words.isEmpty()) { + if (_searchState.filterChatsList() && !words.isEmpty()) { if (_savedSublists) { const auto owner = &session().data(); append(owner->savedMessages().chatsList()->indexed()); @@ -2549,7 +2517,9 @@ void InnerWidget::applyFilterUpdate(QString newFilter, bool force) { } void InnerWidget::onHashtagFilterUpdate(QStringView newFilter) { - if (newFilter.isEmpty() || newFilter.at(0) != '#' || _searchInChat) { + if (newFilter.isEmpty() + || newFilter.at(0) != '#' + || _searchState.inChat) { _hashtagFilter = QString(); if (!_hashtagResults.empty()) { _hashtagResults.clear(); @@ -2751,10 +2721,6 @@ rpl::producer<> InnerWidget::searchMessages() const { return _searchMessages.events(); } -rpl::producer<> InnerWidget::cancelSearchInChatRequests() const { - return _cancelSearchInChat->clicks() | rpl::to_empty; -} - rpl::producer InnerWidget::completeHashtagRequests() const { return _completeHashtagRequests.events(); } @@ -2847,12 +2813,12 @@ void InnerWidget::searchReceived( const auto isMigratedSearch = (type == SearchRequestType::MigratedFromStart) || (type == SearchRequestType::MigratedFromOffset); - const auto key = (!_openedForum || _searchInChat.topic()) - ? _searchInChat + const auto key = (!_openedForum || _searchState.inChat.topic()) + ? _searchState.inChat : Key(_openedForum->history()); if (inject - && (!_searchInChat - || inject->history() == _searchInChat.history())) { + && (!_searchState.inChat + || inject->history() == _searchState.inChat.history())) { Assert(_searchResults.empty()); const auto index = int(_searchResults.size()); _searchResults.push_back( @@ -2984,7 +2950,7 @@ void InnerWidget::refresh(bool toTop) { } } else if (_state == WidgetState::Filtered) { if (_waitingForSearch) { - h = searchedOffset() + (_searchResults.size() * _st->height) + ((_searchResults.empty() && !_searchInChat) ? -st::searchedBarHeight : 0); + h = searchedOffset() + (_searchResults.size() * _st->height) + ((_searchResults.empty() && !_searchState.inChat) ? -st::searchedBarHeight : 0); } else { h = searchedOffset() + (_searchResults.size() * _st->height); } @@ -3124,7 +3090,7 @@ void InnerWidget::searchInChat( _searchTags->selectedChanges( ) | rpl::start_with_next([=](std::vector &&list) { - _searchTagsSelected = std::move(list); + _searchState.tags = std::move(list); }, _searchTags->lifetime()); _searchTags->repaintRequests() | rpl::start_with_next([=] { @@ -3150,20 +3116,17 @@ void InnerWidget::searchInChat( }, _searchTags->lifetime()); } else { _searchTags = nullptr; - _searchTagsSelected.clear(); + _searchState.tags.clear(); } } else { _searchTags = nullptr; - _searchTagsSelected.clear(); + _searchState.tags.clear(); } - _searchInChat = key; - _searchFromPeer = from; + _searchState.inChat = key; + _searchState.fromPeer = from; _searchFromShown = key.sublist() ? key.sublist()->peer().get() : from; - if (_searchInChat) { + if (_searchState.inChat) { onHashtagFilterUpdate(QStringView()); - _cancelSearchInChat->show(); - } else { - _cancelSearchInChat->hide(); } if (_searchFromShown) { _cancelSearchFromUser->show(); @@ -3172,15 +3135,9 @@ void InnerWidget::searchInChat( _cancelSearchFromUser->hide(); _searchFromUserUserpic = {}; } - if (_searchInChat || _searchFromPeer) { + if (_searchState.inChat || _searchState.fromPeer) { refreshSearchInChatLabel(); } - - if (peer) { - _searchInChatUserpic = peer->createUserpicView(); - } else { - _searchInChatUserpic = {}; - } moveCancelSearchButtons(); } @@ -3192,27 +3149,6 @@ auto InnerWidget::searchTagsChanges() const } void InnerWidget::refreshSearchInChatLabel() { - const auto dialog = [&] { - if (const auto topic = _searchInChat.topic()) { - return topic->title(); - } else if (const auto peer = _searchInChat.peer()) { - if (peer->isSelf()) { - return tr::lng_saved_messages(tr::now); - } else if (peer->isRepliesChat()) { - return tr::lng_replies_messages(tr::now); - } - return peer->name(); - } else if (_searchInChat.sublist()) { - return tr::lng_saved_messages(tr::now); - } - return QString(); - }(); - if (!dialog.isEmpty()) { - _searchInChatText.setText( - st::semiboldTextStyle, - dialog, - Ui::DialogTextOptions()); - } const auto from = _searchFromShown ? _searchFromShown->name() : u""_q; if (!from.isEmpty()) { const auto fromUserText = tr::lng_dlg_search_from( @@ -3236,8 +3172,8 @@ void InnerWidget::repaintSearchResult(int index) { } void InnerWidget::clearFilter() { - if (_state == WidgetState::Filtered || _searchInChat) { - if (_searchInChat) { + if (_state == WidgetState::Filtered || _searchState.inChat) { + if (_searchState.inChat) { setState(WidgetState::Filtered); _waitingForSearch = true; } else { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index f78c220ad..805a7ff2b 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -145,6 +145,7 @@ public: } [[nodiscard]] bool hasFilteredResults() const; + void applySearchState(SearchState state); void searchInChat( Key key, PeerData *from, @@ -152,7 +153,6 @@ public: [[nodiscard]] auto searchTagsChanges() const -> rpl::producer>; - void applyFilterUpdate(QString newFilter, bool force = false); void onHashtagFilterUpdate(QStringView newFilter); void appendToFiltered(Key key); @@ -169,7 +169,6 @@ public: [[nodiscard]] rpl::producer mustScrollTo() const; [[nodiscard]] rpl::producer dialogMoved() const; [[nodiscard]] rpl::producer<> searchMessages() const; - [[nodiscard]] rpl::producer<> cancelSearchInChatRequests() const; [[nodiscard]] rpl::producer completeHashtagRequests() const; [[nodiscard]] rpl::producer<> refreshHashtagsRequests() const; @@ -486,21 +485,16 @@ private: WidgetState _state = WidgetState::Default; object_ptr _empty = { nullptr }; - object_ptr _cancelSearchInChat; object_ptr _cancelSearchFromUser; Ui::DraggingScrollManager _draggingScroll; - Key _searchInChat; + SearchState _searchState; History *_searchInMigrated = nullptr; - PeerData *_searchFromPeer = nullptr; PeerData *_searchFromShown = nullptr; - mutable Ui::PeerUserpicView _searchInChatUserpic; mutable Ui::PeerUserpicView _searchFromUserUserpic; - Ui::Text::String _searchInChatText; Ui::Text::String _searchFromUserText; std::unique_ptr _searchTags; - std::vector _searchTagsSelected; int _searchTagsLeft = 0; RowDescriptor _menuRow; diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.cpp b/Telegram/SourceFiles/dialogs/dialogs_key.cpp index dfa728141..263a605cc 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_key.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_folder.h" #include "data/data_forum_topic.h" #include "data/data_saved_sublist.h" +#include "dialogs/ui/chat_search_tabs.h" #include "history/history.h" namespace Dialogs { @@ -84,4 +85,21 @@ PeerData *Key::peer() const { return nullptr; } +[[nodiscard]] bool SearchState::empty() const { + return !inChat + && QStringView(query).trimmed().isEmpty(); +} + +ChatSearchTab SearchState::defaultTabForMe() const { + return inChat.topic() + ? ChatSearchTab::ThisTopic + : inChat.history() + ? ChatSearchTab::ThisPeer + : ChatSearchTab::MyMessages; +} + +bool SearchState::filterChatsList() const { + return !inChat && (tab == ChatSearchTab::MyMessages); +} + } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.h b/Telegram/SourceFiles/dialogs/dialogs_key.h index c43dc9f15..a9278e08c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.h +++ b/Telegram/SourceFiles/dialogs/dialogs_key.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/qt/qt_compare.h" + class History; class PeerData; @@ -15,11 +17,13 @@ class Thread; class Folder; class ForumTopic; class SavedSublist; +struct ReactionId; } // namespace Data namespace Dialogs { class Entry; +enum class ChatSearchTab : uchar; class Key { public: @@ -120,4 +124,29 @@ struct EntryState { = default; }; +struct SearchState { + Key inChat; + PeerData *fromPeer = nullptr; + std::vector tags; + ChatSearchTab tab = {}; + QString query; + + [[nodiscard]] bool empty() const; + [[nodiscard]] ChatSearchTab defaultTabForMe() const; + [[nodiscard]] bool filterChatsList() const; + + explicit operator bool() const { + return !empty(); + } + + friend inline auto operator<=>( + const SearchState&, + const SearchState&) noexcept = default; + friend inline bool operator==( + const SearchState&, + const SearchState&) = default; +}; + +; + } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 15624e29d..cc510a948 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -250,7 +250,7 @@ Widget::Widget( st::dialogsStoriesList, _storiesContents.events() | rpl::flatten_latest()) : nullptr) -, _searchTimer([=] { searchMessages(); }) +, _searchTimer([=] { search(); }) , _singleMessageSearch(&controller->session()) { const auto makeChildListShown = [](PeerId peerId, float64 shown) { return InnerWidget::ChildListShown{ peerId, shown }; @@ -325,11 +325,7 @@ Widget::Widget( }, lifetime()); _inner->searchMessages( ) | rpl::start_with_next([=] { - needSearchMessages(); - }, lifetime()); - _inner->cancelSearchInChatRequests( - ) | rpl::start_with_next([=] { - cancelSearchInChat(); + searchRequested(); }, lifetime()); _inner->completeHashtagRequests( ) | rpl::start_with_next([=](const QString &tag) { @@ -341,12 +337,12 @@ Widget::Widget( }, lifetime()); _inner->cancelSearchFromUserRequests( ) | rpl::start_with_next([=] { - setSearchInChat((_openedForum && !_searchInChat) - ? Key(_openedForum->history()) - : _searchInChat.sublist() - ? Key(session().data().history(session().user())) - : _searchInChat, nullptr); - applySearchUpdate(true); + auto copy = _searchState; + copy.fromPeer = nullptr; + if (copy.inChat.sublist()) { + copy.inChat = session().data().history(session().user()); + } + applySearchState(std::move(copy)); }, lifetime()); _inner->chosenRow( ) | rpl::start_with_next([=](const ChosenRow &row) { @@ -528,7 +524,7 @@ Widget::Widget( void Widget::chosenRow(const ChosenRow &row) { storiesToggleExplicitExpand(false); - if (!_search->getLastText().isEmpty()) { + if (!currentSearchQuery().isEmpty()) { if (const auto history = row.key.history()) { session().recentPeers().bump(history->peer); } @@ -660,7 +656,7 @@ void Widget::setupMoreChatsBar() { controller()->activeChatsFilter( ) | rpl::start_with_next([=](FilterId id) { storiesToggleExplicitExpand(false); - if (!_searchInChat) { + if (!_searchState.inChat) { cancelSearch(); } @@ -1041,7 +1037,7 @@ void Widget::fullSearchRefreshOn(rpl::producer<> events) { _searchQuery = QString(); _scroll->scrollToY(0); cancelSearchRequest(); - searchMessages(); + search(); }, lifetime()); } @@ -1134,7 +1130,7 @@ void Widget::updateHasFocus(not_null focused) { bool Widget::cancelSearchByMouseBack() { return _searchHasFocus && !_searchSuggestionsLocked - && !_searchInChat + && !_searchState.inChat && cancelSearch(); } @@ -1147,7 +1143,7 @@ void Widget::processSearchFocusChange() { void Widget::updateSuggestions(anim::type animated) { const auto suggest = (_searchHasFocus || _searchSuggestionsLocked) - && !_searchInChat + && !_searchState.inChat && (_inner->state() == WidgetState::Default); if (anim::Disabled() || !session().data().chatsListLoaded()) { animated = anim::type::instant; @@ -1225,34 +1221,28 @@ void Widget::updateSuggestions(anim::type animated) { } void Widget::updateSearchTabs() { - const auto has = _searchInChat - || _hiddenSearchInChat - || _searchingHashtag; + const auto has = _searchState.inChat || _searchingHashtag; if (!has) { - _searchTab = ChatSearchTab::MyMessages; if (_searchTabs) { _searchTabs = nullptr; updateControlsGeometry(); } return; } else if (!_searchTabs) { - _searchTabs = std::make_unique(this, _searchTab); + _searchTabs = std::make_unique( + this, + _searchState.tab); _searchTabs->setVisible(!_showAnimation); _searchTabs->tabChanges( ) | rpl::start_with_next([=](ChatSearchTab tab) { - if (_searchTab != tab) { - _searchTab = tab; - applySearchTab(); - } + auto copy = _searchState; + copy.tab = tab; + applySearchState(std::move(copy)); }, _searchTabs->lifetime()); } - const auto topic = _searchInChat.topic() - ? _searchInChat.topic() - : _hiddenSearchInChat.topic(); - const auto peer = _searchInChat.owningHistory() - ? _searchInChat.owningHistory()->peer.get() - : _hiddenSearchInChat.owningHistory() - ? _hiddenSearchInChat.owningHistory()->peer.get() + const auto topic = _searchState.inChat.topic(); + const auto peer = _searchState.inChat.owningHistory() + ? _searchState.inChat.owningHistory()->peer.get() : nullptr; const auto topicShortLabel = topic ? Ui::Text::SingleCustomEmoji(Data::TopicIconEmojiEntity({ @@ -1273,65 +1263,28 @@ void Widget::updateSearchTabs() { const auto publicShortLabel = _searchingHashtag ? DefaultShortLabel(ChatSearchTab::PublicPosts) : TextWithEntities(); - _searchTab = _searchInChat.topic() - ? ChatSearchTab::ThisTopic - : _searchInChat.owningHistory() - ? ChatSearchTab::ThisPeer - : (_searchingHashtag && _searchTab == ChatSearchTab::PublicPosts) - ? ChatSearchTab::PublicPosts - : ChatSearchTab::MyMessages; + if ((_searchState.tab == ChatSearchTab::ThisTopic + && !_searchState.inChat.topic()) + || (_searchState.tab == ChatSearchTab::ThisPeer + && !_searchState.inChat) + || (_searchState.tab == ChatSearchTab::PublicPosts + && !_searchingHashtag)) { + _searchState.tab = _searchState.inChat.topic() + ? ChatSearchTab::ThisTopic + : _searchState.inChat.owningHistory() + ? ChatSearchTab::ThisPeer + : ChatSearchTab::MyMessages; + } _searchTabs->setTabShortLabels({ { ChatSearchTab::ThisTopic, topicShortLabel }, { ChatSearchTab::ThisPeer, peerShortLabel }, { ChatSearchTab::MyMessages, myShortLabel }, { ChatSearchTab::PublicPosts, publicShortLabel }, - }, _searchTab); + }, _searchState.tab); updateControlsGeometry(); } -void Widget::applySearchTab() { - switch (_searchTab) { - case ChatSearchTab::ThisTopic: - if (_searchInChat.topic()) { - } else if (_hiddenSearchInChat.topic()) { - setSearchInChat( - base::take(_hiddenSearchInChat), - base::take(_hiddenSearchFromAuthor), - base::take(_hiddenSearchTags)); - } else { - updateSearchTabs(); - } - case ChatSearchTab::ThisPeer: - if (_searchInChat.topic()) { - _hiddenSearchInChat = _searchInChat; - _hiddenSearchFromAuthor = _searchFromAuthor; - _hiddenSearchTags = _searchTags; - setSearchInChat( - _searchInChat.owningHistory(), - _searchFromAuthor, - _searchTags); - } else if (_searchInChat) { - return; - } else if (_hiddenSearchInChat) { - setSearchInChat( - _hiddenSearchInChat.owningHistory(), - _hiddenSearchFromAuthor, - _hiddenSearchTags); - } - case ChatSearchTab::MyMessages: - if (_searchInChat) { - _hiddenSearchInChat = (_hiddenSearchInChat.topic() - && _hiddenSearchInChat.owningHistory() == _searchInChat.history()) - ? _hiddenSearchInChat - : _searchInChat; - _hiddenSearchFromAuthor = _searchFromAuthor; - _hiddenSearchTags = _searchTags; - setSearchInChat({}); - } - } -} - void Widget::changeOpenedSubsection( FnMut change, bool fromRight, @@ -1575,7 +1528,8 @@ void Widget::showSearchInTopBar(anim::type animated) { _subsectionTopBar->toggleSearch(true, animated); _subsectionTopBar->searchEnableChooseFromUser( true, - !_searchFromAuthor); + !_searchState.fromPeer); + updateForceDisplayWide(); } QPixmap Widget::grabForFolderSlideAnimation() { @@ -1640,7 +1594,7 @@ void Widget::setInnerFocus(bool unfocusSearch) { return; } else if (!unfocusSearch && (!_search->getLastText().isEmpty() - || _searchInChat + || _searchState.inChat || _searchHasFocus || _searchSuggestionsLocked)) { _search->setFocus(); @@ -1657,7 +1611,7 @@ void Widget::jumpToTop(bool belowPinned) { if (session().supportMode()) { return; } - if ((currentSearchQuery().trimmed().isEmpty() && !_searchInChat)) { + if ((currentSearchQuery().trimmed().isEmpty() && !_searchState.inChat)) { auto to = 0; if (belowPinned) { const auto list = _openedForum @@ -1795,8 +1749,8 @@ void Widget::updateStoriesVisibility() { || _childList || _searchHasFocus || _searchSuggestionsLocked - || !_search->getLastText().isEmpty() - || _searchInChat + || !currentSearchQuery().isEmpty() + || _searchState.inChat || _stories->empty(); if (_stories->isHidden() != hidden) { _stories->setVisible(!hidden); @@ -1933,7 +1887,7 @@ void Widget::escape() { controller()->setActiveChatsFilter(first); } } - } else if (!_searchInChat + } else if (!_searchState.inChat && controller()->activeChatEntryCurrent().key) { controller()->content()->dialogsCancelled(); } @@ -1953,7 +1907,7 @@ void Widget::submit() { _inner->selectSkip(1); _inner->chooseRow(); } else { - searchMessages(); + search(); } } @@ -1996,55 +1950,63 @@ void Widget::loadMoreBlockedByDate() { session().api().requestMoreBlockedByDateDialogs(); } -bool Widget::searchMessages(bool searchCache) { +bool Widget::search(bool inCache) { auto result = false; - auto q = currentSearchQuery().trimmed(); - if (q.isEmpty() && !_searchFromAuthor && _searchTags.empty()) { + const auto query = currentSearchQuery().trimmed(); + const auto inPeer = searchInPeer(); + const auto fromPeer = searchFromPeer(); + const auto &inTags = searchInTags(); + const auto tab = _searchState.tab; + const auto skipRequest = (query.isEmpty() && !fromPeer && inTags.empty()) + || (tab == ChatSearchTab::PublicPosts && query.size() < 2); + if (skipRequest) { cancelSearchRequest(); _api.request(base::take(_peerSearchRequest)).cancel(); _api.request(base::take(_topicSearchRequest)).cancel(); return true; - } - if (searchCache) { - const auto success = _singleMessageSearch.lookup(q, [=] { - needSearchMessages(); + } else if (inCache) { + const auto success = _singleMessageSearch.lookup(query, [=] { + searchRequested(); }); if (!success) { return false; } - const auto i = _searchCache.find(q); + const auto i = _searchCache.find(query); if (i != _searchCache.end()) { - _searchQuery = q; - _searchQueryFrom = _searchFromAuthor; - _searchQueryTags = _searchTags; + _searchQuery = query; + _searchQueryFrom = fromPeer; + _searchQueryTags = inTags; + _searchQueryTab = tab; _searchNextRate = 0; _searchFull = _searchFullMigrated = false; cancelSearchRequest(); searchReceived( - ((_searchInChat || _openedForum) + (inPeer ? SearchRequestType::PeerFromStart : SearchRequestType::FromStart), i->second, 0); result = true; } - } else if (_searchQuery != q - || _searchQueryFrom != _searchFromAuthor - || _searchQueryTags != _searchTags) { - _searchQuery = q; - _searchQueryFrom = _searchFromAuthor; - _searchQueryTags = _searchTags; + } else if (_searchQuery != query + || _searchQueryFrom != fromPeer + || _searchQueryTags != inTags + || _searchQueryTab != tab) { + _searchQuery = query; + _searchQueryFrom = fromPeer; + _searchQueryTags = inTags; + _searchQueryTab = tab; _searchNextRate = 0; _searchFull = _searchFullMigrated = false; cancelSearchRequest(); - if (const auto peer = searchInPeer()) { + if (inPeer) { const auto topic = searchInTopic(); auto &histories = session().data().histories(); const auto type = Data::Histories::RequestType::History; - const auto history = session().data().history(peer); + const auto history = session().data().history(inPeer); const auto sublist = _openedForum ? nullptr - : _searchInChat.sublist(); + : _searchState.inChat.sublist(); const auto fromPeer = sublist ? nullptr : _searchQueryFrom; const auto savedPeer = sublist ? sublist->peer().get() @@ -2059,7 +2021,7 @@ bool Widget::searchMessages(bool searchCache) { | (_searchQueryTags.empty() ? Flag() : Flag::f_saved_reaction)), - peer->input, + inPeer->input, MTP_string(_searchQuery), (fromPeer ? fromPeer->input : MTP_inputPeerEmpty()), (savedPeer ? savedPeer->input : MTP_inputPeerEmpty()), @@ -2089,6 +2051,20 @@ bool Widget::searchMessages(bool searchCache) { _searchQueries.emplace(_searchRequest, _searchQuery); return _searchRequest; }); + } else if (_searchState.tab == ChatSearchTab::PublicPosts) { + const auto type = SearchRequestType::FromStart; + _searchRequest = session().api().request(MTPchannels_SearchPosts( + MTP_string(_searchState.query.trimmed().mid(1)), + MTP_int(0), // offset_rate + MTP_inputPeerEmpty(), // offset_peer + MTP_int(0), // offset_id + MTP_int(kSearchPerPage) + )).done([=](const MTPmessages_Messages &result) { + searchReceived(type, result, _searchRequest); + }).fail([=](const MTP::Error &error) { + searchFailed(type, error, _searchRequest); + }).send(); + _searchQueries.emplace(_searchRequest, _searchQuery); } else { const auto type = SearchRequestType::FromStart; const auto flags = session().settings().skipArchiveInSearch() @@ -2102,9 +2078,9 @@ bool Widget::searchMessages(bool searchCache) { MTP_inputMessagesFilterEmpty(), MTP_int(0), // min_date MTP_int(0), // max_date - MTP_int(0), - MTP_inputPeerEmpty(), - MTP_int(0), + MTP_int(0), // offset_rate + MTP_inputPeerEmpty(), // offset_peer + MTP_int(0), // offset_id MTP_int(kSearchPerPage) )).done([=](const MTPmessages_Messages &result) { searchReceived(type, result, _searchRequest); @@ -2114,18 +2090,18 @@ bool Widget::searchMessages(bool searchCache) { _searchQueries.emplace(_searchRequest, _searchQuery); } } - const auto query = Api::ConvertPeerSearchQuery(q); - if (searchForPeersRequired(query)) { - if (searchCache) { - auto i = _peerSearchCache.find(query); + const auto peerQuery = Api::ConvertPeerSearchQuery(query); + if (searchForPeersRequired(peerQuery)) { + if (inCache) { + auto i = _peerSearchCache.find(peerQuery); if (i != _peerSearchCache.end()) { - _peerSearchQuery = query; + _peerSearchQuery = peerQuery; _peerSearchRequest = 0; peerSearchReceived(i->second, 0); result = true; } - } else if (_peerSearchQuery != query) { - _peerSearchQuery = query; + } else if (_peerSearchQuery != peerQuery) { + _peerSearchQuery = peerQuery; _peerSearchFull = false; _peerSearchRequest = _api.request(MTPcontacts_Search( MTP_string(_peerSearchQuery), @@ -2139,7 +2115,7 @@ bool Widget::searchMessages(bool searchCache) { } } else { _api.request(base::take(_peerSearchRequest)).cancel(); - _peerSearchQuery = query; + _peerSearchQuery = peerQuery; _peerSearchFull = true; peerSearchReceived( MTP_contacts_found( @@ -2149,45 +2125,41 @@ bool Widget::searchMessages(bool searchCache) { MTP_vector(0)), 0); } - if (searchForTopicsRequired(query)) { - if (searchCache) { - if (_topicSearchQuery != query) { + if (searchForTopicsRequired(peerQuery)) { + if (inCache) { + if (_topicSearchQuery != peerQuery) { result = false; } - } else if (_topicSearchQuery != query) { - _topicSearchQuery = query; + } else if (_topicSearchQuery != peerQuery) { + _topicSearchQuery = peerQuery; _topicSearchFull = false; searchTopics(); } } else { _api.request(base::take(_topicSearchRequest)).cancel(); - _topicSearchQuery = query; + _topicSearchQuery = peerQuery; _topicSearchFull = true; } return result; } bool Widget::searchForPeersRequired(const QString &query) const { - return !_searchInChat - && !_searchFromAuthor - && _searchTags.empty() + return _searchState.filterChatsList() && !_openedForum && !query.isEmpty() - && (query[0] != '#'); + && !IsHashtagSearchQuery(query); } bool Widget::searchForTopicsRequired(const QString &query) const { - return !_searchInChat - && !_searchFromAuthor - && _searchTags.empty() + return _searchState.filterChatsList() && _openedForum && !query.isEmpty() - && (query[0] != '#') + && !IsHashtagSearchQuery(query) && !_openedForum->topicsList()->loaded(); } -void Widget::needSearchMessages() { - if (!searchMessages(true)) { +void Widget::searchRequested() { + if (!search(true)) { _searchTimer.callOnce(AutoSearchTimeout); } } @@ -2196,63 +2168,66 @@ void Widget::showMainMenu() { controller()->widget()->showMainMenu(); } -void Widget::searchMessages(QString query, Key inChat) { - if (_childList) { - const auto forum = controller()->shownForum().current(); - const auto topic = inChat.topic(); - if ((forum && forum->channel() == inChat.peer()) - || (topic && topic->forum() == forum)) { - _childList->searchMessages(query, inChat); - return; - } - hideChildList(); - } - if (_openedFolder) { - controller()->closeFolder(); - } +void Widget::searchMessages(SearchState state) { + applySearchState(std::move(state)); + session().local().saveRecentSearchHashtags(state.query); + //if (_childList) { + // const auto forum = controller()->shownForum().current(); + // const auto topic = inChat.topic(); + // if ((forum && forum->channel() == inChat.peer()) + // || (topic && topic->forum() == forum)) { + // _childList->searchMessages(query, inChat); + // return; + // } + // hideChildList(); + //} + //if (_openedFolder) { + // controller()->closeFolder(); + //} - auto tags = Data::SearchTagsFromQuery(query); - if (!tags.empty()) { - if (!inChat.sublist()) { - inChat = session().data().history(session().user()); - } - query = QString(); - } - const auto inChatChanged = [&] { - const auto inPeer = inChat.peer(); - const auto inTopic = inChat.topic(); - if (!inTopic - && _openedForum - && inPeer == _openedForum->channel() - && _subsectionTopBar - && _subsectionTopBar->searchMode()) { - return false; - } else if ((inTopic || (inPeer && !inPeer->isForum())) - && (inChat == _searchInChat)) { - return false; - } else if (inPeer) { - if (const auto to = inPeer->migrateTo()) { - if (to == _searchInChat.peer() && !_searchInChat.topic()) { - return false; - } - } - } - return true; - }(); - if ((currentSearchQuery() != query) - || inChatChanged - || _searchTags != tags) { - if (inChat) { - cancelSearch(); - setSearchInChat(inChat, nullptr, tags); - } - setSearchQuery(query); - applySearchUpdate(true); - _searchTimer.cancel(); - searchMessages(); + //auto tags = Data::SearchTagsFromQuery(query); + //if (!tags.empty()) { + // if (!inChat.sublist()) { + // inChat = session().data().history(session().user()); + // } + // query = QString(); + //} + //const auto inChatChanged = [&] { + // const auto inPeer = inChat.peer(); + // const auto inTopic = inChat.topic(); + // if (!inTopic + // && _openedForum + // && inPeer == _openedForum->channel() + // && _subsectionTopBar + // && _subsectionTopBar->searchMode()) { + // return false; + // } else if ((inTopic || (inPeer && !inPeer->isForum())) + // && (inChat == _searchState.inChat)) { + // return false; + // } else if (inPeer) { + // if (const auto to = inPeer->migrateTo()) { + // if (to == _searchState.inChat.peer() + // && !_searchState.inChat.topic()) { + // return false; + // } + // } + // } + // return true; + //}(); + //if ((currentSearchQuery() != query) + // || inChatChanged + // || _searchState.tags != tags) { + // if (inChat) { + // cancelSearch(); + // setSearchInChat(inChat, nullptr, tags); + // } + // setSearchQuery(query); + // applySearchUpdate(true); + // _searchTimer.cancel(); + // searchMessages(); - session().local().saveRecentSearchHashtags(query); - } + // session().local().saveRecentSearchHashtags(query); + //} } void Widget::searchTopics() { @@ -2308,7 +2283,7 @@ void Widget::searchMore() { const auto history = session().data().history(peer); const auto sublist = _openedForum ? nullptr - : _searchInChat.sublist(); + : _searchState.inChat.sublist(); const auto fromPeer = sublist ? nullptr : _searchQueryFrom; const auto savedPeer = sublist ? sublist->peer().get() @@ -2728,7 +2703,7 @@ void Widget::listScrollUpdated() { void Widget::updateCancelSearch() { const auto shown = !_search->getLastText().isEmpty() - || (!_searchInChat + || (!_searchState.inChat && (_searchHasFocus || _searchSuggestionsLocked)); _cancelSearch->toggle(shown, anim::type::normal); } @@ -2738,14 +2713,13 @@ bool Widget::fixSearchQuery() { return false; } _fixingSearchQuery = true; - const auto query = _search->getLastText(); - if (_searchTab == ChatSearchTab::PublicPosts) { + const auto query = currentSearchQuery(); + if (_searchState.tab == ChatSearchTab::PublicPosts) { const auto fixed = FixHashtagSearchQuery( query, - _search->textCursor().position()); + currentSearchQueryCursorPosition()); if (fixed.text != query) { - _search->setText(fixed.text); - _search->setCursorPosition(fixed.cursorPosition); + setSearchQuery(fixed.text, fixed.cursorPosition); } _searchingHashtag = true; } else if (_searchingHashtag != IsHashtagSearchQuery(query)) { @@ -2756,53 +2730,33 @@ bool Widget::fixSearchQuery() { return true; } -void Widget::applySearchUpdate(bool force) { - if (!fixSearchQuery() || (_showAnimation && !force)) { +void Widget::applySearchUpdate() { + if (!fixSearchQuery()) { return; } - - updateLockUnlockVisibility(anim::type::normal); - updateStoriesVisibility(); - const auto filterText = currentSearchQuery(); - _inner->applyFilterUpdate(filterText, force); - if (filterText.isEmpty() && !_searchFromAuthor && _searchTags.empty()) { - clearSearchCache(); - } - updateCancelSearch(); - if (!_postponeProcessSearchFocusChange) { - updateSuggestions(anim::type::instant); - } - updateLoadMoreChatsVisibility(); - updateJumpToDateVisibility(); - updateLockUnlockPosition(); - - if (filterText.isEmpty()) { - _peerSearchCache.clear(); - for (const auto &[requestId, query] : base::take(_peerSearchQueries)) { - _api.request(requestId).cancel(); - } - _peerSearchQuery = QString(); - } + auto copy = _searchState; + copy.query = currentSearchQuery(); + applySearchState(std::move(copy)); if (_chooseFromUser->toggled() - || _searchFromAuthor - || !_searchTags.empty()) { + || _searchState.fromPeer + || !_searchState.tags.empty()) { auto switchToChooseFrom = HistoryView::SwitchToChooseFromQuery(); if (_lastSearchText != switchToChooseFrom && switchToChooseFrom.startsWith(_lastSearchText) - && filterText == switchToChooseFrom) { + && _searchState.query == switchToChooseFrom) { showSearchFrom(); } } - _lastSearchText = filterText; - updateForceDisplayWide(); + _lastSearchText = _searchState.query; } void Widget::updateForceDisplayWide() { controller()->setChatsForceDisplayWide(_searchHasFocus + || (_subsectionTopBar && _subsectionTopBar->searchHasFocus()) || _searchSuggestionsLocked - || !_search->getLastText().isEmpty() - || _searchInChat); + || !currentSearchQuery().isEmpty() + || _searchState.inChat); } void Widget::showForum( @@ -2936,118 +2890,135 @@ void Widget::closeChildList(anim::type animated) { updateStoriesVisibility(); } -void Widget::searchInChat(Key chat) { - searchMessages(QString(), chat); -} - -bool Widget::setSearchInChat( - Key chat, - PeerData *from, - std::vector tags) { - if (_childList) { - if (_childList->setSearchInChat(chat, from, tags)) { +bool Widget::applySearchState(SearchState state) { + if (_searchState == state) { + return true; + } else if (_childList) { + if (_childList->applySearchState(state)) { return true; } hideChildList(); } - const auto peer = chat.peer(); - const auto topic = chat.topic(); + + // Adjust state to be consistent. + if (const auto peer = state.inChat.peer()) { + if (const auto to = peer->migrateTo()) { + state.inChat = peer->owner().history(to); + } + } + const auto peer = state.inChat.peer(); + const auto topic = state.inChat.topic(); const auto forum = peer ? peer->forum() : nullptr; - if (chat.folder() || (forum && !topic)) { - chat = Key(); + if (state.inChat.folder() || (forum && !topic)) { + state.inChat = {}; } - const auto searchInPeerUpdated = (_searchInChat != chat); - if (searchInPeerUpdated) { - from = nullptr; - } else if (!chat && !forum) { - from = nullptr; + if (!state.inChat && !forum) { + state.fromPeer = nullptr; } - const auto searchFromUpdated = searchInPeerUpdated - || (_searchFromAuthor != from); - _searchFromAuthor = from; + if (state.tab == ChatSearchTab::PublicPosts + && !IsHashtagSearchQuery(state.query)) { + state.tab = ChatSearchTab::MyMessages; + } + if (!state.tags.empty()) { + state.inChat = session().data().history(session().user()); + } + + const auto inChatChanged = (_searchState.inChat != state.inChat); + const auto fromPeerChanged = (_searchState.fromPeer != state.fromPeer); + const auto tagsChanged = (_searchState.tags != state.tags); + const auto queryChanged = (_searchState.query != state.query); + const auto tabChanged = (_searchState.tab != state.tab); if (forum) { if (_openedForum == forum) { + _searchState.fromPeer = state.fromPeer; // showSearchInTopBar showSearchInTopBar(anim::type::normal); } else if (_layout == Layout::Main) { _forumSearchRequested = true; + _searchState.fromPeer = state.fromPeer; // showSearchInTopBar controller()->showForum(forum); } else { return false; } + } else if (peer && (_layout != Layout::Main)) { + return false; } - _searchInMigrated = nullptr; - if (peer && !forum) { - if (_layout != Layout::Main) { - return false; - } else if (const auto migrateTo = peer->migrateTo()) { - const auto to = peer->owner().history(migrateTo); - return setSearchInChat(to, from, tags); - } else if (const auto migrateFrom = peer->migrateFrom()) { - _searchInMigrated = peer->owner().history(migrateFrom); - } + + const auto migrateFrom = (peer && !topic) + ? peer->migrateFrom() + : nullptr; + _searchInMigrated = migrateFrom + ? peer->owner().history(migrateFrom).get() + : nullptr; + _searchState = state; + if (queryChanged) { + updateLockUnlockVisibility(anim::type::normal); + updateLoadMoreChatsVisibility(); + updateCancelSearch(); } - if (searchInPeerUpdated) { - _searchInChat = chat; - if (chat) { - _hiddenSearchFromAuthor = {}; - _hiddenSearchTags = {}; - const auto hiddenTopic = _hiddenSearchInChat.topic(); - if (!hiddenTopic || hiddenTopic->history() != chat.history()) { - _hiddenSearchInChat = {}; - } - } - controller()->setSearchInChat(_searchInChat); - updateSuggestions(anim::type::instant); + if (inChatChanged) { + controller()->setSearchInChat(_searchState.inChat); updateSearchTabs(); + } + if (queryChanged || inChatChanged) { updateJumpToDateVisibility(); updateStoriesVisibility(); } - if (searchFromUpdated) { + if (fromPeerChanged) { updateSearchFromVisibility(); - clearSearchCache(); } updateLockUnlockPosition(); - if (_searchInChat && _layout == Layout::Main) { + + if ((state.query.isEmpty() && !state.fromPeer && state.tags.empty()) + || inChatChanged + || fromPeerChanged + || tagsChanged + || tabChanged) { + clearSearchCache(); + } + if (state.query.isEmpty()) { + _peerSearchCache.clear(); + for (const auto &[requestId, query] : base::take(_peerSearchQueries)) { + _api.request(requestId).cancel(); + } + _peerSearchQuery = QString(); + } + + if (_searchState.inChat && _layout == Layout::Main) { controller()->closeFolder(); } - _searchTags = std::move(tags); - _inner->searchInChat(_searchInChat, _searchFromAuthor, _searchTags); + + _inner->applySearchState(_searchState); + + if (!_postponeProcessSearchFocusChange) { + // Suggestions depend on _inner->state(), not on _searchState. + updateSuggestions(anim::type::instant); + } + _searchTagsLifetime = _inner->searchTagsChanges( ) | rpl::start_with_next([=](std::vector &&list) { - if (_searchTags != list) { - clearSearchCache(); - _searchTags = std::move(list); - if (_searchTags.empty()) { - applySearchUpdate(true); - } else { - searchMessages(); - } - } + auto copy = _searchState; + copy.tags = std::move(list); + applySearchState(std::move(copy)); }); if (_subsectionTopBar) { _subsectionTopBar->searchEnableJumpToDate( - _openedForum && _searchInChat); + _openedForum && _searchState.inChat); } - if (_searchFromAuthor + if (_searchState.fromPeer && _lastSearchText == HistoryView::SwitchToChooseFromQuery()) { cancelSearch(); } - if (_searchInChat || !_search->getLastText().isEmpty()) { + if (_searchState.inChat || !_search->getLastText().isEmpty()) { _search->setFocus(); } else { - setInnerFocus(true); + setInnerFocus(); } updateForceDisplayWide(); + applySearchUpdate(); return true; } -bool Widget::setSearchInChat( - Key chat, - PeerData *from) { - return setSearchInChat(chat, from, {}); -} - void Widget::clearSearchCache() { _searchCache.clear(); _singleMessageSearch.clear(); @@ -3066,27 +3037,31 @@ void Widget::clearSearchCache() { } void Widget::showCalendar() { - if (_searchInChat) { - controller()->showCalendar(_searchInChat, QDate()); + if (_searchState.inChat) { + controller()->showCalendar(_searchState.inChat, QDate()); } } void Widget::showSearchFrom() { if (const auto peer = searchInPeer()) { - const auto weak = base::make_weak(_searchInChat.topic()); - const auto chat = (!_searchInChat && _openedForum) + const auto weak = base::make_weak(_searchState.inChat.topic()); + const auto chat = (!_searchState.inChat && _openedForum) ? Key(_openedForum->history()) - : _searchInChat; + : _searchState.inChat; auto box = SearchFromBox( peer, crl::guard(this, [=](not_null from) { controller()->hideLayer(); + auto copy = _searchState; if (!chat.topic()) { - setSearchInChat(chat, from); + copy.inChat = chat; + copy.fromPeer = from; + applySearchState(std::move(copy)); } else if (const auto strong = weak.get()) { - setSearchInChat(strong, from); + copy.inChat = strong; + copy.fromPeer = from; + applySearchState(std::move(copy)); } - applySearchUpdate(true); }), crl::guard(this, [=] { _search->setFocus(); })); if (box) { @@ -3132,9 +3107,8 @@ void Widget::completeHashtag(QString tag) { } if (cur - start - 1 == tag.size() && cur < t.size() && t.at(cur) == ' ') ++cur; hashtag = t.mid(0, start + 1) + tag + ' ' + t.mid(cur); - _search->setText(hashtag); - _search->setCursorPosition(start + 1 + tag.size() + 1); - applySearchUpdate(true); + setSearchQuery(hashtag, start + 1 + tag.size() + 1); + applySearchUpdate(); return; } break; @@ -3142,9 +3116,10 @@ void Widget::completeHashtag(QString tag) { break; } } - _search->setText(t.mid(0, cur) + '#' + tag + ' ' + t.mid(cur)); - _search->setCursorPosition(cur + 1 + tag.size() + 1); - applySearchUpdate(true); + setSearchQuery( + t.mid(0, cur) + '#' + tag + ' ' + t.mid(cur), + cur + 1 + tag.size() + 1); + applySearchUpdate(); } void Widget::resizeEvent(QResizeEvent *e) { @@ -3162,7 +3137,7 @@ void Widget::updateLockUnlockVisibility(anim::type animated) { || _childList || _searchHasFocus || _searchSuggestionsLocked - || _searchInChat + || _searchState.inChat || !_search->getLastText().isEmpty(); if (_lockUnlock->toggled() == hidden) { const auto stories = _stories && !_stories->empty(); @@ -3195,7 +3170,7 @@ void Widget::updateJumpToDateVisibility(bool fast) { } _jumpToDate->toggle( - (_searchInChat && _search->getLastText().isEmpty()), + (_searchState.inChat && _search->getLastText().isEmpty()), fast ? anim::type::instant : anim::type::normal); } @@ -3203,7 +3178,7 @@ void Widget::updateSearchFromVisibility(bool fast) { auto visible = [&] { if (const auto peer = searchInPeer()) { if (peer->isChat() || peer->isMegagroup()) { - return !_searchFromAuthor; + return !_searchState.fromPeer; } } return false; @@ -3427,7 +3402,7 @@ void Widget::keyPressEvent(QKeyEvent *e) { //} } else if ((e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Tab) && _searchHasFocus - && !_searchInChat) { + && !_searchState.inChat) { escape(); } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { submit(); @@ -3586,15 +3561,39 @@ void Widget::cancelSearchRequest() { } PeerData *Widget::searchInPeer() const { - return _openedForum + return (_searchState.tab == ChatSearchTab::MyMessages + || _searchState.tab == ChatSearchTab::PublicPosts) + ? nullptr + : _openedForum ? _openedForum->channel().get() - : _searchInChat.sublist() + : _searchState.inChat.sublist() ? session().user().get() - : _searchInChat.peer(); + : _searchState.inChat.peer(); } Data::ForumTopic *Widget::searchInTopic() const { - return _searchInChat.topic(); + return (_searchState.tab != ChatSearchTab::ThisTopic) + ? nullptr + : _searchState.inChat.topic(); +} + +PeerData *Widget::searchFromPeer() const { + if (const auto peer = searchInPeer()) { + if (peer->isChat() || peer->isMegagroup()) { + return _searchState.fromPeer; + } + } + return nullptr; +} + +const std::vector &Widget::searchInTags() const { + if (const auto peer = searchInPeer()) { + if (peer->isSelf() && _searchState.tab == ChatSearchTab::ThisPeer) { + return _searchState.tags; + } + } + static const auto kEmpty = std::vector(); + return kEmpty; } QString Widget::currentSearchQuery() const { @@ -3603,6 +3602,12 @@ QString Widget::currentSearchQuery() const { : _search->getLastText(); } +int Widget::currentSearchQueryCursorPosition() const { + return _subsectionTopBar + ? _subsectionTopBar->searchQueryCursorPosition() + : _search->textCursor().position(); +} + void Widget::clearSearchField() { if (_subsectionTopBar) { _subsectionTopBar->searchClear(); @@ -3611,28 +3616,36 @@ void Widget::clearSearchField() { } } -void Widget::setSearchQuery(const QString &query) { +void Widget::setSearchQuery(const QString &query, int cursorPosition) { + if (cursorPosition < 0) { + cursorPosition = query.size(); + } if (_subsectionTopBar) { - _subsectionTopBar->searchSetText(query); + _subsectionTopBar->searchSetText(query, cursorPosition); } else { _search->setText(query); + _search->setCursorPosition(cursorPosition); } } bool Widget::cancelSearch() { - auto clearingQuery = !currentSearchQuery().isEmpty(); - auto clearingInChat = false; cancelSearchRequest(); - if (!clearingQuery && (_searchInChat || _searchFromAuthor)) { - if (_searchInChat && controller()->adaptive().isOneColumn()) { - if (const auto thread = _searchInChat.thread()) { + auto updatedState = _searchState; + const auto clearingQuery = !updatedState.query.isEmpty(); + auto clearingInChat = !clearingQuery + && (updatedState.inChat || updatedState.fromPeer); + if (clearingQuery) { + updatedState.query = QString(); + } else if (clearingInChat) { + if (updatedState.inChat && controller()->adaptive().isOneColumn()) { + if (const auto thread = updatedState.inChat.thread()) { controller()->showThread(thread); } else { Unexpected("Empty key in cancelSearch()."); } } - setSearchInChat(Key()); - clearingInChat = true; + updatedState.inChat = {}; + updatedState.fromPeer = nullptr; } if (!clearingQuery && _subsectionTopBar @@ -3640,9 +3653,9 @@ bool Widget::cancelSearch() { setInnerFocus(true); clearingInChat = true; } - const auto clearSearchFocus = !_searchInChat + const auto clearSearchFocus = !updatedState.inChat && (_searchHasFocus || _searchSuggestionsLocked); - if (!_searchInChat && _suggestions) { + if (!updatedState.inChat && _suggestions) { _suggestions->clearPersistance(); _searchSuggestionsLocked = false; } @@ -3654,32 +3667,13 @@ bool Widget::cancelSearch() { _lastSearchId = _lastSearchMigratedId = 0; _inner->clearFilter(); clearSearchField(); - applySearchUpdate(); + applySearchState(std::move(updatedState)); if (_suggestions && clearSearchFocus) { setInnerFocus(true); } return clearingQuery || clearingInChat || clearSearchFocus; } -void Widget::cancelSearchInChat() { - cancelSearchRequest(); - const auto isOneColumn = controller()->adaptive().isOneColumn(); - if (_searchInChat) { - if (isOneColumn && currentSearchQuery().trimmed().isEmpty()) { - if (const auto thread = _searchInChat.thread()) { - controller()->showThread(thread); - } else { - Unexpected("Empty key in cancelSearchInPeer()."); - } - } - setSearchInChat(Key()); - } - applySearchUpdate(true); - if (!isOneColumn && _search->getLastText().isEmpty()) { - controller()->content()->dialogsCancelled(); - } -} - Widget::~Widget() { cancelSearchRequest(); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index a1bc0e4ea..f265ebf87 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -100,7 +100,6 @@ public: void showForum( not_null forum, const Window::SectionShow ¶ms); - void searchInChat(Key chat); void setInnerFocus(bool unfocusSearch = false); [[nodiscard]] bool searchHasFocus() const; @@ -122,9 +121,7 @@ public: void scrollToEntry(const RowDescriptor &entry); - void searchMessages(QString query, Key inChat = {}); - void searchTopics(); - void searchMore(); + void searchMessages(SearchState state); [[nodiscard]] RowDescriptor resolveChatNext(RowDescriptor from = {}) const; [[nodiscard]] RowDescriptor resolveChatPrevious(RowDescriptor from = {}) const; @@ -154,14 +151,16 @@ protected: private: void chosenRow(const ChosenRow &row); void listScrollUpdated(); - void cancelSearchInChat(); void searchCursorMoved(); void completeHashtag(QString tag); [[nodiscard]] QString currentSearchQuery() const; + [[nodiscard]] int currentSearchQueryCursorPosition() const; void clearSearchField(); - bool searchMessages(bool searchCache = false); - void needSearchMessages(); + void searchRequested(); + bool search(bool inCache = false); + void searchTopics(); + void searchMore(); void slideFinished(); void searchReceived( @@ -176,6 +175,8 @@ private: void cancelSearchRequest(); [[nodiscard]] PeerData *searchInPeer() const; [[nodiscard]] Data::ForumTopic *searchInTopic() const; + [[nodiscard]] PeerData *searchFromPeer() const; + [[nodiscard]] const std::vector &searchInTags() const; void setupSupportMode(); void setupConnectingWidget(); @@ -190,18 +191,15 @@ private: void trackScroll(not_null widget); [[nodiscard]] bool searchForPeersRequired(const QString &query) const; [[nodiscard]] bool searchForTopicsRequired(const QString &query) const; - bool setSearchInChat( - Key chat, - PeerData *from, - std::vector tags); - bool setSearchInChat( - Key chat, - PeerData *from = nullptr); + + // Child list may be unable to set specific search state. + bool applySearchState(SearchState state); + void showCalendar(); void showSearchFrom(); void showMainMenu(); void clearSearchCache(); - void setSearchQuery(const QString &query); + void setSearchQuery(const QString &query, int cursorPosition = -1); void updateControlsVisibility(bool fast = false); void updateLockUnlockVisibility( anim::type animated = anim::type::instant); @@ -227,7 +225,6 @@ private: QPixmap newContentCache, Window::SlideDirection direction); - void applySearchTab(); void openChildList( not_null forum, const Window::SectionShow ¶ms); @@ -236,7 +233,7 @@ private: void fullSearchRefreshOn(rpl::producer<> events); void updateCancelSearch(); bool fixSearchQuery(); - void applySearchUpdate(bool force = false); + void applySearchUpdate(); void refreshLoadMoreButton(bool mayBlock, bool isBlocked); void loadMoreBlockedByDate(); @@ -271,7 +268,7 @@ private: Layout _layout = Layout::Main; int _narrowWidth = 0; object_ptr _searchControls; - object_ptr _subsectionTopBar = { nullptr } ; + object_ptr _subsectionTopBar = { nullptr }; struct { object_ptr toggle; object_ptr under; @@ -312,23 +309,16 @@ private: bool _forumSearchRequested = false; bool _fixingSearchQuery = false; bool _searchingHashtag = false; - ChatSearchTab _searchTab = ChatSearchTab(); Data::Folder *_openedFolder = nullptr; Data::Forum *_openedForum = nullptr; - Key _searchInChat; + SearchState _searchState; History *_searchInMigrated = nullptr; - PeerData *_searchFromAuthor = nullptr; - std::vector _searchTags; rpl::lifetime _searchTagsLifetime; QString _lastSearchText; bool _searchSuggestionsLocked = false; bool _searchHasFocus = false; - Key _hiddenSearchInChat; - PeerData *_hiddenSearchFromAuthor = nullptr; - std::vector _hiddenSearchTags; - rpl::event_stream> _storiesContents; base::flat_map _storiesUserpicsViewsHidden; base::flat_map _storiesUserpicsViewsShown; @@ -357,6 +347,7 @@ private: QString _searchQuery; PeerData *_searchQueryFrom = nullptr; std::vector _searchQueryTags; + ChatSearchTab _searchQueryTab = {}; int32 _searchNextRate = 0; bool _searchFull = false; bool _searchFullMigrated = false; diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 31764b970..59a94d585 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -1412,15 +1412,25 @@ QString TopBarWidget::searchQueryCurrent() const { return _searchQuery.current(); } +int TopBarWidget::searchQueryCursorPosition() const { + return _searchMode + ? _searchField->textCursor().position() + : _searchQuery.current().size(); +} + void TopBarWidget::searchClear() { if (_searchMode) { _searchField->clear(); } } -void TopBarWidget::searchSetText(const QString &query) { +void TopBarWidget::searchSetText(const QString &query, int cursorPosition) { if (_searchMode) { + if (cursorPosition < 0) { + cursorPosition = query.size(); + } _searchField->setText(query); + _searchField->setCursorPosition(cursorPosition); } } diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h index 36d18a5f6..5c94220b1 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -88,8 +88,9 @@ public: [[nodiscard]] rpl::producer<> searchSubmitted() const; [[nodiscard]] rpl::producer searchQuery() const; [[nodiscard]] QString searchQueryCurrent() const; + [[nodiscard]] int searchQueryCursorPosition() const; void searchClear(); - void searchSetText(const QString &query); + void searchSetText(const QString &query, int cursorPosition = -1); [[nodiscard]] rpl::producer<> forwardSelectionRequest() const { return _forwardSelection.events(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 5f94f8a22..d3873d4f4 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -731,8 +731,18 @@ void MainWidget::hideSingleUseKeyboard(FullMsgId replyToId) { } void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) { + auto tags = Data::SearchTagsFromQuery(query); if (controller()->isPrimary()) { - _dialogs->searchMessages(query, inChat); + using Tab = Dialogs::ChatSearchTab; + auto state = Dialogs::SearchState{ + .inChat = ((tags.empty() || inChat.sublist()) + ? inChat + : session().data().history(session().user())), + .tags = tags, + .query = tags.empty() ? query : QString(), + }; + state.tab = state.defaultTabForMe(); + _dialogs->searchMessages(std::move(state)); if (isOneColumn()) { _controller->clearSectionStack(); } else { @@ -742,7 +752,7 @@ void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) { if (const auto sublist = inChat.sublist()) { controller()->showSection( std::make_shared(sublist)); - } else if (!Data::SearchTagsFromQuery(query).empty()) { + } else if (!tags.empty()) { inChat = controller()->session().data().history( controller()->session().user()); }