From f22a804220d360e57ca749790402e1411b679fe3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 31 Aug 2020 17:41:24 +0400 Subject: [PATCH] Add navigation through reply stack. --- .../history/view/history_view_list_widget.cpp | 12 +- .../history/view/history_view_list_widget.h | 1 + .../view/history_view_replies_section.cpp | 122 ++++++++++++++++-- .../view/history_view_replies_section.h | 25 +++- 4 files changed, 142 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index c47d8e719..e67dec98f 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -415,6 +415,10 @@ void ListWidget::animatedScrollTo( transition); } +bool ListWidget::animatedScrolling() const { + return _scrollToAnimation.animating(); +} + void ListWidget::scrollToAnimationCallback( FullMsgId attachToId, int relativeTo) { @@ -495,9 +499,11 @@ void ListWidget::saveScrollState() { } void ListWidget::restoreScrollState() { - if (_items.empty() - || (_overrideInitialScroll - && base::take(_overrideInitialScroll)())) { + if (_items.empty()) { + return; + } else if (_overrideInitialScroll + && base::take(_overrideInitialScroll)()) { + _scrollTopState = ScrollTopState(); return; } if (!_scrollTopState.item) { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 44322a516..073cc7ff1 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -161,6 +161,7 @@ public: Data::MessagePosition attachPosition, int delta, AnimatedScroll type); + [[nodiscard]] bool animatedScrolling() const; bool isAbovePosition(Data::MessagePosition position) const; bool isBelowPosition(Data::MessagePosition position) const; void highlightMessage(FullMsgId itemId); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index cf4984c9d..e9784ecd8 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_replies_list.h" #include "data/data_user.h" +#include "data/data_changes.h" #include "storage/storage_media_prepare.h" #include "storage/storage_account.h" #include "inline_bots/inline_bot_result.h" @@ -53,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include +#include namespace HistoryView { namespace { @@ -156,6 +158,16 @@ RepliesWidget::RepliesWidget( } }, _inner->lifetime()); + _history->session().changes().messageUpdates( + Data::MessageUpdate::Flag::Destroyed + ) | rpl::filter([=](const Data::MessageUpdate &update) { + return (update.item == _replyReturn); + }) | rpl::start_with_next([=](const Data::MessageUpdate &update) { + while (update.item == _replyReturn) { + calculateNextReplyReturn(); + } + }, lifetime()); + setupScrollDownButton(); setupComposeControls(); } @@ -468,6 +480,57 @@ void RepliesWidget::uploadFilesAfterConfirmation( action); } +void RepliesWidget::pushReplyReturn(not_null item) { + if (item->history() == _history && item->replyToTop() == _rootId) { + _replyReturns.push_back(item->id); + } else { + return; + } + _replyReturn = item; + updateScrollDownVisibility(); +} + +void RepliesWidget::restoreReplyReturns(const std::vector &list) { + _replyReturns = list; + computeCurrentReplyReturn(); + if (!_replyReturn) { + calculateNextReplyReturn(); + } +} + +void RepliesWidget::computeCurrentReplyReturn() { + _replyReturn = _replyReturns.empty() + ? nullptr + : _history->owner().message( + _history->channelId(), + _replyReturns.back()); +} + +void RepliesWidget::calculateNextReplyReturn() { + _replyReturn = nullptr; + while (!_replyReturns.empty() && !_replyReturn) { + _replyReturns.pop_back(); + computeCurrentReplyReturn(); + } + if (!_replyReturn) { + updateScrollDownVisibility(); + } +} + +void RepliesWidget::checkReplyReturns() { + const auto currentTop = _scroll->scrollTop(); + for (; _replyReturn != nullptr; calculateNextReplyReturn()) { + const auto position = _replyReturn->position(); + const auto scrollTop = _inner->scrollTopForPosition(position); + const auto scrolledBelow = scrollTop + ? (currentTop >= std::min(*scrollTop, _scroll->scrollTopMax())) + : _inner->isBelowPosition(position); + if (!scrolledBelow) { + break; + } + } +} + void RepliesWidget::uploadFile( const QByteArray &fileContent, SendMediaType type) { @@ -776,19 +839,32 @@ void RepliesWidget::setupScrollDownButton() { } void RepliesWidget::scrollDownClicked() { - showAtPosition(Data::MaxMessagePosition); + if (QGuiApplication::keyboardModifiers() == Qt::ControlModifier) { + showAtPosition(Data::MaxMessagePosition); + } else if (_replyReturn) { + showAtPosition(_replyReturn->position()); + } else { + showAtPosition(Data::MaxMessagePosition); + } } -void RepliesWidget::showAtPosition(Data::MessagePosition position) { - if (!showAtPositionNow(position)) { +void RepliesWidget::showAtPosition( + Data::MessagePosition position, + HistoryItem *originItem) { + if (!showAtPositionNow(position, originItem)) { _inner->showAroundPosition(position, [=] { - return showAtPositionNow(position); + return showAtPositionNow(position, originItem); }); } } -bool RepliesWidget::showAtPositionNow(Data::MessagePosition position) { +bool RepliesWidget::showAtPositionNow( + Data::MessagePosition position, + HistoryItem *originItem) { if (const auto scrollTop = _inner->scrollTopForPosition(position)) { + while (_replyReturn && position.fullId.msg == _replyReturn->id) { + calculateNextReplyReturn(); + } const auto currentScrollTop = _scroll->scrollTop(); const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax()); const auto fullDelta = (wanted - currentScrollTop); @@ -801,8 +877,12 @@ bool RepliesWidget::showAtPositionNow(Data::MessagePosition position) { (std::abs(fullDelta) > limit ? HistoryView::ListWidget::AnimatedScroll::Part : HistoryView::ListWidget::AnimatedScroll::Full)); - if (const auto highlight = base::take(_highlightMessageId)) { - _inner->highlightMessage(highlight); + if (position != Data::MaxMessagePosition + && position != Data::UnreadMessagePosition) { + _inner->highlightMessage(position.fullId); + } + if (originItem) { + pushReplyReturn(originItem); } return true; } @@ -816,10 +896,9 @@ void RepliesWidget::updateScrollDownVisibility() { const auto scrollDownIsVisible = [&]() -> std::optional { const auto top = _scroll->scrollTop() + st::historyToDownShownAfter; - if (top < _scroll->scrollTopMax()) { + if (top < _scroll->scrollTopMax() || _replyReturn) { return true; - } - if (_inner->loadedAtBottomKnown()) { + } else if (_inner->loadedAtBottomKnown()) { return !_inner->loadedAtBottom(); } return std::nullopt; @@ -939,13 +1018,27 @@ bool RepliesWidget::showMessage( if (!message || message->replyToTop() != _rootId) { return false; } - _highlightMessageId = id; - showAtPosition(Data::MessagePosition(message->date(), id)); + + const auto originItem = [&]() -> HistoryItem* { + using OriginMessage = Window::SectionShow::OriginMessage; + if (const auto origin = std::get_if(¶ms.origin)) { + if (const auto returnTo = session().data().message(origin->id)) { + if (returnTo->history() == _history + && returnTo->replyToTop() == _rootId + && _replyReturn != returnTo) { + return returnTo; + } + } + } + return nullptr; + }(); + showAtPosition(Data::MessagePosition(message->date(), id), originItem); return true; } void RepliesWidget::saveState(not_null memento) { memento->setReplies(_replies); + memento->setReplyReturns(_replyReturns); _inner->saveState(memento->list()); } @@ -972,6 +1065,7 @@ void RepliesWidget::restoreState(not_null memento) { } else if (!_replies) { setReplies(std::make_shared(_history, _rootId)); } + restoreReplyReturns(memento->replyReturns()); _inner->restoreState(memento->list()); } @@ -1039,6 +1133,9 @@ void RepliesWidget::onScroll() { } void RepliesWidget::updateInnerVisibleArea() { + if (!_inner->animatedScrolling()) { + checkReplyReturns(); + } const auto scrollTop = _scroll->scrollTop(); _inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); updateScrollDownVisibility(); @@ -1144,6 +1241,7 @@ std::optional RepliesWidget::listUnreadBarView( } void RepliesWidget::listContentRefreshed() { + } ClickHandlerPtr RepliesWidget::listDateLink(not_null view) { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 7a9969be6..9ee365d16 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -137,8 +137,12 @@ private: void updateAdaptiveLayout(); void saveState(not_null memento); void restoreState(not_null memento); - void showAtPosition(Data::MessagePosition position); - bool showAtPositionNow(Data::MessagePosition position); + void showAtPosition( + Data::MessagePosition position, + HistoryItem *originItem = nullptr); + bool showAtPositionNow( + Data::MessagePosition position, + HistoryItem *originItem); void setupComposeControls(); @@ -163,6 +167,12 @@ private: void chooseAttach(); [[nodiscard]] SendMenu::Type sendMenuType() const; + void pushReplyReturn(not_null item); + void computeCurrentReplyReturn(); + void calculateNextReplyReturn(); + void restoreReplyReturns(const std::vector &list); + void checkReplyReturns(); + void uploadFile(const QByteArray &fileContent, SendMediaType type); bool confirmSendingFiles( QImage &&image, @@ -212,7 +222,8 @@ private: std::unique_ptr _composeControls; bool _skipScrollEvent = false; - FullMsgId _highlightMessageId; + std::vector _replyReturns; + HistoryItem *_replyReturn = nullptr; Ui::Animations::Simple _scrollDownShown; bool _scrollDownIsShown = false; @@ -250,6 +261,13 @@ public: return _replies; } + void setReplyReturns(const std::vector &list) { + _replyReturns = list; + } + const std::vector &replyReturns() const { + return _replyReturns; + } + [[nodiscard]] not_null list() { return &_list; } @@ -259,6 +277,7 @@ private: const MsgId _rootId = 0; ListMemento _list; std::shared_ptr _replies; + std::vector _replyReturns; };