From b24fe4e5c1d073e8592ad9d5b0c4883b4ab9db59 Mon Sep 17 00:00:00 2001 From: Ivy233 Date: Tue, 11 Aug 2026 17:21:45 +0800 Subject: [PATCH] fix(dock): fix wrong docked app icons after login MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Track rowsInserted of the apps model in DockGlobalElementModel to keep the cached sourceRow of docked items in sync while the apps model is rebuilt at startup; only rowsRemoved was handled before, so inserted rows made docked items resolve to the wrong app and show the wrong icon/name 2. Handle apps-model modelReset by re-resolving every cached sourceRow by desktopId 3. Guard DockGlobalElementModel::data() against an out-of-range cached sourceRow and re-resolve it on demand 4. Include IconNameRole/NameRole in dataChanged when a dock row switches its source between the apps model and the active-app model, so QML delegates refresh the icon/name 5. Drop invalid dataChanged (row -1) in DockItemModel before forwarding to QML Log: fix docked app icon/name showing another app's data after login Influence: 1. Verify docked app icons are correct right after login/restart 2. Verify icons stay correct while apps are installed/uninstalled/launched 3. Verify dock item name/tooltip matches the hovered app 4. Verify window split and group modes still work fix(dock): 修复登录后驻留应用图标显示错误 1. 在 DockGlobalElementModel 中跟踪应用模型的行插入(rowsInserted),使 驻留项缓存的 sourceRow 在启动期应用模型重建时保持同步;此前仅处理行删除, 插入的行会使驻留项解析到错误的应用,显示错误的图标/名称 2. 应用模型整体重建(modelReset)时按 desktopId 重新解析所有缓存的 sourceRow 3. DockGlobalElementModel::data() 对越界的缓存 sourceRow 增加保护,并按需 重新解析 4. 驻留行在应用模型与运行应用模型之间切换时,dataChanged 补充 IconNameRole/NameRole,确保 QML 委托刷新图标/名称 5. DockItemModel 转发 dataChanged 前过滤无效索引(row 为 -1) Log: 修复登录后驻留应用图标/名称显示为其他应用数据的问题 Influence: 1. 验证重启登录后驻留应用图标正确 2. 验证应用安装/卸载、启动过程中图标仍正确 3. 验证驻留项名称/悬浮提示与所悬停应用一致 4. 验证窗口拆分/分组模式正常 PMS: BUG-372179 --- .../taskmanager/dockglobalelementmodel.cpp | 57 ++++++++++++++++++- panels/dock/taskmanager/dockitemmodel.cpp | 2 +- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/panels/dock/taskmanager/dockglobalelementmodel.cpp b/panels/dock/taskmanager/dockglobalelementmodel.cpp index 65a7125d7..23de5c700 100644 --- a/panels/dock/taskmanager/dockglobalelementmodel.cpp +++ b/panels/dock/taskmanager/dockglobalelementmodel.cpp @@ -57,6 +57,40 @@ DockGlobalElementModel::DockGlobalElementModel(QAbstractItemModel *appsModel, Do }, Qt::QueuedConnection); + // Keep the cached apps-model sourceRow in sync when apps are inserted. + // Without this the cached sourceRow drifts during the startup app-model + // rebuild and docked items resolve to the wrong app (wrong icon/name). + connect( + m_appsModel, + &QAbstractItemModel::rowsInserted, + this, + [this](const QModelIndex &parent, int first, int last) { + Q_UNUSED(parent) + const int insertedCount = (last - first) + 1; + std::for_each(m_data.begin(), m_data.end(), [this, first, insertedCount](auto &data) { + if (std::get<1>(data) == m_appsModel && std::get<2>(data) >= first) { + data = std::make_tuple(std::get<0>(data), std::get<1>(data), std::get<2>(data) + insertedCount); + } + }); + }, + Qt::QueuedConnection); + + // Apps model full rebuild (modelReset): re-resolve every cached sourceRow. + connect( + m_appsModel, + &QAbstractItemModel::modelReset, + this, + [this]() { + for (auto &data : m_data) { + if (std::get<1>(data) != m_appsModel) + continue; + const auto id = std::get<0>(data); + auto res = m_appsModel->match(m_appsModel->index(0, 0), TaskManager::DesktopIdRole, id, 1, Qt::MatchExactly); + std::get<2>(data) = res.isEmpty() ? -1 : res.first().row(); + } + }, + Qt::QueuedConnection); + connect( m_activeAppModel, &QAbstractItemModel::rowsInserted, @@ -94,7 +128,9 @@ DockGlobalElementModel::DockGlobalElementModel(QAbstractItemModel *appsModel, Do TaskManager::AttentionRole, TaskManager::WindowsRole, TaskManager::MenusRole, - TaskManager::WinTitleRole}); + TaskManager::WinTitleRole, + TaskManager::IconNameRole, + TaskManager::NameRole}); continue; } @@ -181,7 +217,7 @@ DockGlobalElementModel::DockGlobalElementModel(QAbstractItemModel *appsModel, Do auto pIndex = this->index(pos, 0); Q_EMIT dataChanged(pIndex, pIndex, - {TaskManager::ActiveRole, TaskManager::AttentionRole, TaskManager::WindowsRole, TaskManager::MenusRole, TaskManager::WinTitleRole}); + {TaskManager::ActiveRole, TaskManager::AttentionRole, TaskManager::WindowsRole, TaskManager::MenusRole, TaskManager::WinTitleRole, TaskManager::IconNameRole, TaskManager::NameRole}); } }, Qt::QueuedConnection); @@ -318,7 +354,7 @@ void DockGlobalElementModel::loadDockedElements() if (!m_data.isEmpty()) { // MenusRole should also be handled here due to it contains the copywriting of docked or undocked - Q_EMIT dataChanged(index(0, 0), index(m_data.size() - 1, 0), {TaskManager::DockedRole, TaskManager::MenusRole}); + Q_EMIT dataChanged(index(0, 0), index(m_data.size() - 1, 0), {TaskManager::DockedRole, TaskManager::MenusRole, TaskManager::IconNameRole, TaskManager::NameRole}); } } @@ -379,20 +415,33 @@ QVariant DockGlobalElementModel::data(const QModelIndex &index, int role) const auto model = std::get<1>(data); auto row = std::get<2>(data); + // cached sourceRow can be out of range after the apps model is rebuilt; + // re-resolve by desktopId instead of reading a wrong row + if (model == m_appsModel && (row < 0 || row >= model->rowCount())) { + auto res = m_appsModel->match(m_appsModel->index(0, 0), TaskManager::DesktopIdRole, id, 1, Qt::MatchExactly); + row = res.isEmpty() ? -1 : res.first().row(); + } + switch (role) { case TaskManager::ItemIdRole: return id; case TaskManager::WindowsRole: { if (model == m_activeAppModel) { + if (row < 0 || row >= model->rowCount()) + return {}; return QStringList{model->index(row, 0).data(TaskManager::WinIdRole).toString()}; } // For m_appsModel data, when it's GroupModel we can directly get all window IDs for this desktop ID + if (row < 0 || row >= model->rowCount()) + return {}; QModelIndex groupIndex = model->index(row, 0); return groupIndex.data(TaskManager::WindowsRole).toStringList(); } case TaskManager::ActiveRole: case TaskManager::AttentionRole: { if (model == m_activeAppModel) { + if (row < 0 || row >= model->rowCount()) + return false; return model->index(row, 0).data(role); } return false; @@ -404,6 +453,8 @@ QVariant DockGlobalElementModel::data(const QModelIndex &index, int role) const default: { if (model) { + if (row < 0 || row >= model->rowCount()) + return {}; return model->index(row, 0).data(role); } return {}; diff --git a/panels/dock/taskmanager/dockitemmodel.cpp b/panels/dock/taskmanager/dockitemmodel.cpp index 767b2df65..0a63d3160 100644 --- a/panels/dock/taskmanager/dockitemmodel.cpp +++ b/panels/dock/taskmanager/dockitemmodel.cpp @@ -65,7 +65,7 @@ void DockItemModel::setSourceModel(QAbstractItemModel *model) endRemoveRows(); }); connect(sourceModel(), &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) { - if (m_isUpdating) + if (m_isUpdating || !topLeft.isValid() || !bottomRight.isValid()) return; auto first = topLeft.row(); auto last = bottomRight.row();