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);
}
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) {

View file

@ -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);

View file

@ -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 <QtCore/QMimeData>
#include <QtGui/QGuiApplication>
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<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(
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<bool> {
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<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;
}
void RepliesWidget::saveState(not_null<RepliesMemento*> memento) {
memento->setReplies(_replies);
memento->setReplyReturns(_replyReturns);
_inner->saveState(memento->list());
}
@ -972,6 +1065,7 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
} else if (!_replies) {
setReplies(std::make_shared<Data::RepliesList>(_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<int> RepliesWidget::listUnreadBarView(
}
void RepliesWidget::listContentRefreshed() {
}
ClickHandlerPtr RepliesWidget::listDateLink(not_null<Element*> view) {

View file

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