diff --git a/docs/superpowers/plans/2026-07-18-ui-redesign.md b/docs/superpowers/plans/2026-07-18-ui-redesign.md deleted file mode 100644 index c15d6a8..0000000 --- a/docs/superpowers/plans/2026-07-18-ui-redesign.md +++ /dev/null @@ -1,841 +0,0 @@ -# JNetApplet UI 重新设计实现计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 重写 `package/networkview.qml`,实现 deepin 原生简约风格的任务栏图标、左键弹出面板、右键菜单和 Tooltip。 - -**Architecture:** 单文件 QML 重写,不动 C++ 后端。颜色派生自 `DockPalette.iconTextPalette`,深/浅色自适应。新增 `formatSpeedShort`、右键 `Menu`、chip 接口切换器。 - -**Tech Stack:** QML 2.15、QtQuick.Controls 2.15、QtQuick.Layouts 1.15、org.deepin.ds 1.0、org.deepin.ds.dock 1.0、org.deepin.dtk 1.0。 - -## Global Constraints - -- 仅修改 `package/networkview.qml`,不改 C++、`metadata.json`、`CMakeLists.txt` -- 保留 SPDX 文件头与所有 imports -- Root 元素为 `AppletItem`(来自 `import org.deepin.ds 1.0`) -- QML 属性类型用 `double`/`real`,不用 `qint64` -- 注释统一中文 -- 项目无测试/lint/CI,验证方式:`cmake --build build` + 手动安装重启 dde-shell -- Commit 中文 conventional commits,仅在被显式请求时提交 - -**Spec 引用:** `docs/superpowers/specs/2026-07-18-ui-redesign-design.md` - ---- - -## File Structure - -仅修改 `package/networkview.qml`(当前 521 行)。无新文件、无 C++ 改动。 - -重写后内部结构: - -``` -AppletItem (root) -├── 属性区:applet 绑定、颜色派生、formatSpeedShort -├── 任务栏图标区:Column { Row(↓值), Row(↑值) } -├── PanelToolTip -├── Timer × 2 -├── HoverHandler -├── PanelPopup -│ └── Control > ColumnLayout -│ ├── 标题栏(标题+副标题+刷新按钮) -│ ├── 实时速度卡片(双列) -│ ├── 总量统计(单行) -│ ├── 接口切换 chip 列表 -│ └── 未检测到接口占位 -├── Menu (contextMenu) ← 新增 -├── Dialog (aboutDialog) ← 新增 -├── TapHandler (左键) -└── TapHandler (右键) ← 新增 -``` - ---- - -## Task 1: 属性区与格式化函数(基础层) - -**Files:** Modify `package/networkview.qml:32-72` - -**Interfaces:** -- Produces: `formatSpeedShort()`、`accentGreen`、`accentGreenLight`、`accentOrange`、`accentRed`、`downloadValueColor`、`uploadValueColor` - 后续任务全部依赖 - -**说明:** 替换 root 的函数块和颜色属性块。保留所有 `applet` 绑定属性不变。 - -- [ ] **Step 1: 替换函数区(原 32-56 行)** - -将 `// 格式化速度显示` 注释到 `formatTotal` 函数结束替换为: - -```qml - // 紧凑格式化速度(用于任务栏图标和 tooltip,不带单位后缀,节省空间) - // 设计原因:任务栏 ~48px 空间有限,"1.2M" 比 "1.2 MB/s" 节省约一半宽度 - function formatSpeedShort(bytesPerSec) { - if (bytesPerSec < 1024) { - return bytesPerSec.toFixed(0) - } else if (bytesPerSec < 1024 * 1024) { - return (bytesPerSec / 1024).toFixed(1) + "K" - } else if (bytesPerSec < 1024 * 1024 * 1024) { - return (bytesPerSec / (1024 * 1024)).toFixed(1) + "M" - } else { - return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + "G" - } - } - - // 格式化速度显示(带单位,用于弹出面板,信息更完整) - function formatSpeed(bytesPerSec) { - if (bytesPerSec < 1024) { - return bytesPerSec.toFixed(0) + " B/s" - } else if (bytesPerSec < 1024 * 1024) { - return (bytesPerSec / 1024).toFixed(1) + " KB/s" - } else if (bytesPerSec < 1024 * 1024 * 1024) { - return (bytesPerSec / (1024 * 1024)).toFixed(2) + " MB/s" - } else { - return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(2) + " GB/s" - } - } - - // 格式化总量显示(用于弹出面板的累计统计) - function formatTotal(bytes) { - if (bytes < 1024) { - return bytes.toFixed(0) + " B" - } else if (bytes < 1024 * 1024) { - return (bytes / 1024).toFixed(1) + " KB" - } else if (bytes < 1024 * 1024 * 1024) { - return (bytes / (1024 * 1024)).toFixed(1) + " MB" - } else { - return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB" - } - } -``` - -- [ ] **Step 2: 替换颜色属性区(原 58-72 行)** - -将 `// 根据速度计算图标颜色` 注释到 `accentBlueLight` 属性结束替换为: - -```qml - // 任务栏与面板的颜色全部派生自 DockPalette.iconTextPalette - // 这样在深色/浅色任务栏下自动适配,无需手动判断主题 - property Palette basePalette: DockPalette.iconTextPalette - readonly property color primaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.95) - readonly property color secondaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.80) - readonly property color tertiaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.65) - readonly property color cardBackground: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.06) - readonly property color cardBorder: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.10) - - // 强调色:下载蓝、上传绿,是面板与任务栏的主视觉区分 - readonly property color accentBlue: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 1) - readonly property color accentBlueLight: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 0.12) - readonly property color accentGreen: Qt.rgba(22 / 255, 163 / 255, 74 / 255, 1) - readonly property color accentGreenLight: Qt.rgba(22 / 255, 163 / 255, 74 / 255, 0.12) - - // 高速警示色:下载速度超过阈值时由蓝转橙再转红 - readonly property color accentOrange: Qt.rgba(245 / 255, 158 / 255, 11 / 255, 1) - readonly property color accentRed: Qt.rgba(220 / 255, 38 / 255, 38 / 255, 1) - - // 下载值颜色:保留原有阈值逻辑(>10MB/s 红、>1MB/s 橙、否则蓝),仅作用于下载值 - readonly property color downloadValueColor: { - if (downloadSpeed > 10 * 1024 * 1024) return accentRed - if (downloadSpeed > 1 * 1024 * 1024) return accentOrange - return accentBlue - } - - // 上传值颜色:固定绿色,不做高速警示 - readonly property color uploadValueColor: accentGreen -``` - -- [ ] **Step 3: 验证标识符已定义** - -Run: `grep -n "formatSpeedShort\|downloadValueColor\|uploadValueColor\|accentGreen " /home/Jokul/Documents/projects/JNetApplet/package/networkview.qml` -Expected: 每个标识符至少出现一次。 - -- [ ] **Step 4: 验证 C++ 仍可构建** - -Run: `cmake --build /home/Jokul/Documents/projects/JNetApplet/build 2>&1 | tail -5` -Expected: 构建成功,无 error。 - ---- - -## Task 2: 任务栏图标区重写 - -**Files:** Modify `package/networkview.qml`(原 74-120 行的圆圈 `Rectangle`) - -**Interfaces:** -- Consumes: `formatSpeedShort()`、`downloadValueColor`、`uploadValueColor`、`secondaryText`、`dockSize` -- Produces: 任务栏双行数值显示 - -- [ ] **Step 1: 替换图标区域** - -将 `// 图标区域` 注释到对应 `Rectangle` 闭合 `}` 替换为: - -```qml - // 任务栏图标区:双行紧凑数值,移除原圆圈边框 - // 上行下载、下行上传,箭头为次级色、数值为强调色 - // 设计原因:圆圈边框在 ~48px dock 内压缩可用空间,去掉后数值字号可放大提升可读性 - Column { - anchors.centerIn: parent - spacing: 1 - - // 下载速度行 - Row { - spacing: 2 - anchors.horizontalCenter: parent.horizontalCenter - - Text { - text: "↓" - font.pixelSize: root.dockSize * 0.16 - color: root.secondaryText - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: root.formatSpeedShort(root.downloadSpeed) - font.pixelSize: root.dockSize * 0.22 - font.weight: Font.Medium - color: root.downloadValueColor - anchors.verticalCenter: parent.verticalCenter - } - } - - // 上传速度行 - Row { - spacing: 2 - anchors.horizontalCenter: parent.horizontalCenter - - Text { - text: "↑" - font.pixelSize: root.dockSize * 0.16 - color: root.secondaryText - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: root.formatSpeedShort(root.uploadSpeed) - font.pixelSize: root.dockSize * 0.22 - font.weight: Font.Medium - color: root.uploadValueColor - anchors.verticalCenter: parent.verticalCenter - } - } - } -``` - -- [ ] **Step 2: 确认旧 `iconColor` 已无引用** - -Run: `grep -n "iconColor" /home/Jokul/Documents/projects/JNetApplet/package/networkview.qml` -Expected: 无输出。 - -- [ ] **Step 3: 安装并目视验证** - -Run: `sudo cmake --install /home/Jokul/Documents/projects/JNetApplet/build && systemctl --user restart dde-shell@DDE` -Expected: 任务栏图标显示双行「↓数值 / ↑数值」,下载蓝、上传绿,无圆圈边框。 - ---- - -## Task 3: Tooltip 文本更新 - -**Files:** Modify `package/networkview.qml`(`buildToolTipText` 函数) - -**Interfaces:** -- Consumes: `formatSpeedShort()`、`activeInterface`、`ready` - -**说明:** Tooltip 改用 `formatSpeedShort` 与任务栏一致。`PanelToolTip.text` 是纯文本,不支持富文本着色,箭头无法单独着色。 - -- [ ] **Step 1: 替换 buildToolTipText 函数体** - -```qml - // 构建 tooltip 文本:单行紧凑展示下载/上传速度和活动接口 - // 设计原因:tooltip 空间有限,用 formatSpeedShort 与任务栏保持视觉一致 - // 用 "·" 分隔速度区与接口名,比 " | " 更轻量 - function buildToolTipText() { - if (!root.ready) { - return qsTr("未检测到网络接口") - } - - var lines = [] - lines.push("↓ " + root.formatSpeedShort(root.downloadSpeed)) - lines.push("↑ " + root.formatSpeedShort(root.uploadSpeed)) - lines.push(root.activeInterface) - return lines.join(" · ") - } -``` - -- [ ] **Step 2: 确认 PanelToolTip 未就绪文案为中文** - -确认 `PanelToolTip` 的 `text` 属性为: - -```qml - PanelToolTip { - id: toolTip - text: root.ready ? root.buildToolTipText() : qsTr("未检测到网络接口") - toolTipX: DockPanelPositioner.x - toolTipY: DockPanelPositioner.y - } -``` - -若为英文 `"No network interface detected"`,替换为上述中文。 - -- [ ] **Step 3: 安装并目视验证** - -Run: `sudo cmake --install /home/Jokul/Documents/projects/JNetApplet/build && systemctl --user restart dde-shell@DDE` -Expected: 悬停时 tooltip 显示形如 `↓ 1.2M · ↑ 256K · eth0`。 - ---- - -## Task 4: 左键弹出面板重写 - -**Files:** Modify `package/networkview.qml`(`PanelPopup` 内部,原 173-489 行) - -**Interfaces:** -- Consumes: `formatSpeed`、`formatTotal`、`primaryText`、`secondaryText`、`tertiaryText`、`cardBackground`、`cardBorder`、`accentBlue`、`accentBlueLight`、`accentGreen`、`accentGreenLight`、`downloadValueColor`、`uploadValueColor`、`ready`、`activeInterface`、`downloadSpeed`、`uploadSpeed`、`totalDownload`、`totalUpload`、`networkInterfaces`、`applet` -- Produces: 360×320 重构面板 - -**说明:** 尺寸从 400×350 改为 360×320。结构:标题栏(含副标题)+ 实时速度双列卡片 + 总量单行 + chip 接口切换 + 未就绪占位。 - -- [ ] **Step 1: 调整 PanelPopup 尺寸** - -将 `PanelPopup` 的 `width: 400` / `height: 350` 改为 `width: 360` / `height: 320`。保留 `popupX`/`popupY`/`onPopupVisibleChanged` 不变。 - -- [ ] **Step 2: 替换 Control 及其 contentItem 全部内容** - -将 `PanelPopup` 内的 `Control { id: popupContainer ... }` 整体替换为: - -```qml - Control { - id: popupContainer - anchors.fill: parent - padding: 16 - - contentItem: ColumnLayout { - spacing: 12 - - // 标题栏:左标题+副标题,右刷新按钮 - RowLayout { - Layout.fillWidth: true - spacing: 8 - - ColumnLayout { - spacing: 0 - Text { - text: qsTr("网络速度监控") - font.pixelSize: 15 - font.weight: Font.Bold - color: root.primaryText - } - Text { - text: root.ready ? root.activeInterface : qsTr("未连接") - font.pixelSize: 11 - color: root.secondaryText - visible: root.ready - } - } - - Item { Layout.fillWidth: true } - - // 刷新按钮:hover 高亮 + 旋转动画 - Rectangle { - width: 28 - height: 28 - radius: 6 - color: refreshIconArea.containsMouse ? root.accentBlueLight : "transparent" - - Text { - anchors.centerIn: parent - text: "⟳" - font.pixelSize: 16 - color: refreshIconArea.containsMouse ? root.accentBlue : root.secondaryText - } - - MouseArea { - id: refreshIconArea - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - if (root.applet) root.applet.refresh() - refreshSpin.start() - } - } - - RotationAnimator on rotation { - id: refreshSpin - running: false - from: 0 - to: 360 - duration: 500 - } - } - } - - // 实时速度卡片:双列布局,主视觉 - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 110 - color: root.cardBackground - radius: 12 - border.width: 1 - border.color: root.cardBorder - - RowLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 0 - - // 下载列 - ColumnLayout { - Layout.fillWidth: true - spacing: 4 - anchors.verticalCenter: parent.verticalCenter - - Rectangle { - width: 32 - height: 32 - radius: 16 - color: root.accentBlueLight - Layout.alignment: Qt.AlignHCenter - - Text { - anchors.centerIn: parent - text: "↓" - font.pixelSize: 18 - font.weight: Font.Bold - color: root.accentBlue - } - } - - Text { - text: qsTr("下载") - font.pixelSize: 11 - color: root.secondaryText - Layout.alignment: Qt.AlignHCenter - } - - Text { - text: root.formatSpeed(root.downloadSpeed) - font.pixelSize: 18 - font.weight: Font.Bold - color: root.downloadValueColor - Layout.alignment: Qt.AlignHCenter - } - } - - // 分隔线 - Rectangle { - width: 1 - Layout.fillHeight: true - color: root.cardBorder - } - - // 上传列 - ColumnLayout { - Layout.fillWidth: true - spacing: 4 - anchors.verticalCenter: parent.verticalCenter - - Rectangle { - width: 32 - height: 32 - radius: 16 - color: root.accentGreenLight - Layout.alignment: Qt.AlignHCenter - - Text { - anchors.centerIn: parent - text: "↑" - font.pixelSize: 18 - font.weight: Font.Bold - color: root.accentGreen - } - } - - Text { - text: qsTr("上传") - font.pixelSize: 11 - color: root.secondaryText - Layout.alignment: Qt.AlignHCenter - } - - Text { - text: root.formatSpeed(root.uploadSpeed) - font.pixelSize: 18 - font.weight: Font.Bold - color: root.uploadValueColor - Layout.alignment: Qt.AlignHCenter - } - } - } - } - - // 总量统计:单行紧凑 - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 44 - color: root.cardBackground - radius: 12 - border.width: 1 - border.color: root.cardBorder - visible: root.ready - - RowLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 8 - - Text { - text: qsTr("总计") - font.pixelSize: 11 - font.weight: Font.Bold - color: root.secondaryText - } - - Text { - text: "↓ " + root.formatTotal(root.totalDownload) - font.pixelSize: 12 - color: root.primaryText - } - - Item { Layout.fillWidth: true } - - Text { - text: "↑ " + root.formatTotal(root.totalUpload) - font.pixelSize: 12 - color: root.primaryText - } - } - } - - // 接口切换 chip 列表(仅多接口时显示) - Flow { - Layout.fillWidth: true - spacing: 6 - visible: root.ready && root.networkInterfaces.length > 1 - - Repeater { - model: root.networkInterfaces - - Rectangle { - width: chipText.implicitWidth + 20 - height: 28 - radius: 8 - color: modelData === root.activeInterface - ? root.accentBlue - : (chipMouse.containsMouse ? root.accentBlueLight : root.cardBackground) - border.width: 1 - border.color: modelData === root.activeInterface - ? root.accentBlue - : root.cardBorder - - Text { - id: chipText - anchors.centerIn: parent - text: modelData - font.pixelSize: 11 - font.weight: modelData === root.activeInterface ? Font.Bold : Font.Normal - color: modelData === root.activeInterface ? "white" : root.primaryText - } - - MouseArea { - id: chipMouse - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onClicked: { - if (root.applet) root.applet.setActiveInterface(modelData) - } - } - } - } - } - - // 未检测到接口占位 - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 80 - color: root.cardBackground - radius: 12 - border.width: 1 - border.color: root.cardBorder - visible: !root.ready - - ColumnLayout { - anchors.centerIn: parent - spacing: 6 - - Text { - text: "⚠" - font.pixelSize: 24 - color: root.secondaryText - Layout.alignment: Qt.AlignHCenter - } - - Text { - text: qsTr("未检测到网络接口") - font.pixelSize: 12 - color: root.secondaryText - Layout.alignment: Qt.AlignHCenter - } - } - } - - Item { Layout.fillHeight: true } - } - } -``` - -- [ ] **Step 3: 确认 `Component.onCompleted` 中 `DockPanelPositioner.bounding` 绑定保留** - -`PanelPopup` 末尾的 `Component.onCompleted` 块保留不变(用于定位 popup 位置)。 - -- [ ] **Step 4: 安装并目视验证** - -Run: `sudo cmake --install /home/Jokul/Documents/projects/JNetApplet/build && systemctl --user restart dde-shell@DDE` -Expected: -- 左键点击任务栏图标,弹出 360×320 面板 -- 标题栏显示「网络速度监控」+ 接口名副标题 -- 实时速度卡片双列:下载蓝、上传绿,各带圆形图标底 -- 总量统计单行显示「总计 ↓ X · ↑ Y」 -- 多接口时显示 chip 列表,当前接口高亮蓝色 -- 未检测到接口时显示占位卡片 - ---- - -## Task 5: 右键菜单与关于对话框 - -**Files:** Modify `package/networkview.qml`(新增 `Menu`、`Dialog`、右键 `TapHandler`,替换原左键 `TapHandler`) - -**Interfaces:** -- Consumes: `applet.refresh()`、`applet.setActiveInterface()`、`networkInterfaces`、`activeInterface`、`primaryText`、`secondaryText`、`accentBlue` -- Produces: 右键弹出 deepin 风格菜单 - -**说明:** 在 `PanelPopup` 之后、原左键 `TapHandler` 之前,新增 `Menu` 和 `Dialog`。在原左键 `TapHandler` 之后新增右键 `TapHandler`。 - -- [ ] **Step 1: 新增 contextMenu 和 aboutDialog** - -在 `PanelPopup` 闭合 `}` 之后,原 `// 构建 tooltip 文本` 注释之前,插入: - -```qml - // 右键菜单:deepin 风格 Menu,提供刷新/切换接口/关于快捷操作 - Menu { - id: contextMenu - - MenuItem { - text: qsTr("刷新") - onTriggered: { - if (root.applet) root.applet.refresh() - } - } - - MenuSeparator {} - - // 切换接口子菜单:动态构建可用接口列表 - Menu { - title: qsTr("切换接口") - enabled: root.networkInterfaces.length > 1 - - Instantiator { - model: root.networkInterfaces - delegate: MenuItem { - text: modelData - checkable: true - checked: modelData === root.activeInterface - onTriggered: { - if (root.applet) root.applet.setActiveInterface(modelData) - } - } - onObjectAdded: (index, object) => contextMenu.children[2].insertItem(index, object) - onObjectRemoved: (index, object) => contextMenu.children[2].removeItem(object) - } - } - - MenuSeparator {} - - MenuItem { - text: qsTr("关于") - onTriggered: aboutDialog.open() - } - } - - // 关于对话框:显示插件信息 - Dialog { - id: aboutDialog - anchors.centerIn: parent - modal: true - width: 280 - height: 160 - - background: Rectangle { - color: root.cardBackground - radius: 12 - border.width: 1 - border.color: root.cardBorder - } - - contentItem: ColumnLayout { - spacing: 8 - - Text { - text: qsTr("网络速度监控") - font.pixelSize: 16 - font.weight: Font.Bold - color: root.primaryText - Layout.alignment: Qt.AlignHCenter - } - - Text { - text: qsTr("版本:1.0") - font.pixelSize: 12 - color: root.secondaryText - Layout.alignment: Qt.AlignHCenter - } - - Text { - text: qsTr("监控网络速度和流量") - font.pixelSize: 11 - color: root.tertiaryText - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: 240 - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - } - - Item { Layout.fillHeight: true } - - Button { - text: qsTr("确定") - Layout.alignment: Qt.AlignHCenter - onClicked: aboutDialog.close() - } - } - } -``` - -- [ ] **Step 2: 新增右键 TapHandler** - -在原左键 `TapHandler`(`acceptedButtons: Qt.LeftButton`)之后,文件末尾的 root 闭合 `}` 之前,插入: - -```qml - // 右键点击:弹出上下文菜单 - TapHandler { - acceptedButtons: Qt.RightButton - gesturePolicy: TapHandler.ReleaseWithinBounds - - onTapped: { - // 关闭可能打开的 popup,避免视觉冲突 - if (networkPopup.popupVisible) networkPopup.close() - toolTip.close() - // 使用 root 中心点定位菜单 - contextMenu.popup() - } - } -``` - -- [ ] **Step 3: 确认 Instantiator 子菜单挂载点正确** - -Run: `grep -n "Instantiator\|onObjectAdded" /home/Jokul/Documents/projects/JNetApplet/package/networkview.qml` -Expected: 出现一次 Instantiator 块。 - -> 注:Instantiator 的 `onObjectAdded`/`onObjectRemoved` 中 `contextMenu.children[2]` 是切换接口子菜单(菜单顺序:刷新 MenuItem / MenuSeparator / 切换接口 Menu / MenuSeparator / 关于 MenuItem)。若 QtQuick.Controls 版本不支持 `insertItem`/`removeItem`,可改用 `Menu` 的 `MenuItem` 直接列出(无 Instantiator),通过 `Instantiator` 的 `onObjectAdded` 调用 `parent.addItem(object)` 或直接在子 Menu 内用 `Repeater`+`MenuItem` 写法。验证时如报错,将子菜单改为: - -```qml - Menu { - title: qsTr("切换接口") - enabled: root.networkInterfaces.length > 1 - - Repeater { - model: root.networkInterfaces - delegate: MenuItem { - text: modelData - checkable: true - checked: modelData === root.activeInterface - onTriggered: { - if (root.applet) root.applet.setActiveInterface(modelData) - } - } - } - } -``` - -(两种写法二选一,优先用 Repeater 版本更稳。) - -- [ ] **Step 4: 安装并目视验证** - -Run: `sudo cmake --install /home/Jokul/Documents/projects/JNetApplet/build && systemctl --user restart dde-shell@DDE` -Expected: -- 右键点击任务栏图标,弹出 deepin 风格菜单 -- 菜单项:刷新 / 切换接口(子菜单)/ 关于 -- 切换接口子菜单列出所有接口,当前接口打勾 -- 接口数 ≤ 1 时「切换接口」项禁用(灰色) -- 点击「关于」弹出对话框显示版本信息 - ---- - -## Task 6: 最终验证清单 - -**Files:** 无改动,仅验证 - -**说明:** 按 spec 验证清单逐项检查。 - -- [ ] **Step 1: 重新构建并安装** - -Run: `cmake --build /home/Jokul/Documents/projects/JNetApplet/build && sudo cmake --install /home/Jokul/Documents/projects/JNetApplet/build && systemctl --user restart dde-shell@DDE` -Expected: 构建成功、安装成功、dde-shell 重启无报错。 - -- [ ] **Step 2: 任务栏图标验证** - -逐项检查: -- [ ] 双行数值显示,下载在上、上传在下 -- [ ] 颜色正确(下载蓝、上传绿) -- [ ] 高速时下载值变橙(>1MB/s)/红(>10MB/s) -- [ ] 深色任务栏下文字可读 -- [ ] 浅色任务栏下文字可读 -- [ ] 无圆圈边框 - -- [ ] **Step 3: Tooltip 验证** - -悬停任务栏图标: -- [ ] 单行显示 `↓ X · ↑ Y · 接口名` -- [ ] 未检测到接口时显示「未检测到网络接口」 - -- [ ] **Step 4: 左键弹出面板验证** - -左键点击任务栏图标: -- [ ] 尺寸 360×320 -- [ ] 标题栏:「网络速度监控」+ 接口名副标题 -- [ ] 实时速度双列卡片:下载蓝、上传绿,圆形图标底 -- [ ] 总量统计单行:「总计 ↓ X · ↑ Y」 -- [ ] 多接口时显示 chip 列表,当前接口高亮 -- [ ] 刷新按钮 hover 高亮 + 旋转动画 -- [ ] 未检测到接口时显示占位卡片 - -- [ ] **Step 5: 右键菜单验证** - -右键点击任务栏图标: -- [ ] 弹出 deepin 风格菜单 -- [ ] 刷新项可用,点击后数据更新 -- [ ] 切换接口子菜单列出接口、当前项打勾 -- [ ] 接口数 ≤ 1 时切换项禁用 -- [ ] 关于对话框显示版本信息 - -- [ ] **Step 6: 检查无 QML 运行时错误** - -Run: `journalctl --user -u dde-shell@DDE -n 50 --no-pager | grep -i "qml\|error" | head -20` -Expected: 无 QML 加载或运行时错误。 - ---- - -## Self-Review - -**Spec 覆盖检查:** -- §1 任务栏图标区 → Task 1 (颜色/函数) + Task 2 (布局) ✓ -- §2 左键弹出面板 → Task 4 ✓ -- §3 右键菜单 → Task 5 ✓ -- §4 Tooltip → Task 3 ✓ -- §5 视觉规范 → Task 1 (颜色表) + 各任务内尺寸 ✓ -- §6 排除项 → Global Constraints 明确 ✓ - -**占位符扫描:** 无 TBD/TODO,所有代码块完整。 - -**类型一致性:** -- `formatSpeedShort` 在 Task 1 定义,Task 2/3 使用 ✓ -- `downloadValueColor`/`uploadValueColor` 在 Task 1 定义,Task 2/4 使用 ✓ -- `accentGreen`/`accentGreenLight` 在 Task 1 定义,Task 4 使用 ✓ -- 颜色派生自 `basePalette` 全局一致 ✓ - -**已知风险:** -- Task 5 Step 1 的 `Instantiator`+`insertItem`/`removeItem` 写法在不同 QtQuick.Controls 版本可能有差异,提供了 Repeater 备选方案。 -- 右键菜单的 `contextMenu.popup()` 默认在鼠标位置弹出,dde-shell 环境下应正常工作;如定位异常可改用 `contextMenu.popup(mouseX, mouseY)`。 diff --git a/docs/superpowers/specs/2026-07-18-ui-redesign-design.md b/docs/superpowers/specs/2026-07-18-ui-redesign-design.md deleted file mode 100644 index 9a0f72c..0000000 --- a/docs/superpowers/specs/2026-07-18-ui-redesign-design.md +++ /dev/null @@ -1,228 +0,0 @@ -# JNetApplet UI 重新设计规范 - -**日期**:2026-07-18 -**范围**:`package/networkview.qml` 单文件 -**方案**:A - 精简重塑(deepin 原生简约风) - -## 背景与目标 - -当前 JNetApplet 任务栏图标在 ~48px dock 区域内塞入圆圈边框 + 4 行内容(↓/下载速度/↑/上传速度),视觉拥挤;左键弹出面板色调偏灰、层次弱、接口切换 `ComboBox` 不够直观;右键行为未实现。 - -本次重新设计目标: - -- 任务栏展示精简、易读,跟随 deepin 任务栏视觉语言 -- 左键弹出面板信息层次清晰、视觉舒适 -- 新增右键菜单提供快捷操作 -- 全部颜色派生自 `DockPalette`,深/浅色任务栏自适应 - -## 范围 - -**包含**: - -- `package/networkview.qml` 全部重写 -- 任务栏图标区、左键弹出面板、右键菜单、Tooltip 四处视觉与交互 - -**排除**(YAGNI): - -- 不改 C++ 后端(`networkmonitorapplet.h/.cpp`) -- 不新增 `Q_PROPERTY` 或 `Q_INVOKABLE` -- 不引入设置面板(刷新间隔、单位制等) -- 不加历史趋势图表、sparkline -- 不持久化任何用户配置 -- 不改 `metadata.json`、`CMakeLists.txt` - -## 设计决策记录 - -| 决策点 | 选择 | 理由 | -|---|---|---| -| 整体风格 | deepin 原生简约风 | 跟随宿主环境视觉语言,自适应深/浅色 | -| 任务栏展示 | 双行数值(↓下载/↑上传) | 信息密集但不拥挤,无需 hover 即可读 | -| 右键行为 | deepin 风格 Menu | 符合 deepin 交互习惯,提供快捷操作 | -| 设置项 | 不做 | 控制范围,避免不持久化带来的困惑 | -| 历史图表 | 不做 | 超出"重新设计样式与交互"范围 | - -## §1. 任务栏图标区(dock item) - -### 布局 - -- 移除当前圆圈 `Rectangle` 容器 -- 双行 `Column` 居中: - - 上行:小号 `↓` + 下载速度数值(如 `1.2M`) - - 下行:小号 `↑` + 上传速度数值(如 `256K`) -- 箭头与数值同行,箭头为次级色、数值为强调色 - -### 新格式化函数 `formatSpeedShort(bytesPerSec)` - -紧凑格式,用于任务栏与 tooltip: - -| 字节范围 | 输出 | -|---|---| -| `< 1024` | 整数 B/s,如 `"512"` | -| `< 1024²` | 1 位小数 + `K`,如 `"1.2K"` | -| `< 1024³` | 1 位小数 + `M`,如 `"3.5M"` | -| `≥ 1024³` | 1 位小数 + `G`,如 `"2.4G"` | - -保留原 `formatSpeed` / `formatTotal` 函数供弹出面板使用(带单位更清晰)。 - -### 字号与字重 - -- 箭头:`dockSize * 0.16` -- 数值:`dockSize * 0.22`(当前 0.12 太小) -- 数值字重:`Font.Medium` - -### 颜色 - -- 下载值:`accentBlue` (`#1450A0`) -- 上传值:`accentGreen` (`#16A34A`) -- 高速警示(保留现有阈值逻辑): - - 下载 > 10 MB/s:红 (`#DC2626`) - - 下载 > 1 MB/s:橙 (`#F59E0B`) - -## §2. 左键弹出面板(popup) - -### 尺寸 - -从 `400×350` 调整为 `360×320`(更紧凑)。 - -### 结构(顶到底) - -1. **标题栏** - - 左:主标题「网络速度监控」+ 副标题(活动接口名,次级色 12px) - - 右:圆形刷新按钮(hover 高亮 + 旋转动画) - -2. **实时速度卡片**(主视觉) - - 横向双列布局 - - 左列:`↓` 圆形底(`accentBlueLight` 背景)+ 「下载」标签 + 大字号速度值(蓝) - - 右列:`↑` 圆形底(`accentGreenLight` 背景)+ 「上传」标签 + 大字号速度值(绿) - - 数值字号 22px、字重 Bold - - 使用 `formatSpeed`(带单位,更清晰) - -3. **总量统计** - - 单行紧凑横向布局 - - 「总计」标签 + `↓ 1.07 GB` + `↑ 74.8 MB` - - 字号 12px、次级色 - -4. **接口切换 chip 列表**(仅当 `networkInterfaces.length > 1` 时显示) - - 横向排列,每个接口一个圆角 chip(`radius: 8`) - - 当前活动 chip:`accentBlue` 背景 + 白字 - - 其他 chip:`cardBackground` + `primaryText` - - hover:`accentBlueLight` 背景 - - 点击触发 `applet.setActiveInterface(name)` - -5. **未检测到接口占位** - - 保留现有卡片样式 - - 文案居中:`⚠` + 「未检测到网络接口」 - -### 视觉 - -- 卡片圆角 12px -- 内边距 16px、卡片内边距 12px、元素间距 12px -- 分隔线:`cardBorder` 色、1px - -## §3. 右键菜单(新功能) - -### 触发 - -在现有 `TapHandler`(左键)旁新增 `TapHandler` 处理 `Qt.RightButton`,调用 `menu.popup()`。 - -### 菜单项 - -``` -刷新 -───────────── -切换接口 ▸ (子菜单) -───────────── -关于 -``` - -### 行为 - -- **刷新**:调用 `applet.refresh()` -- **切换接口子菜单**: - - 动态构建 `MenuItem`,绑定 `networkInterfaces` - - 当前 `activeInterface` 项打勾(`checkable: true`、`checked: true`) - - 点击触发 `applet.setActiveInterface(name)` - - 接口数 ≤ 1 时禁用此项 -- **关于**:弹出小型 `Dialog` - - 插件名:网络速度监控 - - 版本:1.0(硬编码,与 `metadata.json` 一致) - - 描述:监控网络速度和流量 - -### 实现要点 - -- 使用 `QtQuick.Controls` 的 `Menu`(deepin DTK 自动适配风格) -- `Menu` 作为 root 的子元素,id 为 `contextMenu` -- 子菜单用 `Menu` 嵌套 - -## §4. Tooltip - -单行简洁: - -``` -↓ 1.2M/s ↑ 256K/s · eth0 -``` - -- 方向箭头用对应强调色(下载蓝、上传绿) -- 接口名前用 `·` 分隔 -- 复用 `formatSpeedShort` 保持与任务栏一致 -- 未检测到接口时显示「未检测到网络接口」 - -## §5. 视觉规范 - -### 颜色派生表 - -| 用途 | 计算 | -|---|---| -| 主文字 `primaryText` | `basePalette` α 0.95 | -| 次级文字 `secondaryText` | `basePalette` α 0.80 | -| 三级文字 `tertiaryText` | `basePalette` α 0.65 | -| 卡片背景 `cardBackground` | `basePalette` α 0.06 | -| 卡片边框 `cardBorder` | `basePalette` α 0.10 | -| 强调蓝 `accentBlue` | `#1450A0` | -| 强调蓝浅 `accentBlueLight` | `#1450A0` α 0.12 | -| 强调绿 `accentGreen` | `#16A34A` | -| 强调绿浅 `accentGreenLight` | `#16A34A` α 0.12 | -| 高速橙 `accentOrange` | `#F59E0B` | -| 高速红 `accentRed` | `#DC2626` | - -### 尺寸规范 - -- 卡片圆角:12px -- chip 圆角:8px -- 按钮圆角:6px -- 面板内边距:16px -- 卡片内边距:12px -- 元素间距:12px - -### 字体 - -跟随系统无衬线字体,不指定 family。字重使用 `Font.Medium` / `Font.Bold`。 - -## 实现性质 - -- **文件**:仅 `package/networkview.qml` -- **风险**:低(纯前端、不动后端、不改构建) -- **验证**:手动安装 + 重启 dde-shell - -## 验证清单 - -安装后重启 dde-shell,依次检查: - -- [ ] 任务栏图标:双行数值显示,下载在上、上传在下 -- [ ] 任务栏图标:颜色正确(下载蓝、上传绿) -- [ ] 任务栏图标:高速时下载值变橙/红 -- [ ] 任务栏图标:深色任务栏下文字可读 -- [ ] 任务栏图标:浅色任务栏下文字可读 -- [ ] 悬停 tooltip:单行显示 ↓↑速度 + 接口名 -- [ ] 左键弹出:尺寸 360×320 -- [ ] 左键弹出:标题栏 + 接口名副标题 -- [ ] 左键弹出:实时速度双列卡片 -- [ ] 左键弹出:总量统计单行 -- [ ] 左键弹出:接口 chip 列表(多接口时) -- [ ] 左键弹出:刷新按钮 hover + 旋转动画 -- [ ] 左键弹出:未检测到接口时显示占位 -- [ ] 右键菜单:弹出 deepin 风格菜单 -- [ ] 右键菜单:刷新项可用 -- [ ] 右键菜单:切换接口子菜单列出接口、当前项打勾 -- [ ] 右键菜单:接口数 ≤ 1 时切换项禁用 -- [ ] 右键菜单:关于对话框显示版本信息