From d31583dc2dcbc42403a34bb7e9acbcb7db87ed09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Wed, 12 Aug 2026 01:16:14 +0200 Subject: [PATCH] Implement stick-to-bottom flag --- .../widgets/server/chat_view/chat_view.cpp | 31 ++++++++++++++----- .../widgets/server/chat_view/chat_view.h | 4 ++- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp index 64812b4a4..5b567e77e 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp @@ -51,6 +51,9 @@ ChatView::ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _sho setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); setOpenLinks(false); connect(this, &ChatView::anchorClicked, this, &ChatView::openLink); + + connect(verticalScrollBar(), &QScrollBar::rangeChanged, this, &ChatView::onScrollBarRangeChanged); + connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &ChatView::onScrollBarValueChanged); } void ChatView::adjustColorsToPalette() @@ -149,7 +152,7 @@ void ChatView::appendHtml(const QString &html) bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum(); prepareBlock().insertHtml(html); if (atBottom) { - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); + scrollToBottom(); } } @@ -167,7 +170,7 @@ void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold, prepareBlock().insertHtml(htmlText); if (atBottom) { - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); + scrollToBottom(); } } @@ -327,6 +330,8 @@ void ChatView::appendMessage(QString message, } } + // ChatHistory messages are only ever sent once per room, right after joining, before the user can + // interact with the view. Always scroll to the bottom so the whole history is visible on join. if (atBottom || messageType.testFlag(Event_RoomSay::ChatHistory)) { scrollToBottom(); } @@ -334,14 +339,24 @@ void ChatView::appendMessage(QString message, void ChatView::scrollToBottom() { + // The document layout, and therefore the scrollbar range, may be updated asynchronously (e.g. while + // the chat history is loaded into a view that has not been laid out yet). Setting the value once is + // not enough: keep stickToBottom set so any later range change scrolls to the new maximum as well. + stickToBottom = true; verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} - if (!scrollToBottomPending) { - scrollToBottomPending = true; - QTimer::singleShot(0, this, [this] { - scrollToBottomPending = false; - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); - }); +void ChatView::onScrollBarRangeChanged() +{ + if (stickToBottom) { + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); + } +} + +void ChatView::onScrollBarValueChanged(int value) +{ + if (value < verticalScrollBar()->maximum()) { + stickToBottom = false; } } diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h index 447bb33aa..e722ab1c1 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h @@ -60,7 +60,7 @@ private: QStringList highlightedWords; bool evenNumber; bool showTimestamps; - bool scrollToBottomPending = false; + bool stickToBottom = false; HoveredItemType hoveredItemType; QString hoveredContent; QAction *messageClicked; @@ -89,6 +89,8 @@ private slots: void actMessageClicked(); void adjustColorsToPalette(); void refreshBlockColors(); + void onScrollBarRangeChanged(); + void onScrollBarValueChanged(int value); public: ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _showTimestamps, QWidget *parent = nullptr);