Skip to content

Commit eafdb84

Browse files
committed
feat: add card-type plugin support to dock panel
1. Add Card plugin type to TrayPluginType enum in constants.h 2. Create CardLeftDockArea QML component for displaying card-type plugins in a carousel 3. Integrate card plugin surfaces into DockCompositor lifecycle (add/ remove/find) 4. Implement card area with left-side dock placement, using current/ index navigation 5. Support hover effects, page indicators, and wheel navigation for card area 6. Replace legacy left plugin area with CardLeftDockArea when card plugins exist Log: Added card-type plugin display area with carousel navigation in dock Influence: 1. Test card plugin registration and display in dock left area 2. Verify card plugin area visibility only when card plugins exist 3. Test mouse wheel navigation through card plugin pages 4. Verify hover effects and page indicator behavior 5. Test card plugin surface geometry updates on resize 6. Verify compatibility with existing tray/quick/fixed plugin types 7. Test card area behavior in both horizontal and vertical dock modes 8. Verify area width adapts to dock size and card content feat: 为任务栏添加卡片类型插件支持 1. 在 constants.h 中添加 Card 插件类型到 TrayPluginType 枚举 2. 创建 CardLeftDockArea QML 组件,用于以轮播方式显示卡片类型插件 3. 将卡片插件表面集成到 DockCompositor 的生命周期(添加/移除/查找) 4. 实现左侧任务栏卡片区域,支持当前索引/导航 5. 支持悬停效果、页面指示器和鼠标滚轮导航 6. 当存在卡片插件时,用 CardLeftDockArea 替换传统的左侧插件区域 Log: 新增卡片类型插件显示区域,支持轮播导航 Influence: 1. 测试卡片插件的注册和在任务栏左侧区域的显示 2. 验证卡片插件区域仅在存在卡片插件时可见 3. 测试通过鼠标滚轮在卡片插件页面间导航 4. 验证悬停效果和页面指示器的行为 5. 测试卡片插件表面在窗口大小调整时的几何更新 6. 验证与现有托盘/快捷/固定插件类型的兼容性 7. 测试卡片区域在任务栏水平和垂直模式下的表现 8. 验证区域宽度根据任务栏尺寸和卡片内容自适应 PMS: TASK-392671
1 parent 3a0695f commit eafdb84

14 files changed

Lines changed: 360 additions & 39 deletions

File tree

debian/control

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ Breaks:
8989
dde-clipboard (<< 1:6.0.14),
9090
libdde-shell-dev (<< 2.0.38)
9191
Replaces: libdde-shell-dev (<< 2.0.38)
92-
Description: An wrapper for developed based on dde-shell plugin system
92+
Description: desktop shell and plugin host for DDE
93+
DDE Shell provides the desktop shell infrastructure used by the Deepin
94+
Desktop Environment and hosts components implemented through its plugin
95+
system, including panels and other desktop user-interface elements.
9396

9497
Package: dde-shell-example
9598
Architecture: any
@@ -99,8 +102,9 @@ Depends:
99102
${misc:Depends},
100103
${shlibs:Depends},
101104
Multi-Arch: same
102-
Description: DDE Shell example
103-
This package contains some plugins based on dde-shell plugin system.
105+
Description: example plugins for the DDE desktop shell
106+
This package contains example plugins that demonstrate how to develop and
107+
integrate components with the DDE Shell plugin system.
104108

105109
Package: libdde-shell-dev
106110
Architecture: any

panels/dock/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ ds_install_package(PACKAGE org.deepin.ds.dock TARGET dockpanel)
9797
ds_handle_package_translation(PACKAGE org.deepin.ds.dock)
9898

9999
# sub plugins
100+
add_subdirectory(cardleft)
100101
add_subdirectory(showdesktop)
101102
add_subdirectory(taskmanager)
102103
add_subdirectory(tray)

