[Chat] Scroll chat view to bottom when loading chat history (#7079)

* [Chat] Scroll chat view to bottom when loading chat history (#2725)

Took 4 minutes


Took 38 seconds

* Harden scrolling to bottom

Took 11 minutes

* Implement stick-to-bottom flag

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-08-12 15:52:49 +02:00 committed by GitHub
parent ef3929356b
commit ce2c31424c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 3 deletions

View file

@ -13,6 +13,7 @@
#include <QDesktopServices>
#include <QMouseEvent>
#include <QScrollBar>
#include <QTimer>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/network/server/remote/user_level.h>
#include <libcockatrice/settings/chat_settings.h>
@ -51,6 +52,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()
@ -151,7 +155,7 @@ void ChatView::appendHtml(const QString &html)
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
prepareBlock().insertHtml(html);
if (atBottom) {
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
scrollToBottom();
}
}
@ -169,7 +173,7 @@ void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold,
prepareBlock().insertHtml(htmlText);
if (atBottom) {
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
scrollToBottom();
}
}
@ -338,11 +342,36 @@ void ChatView::appendMessage(QString message,
}
}
if (atBottom) {
// 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();
}
}
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());
}
void ChatView::onScrollBarRangeChanged()
{
if (stickToBottom) {
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
}
}
void ChatView::onScrollBarValueChanged(int value)
{
if (value < verticalScrollBar()->maximum()) {
stickToBottom = false;
}
}
void ChatView::checkTag(QTextCursor &cursor, QString &message)
{
if (message.startsWith("[card]")) {

View file

@ -60,6 +60,7 @@ private:
QStringList highlightedWords;
bool evenNumber;
bool showTimestamps;
bool stickToBottom = false;
HoveredItemType hoveredItemType;
QString hoveredContent;
QAction *messageClicked;
@ -67,6 +68,7 @@ private:
[[nodiscard]] QTextFragment getFragmentUnderMouse(const QPoint &pos) const;
QTextCursor prepareBlock(bool same = false);
void scrollToBottom();
void appendCardTag(QTextCursor &cursor, const QString &cardName);
void appendUrlTag(QTextCursor &cursor, QString url);
static QColor getCustomMentionColor();
@ -88,6 +90,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);