Implement stick-to-bottom flag

This commit is contained in:
Lukas Brübach 2026-08-12 01:16:14 +02:00
parent 5da29fc52b
commit d31583dc2d
2 changed files with 26 additions and 9 deletions

View file

@ -51,6 +51,9 @@ ChatView::ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _sho
setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
setOpenLinks(false); setOpenLinks(false);
connect(this, &ChatView::anchorClicked, this, &ChatView::openLink); connect(this, &ChatView::anchorClicked, this, &ChatView::openLink);
connect(verticalScrollBar(), &QScrollBar::rangeChanged, this, &ChatView::onScrollBarRangeChanged);
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &ChatView::onScrollBarValueChanged);
} }
void ChatView::adjustColorsToPalette() void ChatView::adjustColorsToPalette()
@ -149,7 +152,7 @@ void ChatView::appendHtml(const QString &html)
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum(); bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
prepareBlock().insertHtml(html); prepareBlock().insertHtml(html);
if (atBottom) { if (atBottom) {
verticalScrollBar()->setValue(verticalScrollBar()->maximum()); scrollToBottom();
} }
} }
@ -167,7 +170,7 @@ void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold,
prepareBlock().insertHtml(htmlText); prepareBlock().insertHtml(htmlText);
if (atBottom) { 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)) { if (atBottom || messageType.testFlag(Event_RoomSay::ChatHistory)) {
scrollToBottom(); scrollToBottom();
} }
@ -334,14 +339,24 @@ void ChatView::appendMessage(QString message,
void ChatView::scrollToBottom() 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()); verticalScrollBar()->setValue(verticalScrollBar()->maximum());
}
if (!scrollToBottomPending) { void ChatView::onScrollBarRangeChanged()
scrollToBottomPending = true; {
QTimer::singleShot(0, this, [this] { if (stickToBottom) {
scrollToBottomPending = false;
verticalScrollBar()->setValue(verticalScrollBar()->maximum()); verticalScrollBar()->setValue(verticalScrollBar()->maximum());
}); }
}
void ChatView::onScrollBarValueChanged(int value)
{
if (value < verticalScrollBar()->maximum()) {
stickToBottom = false;
} }
} }

View file

@ -60,7 +60,7 @@ private:
QStringList highlightedWords; QStringList highlightedWords;
bool evenNumber; bool evenNumber;
bool showTimestamps; bool showTimestamps;
bool scrollToBottomPending = false; bool stickToBottom = false;
HoveredItemType hoveredItemType; HoveredItemType hoveredItemType;
QString hoveredContent; QString hoveredContent;
QAction *messageClicked; QAction *messageClicked;
@ -89,6 +89,8 @@ private slots:
void actMessageClicked(); void actMessageClicked();
void adjustColorsToPalette(); void adjustColorsToPalette();
void refreshBlockColors(); void refreshBlockColors();
void onScrollBarRangeChanged();
void onScrollBarValueChanged(int value);
public: public:
ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _showTimestamps, QWidget *parent = nullptr); ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _showTimestamps, QWidget *parent = nullptr);