From 37c5fa560769c5ab1fcc85173e366bed7e072c94 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 24 Jul 2026 02:59:15 +0300 Subject: [PATCH] chatgui: fix scroll jump on refresh, force debounce, add DEBUG logs - Same-channel refresh: save scroll pos in memory, restore from memory (no DB race) - Force m_readDebounce->start() after loadChannel (scrollToBottom may not trigger valueChanged) - DEBUG_DEBUG logs in updateReadPosition, saveChannelState, onMessageReceived, setChannelUnreadCount --- tools/chatgui/src/channellist.cpp | 4 +++ tools/chatgui/src/mainwindow.cpp | 7 +++-- tools/chatgui/src/messagelist.cpp | 47 +++++++++++++++++++++++++++---- tools/chatgui/src/messagelist.h | 2 ++ 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/tools/chatgui/src/channellist.cpp b/tools/chatgui/src/channellist.cpp index 31c167b5..a11b91fb 100644 --- a/tools/chatgui/src/channellist.cpp +++ b/tools/chatgui/src/channellist.cpp @@ -245,11 +245,15 @@ void ChannelList::setChannelUnreadCount(const QString& channelId, int count) { for (int row = 0; row < m_model->rowCount(); row++) { QStandardItem* item = m_model->item(row); if (item && item->data(ChannelChannelIdRole).toString() == channelId) { + int old = item->data(ChannelUnreadCountRole).toInt(); + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "ChannelList::setChannelUnreadCount ch=%s %d->%d", + qPrintable(channelId), old, count); item->setData(count, ChannelUnreadCountRole); emit m_model->dataChanged(m_model->index(row, 0), m_model->index(row, 0)); return; } } + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "ChannelList::setChannelUnreadCount ch=%s not found", qPrintable(channelId)); } int ChannelList::getChannelUnreadCount(const QString& channelId) const { diff --git a/tools/chatgui/src/mainwindow.cpp b/tools/chatgui/src/mainwindow.cpp index 6bf9f55d..058f4e84 100644 --- a/tools/chatgui/src/mainwindow.cpp +++ b/tools/chatgui/src/mainwindow.cpp @@ -327,14 +327,17 @@ void MainWindow::showSettings() { } void MainWindow::onMessageReceived(const char* ch_id) { - DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "MainWindow: msg received ch=%s", ch_id); - SoundManager::instance()->play("msg_incoming"); QString cid = QString::fromUtf8(ch_id); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "MainWindow::onMessageReceived ch=%s cur=%s", + qPrintable(cid), qPrintable(m_currentChannelId)); + SoundManager::instance()->play("msg_incoming"); if (m_messageList) { if (cid == m_currentChannelId) { m_messageList->refresh(); } else if (m_channelList) { int unread = m_channelList->getChannelUnreadCount(cid) + 1; + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "MainWindow::onMessageReceived increment unread ch=%s %d->%d", + qPrintable(cid), unread - 1, unread); m_channelList->setChannelUnreadCount(cid, unread); } } diff --git a/tools/chatgui/src/messagelist.cpp b/tools/chatgui/src/messagelist.cpp index 4053ac4f..514e5d97 100644 --- a/tools/chatgui/src/messagelist.cpp +++ b/tools/chatgui/src/messagelist.cpp @@ -23,6 +23,7 @@ extern "C" { #include "../transport/chat_core.h" #include "../../lib/u_async.h" #include "../../lib/mem.h" +#include "../../lib/debug_config.h" } static QPixmap makeAvatar(const QColor &color, const QString &letter, int size = 36) { @@ -176,16 +177,30 @@ MessageList::MessageList(DbManager* db, QWidget *parent) void MessageList::loadChannel(const QString& channelId) { QWidget* prevFocus = QApplication::focusWidget(); + bool sameChannel = (m_currentChannelId == channelId); + /* Save state of current channel before switching */ - if (!m_currentChannelId.isEmpty() && m_currentChannelId != channelId) - saveChannelState(); + if (!m_currentChannelId.isEmpty()) { + if (!sameChannel) { + saveChannelState(); + } else { + QModelIndex topIdx = m_view->indexAt(QPoint(0, 0)); + m_savedTopMsgId = topIdx.isValid() ? topIdx.data(MsgIdRole).toLongLong() : 0; + QScrollBar* sb = m_view->verticalScrollBar(); + m_wasAtBottom = sb && (sb->value() >= sb->maximum()); + } + } m_model->clear(); m_avatarCache.clear(); m_currentChannelId = channelId; m_readDebounce->stop(); - m_lastReadMsgId = m_db->getUiStateInt64( - QStringLiteral("ch_%1_last_read").arg(channelId), 0); + if (!m_db || !m_db->isOpen()) return; + + if (sameChannel) + m_lastReadMsgId = m_db->getUiStateInt64( + QStringLiteral("ch_%1_last_read").arg(channelId), 0); + if (!m_db || !m_db->isOpen()) return; auto msgs = m_db->getMessages(channelId, 100); @@ -211,7 +226,22 @@ void MessageList::loadChannel(const QString& channelId) { m_model->appendRow(item); } - restoreScrollPosition(channelId); + if (sameChannel) { + if (m_wasAtBottom || m_savedTopMsgId == 0) + m_view->scrollToBottom(); + else + for (int row = 0; row < m_model->rowCount(); row++) + if (m_model->item(row)->data(MsgIdRole).toLongLong() == m_savedTopMsgId) { + m_view->scrollTo(m_model->index(row, 0), QAbstractItemView::PositionAtTop); + break; + } + } else { + m_lastReadMsgId = m_db->getUiStateInt64( + QStringLiteral("ch_%1_last_read").arg(channelId), 0); + restoreScrollPosition(channelId); + } + + m_readDebounce->start(); if (prevFocus) prevFocus->setFocus(); @@ -264,6 +294,8 @@ void MessageList::saveChannelState() { lastPosMsgId = topIdx.data(MsgIdRole).toLongLong(); qint64 lastReadMsgId = m_lastReadMsgId; + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "MessageList::saveChannelState ch=%s read=%lld pos=%lld", + qPrintable(m_currentChannelId), (long long)lastReadMsgId, (long long)lastPosMsgId); if (lastReadMsgId == 0 && lastPosMsgId == 0) return; QByteArray cid = m_currentChannelId.toUtf8(); @@ -326,6 +358,11 @@ void MessageList::updateReadPosition() { newReadMsgId = msgId; } + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, + "MessageList::updateReadPosition ch=%s oldRead=%lld newRead=%lld lastVisRow=%d/%d", + qPrintable(m_currentChannelId), (long long)m_lastReadMsgId, + (long long)newReadMsgId, lastFullyVisibleRow, m_model->rowCount()); + if (newReadMsgId > m_lastReadMsgId) { m_lastReadMsgId = newReadMsgId; diff --git a/tools/chatgui/src/messagelist.h b/tools/chatgui/src/messagelist.h index 192da3a8..0ad6835f 100644 --- a/tools/chatgui/src/messagelist.h +++ b/tools/chatgui/src/messagelist.h @@ -35,6 +35,8 @@ private: QHash m_avatarCache; qint64 m_lastReadMsgId = 0; QTimer* m_readDebounce; + qint64 m_savedTopMsgId = 0; + bool m_wasAtBottom = false; void saveChannelState(); void restoreScrollPosition(const QString& channelId);