Add navigation through reply stack.

This commit is contained in:
John Preston 2020-08-31 17:41:24 +04:00
parent c563df7d9d
commit f22a804220
4 changed files with 142 additions and 18 deletions

View file

@ -415,6 +415,10 @@ void ListWidget::animatedScrollTo(
transition); transition);
} }
bool ListWidget::animatedScrolling() const {
return _scrollToAnimation.animating();
}
void ListWidget::scrollToAnimationCallback( void ListWidget::scrollToAnimationCallback(
FullMsgId attachToId, FullMsgId attachToId,
int relativeTo) { int relativeTo) {
@ -495,9 +499,11 @@ void ListWidget::saveScrollState() {
} }
void ListWidget::restoreScrollState() { void ListWidget::restoreScrollState() {
if (_items.empty() if (_items.empty()) {
|| (_overrideInitialScroll return;
&& base::take(_overrideInitialScroll)())) { } else if (_overrideInitialScroll
&& base::take(_overrideInitialScroll)()) {
_scrollTopState = ScrollTopState();
return; return;
} }
if (!_scrollTopState.item) { if (!_scrollTopState.item) {

View file

@ -161,6 +161,7 @@ public:
Data::MessagePosition attachPosition, Data::MessagePosition attachPosition,
int delta, int delta,
AnimatedScroll type); AnimatedScroll type);
[[nodiscard]] bool animatedScrolling() const;
bool isAbovePosition(Data::MessagePosition position) const; bool isAbovePosition(Data::MessagePosition position) const;
bool isBelowPosition(Data::MessagePosition position) const; bool isBelowPosition(Data::MessagePosition position) const;
void highlightMessage(FullMsgId itemId); void highlightMessage(FullMsgId itemId);

View file

@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_replies_list.h" #include "data/data_replies_list.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_changes.h"
#include "storage/storage_media_prepare.h" #include "storage/storage_media_prepare.h"
#include "storage/storage_account.h" #include "storage/storage_account.h"
#include "inline_bots/inline_bot_result.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 "styles/style_boxes.h"
#include <QtCore/QMimeData> #include <QtCore/QMimeData>
#include <QtGui/QGuiApplication>
namespace HistoryView { namespace HistoryView {
namespace { namespace {
@ -156,6 +158,16 @@ RepliesWidget::RepliesWidget(
} }
}, _inner->lifetime()); }, _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(); setupScrollDownButton();
setupComposeControls(); setupComposeControls();
} }
@ -468,6 +480,57 @@ void RepliesWidget::uploadFilesAfterConfirmation(
action); action);
} }
void RepliesWidget::pushReplyReturn(not_null<HistoryItem*> item) {
if (item->history() == _history && item->replyToTop() == _rootId) {
_replyReturns.push_back(item->id);
} else {
return;
}
_replyReturn = item;
updateScrollDownVisibility();
}
void RepliesWidget::restoreReplyReturns(const std::vector<MsgId> &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( void RepliesWidget::uploadFile(
const QByteArray &fileContent, const QByteArray &fileContent,
SendMediaType type) { SendMediaType type) {
@ -776,19 +839,32 @@ void RepliesWidget::setupScrollDownButton() {
} }
void RepliesWidget::scrollDownClicked() { 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) { void RepliesWidget::showAtPosition(
if (!showAtPositionNow(position)) { Data::MessagePosition position,
HistoryItem *originItem) {
if (!showAtPositionNow(position, originItem)) {
_inner->showAroundPosition(position, [=] { _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)) { if (const auto scrollTop = _inner->scrollTopForPosition(position)) {
while (_replyReturn && position.fullId.msg == _replyReturn->id) {
calculateNextReplyReturn();
}
const auto currentScrollTop = _scroll->scrollTop(); const auto currentScrollTop = _scroll->scrollTop();
const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax()); const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax());
const auto fullDelta = (wanted - currentScrollTop); const auto fullDelta = (wanted - currentScrollTop);
@ -801,8 +877,12 @@ bool RepliesWidget::showAtPositionNow(Data::MessagePosition position) {
(std::abs(fullDelta) > limit (std::abs(fullDelta) > limit
? HistoryView::ListWidget::AnimatedScroll::Part ? HistoryView::ListWidget::AnimatedScroll::Part
: HistoryView::ListWidget::AnimatedScroll::Full)); : HistoryView::ListWidget::AnimatedScroll::Full));
if (const auto highlight = base::take(_highlightMessageId)) { if (position != Data::MaxMessagePosition
_inner->highlightMessage(highlight); && position != Data::UnreadMessagePosition) {
_inner->highlightMessage(position.fullId);
}
if (originItem) {
pushReplyReturn(originItem);
} }
return true; return true;
} }
@ -816,10 +896,9 @@ void RepliesWidget::updateScrollDownVisibility() {
const auto scrollDownIsVisible = [&]() -> std::optional<bool> { const auto scrollDownIsVisible = [&]() -> std::optional<bool> {
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter; const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
if (top < _scroll->scrollTopMax()) { if (top < _scroll->scrollTopMax() || _replyReturn) {
return true; return true;
} } else if (_inner->loadedAtBottomKnown()) {
if (_inner->loadedAtBottomKnown()) {
return !_inner->loadedAtBottom(); return !_inner->loadedAtBottom();
} }
return std::nullopt; return std::nullopt;
@ -939,13 +1018,27 @@ bool RepliesWidget::showMessage(
if (!message || message->replyToTop() != _rootId) { if (!message || message->replyToTop() != _rootId) {
return false; 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<OriginMessage>(&params.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; return true;
} }
void RepliesWidget::saveState(not_null<RepliesMemento*> memento) { void RepliesWidget::saveState(not_null<RepliesMemento*> memento) {
memento->setReplies(_replies); memento->setReplies(_replies);
memento->setReplyReturns(_replyReturns);
_inner->saveState(memento->list()); _inner->saveState(memento->list());
} }
@ -972,6 +1065,7 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
} else if (!_replies) { } else if (!_replies) {
setReplies(std::make_shared<Data::RepliesList>(_history, _rootId)); setReplies(std::make_shared<Data::RepliesList>(_history, _rootId));
} }
restoreReplyReturns(memento->replyReturns());
_inner->restoreState(memento->list()); _inner->restoreState(memento->list());
} }
@ -1039,6 +1133,9 @@ void RepliesWidget::onScroll() {
} }
void RepliesWidget::updateInnerVisibleArea() { void RepliesWidget::updateInnerVisibleArea() {
if (!_inner->animatedScrolling()) {
checkReplyReturns();
}
const auto scrollTop = _scroll->scrollTop(); const auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); _inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
updateScrollDownVisibility(); updateScrollDownVisibility();
@ -1144,6 +1241,7 @@ std::optional<int> RepliesWidget::listUnreadBarView(
} }
void RepliesWidget::listContentRefreshed() { void RepliesWidget::listContentRefreshed() {
} }
ClickHandlerPtr RepliesWidget::listDateLink(not_null<Element*> view) { ClickHandlerPtr RepliesWidget::listDateLink(not_null<Element*> view) {

View file

@ -137,8 +137,12 @@ private:
void updateAdaptiveLayout(); void updateAdaptiveLayout();
void saveState(not_null<RepliesMemento*> memento); void saveState(not_null<RepliesMemento*> memento);
void restoreState(not_null<RepliesMemento*> memento); void restoreState(not_null<RepliesMemento*> memento);
void showAtPosition(Data::MessagePosition position); void showAtPosition(
bool showAtPositionNow(Data::MessagePosition position); Data::MessagePosition position,
HistoryItem *originItem = nullptr);
bool showAtPositionNow(
Data::MessagePosition position,
HistoryItem *originItem);
void setupComposeControls(); void setupComposeControls();
@ -163,6 +167,12 @@ private:
void chooseAttach(); void chooseAttach();
[[nodiscard]] SendMenu::Type sendMenuType() const; [[nodiscard]] SendMenu::Type sendMenuType() const;
void pushReplyReturn(not_null<HistoryItem*> item);
void computeCurrentReplyReturn();
void calculateNextReplyReturn();
void restoreReplyReturns(const std::vector<MsgId> &list);
void checkReplyReturns();
void uploadFile(const QByteArray &fileContent, SendMediaType type); void uploadFile(const QByteArray &fileContent, SendMediaType type);
bool confirmSendingFiles( bool confirmSendingFiles(
QImage &&image, QImage &&image,
@ -212,7 +222,8 @@ private:
std::unique_ptr<ComposeControls> _composeControls; std::unique_ptr<ComposeControls> _composeControls;
bool _skipScrollEvent = false; bool _skipScrollEvent = false;
FullMsgId _highlightMessageId; std::vector<MsgId> _replyReturns;
HistoryItem *_replyReturn = nullptr;
Ui::Animations::Simple _scrollDownShown; Ui::Animations::Simple _scrollDownShown;
bool _scrollDownIsShown = false; bool _scrollDownIsShown = false;
@ -250,6 +261,13 @@ public:
return _replies; return _replies;
} }
void setReplyReturns(const std::vector<MsgId> &list) {
_replyReturns = list;
}
const std::vector<MsgId> &replyReturns() const {
return _replyReturns;
}
[[nodiscard]] not_null<ListMemento*> list() { [[nodiscard]] not_null<ListMemento*> list() {
return &_list; return &_list;
} }
@ -259,6 +277,7 @@ private:
const MsgId _rootId = 0; const MsgId _rootId = 0;
ListMemento _list; ListMemento _list;
std::shared_ptr<Data::RepliesList> _replies; std::shared_ptr<Data::RepliesList> _replies;
std::vector<MsgId> _replyReturns;
}; };