From 94c56a993bf815441130dc5dd0406e753ae8cb29 Mon Sep 17 00:00:00 2001 From: wjyrich Date: Mon, 27 Jul 2026 10:50:50 +0800 Subject: [PATCH] refactor: centralize category name localization to C++ model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the category name mapping logic from multiple QML files into the C+ + ItemArrangementProxyModel to reduce code duplication and fix potential string localization issues. This ensures consistent category display names across all views. Log: Optimize category name processing logic Influence: 1. Verify that app category names display correctly in folder grid and list views 2. Test category names in windowed mode (Alphabetical and Category sorting) 3. Check that folder popups show correct category names 4. Verify no untranslated "internal/category/" strings appear in the UI 5. Ensure localized strings still support proper translation 6. Test the dock launcher applet's folder interactions refactor: 将分类名称本地化逻辑集中到 C++ 模型中 将多个 QML 文件中的分类名称映射逻辑移到 C++ 的 ItemArrangementProxyModel 中,减少代码重复并修复潜在的字符串本地化问题。这确保了所有视图中分类显示 名称的一致性。 Log: 优化分类名称处理逻辑 Influence: 1. 验证文件夹网格和列表视图中应用分类名称显示正确 2. 测试窗口模式下(按字母排序和按分类排序)的分类名称 3. 检查文件夹弹出窗口显示正确的分类名称 4. 确保界面中不会出现未翻译的 "internal/category/" 字符串 5. 确认本地化字符串仍支持正常翻译 6. 测试 dock 启动器控件的文件夹交互功能 PMS: TASK-392671 --- qml/FullscreenFrame.qml | 2 +- qml/IconItemDelegate.qml | 2 +- qml/Main.qml | 29 +-------- qml/windowed/AppListView.qml | 4 +- qml/windowed/FreeSortListView.qml | 4 +- qml/windowed/IconItemDelegate.qml | 2 +- shell-launcher-applet/CMakeLists.txt | 3 + shell-launcher-applet/launcheritem.cpp | 59 ++++++++++++++++++- shell-launcher-applet/launcheritem.h | 10 +++- .../package/launcheritem.qml | 27 --------- src/models/itemarrangementproxymodel.cpp | 53 ++++++++++++++++- src/models/itemarrangementproxymodel.h | 5 +- 12 files changed, 133 insertions(+), 67 deletions(-) diff --git a/qml/FullscreenFrame.qml b/qml/FullscreenFrame.qml index 310dc229..9bae78fc 100644 --- a/qml/FullscreenFrame.qml +++ b/qml/FullscreenFrame.qml @@ -657,7 +657,7 @@ InputEventItem { folderGridViewPopup.startPointX = itemPos.x + width / 2 folderGridViewPopup.startPointY = itemPos.y + height / 2 folderGridViewPopup.open() - folderGridViewPopup.folderName = model.display.startsWith("internal/category/") ? getCategoryName(model.display.substring(18)) : model.display + folderGridViewPopup.folderName = model.display console.log("open folder id:" + idNum) } onMenuTriggered: { diff --git a/qml/IconItemDelegate.qml b/qml/IconItemDelegate.qml index 189335f9..09953f58 100644 --- a/qml/IconItemDelegate.qml +++ b/qml/IconItemDelegate.qml @@ -24,7 +24,7 @@ Control { // So we give the max sourceSize and use scale to solve it. property int maxIconSize: 128 property int maxIconSizeInFolder: 64 - readonly property string text: display.startsWith("internal/category/") ? getCategoryName(display.substring(18)) : display + readonly property string text: display property string iconSource property bool dndEnabled: false diff --git a/qml/Main.qml b/qml/Main.qml index 1276629e..1c6234f8 100644 --- a/qml/Main.qml +++ b/qml/Main.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -16,33 +16,6 @@ import org.deepin.launchpad.models 1.0 import org.deepin.launchpad.windowed 1.0 QtObject { - function getCategoryName(section) { - switch (Number(section)) { - case AppItem.Internet: - return qsTr("Internet"); - case AppItem.Chat: - return qsTr("Chat"); - case AppItem.Music: - return qsTr("Music"); - case AppItem.Video: - return qsTr("Video"); - case AppItem.Graphics: - return qsTr("Graphics"); - case AppItem.Game: - return qsTr("Games"); - case AppItem.Office: - return qsTr("Office"); - case AppItem.Reading: - return qsTr("Reading"); - case AppItem.Development: - return qsTr("Development"); - case AppItem.System: - return qsTr("System"); - default: - return qsTr("Others"); - } - } - function launchApp(desktopId) { if (DebugHelper.avoidLaunchApp) { DTK.sendSystemMessage("dde-launchpad (debug)", diff --git a/qml/windowed/AppListView.qml b/qml/windowed/AppListView.qml index c4dcef0a..d5c6aee3 100644 --- a/qml/windowed/AppListView.qml +++ b/qml/windowed/AppListView.qml @@ -106,7 +106,7 @@ FocusScope { if (CategorizedSortProxyModel.categoryType === CategorizedSortProxyModel.Alphabetary) { return section.toUpperCase(); } else { - return getCategoryName(section) + return ItemArrangementProxyModel.categoryDisplayName(section) } } @@ -295,7 +295,7 @@ FocusScope { model: ddeCategoryMenu.existingSections delegate: MenuItem { id: menuItem - text: getCategoryName(modelData) + text: ItemArrangementProxyModel.categoryDisplayName(modelData) textColor: DStyle.Style.menu.itemText font: DTK.fontManager.t6 onTriggered: { diff --git a/qml/windowed/FreeSortListView.qml b/qml/windowed/FreeSortListView.qml index 3bb6a2a4..0ae94cfe 100644 --- a/qml/windowed/FreeSortListView.qml +++ b/qml/windowed/FreeSortListView.qml @@ -200,7 +200,7 @@ Item { console.log("freesort view folder clicked:", desktopId); let idStr = model.desktopId let strFolderId = Number(idStr.replace("internal/folders/", "")) - let strFolderName = model.display.startsWith("internal/category/") ? getCategoryName(model.display.substring(18)) : model.display + let strFolderName = model.display let offset = height / 2 folderClicked(strFolderId, strFolderName, mapToItem(listView, offset, offset)) } else { @@ -260,7 +260,7 @@ Item { ItemDelegate { id: itemDelegate - text: model.display.startsWith("internal/category/") ? getCategoryName(model.display.substring(18)) : model.display + text: model.display checkable: false icon.name: itemType === ItemArrangementProxyModel.FolderItemType ? "folder" : iconName DciIcon.mode: DTK.NormalState diff --git a/qml/windowed/IconItemDelegate.qml b/qml/windowed/IconItemDelegate.qml index d5fa0ea7..73d783bc 100644 --- a/qml/windowed/IconItemDelegate.qml +++ b/qml/windowed/IconItemDelegate.qml @@ -14,7 +14,7 @@ Control { id: root opacity: Drag.active ? 0 : 1 - property string text: display.startsWith("internal/category/") ? getCategoryName(display.substring(18)) : display + property string text: display property string iconSource property bool dndEnabled: false diff --git a/shell-launcher-applet/CMakeLists.txt b/shell-launcher-applet/CMakeLists.txt index 73039e47..8e7f5eb2 100644 --- a/shell-launcher-applet/CMakeLists.txt +++ b/shell-launcher-applet/CMakeLists.txt @@ -20,4 +20,7 @@ PACKAGE org.deepin.ds.dock.launcherapplet QML_FILES ${QML_FILES_NEED_TRANSLATION} ${CMAKE_CURRENT_LIST_DIR}/package/launcheritem.qml +SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/launcheritem.cpp + ${PROJECT_SOURCE_DIR}/src/models/itemarrangementproxymodel.cpp ) diff --git a/shell-launcher-applet/launcheritem.cpp b/shell-launcher-applet/launcheritem.cpp index 45186803..ee3fee44 100644 --- a/shell-launcher-applet/launcheritem.cpp +++ b/shell-launcher-applet/launcheritem.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -6,6 +6,9 @@ #include "pluginfactory.h" #include "../launchercontroller.h" #include +#include +#include +#include #include #include @@ -16,6 +19,18 @@ DS_USE_NAMESPACE namespace { Q_LOGGING_CATEGORY(logApplet, "org.deepin.dde.launchpad.applet") + +constexpr QLatin1StringView groupIdPrefix("internal/folders/"); + +int folderIdFromGroupId(const QString &groupId) +{ + if (!groupId.startsWith(groupIdPrefix)) + return -1; + + bool ok = false; + const int folderId = QStringView(groupId).sliced(groupIdPrefix.size()).toInt(&ok); + return ok && folderId > 0 ? folderId : -1; +} } namespace dock { @@ -42,6 +57,48 @@ bool LauncherItem::init() return true; } +QAbstractItemModel *LauncherItem::itemArrangementModel() const +{ + return &ItemArrangementProxyModel::instance(); +} + +QString LauncherItem::groupDisplayName(const QString &groupId) const +{ + if (folderIdFromGroupId(groupId) < 0) + return {}; + + auto &model = ItemArrangementProxyModel::instance(); + const auto matches = model.match(model.index(0, 0), AppItem::DesktopIdRole, + groupId, 1, Qt::MatchExactly); + if (matches.isEmpty()) + return {}; + + return matches.constFirst().data(Qt::DisplayRole).toString(); +} + +QVariantList LauncherItem::groupItemDetails(const QString &groupId) const +{ + const int folderId = folderIdFromGroupId(groupId); + if (folderId < 0) + return {}; + + const auto desktopIds = ItemArrangementProxyModel::instance().folderItems(folderId); + QVariantList details; + details.reserve(desktopIds.size()); + for (const auto &desktopId : desktopIds) { + const auto item = AppsModel::instance().itemFromDesktopId(desktopId); + if (!item) + continue; + + details.append(QVariantMap{ + {QStringLiteral("desktopId"), desktopId}, + {QStringLiteral("name"), item->name()}, + {QStringLiteral("iconName"), item->iconName()}, + }); + } + return details; +} + D_APPLET_CLASS(LauncherItem) } diff --git a/shell-launcher-applet/launcheritem.h b/shell-launcher-applet/launcheritem.h index 229258ed..5e1f41aa 100644 --- a/shell-launcher-applet/launcheritem.h +++ b/shell-launcher-applet/launcheritem.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -7,16 +7,24 @@ #include "applet.h" #include "dsglobal.h" +#include +#include + namespace dock { class LauncherItem : public DS_NAMESPACE::DApplet { Q_OBJECT Q_PROPERTY(QString iconName MEMBER m_iconName NOTIFY iconNameChanged FINAL) + Q_PROPERTY(QAbstractItemModel *itemArrangementModel READ itemArrangementModel CONSTANT FINAL) public: explicit LauncherItem(QObject *parent = nullptr); virtual bool init() override; + QAbstractItemModel *itemArrangementModel() const; + Q_INVOKABLE QString groupDisplayName(const QString &groupId) const; + Q_INVOKABLE QVariantList groupItemDetails(const QString &groupId) const; + Q_SIGNALS: void iconNameChanged(); diff --git a/shell-launcher-applet/package/launcheritem.qml b/shell-launcher-applet/package/launcheritem.qml index 17201c99..8c759357 100644 --- a/shell-launcher-applet/package/launcheritem.qml +++ b/shell-launcher-applet/package/launcheritem.qml @@ -124,33 +124,6 @@ AppletItem { } } - function getCategoryName(section) { - switch (Number(section)) { - case AppItem.Internet: - return qsTr("Internet"); - case AppItem.Chat: - return qsTr("Chat"); - case AppItem.Music: - return qsTr("Music"); - case AppItem.Video: - return qsTr("Video"); - case AppItem.Graphics: - return qsTr("Graphics"); - case AppItem.Game: - return qsTr("Games"); - case AppItem.Office: - return qsTr("Office"); - case AppItem.Reading: - return qsTr("Reading"); - case AppItem.Development: - return qsTr("Development"); - case AppItem.System: - return qsTr("System"); - default: - return qsTr("Others"); - } - } - function launchApp(desktopId) { DesktopIntegration.launchByDesktopId(desktopId); LauncherController.visible = false; diff --git a/src/models/itemarrangementproxymodel.cpp b/src/models/itemarrangementproxymodel.cpp index 56ab91c4..5846df6e 100644 --- a/src/models/itemarrangementproxymodel.cpp +++ b/src/models/itemarrangementproxymodel.cpp @@ -7,6 +7,7 @@ #include "appsmodel.h" #include "categoryutils.h" +#include #include #include #include @@ -15,11 +16,53 @@ Q_DECLARE_LOGGING_CATEGORY(logModels) +namespace { +constexpr QLatin1StringView categoryNamePrefix("internal/category/"); +} + ItemArrangementProxyModel::~ItemArrangementProxyModel() { qCDebug(logModels) << "Destroying ItemArrangementProxyModel"; } +QString ItemArrangementProxyModel::categoryDisplayName(int category) const +{ + switch (category) { + case AppItem::Internet: + return QCoreApplication::translate("launcheritem", "Internet"); + case AppItem::Chat: + return QCoreApplication::translate("launcheritem", "Chat"); + case AppItem::Music: + return QCoreApplication::translate("launcheritem", "Music"); + case AppItem::Video: + return QCoreApplication::translate("launcheritem", "Video"); + case AppItem::Graphics: + return QCoreApplication::translate("launcheritem", "Graphics"); + case AppItem::Game: + return QCoreApplication::translate("launcheritem", "Games"); + case AppItem::Office: + return QCoreApplication::translate("launcheritem", "Office"); + case AppItem::Reading: + return QCoreApplication::translate("launcheritem", "Reading"); + case AppItem::Development: + return QCoreApplication::translate("launcheritem", "Development"); + case AppItem::System: + return QCoreApplication::translate("launcheritem", "System"); + default: + return QCoreApplication::translate("launcheritem", "Others"); + } +} + +QString ItemArrangementProxyModel::localizedFolderName(const QString &folderName) const +{ + if (!folderName.startsWith(categoryNamePrefix)) + return folderName; + + bool ok = false; + const int category = QStringView(folderName).sliced(categoryNamePrefix.size()).toInt(&ok); + return categoryDisplayName(ok ? category : AppItem::Others); +} + int ItemArrangementProxyModel::pageCount(int folderId) const { if (folderId == 0) return m_topLevel->pageCount(); @@ -248,7 +291,7 @@ QVariant ItemArrangementProxyModel::data(const QModelIndex &index, int role) con switch (role) { case Qt::DisplayRole: - return m_folders.value(id)->name(); + return localizedFolderName(m_folders.value(id)->name()); case AppItem::DesktopIdRole: return id; case AppItem::IsAutoStartRole: @@ -502,13 +545,19 @@ void ItemArrangementProxyModel::removeFolder(const QString &idNumber) } // get folder by id. 0 is top level, >=1 is folder -ItemsPage *ItemArrangementProxyModel::folderById(int id) +ItemsPage *ItemArrangementProxyModel::folderById(int id) const { if (id == 0) return m_topLevel; const QString folderId("internal/folders/" + QString::number(id)); return m_folders.value(folderId); } +QStringList ItemArrangementProxyModel::folderItems(int folderId) const +{ + const auto page = folderById(folderId); + return page ? page->allArrangedItems() : QStringList{}; +} + QStringList ItemArrangementProxyModel::allArrangedItems() const { return m_topLevel->allArrangedItems(); diff --git a/src/models/itemarrangementproxymodel.h b/src/models/itemarrangementproxymodel.h index 5b70564e..e9ac1383 100644 --- a/src/models/itemarrangementproxymodel.h +++ b/src/models/itemarrangementproxymodel.h @@ -60,8 +60,10 @@ class ItemArrangementProxyModel : public QConcatenateTablesProxyModel Q_INVOKABLE void commitDndOperation(const QString & dragId, const QString & dropId, const DndOperation op, int pageHint = -1); Q_INVOKABLE int creatEmptyPage(int folderId = 0) const; Q_INVOKABLE void removeEmptyPage() const; + Q_INVOKABLE QString categoryDisplayName(int category) const; ItemsPage *itemsPage() { return m_topLevel; } + QStringList folderItems(int folderId) const; // QAbstractItemModel interface public: @@ -85,7 +87,8 @@ class ItemArrangementProxyModel : public QConcatenateTablesProxyModel QString findAvailableFolderId(); ItemsPage * createFolder(const QString & id); void removeFolder(const QString & idNumber); - ItemsPage * folderById(int id); + QString localizedFolderName(const QString &folderName) const; + ItemsPage * folderById(int id) const; QStringList allArrangedItems() const; // folder-id: internal/folder/