From da1e7848037942fa404c738420e889bc332e79fc Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 27 Oct 2022 13:22:31 +0400 Subject: [PATCH] Search from a user inside forum / topic. --- .../dialogs/dialogs_inner_widget.cpp | 81 +++++++++++-------- .../dialogs/dialogs_inner_widget.h | 1 + .../SourceFiles/dialogs/dialogs_widget.cpp | 74 +++++++++++------ Telegram/SourceFiles/dialogs/dialogs_widget.h | 2 +- .../view/history_view_top_bar_widget.cpp | 66 ++++++++++++--- .../view/history_view_top_bar_widget.h | 7 ++ 6 files changed, 160 insertions(+), 71 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 098a8ee74e..e0689d62b0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -386,16 +386,20 @@ int InnerWidget::searchedOffset() const { result += (_peerSearchResults.size() * st::dialogsRowHeight) + st::searchedBarHeight; } - if (_searchInChat) { - result += searchInChatSkip(); - } + result += searchInChatSkip(); return result; } int InnerWidget::searchInChatSkip() const { - auto result = st::searchedBarHeight + st::dialogsSearchInHeight; + auto result = 0; + if (_searchInChat) { + result += st::searchedBarHeight + st::dialogsSearchInHeight; + } if (_searchFromPeer) { - result += st::lineWidth + st::dialogsSearchInHeight; + if (_searchInChat) { + result += st::lineWidth; + } + result += st::dialogsSearchInHeight; } return result; } @@ -674,7 +678,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) { } } - if (_searchInChat) { + if (_searchInChat || _searchFromPeer) { paintSearchInChat(p, { .st = &st::forumTopicRow, .now = ms, @@ -936,37 +940,40 @@ void InnerWidget::paintSearchInChat( const Ui::PaintContext &context) const { auto height = searchInChatSkip(); - auto top = st::searchedBarHeight; - p.fillRect(0, 0, width(), top, st::searchedBarBg); + auto top = 0; p.setFont(st::searchedBarFont); - p.setPen(st::searchedBarFg); - p.drawTextLeft(st::searchedBarPosition.x(), st::searchedBarPosition.y(), width(), tr::lng_dlg_search_in(tr::now)); - + if (_searchInChat) { + top += st::searchedBarHeight; + p.fillRect(0, 0, width(), top, st::searchedBarBg); + p.setPen(st::searchedBarFg); + p.drawTextLeft(st::searchedBarPosition.x(), st::searchedBarPosition.y(), width(), tr::lng_dlg_search_in(tr::now)); + } auto fullRect = QRect(0, top, width(), height - top); p.fillRect(fullRect, st::dialogsBg); - if (_searchFromPeer) { - 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); + if (_searchInChat) { + if (_searchFromPeer) { + 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 { + Unexpected("Empty Key in paintSearchInChat."); } - } else { - Unexpected("Empty Key in paintSearchInChat."); - } - if (const auto from = _searchFromPeer) { top += st::dialogsSearchInHeight + st::lineWidth; + } + if (_searchFromPeer) { p.setPen(st::dialogsTextFg); p.setTextPalette(st::dialogsSearchFromPalette); - paintSearchInPeer(p, from, _searchFromUserUserpic, top, _searchFromUserText); + paintSearchInPeer(p, _searchFromPeer, _searchFromUserUserpic, top, _searchFromUserText); p.restoreTextPalette(); } } @@ -1561,11 +1568,16 @@ void InnerWidget::setSearchedPressed(int pressed) { void InnerWidget::resizeEvent(QResizeEvent *e) { resizeEmptyLabel(); + moveCancelSearchButtons(); +} + +void InnerWidget::moveCancelSearchButtons() { const auto widthForCancelButton = qMax(width(), st::columnMinimalWidthLeft); const auto left = widthForCancelButton - st::dialogsSearchInSkip - _cancelSearchInChat->width(); const auto top = (st::dialogsSearchInHeight - st::dialogsCancelSearchInPeer.height) / 2; _cancelSearchInChat->moveToLeft(left, st::searchedBarHeight + top); - _cancelSearchFromUser->moveToLeft(left, st::searchedBarHeight + st::dialogsSearchInHeight + st::lineWidth + top); + const auto skip = _searchInChat ? (st::searchedBarHeight + st::dialogsSearchInHeight + st::lineWidth) : 0; + _cancelSearchFromUser->moveToLeft(left, skip + top); } void InnerWidget::dialogRowReplaced( @@ -2034,7 +2046,7 @@ void InnerWidget::applyFilterUpdate(QString newFilter, bool force) { begin(results), end(results)); }; - if (!_searchInChat && !words.isEmpty()) { + if (!_searchInChat && !_searchFromPeer && !words.isEmpty()) { if (_openedForum) { append(_openedForum->topicsList()->indexed()); } else { @@ -2599,7 +2611,6 @@ void InnerWidget::searchInChat(Key key, PeerData *from) { _controller->closeFolder(); onHashtagFilterUpdate(QStringView()); _cancelSearchInChat->show(); - refreshSearchInChatLabel(); } else { _cancelSearchInChat->hide(); } @@ -2610,12 +2621,16 @@ void InnerWidget::searchInChat(Key key, PeerData *from) { _cancelSearchFromUser->hide(); _searchFromUserUserpic = nullptr; } + if (_searchInChat || _searchFromPeer) { + refreshSearchInChatLabel(); + } if (const auto peer = _searchInChat.peer()) { _searchInChatUserpic = peer->createUserpicView(); } else { _searchInChatUserpic = nullptr; } + moveCancelSearchButtons(); _controller->dialogsListDisplayForced().set( _searchInChat || !_filter.isEmpty(), diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index a06afcc4e6..515b89ff28 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -354,6 +354,7 @@ private: void savePinnedOrder(); bool pinnedShiftAnimationCallback(crl::time now); void handleChatListEntryRefreshes(); + void moveCancelSearchButtons(); const not_null _controller; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 81c209862b..485636464c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -76,10 +76,6 @@ namespace { constexpr auto kSearchPerPage = 50; -[[nodiscard]] QString SwitchToChooseFromQuery() { - return u"from:"_q; -} - } // namespace class Widget::BottomButton : public Ui::RippleButton { @@ -276,7 +272,9 @@ Widget::Widget( }, lifetime()); _inner->cancelSearchFromUserRequests( ) | rpl::start_with_next([=] { - setSearchInChat(_searchInChat, nullptr); + setSearchInChat((_openedForum && !_searchInChat) + ? Key(_openedForum->forum()->history()) + : _searchInChat, nullptr); applyFilterUpdate(true); }, lifetime()); _inner->chosenRow( @@ -783,6 +781,10 @@ void Widget::refreshTopBars() { ) | rpl::start_with_next([=] { showCalendar(); }, _subsectionTopBar->lifetime()); + _subsectionTopBar->chooseFromUserRequest( + ) | rpl::start_with_next([=] { + showSearchFrom(); + }, _subsectionTopBar->lifetime()); updateControlsGeometry(); } const auto history = _openedForum @@ -796,7 +798,7 @@ void Widget::refreshTopBars() { .section = Dialogs::EntryState::Section::ChatsList, }, history ? history->sendActionPainter().get() : nullptr); if (_forumSearchRequested) { - _subsectionTopBar->toggleSearch(true, anim::type::instant); + showSearchInTopBar(anim::type::instant); } } else if (_subsectionTopBar) { if (_subsectionTopBar->searchHasFocus()) { @@ -872,6 +874,15 @@ void Widget::refreshTopBars() { } } +void Widget::showSearchInTopBar(anim::type animated) { + Expects(_subsectionTopBar != nullptr); + + _subsectionTopBar->toggleSearch(true, animated); + _subsectionTopBar->searchEnableChooseFromUser( + true, + !_searchFromAuthor); +} + QPixmap Widget::grabForFolderSlideAnimation() { const auto hidden = _scrollToTop->isHidden(); if (!hidden) { @@ -1289,6 +1300,7 @@ bool Widget::searchMessages(bool searchCache) { _peerSearchQueries.emplace(_peerSearchRequest, _peerSearchQuery); } } else { + _api.request(base::take(_peerSearchRequest)).cancel(); _peerSearchQuery = query; _peerSearchFull = true; peerSearchReceived( @@ -1310,6 +1322,7 @@ bool Widget::searchMessages(bool searchCache) { searchTopics(); } } else { + _api.request(base::take(_topicSearchRequest)).cancel(); _topicSearchQuery = query; _topicSearchFull = true; } @@ -1318,6 +1331,7 @@ bool Widget::searchMessages(bool searchCache) { bool Widget::searchForPeersRequired(const QString &query) const { return !_searchInChat + && !_searchFromAuthor && !_openedForum && !query.isEmpty() && (query[0] != '#'); @@ -1325,6 +1339,7 @@ bool Widget::searchForPeersRequired(const QString &query) const { bool Widget::searchForTopicsRequired(const QString &query) const { return !_searchInChat + && !_searchFromAuthor && _openedForum && !query.isEmpty() && (query[0] != '#') @@ -1842,7 +1857,7 @@ void Widget::applyFilterUpdate(bool force) { } if (_chooseFromUser->toggled() || _searchFromAuthor) { - auto switchToChooseFrom = SwitchToChooseFromQuery(); + auto switchToChooseFrom = HistoryView::SwitchToChooseFromQuery(); if (_lastFilterText != switchToChooseFrom && switchToChooseFrom.startsWith(_lastFilterText) && filterText == switchToChooseFrom) { @@ -1853,12 +1868,12 @@ void Widget::applyFilterUpdate(bool force) { } void Widget::searchInChat(Key chat) { + if (_openedForum && !chat.peer()->forum()) { + controller()->closeForum(); + } if (_openedFolder) { controller()->closeFolder(); } - if (const auto topic = chat.topic()) { - controller()->openForum(topic->channel()); - } cancelSearch(); setSearchInChat(chat); applyFilterUpdate(true); @@ -1868,38 +1883,43 @@ void Widget::setSearchInChat(Key chat, PeerData *from) { const auto peer = chat.peer(); const auto topic = chat.topic(); const auto forum = peer ? peer->forum() : nullptr; + if (chat.folder() || (forum && !topic)) { + chat = Key(); + } + const auto searchInPeerUpdated = (_searchInChat != chat); + if (searchInPeerUpdated) { + from = nullptr; + } else if (!chat && !forum) { + from = nullptr; + } + const auto searchFromUpdated = searchInPeerUpdated + || (_searchFromAuthor != from); + _searchFromAuthor = from; + if (forum) { if (controller()->openedForum().current() == peer) { - _subsectionTopBar->toggleSearch(true, anim::type::normal); + showSearchInTopBar(anim::type::normal); } else { _forumSearchRequested = true; controller()->openForum(forum->channel()); } } - if (chat.folder() || (forum && !topic)) { - chat = Key(); - } _searchInMigrated = nullptr; if (peer) { if (const auto migrateTo = peer->migrateTo()) { return setSearchInChat(peer->owner().history(migrateTo), from); } else if (const auto migrateFrom = peer->migrateFrom()) { - if (!topic) { + if (!forum) { _searchInMigrated = peer->owner().history(migrateFrom); } } } - const auto searchInPeerUpdated = (_searchInChat != chat); if (searchInPeerUpdated) { _searchInChat = chat; - from = nullptr; controller()->searchInChat = _searchInChat; updateJumpToDateVisibility(); - } else if (!_searchInChat) { - from = nullptr; } - if (_searchFromAuthor != from || searchInPeerUpdated) { - _searchFromAuthor = from; + if (searchFromUpdated) { updateSearchFromVisibility(); clearSearchCache(); } @@ -1908,7 +1928,8 @@ void Widget::setSearchInChat(Key chat, PeerData *from) { _subsectionTopBar->searchEnableJumpToDate( _openedForum && _searchInChat); } - if (_searchFromAuthor && _lastFilterText == SwitchToChooseFromQuery()) { + if (_searchFromAuthor + && _lastFilterText == HistoryView::SwitchToChooseFromQuery()) { cancelSearch(); } _filter->setFocus(); @@ -1938,14 +1959,19 @@ void Widget::showCalendar() { void Widget::showSearchFrom() { if (const auto peer = searchInPeer()) { - const auto chat = _openedForum + const auto weak = base::make_weak(_searchInChat.topic()); + const auto chat = (!_searchInChat && _openedForum) ? Key(_openedForum->forum()->history()) : _searchInChat; auto box = SearchFromBox( peer, crl::guard(this, [=](not_null from) { Ui::hideLayer(); - setSearchInChat(chat, from); + if (!chat.topic()) { + setSearchInChat(chat, from); + } else if (const auto strong = weak.get()) { + setSearchInChat(strong, from); + } applyFilterUpdate(true); }), crl::guard(this, [=] { _filter->setFocus(); })); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index e07d2ca1f0..e92338bcd2 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -60,7 +60,6 @@ class InnerWidget; enum class SearchRequestType; class Widget final : public Window::AbstractSectionWidget { - public: Widget(QWidget *parent, not_null controller); @@ -165,6 +164,7 @@ private: void updateSearchFromVisibility(bool fast = false); void updateControlsGeometry(); void refreshTopBars(); + void showSearchInTopBar(anim::type animated); void checkUpdateStatus(); void changeOpenedSubsection( FnMut change, 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 0f547b4dba..6b5383b231 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -82,6 +82,10 @@ struct TopBarWidget::EmojiInteractionSeenAnimation { crl::time till = 0; }; +QString SwitchToChooseFromQuery() { + return u"from:"_q; +} + TopBarWidget::TopBarWidget( QWidget *parent, not_null controller) @@ -898,6 +902,7 @@ void TopBarWidget::updateControlsGeometry() { _searchField.destroy(); _searchCancel.destroy(); _jumpToDate.destroy(); + _chooseFromUser.destroy(); } auto searchFieldTop = _searchField ? countSelectedButtonsTop(_searchShown.value(_searchMode ? 1. : 0.)) @@ -970,12 +975,14 @@ void TopBarWidget::updateControlsGeometry() { _searchCancel->moveToLeft( right - _searchCancel->width(), _searchField->y()); + right -= st::dialogsCalendar.width; if (_jumpToDate) { - _jumpToDate->moveToLeft( - right - _jumpToDate->width(), - _searchField->y()); + _jumpToDate->moveToLeft(right, _searchField->y()); + } + right -= st::dialogsSearchFrom.width; + if (_chooseFromUser) { + _chooseFromUser->moveToLeft(right, _searchField->y()); } - right -= _searchCancel->width(); } _rightTaken = 0; @@ -1223,13 +1230,20 @@ bool TopBarWidget::toggleSearch(bool shown, anim::type animated) { _searchSubmitted.fire({}); }); QObject::connect(_searchField, &Ui::InputField::changed, [=] { - const auto wasEmpty = _searchQuery.current().isEmpty(); - const auto query = _searchField->getLastText(); - const auto nowEmpty = query.isEmpty(); - if (_jumpToDate && nowEmpty != wasEmpty) { + const auto was = _searchQuery.current(); + const auto now = _searchField->getLastText(); + if (_jumpToDate && was.isEmpty() != now.isEmpty()) { updateControlsVisibility(); } - _searchQuery = query; + if (_chooseFromUser) { + auto switchToChooseFrom = SwitchToChooseFromQuery(); + if (was != switchToChooseFrom + && switchToChooseFrom.startsWith(was) + && now == switchToChooseFrom) { + _chooseFromUserRequests.fire({}); + } + } + _searchQuery = now; }); } else { Assert(_searchField != nullptr); @@ -1253,9 +1267,11 @@ bool TopBarWidget::toggleSearch(bool shown, anim::type animated) { } void TopBarWidget::searchEnableJumpToDate(bool enable) { - if (!_searchMode && !enable) { + if (!_searchMode) { return; - } else if (enable) { + } else if (!enable) { + _jumpToDate.destroy(); + } else if (!_jumpToDate) { _jumpToDate.create( this, object_ptr(this, st::dialogsCalendar)); @@ -1266,13 +1282,37 @@ void TopBarWidget::searchEnableJumpToDate(bool enable) { ) | rpl::to_empty | rpl::start_to_stream( _jumpToDateRequests, _jumpToDate->lifetime()); - } else { - _jumpToDate.destroy(); } updateControlsVisibility(); updateControlsGeometry(); } +void TopBarWidget::searchEnableChooseFromUser(bool enable, bool visible) { + if (!_searchMode) { + return; + } else if (!enable) { + _chooseFromUser.destroy(); + } else if (!_chooseFromUser) { + _chooseFromUser.create( + this, + object_ptr(this, st::dialogsSearchFrom)); + _chooseFromUser->toggle(visible, anim::type::instant); + _chooseFromUser->entity()->clicks( + ) | rpl::to_empty | rpl::start_to_stream( + _chooseFromUserRequests, + _chooseFromUser->lifetime()); + } else { + _chooseFromUser->toggle(visible, anim::type::normal); + } + auto additional = QMargins(); + if (_chooseFromUser && _chooseFromUser->toggled()) { + additional.setRight(_chooseFromUser->width()); + } + _searchField->setAdditionalMargins(additional); + updateControlsVisibility(); + updateControlsGeometry(); +} + bool TopBarWidget::searchSetFocus() { if (!_searchMode) { return false; 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 9ff074cf24..d283aab2aa 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -40,6 +40,8 @@ namespace HistoryView { class SendActionPainter; +[[nodiscard]] QString SwitchToChooseFromQuery(); + class TopBarWidget final : public Ui::RpWidget { public: struct SelectedState { @@ -78,6 +80,7 @@ public: bool toggleSearch(bool shown, anim::type animated); void searchEnableJumpToDate(bool enable); + void searchEnableChooseFromUser(bool enable, bool visible); bool searchSetFocus(); [[nodiscard]] bool searchHasFocus() const; [[nodiscard]] rpl::producer<> searchCancelled() const; @@ -104,6 +107,9 @@ public: [[nodiscard]] rpl::producer<> jumpToDateRequest() const { return _jumpToDateRequests.events(); } + [[nodiscard]] rpl::producer<> chooseFromUserRequest() const { + return _chooseFromUserRequests.events(); + } [[nodiscard]] rpl::producer<> searchRequest() const; protected: @@ -203,6 +209,7 @@ private: rpl::event_stream<> _searchCancelled; rpl::event_stream<> _searchSubmitted; rpl::event_stream<> _jumpToDateRequests; + rpl::event_stream<> _chooseFromUserRequests; object_ptr _back; object_ptr _cancelChoose;