panels/dock/DockCompositor.qml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Item {
3030
property ListModel trayPluginSurfaces: ListModel {}
3131
property ListModel quickPluginSurfaces: ListModel {}
3232
property ListModel fixedPluginSurfaces: ListModel {}
33+
property ListModel cardPluginSurfaces: ListModel {}
3334

3435
property var compositor: waylandCompositor
3536
property var panelScale: 1.0
@@ -62,6 +63,7 @@ Item {
6263
let ret = findSurfaceFromModel(trayPluginSurfaces, surfaceId)
6364
if (ret === null) ret = findSurfaceFromModel(quickPluginSurfaces, surfaceId)
6465
if (ret === null) ret = findSurfaceFromModel(fixedPluginSurfaces, surfaceId)
66+
if (ret === null) ret = findSurfaceFromModel(cardPluginSurfaces, surfaceId)
6567
return ret
6668
}
6769

@@ -92,6 +94,8 @@ Item {
9294
quickPluginSurfaces.append({shellSurface: dockPluginSurface})
9395
} else if (dockPluginSurface.pluginType === Dock.Fixed) {
9496
fixedPluginSurfaces.append({shellSurface: dockPluginSurface})
97+
} else if (dockPluginSurface.pluginType === Dock.Card) {
98+
cardPluginSurfaces.append({shellSurface: dockPluginSurface})
9599
}
96100
dockCompositor.pluginSurfacesUpdated()
97101
}
@@ -104,6 +108,8 @@ Item {
104108
removeDockPluginSurface(quickPluginSurfaces, dockPluginSurface)
105109
} else if (dockPluginSurface.pluginType === Dock.Fixed) {
106110
removeDockPluginSurface(fixedPluginSurfaces, dockPluginSurface)
111+
} else if (dockPluginSurface.pluginType === Dock.Card) {
112+
removeDockPluginSurface(cardPluginSurfaces, dockPluginSurface)
107113
}
108114
dockCompositor.pluginSurfacesUpdated()
109115
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
#
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
ds_install_package(PACKAGE org.deepin.ds.dock.cardleft)
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
import QtQuick 2.15
6+
import QtQuick.Window 2.15
7+
import QtWayland.Compositor
8+
9+
import org.deepin.ds 1.0
10+
import org.deepin.ds.dock 1.0
11+
import org.deepin.dtk 1.0 as D
12+
13+
AppletDockItem {
14+
id: root
15+
16+
readonly property bool fashionMode: Panel.fashionMode
17+
readonly property int dockSize: Panel.rootObject.dockSize
18+
readonly property int hoverInset: 5
19+
readonly property real taskbarRadius: Panel.rootObject.fashionDock.backgroundRadius
20+
readonly property real hoverBackgroundRadius: taskbarRadius - hoverInset
21+
readonly property int adaptiveCardLeftWidth: 150 + Math.max(0, dockSize / 4)
22+
readonly property int rightContentPadding: Math.max(10, Math.round(adaptiveCardLeftWidth * 0.07))
23+
readonly property int verticalInset: Math.max(5, Math.round(dockSize * 0.16))
24+
readonly property int pageContentHeight: Math.max(24, dockSize - verticalInset * 2)
25+
26+
readonly property color primaryTextColor: Panel.colorTheme === Dock.Dark
27+
? Qt.rgba(1, 1, 1, 0.96)
28+
: Qt.rgba(0, 0, 0, 0.92)
29+
30+
dockOrder: 5
31+
shouldVisible: fashionMode && pageCount > 0
32+
property int currentIndex: 0
33+
property int previousIndex: -1
34+
property int switchDirection: 1
35+
property real pageTransitionProgress: 1
36+
readonly property int pageCount: DockCompositor.cardPluginSurfaces.count
37+
property bool contentHovered: false
38+
readonly property bool pageTransitioning: pageTransitionAnimation.running || pageTransitionProgress < 1
39+
readonly property bool effectiveHovered: rootHoverHandler.hovered || contentHovered
40+
41+
visible: shouldVisible
42+
implicitWidth: adaptiveCardLeftWidth
43+
implicitHeight: dockSize
44+
clip: true
45+
46+
function step(delta) {
47+
if (pageCount <= 1) {
48+
return
49+
}
50+
51+
const nextIndex = (currentIndex + delta + pageCount) % pageCount
52+
if (nextIndex === currentIndex) {
53+
return
54+
}
55+
56+
pageTransitionAnimation.stop()
57+
previousIndex = currentIndex
58+
switchDirection = delta > 0 ? 1 : -1
59+
pageTransitionProgress = 0
60+
currentIndex = nextIndex
61+
pageTransitionAnimation.restart()
62+
}
63+
64+
function updateContentHovered() {
65+
const item = pluginRepeater.itemAt(currentIndex)
66+
contentHovered = !!item && item.visible && item.surfaceHovered
67+
}
68+
69+
onCurrentIndexChanged: updateContentHovered()
70+
onPageCountChanged: {
71+
if (pageCount <= 0) {
72+
previousIndex = -1
73+
pageTransitionProgress = 1
74+
currentIndex = 0
75+
return
76+
}
77+
78+
if (currentIndex >= pageCount) {
79+
previousIndex = -1
80+
pageTransitionProgress = 1
81+
currentIndex = pageCount - 1
82+
} else if (currentIndex < 0) {
83+
previousIndex = -1
84+
pageTransitionProgress = 1
85+
currentIndex = 0
86+
}
87+
88+
updateContentHovered()
89+
}
90+
91+
NumberAnimation {
92+
id: pageTransitionAnimation
93+
94+
target: root
95+
property: "pageTransitionProgress"
96+
from: 0
97+
to: 1
98+
duration: 300
99+
easing.type: Easing.OutCubic
100+
onStopped: {
101+
root.pageTransitionProgress = 1
102+
root.previousIndex = -1
103+
root.updateContentHovered()
104+
}
105+
}
106+
107+
HoverHandler {
108+
id: rootHoverHandler
109+
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
110+
}
111+
112+
AppletItemBackground {
113+
x: root.hoverInset
114+
y: root.hoverInset
115+
width: parent.width - root.hoverInset
116+
height: parent.height - root.hoverInset * 2
117+
radius: root.hoverBackgroundRadius
118+
enabled: false
119+
opacity: root.effectiveHovered ? 1 : 0
120+
D.ColorSelector.hovered: root.effectiveHovered
121+
122+
Behavior on opacity {
123+
NumberAnimation {
124+
duration: 150
125+
easing.type: Easing.OutCubic
126+
}
127+
}
128+
}
129+
130+
CardLeftDockSwitcher {
131+
anchors.right: parent.right
132+
anchors.rightMargin: 8
133+
anchors.verticalCenter: parent.verticalCenter
134+
visible: root.effectiveHovered && root.pageCount > 1
135+
itemCount: root.pageCount
136+
currentIndex: root.currentIndex
137+
indicatorSize: 2
138+
indicatorSpacing: 4
139+
indicatorColor: root.primaryTextColor
140+
}
141+
142+
Item {
143+
anchors.left: parent.left
144+
anchors.right: parent.right
145+
anchors.verticalCenter: parent.verticalCenter
146+
anchors.leftMargin: 8
147+
anchors.rightMargin: root.rightContentPadding
148+
height: root.pageContentHeight
149+
clip: true
150+
151+
Repeater {
152+
id: pluginRepeater
153+
model: DockCompositor.cardPluginSurfaces
154+
155+
delegate: Item {
156+
id: surfaceHost
157+
158+
property var plugin: model.shellSurface
159+
readonly property bool surfaceHovered: visible && surfaceHoverHandler.hovered
160+
function itemGlobalPoint() {
161+
var item = surfaceHost
162+
var x = 0
163+
var y = 0
164+
while (item) {
165+
x += item.x
166+
y += item.y
167+
item = item.parent
168+
}
169+
return Qt.point(x, y)
170+
}
171+
function itemGlobalPos() {
172+
var point = itemGlobalPoint()
173+
if (surfaceHost.Window.window) {
174+
point.x += surfaceHost.Window.window.x
175+
point.y += surfaceHost.Window.window.y
176+
}
177+
return point
178+
}
179+
180+
y: {
181+
if (index === root.currentIndex) {
182+
return (1 - root.pageTransitionProgress) * root.switchDirection * height
183+
}
184+
185+
if (index === root.previousIndex && root.pageTransitioning) {
186+
return -root.pageTransitionProgress * root.switchDirection * height
187+
}
188+
189+
return 0
190+
}
191+
width: parent.width
192+
height: parent.height
193+
visible: !!plugin && (index === root.currentIndex || (root.pageTransitioning && index === root.previousIndex))
194+
195+
ShellSurfaceItem {
196+
id: surfaceItem
197+
width: parent.width
198+
height: parent.height
199+
shellSurface: surfaceHost.plugin
200+
smooth: false
201+
HoverHandler {
202+
id: surfaceHoverHandler
203+
}
204+
}
205+
206+
function updateSurfaceGeometry() {
207+
if (!plugin || !visible) {
208+
return
209+
}
210+
211+
const globalPoint = itemGlobalPoint()
212+
const globalPos = itemGlobalPos()
213+
plugin.updatePluginGeometry(Qt.rect(Math.round(globalPoint.x),
214+
Math.round(globalPoint.y),
215+
Math.round(width),
216+
Math.round(height)))
217+
plugin.setGlobalPos(Qt.point(Math.round(globalPos.x),
218+
Math.round(globalPos.y)))
219+
surfaceItem.fixPosition()
220+
}
221+
222+
Component.onCompleted: updateSurfaceGeometry()
223+
onYChanged: geometryUpdateTimer.restart()
224+
onWidthChanged: geometryUpdateTimer.restart()
225+
onHeightChanged: geometryUpdateTimer.restart()
226+
onVisibleChanged: {
227+
geometryUpdateTimer.restart()
228+
root.updateContentHovered()
229+
}
230+
onSurfaceHoveredChanged: root.updateContentHovered()
231+
232+
Timer {
233+
id: geometryUpdateTimer
234+
interval: 50
235+
repeat: false
236+
onTriggered: surfaceHost.updateSurfaceGeometry()
237+
}
238+
}
239+
}
240+
}
241+
242+
Item {
243+
anchors.fill: parent
244+
z: 1
245+
246+
WheelHandler {
247+
target: null
248+
enabled: root.pageCount > 1
249+
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
250+
onWheel: function(wheel) {
251+
const deltaY = wheel.angleDelta.y !== 0 ? wheel.angleDelta.y : wheel.pixelDelta.y
252+
if (deltaY !== 0) {
253+
root.step(deltaY < 0 ? 1 : -1)
254+
wheel.accepted = true
255+
}
256+
}
257+
}
258+
}
259+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
import QtQuick 2.15
6+
7+
Item {
8+
id: root
9+
10+
required property int itemCount
11+
property int currentIndex: 0
12+
property int indicatorSize: 2
13+
property int indicatorSpacing: 4
14+
property color indicatorColor: Qt.rgba(1, 1, 1, 0.96)
15+
16+
visible: itemCount > 1
17+
implicitWidth: indicatorColumn.implicitWidth
18+
implicitHeight: indicatorColumn.implicitHeight
19+
20+
Column {
21+
id: indicatorColumn
22+
23+
spacing: root.indicatorSpacing
24+
25+
Repeater {
26+
model: root.itemCount
27+
28+
Rectangle {
29+
width: root.indicatorSize
30+
height: root.indicatorSize
31+
radius: width / 2
32+
color: root.indicatorColor
33+
opacity: index === root.currentIndex ? 1 : 0.35
34+
}
35+
}
36+
}
37+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Plugin": {
3+
"Version": "1.0",
4+
"Id": "org.deepin.ds.dock.cardleft",
5+
"Url": "CardLeftDockArea.qml",
6+
"Parent": "org.deepin.ds.dock"
7+
}
8+
}

panels/dock/constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ enum TrayPluginType {
8080
Tray = 1,
8181
Fixed,
8282
Quick,
83+
Card,
8384
};
8485

8586
enum TrayPluginSizePolicy {

0 commit comments

Comments
 (0)