Skip to content

Commit 036416a

Browse files
committed
feat: add folder group support to dock taskbar
1. Add DockGlobalElementModel support for group elements (launcher folders) 2. Update AppGroup to use new folder ID format ("internal/folders/" instead of "internal/folder/") 3. Implement group item details and launch functionality in AppsApplet 4. Display composite preview icons for folder groups in dock items 5. Add FolderPopup QML component with grid layout for folder contents 6. Implement drag-and-drop support for folder elements from launcher 7. Update the dock settings to handle folder dock/undock operations 8. Improve group ID parsing with input validation Log: Added folder group display in dock taskbar with composite preview and popup Influence: 1. Test folder group display in dock: verify composite preview icons show correctly 2. Test folder popup: verify grid layout, scrolling, and application launch 3. Test drag-and-drop folders from launcher to dock 4. Test folder dock/undock via context menu 5. Test folder group ID format migration ("internal/folders/" prefix) 6. Test group ID parsing with invalid inputs (empty, non-numeric, negative) 7. Test group display name and item details retrieval 8. Test folder persistence after dock restart 9. Test launcher folder operations with multiple items and special characters feat: 在任务栏中添加文件夹组支持 1. 为 DockGlobalElementModel 添加组元素支持(启动器文件夹) 2. 更新 AppGroup 使用新的文件夹 ID 格式("internal/folders/" 替代 "internal/folder/") 3. 在 AppsApplet 中实现组项目详情和启动功能 4. 在任务栏项目中显示文件夹组的复合预览图标 5. 添加带有网格布局的 FolderPopup QML 组件用于显示文件夹内容 6. 实现从启动器拖放文件夹元素到任务栏的功能 7. 更新任务栏设置以支持文件夹的停靠/取消停靠操作 8. 改进组 ID 解析验证 Log: 在任务栏中添加文件夹组显示,支持复合预览和弹出窗口 Influence: 1. 测试任务栏中的文件夹组显示:验证复合预览图标是否正确显示 2. 测试文件夹弹窗:验证网格布局、滚动和应用启动功能 3. 测试从启动器拖放文件夹到任务栏 4. 测试通过右键菜单停靠/取消停靠文件夹 5. 测试文件夹组 ID 格式迁移("internal/folders/" 前缀) 6. 测试组 ID 解析对无效输入的处理(空值、非数字、负数) 7. 测试组显示名称和项目详情的获取 8. 测试重启任务栏后文件夹状态的持久化 9. 测试启动器文件夹操作在包含多个项目和特殊字符时的表现 PMS: TASK-392671
1 parent 7331383 commit 036416a

21 files changed

Lines changed: 735 additions & 70 deletions

