Add jump-to-end button to chat preview.

This commit is contained in:
John Preston 2024-09-05 14:40:05 +04:00
parent 40cf96202d
commit 6fce718252
4 changed files with 130 additions and 22 deletions

View file

@ -189,7 +189,8 @@ rpl::producer<MessagesSlice> HistoryMessagesViewer(
}; };
const auto messageId = (aroundId.fullId.msg == ShowAtUnreadMsgId) const auto messageId = (aroundId.fullId.msg == ShowAtUnreadMsgId)
? computeUnreadAroundId() ? computeUnreadAroundId()
: (aroundId.fullId.msg == ShowAtTheEndMsgId) : ((aroundId.fullId.msg == ShowAtTheEndMsgId)
|| (aroundId == MaxMessagePosition))
? (ServerMaxMsgId - 1) ? (ServerMaxMsgId - 1)
: (aroundId.fullId.peer == history->peer->id) : (aroundId.fullId.peer == history->peer->id)
? aroundId.fullId.msg ? aroundId.fullId.msg

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_thread.h" #include "data/data_thread.h"
#include "history/view/reactions/history_view_reactions_button.h" #include "history/view/reactions/history_view_reactions_button.h"
#include "history/view/history_view_corner_buttons.h"
#include "history/view/history_view_list_widget.h" #include "history/view/history_view_list_widget.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
@ -52,7 +53,8 @@ namespace {
class Item final class Item final
: public Ui::Menu::ItemBase : public Ui::Menu::ItemBase
, private HistoryView::ListDelegate { , private ListDelegate
, private CornerButtonsDelegate {
public: public:
Item(not_null<Ui::RpWidget*> parent, not_null<Data::Thread*> thread); Item(not_null<Ui::RpWidget*> parent, not_null<Data::Thread*> thread);
@ -73,6 +75,7 @@ private:
void setupHistory(); void setupHistory();
void updateInnerVisibleArea(); void updateInnerVisibleArea();
// ListDelegate delegate.
Context listContext() override; Context listContext() override;
bool listScrollTo(int top, bool syntetic = true) override; bool listScrollTo(int top, bool syntetic = true) override;
void listCancelRequest() override; void listCancelRequest() override;
@ -164,6 +167,16 @@ private:
std::unique_ptr<QMimeData> data, std::unique_ptr<QMimeData> data,
Fn<void()> finished) override; Fn<void()> finished) override;
// CornerButtonsDelegate delegate.
void cornerButtonsShowAtPosition(
Data::MessagePosition position) override;
Data::Thread *cornerButtonsThread() override;
FullMsgId cornerButtonsCurrentId() override;
bool cornerButtonsIgnoreVisibility() override;
std::optional<bool> cornerButtonsDownShown() override;
bool cornerButtonsUnreadMayBeShown() override;
bool cornerButtonsHas(CornerButtonType type) override;
const not_null<QAction*> _dummyAction; const not_null<QAction*> _dummyAction;
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
const not_null<Data::Thread*> _thread; const not_null<Data::Thread*> _thread;
@ -176,7 +189,8 @@ private:
const std::unique_ptr<Ui::ElasticScroll> _scroll; const std::unique_ptr<Ui::ElasticScroll> _scroll;
const std::unique_ptr<Ui::FlatButton> _markRead; const std::unique_ptr<Ui::FlatButton> _markRead;
QPointer<HistoryView::ListWidget> _inner; QPointer<ListWidget> _inner;
std::unique_ptr<CornerButtons> _cornerButtons;
rpl::event_stream<ChatPreviewAction> _actions; rpl::event_stream<ChatPreviewAction> _actions;
QImage _bg; QImage _bg;
@ -446,11 +460,16 @@ void Item::setupHistory() {
this, this,
_session, _session,
static_cast<ListDelegate*>(this))); static_cast<ListDelegate*>(this)));
_cornerButtons = std::make_unique<CornerButtons>(
_scroll.get(),
_chatStyle.get(),
static_cast<CornerButtonsDelegate*>(this));
_markRead->shownValue() | rpl::start_with_next([=](bool shown) { _markRead->shownValue() | rpl::start_with_next([=](bool shown) {
const auto top = _top->height(); const auto top = _top->height();
const auto bottom = shown ? _markRead->height() : 0; const auto bottom = shown ? _markRead->height() : 0;
_scroll->setGeometry(rect().marginsRemoved({ 0, top, 0, bottom })); _scroll->setGeometry(rect().marginsRemoved({ 0, top, 0, bottom }));
_cornerButtons->updatePositions();
}, _markRead->lifetime()); }, _markRead->lifetime());
_scroll->scrolls( _scroll->scrolls(
@ -495,6 +514,7 @@ void Item::paintEvent(QPaintEvent *e) {
void Item::updateInnerVisibleArea() { void Item::updateInnerVisibleArea() {
const auto scrollTop = _scroll->scrollTop(); const auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); _inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
_cornerButtons->updateJumpDownVisibility();
} }
Context Item::listContext() { Context Item::listContext() {
@ -592,18 +612,28 @@ MessagesBarData Item::listMessagesBar(
return {}; return {};
} }
auto skipped = false;
const auto hidden = _replies && (repliesTill < 2); const auto hidden = _replies && (repliesTill < 2);
for (auto i = 0, count = int(elements.size()); i != count; ++i) { for (auto i = 0, count = int(elements.size()); i != count; ++i) {
const auto item = elements[i]->data(); const auto item = elements[i]->data();
if (!item->isRegular() if (!item->isRegular() || (_replies && !item->replyToId())) {
|| item->out()
|| (_replies && !item->replyToId())) {
continue; continue;
} }
const auto inHistory = (item->history() == _history); const auto inHistory = (item->history() == _history);
if ((_replies && item->id > repliesTill) const auto unread = (_replies && item->id > repliesTill)
|| (migratedTill && (inHistory || item->id > migratedTill)) || (migratedTill && (inHistory || item->id > migratedTill))
|| (historyTill && inHistory && item->id > historyTill)) { || (historyTill && inHistory && item->id > historyTill);
if (!unread) {
skipped = true;
}
if (item->out()) {
continue;
}
if (unread) {
if (!skipped) {
// Don't show jumping unread bar if scrolling up from bottom.
return {};
}
return { return {
.bar = { .bar = {
.element = elements[i], .element = elements[i],
@ -800,6 +830,46 @@ void Item::listLaunchDrag(
Fn<void()> finished) { Fn<void()> finished) {
} }
void Item::cornerButtonsShowAtPosition(Data::MessagePosition position) {
if (position == Data::UnreadMessagePosition) {
position = Data::MaxMessagePosition;
}
_inner->showAtPosition(
position,
{},
_cornerButtons->doneJumpFrom(position.fullId, {}, true));
}
Data::Thread *Item::cornerButtonsThread() {
return _thread;
}
FullMsgId Item::cornerButtonsCurrentId() {
return {};
}
bool Item::cornerButtonsIgnoreVisibility() {
return false;
}
std::optional<bool> Item::cornerButtonsDownShown() {
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
if (top < _scroll->scrollTopMax()) {
return true;
} else if (_inner->loadedAtBottomKnown()) {
return !_inner->loadedAtBottom();
}
return std::nullopt;
}
bool Item::cornerButtonsUnreadMayBeShown() {
return _inner->loadedAtBottomKnown();
}
bool Item::cornerButtonsHas(CornerButtonType type) {
return (type == CornerButtonType::Down);
}
} // namespace } // namespace
ChatPreview MakeChatPreview( ChatPreview MakeChatPreview(

View file

@ -7,9 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "history/view/history_view_corner_buttons.h" #include "history/view/history_view_corner_buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/chat/chat_style.h" #include "ui/chat/chat_style.h"
#include "ui/controls/jump_down_button.h" #include "ui/controls/jump_down_button.h"
#include "ui/widgets/elastic_scroll.h"
#include "ui/widgets/scroll_area.h"
#include "base/qt/qt_key_modifiers.h" #include "base/qt/qt_key_modifiers.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
@ -33,17 +34,41 @@ CornerButtons::CornerButtons(
not_null<Ui::ScrollArea*> parent, not_null<Ui::ScrollArea*> parent,
not_null<const Ui::ChatStyle*> st, not_null<const Ui::ChatStyle*> st,
not_null<CornerButtonsDelegate*> delegate) not_null<CornerButtonsDelegate*> delegate)
: _scroll(parent) : CornerButtons(
parent,
[=](QEvent *e) { return parent->viewportEvent(e); },
st,
delegate) {
}
CornerButtons::CornerButtons(
not_null<Ui::ElasticScroll*> parent,
not_null<const Ui::ChatStyle*> st,
not_null<CornerButtonsDelegate*> delegate)
: CornerButtons(
parent,
[=](QEvent *e) { return parent->viewportEvent(e); },
st,
delegate) {
}
CornerButtons::CornerButtons(
not_null<QWidget*> parent,
Fn<bool(QEvent*)> scrollViewportEvent,
not_null<const Ui::ChatStyle*> st,
not_null<CornerButtonsDelegate*> delegate)
: _parent(parent)
, _scrollViewportEvent(std::move(scrollViewportEvent))
, _delegate(delegate) , _delegate(delegate)
, _down( , _down(
parent, parent,
st->value(parent->lifetime(), st::historyToDown)) st->value(_stLifetime, st::historyToDown))
, _mentions( , _mentions(
parent, parent,
st->value(parent->lifetime(), st::historyUnreadMentions)) st->value(_stLifetime, st::historyUnreadMentions))
, _reactions( , _reactions(
parent, parent,
st->value(parent->lifetime(), st::historyUnreadReactions)) { st->value(_stLifetime, st::historyUnreadReactions)) {
_down.widget->addClickHandler([=] { downClick(); }); _down.widget->addClickHandler([=] { downClick(); });
_mentions.widget->addClickHandler([=] { mentionsClick(); }); _mentions.widget->addClickHandler([=] { mentionsClick(); });
_reactions.widget->addClickHandler([=] { reactionsClick(); }); _reactions.widget->addClickHandler([=] { reactionsClick(); });
@ -68,7 +93,7 @@ bool CornerButtons::eventFilter(QObject *o, QEvent *e) {
&& (o == _down.widget && (o == _down.widget
|| o == _mentions.widget || o == _mentions.widget
|| o == _reactions.widget)) { || o == _reactions.widget)) {
return _scroll->viewportEvent(e); return _scrollViewportEvent(e);
} }
return QObject::eventFilter(o, e); return QObject::eventFilter(o, e);
} }
@ -200,9 +225,7 @@ void CornerButtons::showAt(MsgId id) {
} }
} }
void CornerButtons::updateVisibility( void CornerButtons::updateVisibility(Type type, bool shown) {
CornerButtonType type,
bool shown) {
auto &button = buttonByType(type); auto &button = buttonByType(type);
if (button.shown != shown) { if (button.shown != shown) {
button.shown = shown; button.shown = shown;
@ -291,7 +314,7 @@ void CornerButtons::updatePositions() {
historyDownShown); historyDownShown);
_down.widget->moveToRight( _down.widget->moveToRight(
st::historyToDownPosition.x(), st::historyToDownPosition.x(),
_scroll->height() - top); _parent->height() - top);
} }
{ {
const auto right = anim::interpolate( const auto right = anim::interpolate(
@ -302,7 +325,7 @@ void CornerButtons::updatePositions() {
0, 0,
_down.widget->height() + skip, _down.widget->height() + skip,
historyDownShown); historyDownShown);
const auto top = _scroll->height() const auto top = _parent->height()
- _mentions.widget->height() - _mentions.widget->height()
- st::historyToDownPosition.y() - st::historyToDownPosition.y()
- shift; - shift;
@ -321,7 +344,7 @@ void CornerButtons::updatePositions() {
0, 0,
_mentions.widget->height() + skip, _mentions.widget->height() + skip,
unreadMentionsShown); unreadMentionsShown);
const auto top = _scroll->height() const auto top = _parent->height()
- _reactions.widget->height() - _reactions.widget->height()
- st::historyToDownPosition.y() - st::historyToDownPosition.y()
- shift; - shift;
@ -355,7 +378,7 @@ Fn<void(bool found)> CornerButtons::doneJumpFrom(
} }
if (!found && !ignoreMessageNotFound) { if (!found && !ignoreMessageNotFound) {
Ui::Toast::Show( Ui::Toast::Show(
_scroll.get(), _parent.get(),
tr::lng_message_not_found(tr::now)); tr::lng_message_not_found(tr::now));
} }
}; };

View file

@ -17,6 +17,7 @@ struct FullMsgId;
namespace Ui { namespace Ui {
class ChatStyle; class ChatStyle;
class ScrollArea; class ScrollArea;
class ElasticScroll;
class JumpDownButton; class JumpDownButton;
} // namespace Ui } // namespace Ui
@ -61,6 +62,10 @@ public:
not_null<Ui::ScrollArea*> parent, not_null<Ui::ScrollArea*> parent,
not_null<const Ui::ChatStyle*> st, not_null<const Ui::ChatStyle*> st,
not_null<CornerButtonsDelegate*> delegate); not_null<CornerButtonsDelegate*> delegate);
CornerButtons(
not_null<Ui::ElasticScroll*> parent,
not_null<const Ui::ChatStyle*> st,
not_null<CornerButtonsDelegate*> delegate);
using Type = CornerButtonType; using Type = CornerButtonType;
@ -91,6 +96,12 @@ public:
bool ignoreMessageNotFound = false); bool ignoreMessageNotFound = false);
private: private:
CornerButtons(
not_null<QWidget*> parent,
Fn<bool(QEvent*)> scrollViewportEvent,
not_null<const Ui::ChatStyle*> st,
not_null<CornerButtonsDelegate*> delegate);
bool eventFilter(QObject *o, QEvent *e) override; bool eventFilter(QObject *o, QEvent *e) override;
void computeCurrentReplyReturn(); void computeCurrentReplyReturn();
@ -99,9 +110,12 @@ private:
[[nodiscard]] History *lookupHistory() const; [[nodiscard]] History *lookupHistory() const;
void showAt(MsgId id); void showAt(MsgId id);
const not_null<Ui::ScrollArea*> _scroll; const not_null<QWidget*> _parent;
const Fn<bool(QEvent*)> _scrollViewportEvent;
const not_null<CornerButtonsDelegate*> _delegate; const not_null<CornerButtonsDelegate*> _delegate;
rpl::lifetime _stLifetime;
CornerButton _down; CornerButton _down;
CornerButton _mentions; CornerButton _mentions;
CornerButton _reactions; CornerButton _reactions;