applets/dde-apps/appgroup.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -15,11 +15,11 @@ Q_LOGGING_CATEGORY(appGroupLog, "org.deepin.dde.shell.dde-apps.appgroup")
1515
namespace apps {
1616
AppGroup::AppGroup(const QString &groupId, const QString &name, const QList<QStringList> &appIDs)
1717
: AppItem(groupId, AppItemModel::FolderItemType)
18-
, m_itemsPage(new ItemsPage(name, groupId == QStringLiteral("internal/folder/0") ? (4 * 8) : (3 * 4)))
18+
, m_itemsPage(new ItemsPage(name, parseGroupId(groupId) == 0 ? (4 * 8) : (3 * 4)))
1919
{
2020
setItemsPerPage(m_itemsPage->maxItemCountPerPage());
2121
setAppName(m_itemsPage->name());
22-
// folder id is a part of its groupId: "internal/folder/{folderId}"
22+
// folder id is a part of its groupId: "internal/folders/{folderId}"
2323
setFolderId(parseGroupId(groupId));
2424

2525
for (const QStringList &items : appIDs) {
@@ -49,19 +49,33 @@ ItemsPage *AppGroup::itemsPage()
4949

5050
bool AppGroup::idIsFolder(const QString & id)
5151
{
52-
return id.startsWith(QStringLiteral("internal/folder/"));
52+
return parseGroupId(id) >= 0;
5353
}
5454

5555
QString AppGroup::groupIdFromNumber(int groupId)
5656
{
57-
return QStringLiteral("internal/folder/%1").arg(groupId);
57+
return QStringLiteral("internal/folders/%1").arg(groupId);
5858
}
5959

6060
int AppGroup::parseGroupId(const QString & id)
6161
{
62-
using namespace std::string_view_literals;
63-
constexpr size_t len = "internal/folder/"sv.size();
64-
return QStringView{id}.mid(len + 1).toInt();
62+
constexpr QLatin1StringView prefix("internal/folders/");
63+
if (!id.startsWith(prefix))
64+
return -1;
65+
66+
const QStringView number = QStringView(id).sliced(prefix.size());
67+
68+
if (number.isEmpty())
69+
return -1;
70+
71+
for (const QChar ch : number) {
72+
if (!ch.isDigit())
73+
return -1;
74+
}
75+
76+
bool ok = false;
77+
const int groupId = number.toInt(&ok);
78+
return ok && groupId >= 0 ? groupId : -1;
6579
}
6680

6781
void AppGroup::setItemsPerPage(int number)

applets/dde-apps/appgroup.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

applets/dde-apps/appgroupmanager.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ QModelIndex AppGroupManager::groupIndexById(int groupId)
8787
{
8888
for (int i = 0; i < rowCount(); i++) {
8989
auto groupIndex = index(i, 0);
90-
auto data = groupIndex.data(GroupIdRole);
91-
if (data.toInt() == groupId) {
90+
auto appGroup = static_cast<AppGroup *>(itemFromIndex(groupIndex));
91+
if (appGroup && appGroup->folderId() == groupId) {
9292
return groupIndex;
9393
}
9494
}
@@ -352,8 +352,8 @@ void AppGroupManager::loadAppGroupInfo()
352352
}
353353

354354
// always ensure top-level group exists
355-
if (rowCount() == 0) {
356-
auto p = appendGroup(assignGroupId(), "Top Level", {});
355+
if (!group(TOPLEVEL_FOLDERID)) {
356+
auto p = appendGroup(AppGroup::groupIdFromNumber(TOPLEVEL_FOLDERID), "Top Level", {});
357357
Q_ASSERT(p->folderId() == TOPLEVEL_FOLDERID);
358358
}
359359
}
@@ -375,18 +375,19 @@ void AppGroupManager::saveAppGroupInfo()
375375

376376
QString AppGroupManager::assignGroupId() const
377377
{
378-
QStringList knownGroupIds;
378+
QSet<int> knownGroupIds;
379379
for (int i = 0; i < rowCount(); i++) {
380-
auto group = index(i, 0);
381-
knownGroupIds.append(group.data(AppItemModel::DesktopIdRole).toString());
380+
auto appGroup = static_cast<AppGroup *>(itemFromIndex(index(i, 0)));
381+
if (appGroup && appGroup->folderId() >= 0)
382+
knownGroupIds.insert(appGroup->folderId());
382383
}
383384

384-
int idNumber = 0;
385-
while (knownGroupIds.contains(QString("internal/group/%1").arg(idNumber))) {
385+
int idNumber = 1;
386+
while (knownGroupIds.contains(idNumber)) {
386387
idNumber++;
387388
}
388389

389-
return QString("internal/group/%1").arg(idNumber);
390+
return AppGroup::groupIdFromNumber(idNumber);
390391
}
391392

392393
AppGroup * AppGroupManager::appendGroup(int groupId, QString groupName, const QList<QStringList> &appItemIDs)

applets/dde-apps/appsapplet.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

55
#include "appsapplet.h"
6+
#include "amappitem.h"
67
#include "amappitemmodel.h"
8+
#include "appgroup.h"
79
#include "appgroupmanager.h"
10+
#include "appitemmodel.h"
11+
#include "itemspage.h"
812
#include "pluginfactory.h"
913

1014
#include <DUtil>
1115
#include <QtConcurrent>
1216

1317
namespace apps
1418
{
19+
static AMAppItem *findApplication(AMAppItemModel *model, const QString &desktopId)
20+
{
21+
if (auto item = model->appItem(desktopId))
22+
return item;
23+
24+
const auto alternateId = desktopId.endsWith(QStringLiteral(".desktop"))
25+
? desktopId.chopped(8)
26+
: desktopId + QStringLiteral(".desktop");
27+
return model->appItem(alternateId);
28+
}
29+
1530
AppsApplet::AppsApplet(QObject *parent)
1631
: DApplet(parent)
1732
, m_appModel(new AMAppItemModel(this))
@@ -40,6 +55,39 @@ QAbstractItemModel *AppsApplet::appModel() const
4055
return m_appModel;
4156
}
4257

58+
QVariantList AppsApplet::groupItemDetails(const QString &groupId) const
59+
{
60+
const int id = AppGroup::parseGroupId(groupId);
61+
auto group = id > 0 ? m_groupModel->group(id) : nullptr;
62+
if (!group)
63+
return {};
64+
65+
const auto items = group->itemsPage()->allArrangedItems();
66+
QVariantList result;
67+
result.reserve(items.size());
68+
for (const auto &desktopId : items) {
69+
const auto item = findApplication(m_appModel, desktopId);
70+
if (!item)
71+
continue;
72+
result.append(QVariantMap{
73+
{QStringLiteral("desktopId"), desktopId},
74+
{QStringLiteral("name"), item->data(AppItemModel::NameRole)},
75+
{QStringLiteral("iconName"), item->data(AppItemModel::IconNameRole)},
76+
});
77+
}
78+
return result;
79+
}
80+
81+
bool AppsApplet::launchApplication(const QString &desktopId)
82+
{
83+
if (auto item = findApplication(m_appModel, desktopId)) {
84+
item->launch({}, {}, {{QStringLiteral("_launch_type"), QStringLiteral("dde-shell")}});
85+
return true;
86+
}
87+
88+
return false;
89+
}
90+
4391
bool AppsApplet::appModelReady() const
4492
{
4593
return m_appModel->ready();

applets/dde-apps/appsapplet.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -13,6 +13,7 @@ DS_USE_NAMESPACE
1313

1414
namespace apps {
1515
class AMAppItemModel;
16+
class AppGroupManager;
1617
class AppItem;
1718
class AppsApplet : public DApplet
1819
{
@@ -30,6 +31,9 @@ class AppsApplet : public DApplet
3031
QAbstractItemModel *appModel() const;
3132
QAbstractItemModel *groupModel() const;
3233

34+
Q_INVOKABLE QVariantList groupItemDetails(const QString &groupId) const;
35+
Q_INVOKABLE bool launchApplication(const QString &desktopId);
36+
3337
bool appModelReady() const;
3438

3539
signals:
@@ -38,6 +42,6 @@ class AppsApplet : public DApplet
3842
private:
3943
bool m_appModelReady;
4044
AMAppItemModel *m_appModel;
41-
QAbstractItemModel *m_groupModel;
45+
AppGroupManager *m_groupModel;
4246
};
4347
}

panels/dock/MenuHelper.qml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

panels/dock/dockpositioner.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 -2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

panels/dock/dockpositioner.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

panels/dock/package/main.qml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -495,13 +495,13 @@ Window {
495495
cornerRadius: fashionDock.enabled ? fashionDock.backgroundRadius : 0
496496
blendColor: {
497497
if (valid) {
498-
return DStyle.Style.control.selectColor(undefined,
498+
return D.Style.control.selectColor(undefined,
499499
Qt.rgba(235 / 255.0, 235 / 255.0, 235 / 255.0, dock.blendColorAlpha(0.6)),
500500
Qt.rgba(10 / 255, 10 / 255, 10 /255, dock.blendColorAlpha(85 / 255)))
501501
}
502-
return DStyle.Style.control.selectColor(undefined,
503-
DStyle.Style.behindWindowBlur.lightNoBlurColor,
504-
DStyle.Style.behindWindowBlur.darkNoBlurColor)
502+
return D.Style.control.selectColor(undefined,
503+
D.Style.behindWindowBlur.lightNoBlurColor,
504+
D.Style.behindWindowBlur.darkNoBlurColor)
505505
}
506506
}
507507

@@ -622,6 +622,8 @@ Window {
622622
visible: dockLeftPartModel.count > 0
623623
implicitWidth: leftLoader.implicitWidth
624624
implicitHeight: leftLoader.implicitHeight
625+
Accessible.role: Accessible.Grouping
626+
Accessible.name: qsTr("Dock Left Area")
625627
OverflowContainer {
626628
id: leftLoader
627629
anchors.fill: parent
@@ -640,8 +642,8 @@ Window {
640642
implicitHeight: centerLoader.implicitHeight
641643
Accessible.role: Accessible.Grouping
642644
Accessible.name: qsTr("Dock Center Area")
643-
Layout.maximumWidth: useColumnLayout ? -1 : dockRawCenterSpace
644-
Layout.maximumHeight: useColumnLayout ? dockRawCenterSpace : -1
645+
Layout.maximumWidth: useColumnLayout ? -1 : dockEffectiveCenterSpace
646+
Layout.maximumHeight: useColumnLayout ? dockEffectiveCenterSpace : -1
645647
onXChanged: dockCenterPartPosChanged()
646648
onYChanged: dockCenterPartPosChanged()
647649
Layout.leftMargin: !useColumnLayout && !fashionDock.enabled && Panel.itemAlignment === Dock.CenterAlignment ?

0 commit comments

Comments
 (0)