From c614df5fbee16b3a62dc9b6bc8ba67bf595b80a4 Mon Sep 17 00:00:00 2001 From: Jokul Date: Fri, 24 Jul 2026 09:30:18 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=8B=AC=E7=AB=8B=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E5=8F=8A=E4=BB=BB=E5=8A=A1=E6=A0=8F=E5=9B=BE=E6=A0=87=E9=80=82?= =?UTF-8?q?=E9=85=8D=E6=B7=B1=E8=89=B2=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4 个独立窗口(AboutWindow/SettingsWindow/TextColorPicker/TrafficChartWindow) 新增 isDarkMode 属性,由 networkview.qml 检测后注入,各窗口定义深/浅双色方案。 深色模式检测从 DockPalette.iconTextPalette 改为 DTK.palette.window.hslLightness, 因 DockPalette 不随系统深色主题变化。 同时将 networkview.qml 任务栏图标和 NetworkPopup 弹窗的颜色基从 DockPalette.iconTextPalette 改为 DTK.palette.windowText,修复深色模式 下黑底黑字、浅色模式下白底白字的问题。 --- docs/project-review.md | 5 +- package/components/AboutWindow.qml | 58 ++++++++----- package/components/NetworkPopup.qml | 19 +++-- package/components/SettingsWindow.qml | 99 ++++++++++++++--------- package/components/TextColorPicker.qml | 44 ++++++---- package/components/TrafficChartWindow.qml | 73 ++++++++++++----- package/networkview.qml | 37 ++++++--- 7 files changed, 219 insertions(+), 116 deletions(-) diff --git a/docs/project-review.md b/docs/project-review.md index 28767d9..11f6e8f 100644 --- a/docs/project-review.md +++ b/docs/project-review.md @@ -21,8 +21,9 @@ ~~`m_totalDownload = currentRxBytes` 只存储活动接口的当前计数器值,并非真正的累计流量。~~ 已改为累加会话增量 `m_totalDownload += rxDeltaClamped`,"总计"语义从"活动接口计数器值"变为"本次会话累计流量":从 0 开始增长,切换网卡不再跳变,网卡重启不归零。跨重启持久化属于 #18 的范畴。 -**4. 独立窗口不支持深色模式** -`AboutWindow.qml`、`SettingsWindow.qml`、`TextColorPicker.qml`、`TrafficChartWindow.qml` 全部硬编码浅色(`#FFFFFF`、`#333333`、`#f5f5f5` 等),不随系统主题适配。而 `networkview.qml` 和 `NetworkPopup.qml` 正确地从 `DockPalette` 派生颜色。deepin 深色模式下这些窗口视觉割裂严重。 +**4. ~~独立窗口不支持深色模式~~ ✅ 已修复** +~~`AboutWindow.qml`、`SettingsWindow.qml`、`TextColorPicker.qml`、`TrafficChartWindow.qml` 全部硬编码浅色,不随系统主题适配。~~ +已为 4 个独立窗口新增 `isDarkMode` 属性(由 networkview.qml 检测后注入),各窗口定义深/浅双色方案切换。深色模式检测改用 `DTK.palette.window.hslLightness < 0.5`(原 `DockPalette.iconTextPalette` 不随系统主题变化)。同时修复 networkview.qml 任务栏图标和 NetworkPopup 弹窗的颜色基:从 `DockPalette.iconTextPalette` 改为 `DTK.palette.windowText`,解决深色模式黑底黑字问题。 **5. `detectInterfaces()` 过滤规则与 `readNetworkStats()` 不一致** `init()` 先调 `detectInterfaces()`(读 `/sys/class/net`,仅过滤 `lo`),再调 `readNetworkStats()`(读 `/proc/net/dev`,过滤 `lo`/`veth`/`docker`/`br-`)。首次刷新前,docker/veth 接口会短暂出现在列表中,造成 UI 闪烁。且 `detectInterfaces()` 的结果会被 `readNetworkStats()` 完全覆盖,属于冗余调用。 diff --git a/package/components/AboutWindow.qml b/package/components/AboutWindow.qml index fd4da21..1c843cc 100644 --- a/package/components/AboutWindow.qml +++ b/package/components/AboutWindow.qml @@ -3,9 +3,11 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // 关于窗口:DTK 原生风格的独立顶层窗口,桌面居中弹出,展示插件信息 -// 设计要点:白色圆角卡片 + 键值对信息布局,信息标签使用 deepin 蓝(#0081FF), -// 关闭按钮 hover 高亮色由父组件通过 accentColor 传入(红色) -// 对外依赖:accentColor,由 networkview.qml 实例化时传入,触发方式为 show()/raise()/requestActivate() +// 设计要点:圆角卡片 + 键值对信息布局,信息标签使用 deepin 蓝(#0081FF), +// 关闭按钮 hover 高亮色由父组件通过 accentColor 传入(红色); +// 深/浅主题由 isDarkMode 切换(networkview.qml 依据 DockPalette 检测任务栏深浅后传入), +// 默认浅色,保证组件独立预览时与原版视觉一致 +// 对外依赖:accentColor、isDarkMode,由 networkview.qml 实例化时传入,触发方式为 show()/raise()/requestActivate() import QtQuick 2.15 import QtQuick.Layouts 1.15 import QtQuick.Window 2.15 @@ -21,6 +23,18 @@ Window { // 默认值保证组件独立可用(如 qmlscene 预览时未传入) property string version: "1.0" + // 深色模式标记:由 networkview.qml 根据 DockPalette 检测后传入; + // 默认 false(浅色)保证组件独立预览时与原版视觉一致 + property bool isDarkMode: false + + // ---- 主题感知颜色:isDarkMode 为 false 时取值与原浅色硬编码完全一致 ---- + readonly property color winBg: isDarkMode ? "#202020" : "#FFFFFF" + readonly property color cardBg: isDarkMode ? "#2A2A2A" : "#FFFFFF" + readonly property color lineColor: isDarkMode ? "#383838" : "#E8E8E8" + readonly property color textPrimary: isDarkMode ? "#E0E0E0" : "#333333" + readonly property color textSecondary: isDarkMode ? "#AAAAAA" : "#666666" + readonly property color textTertiary: isDarkMode ? "#888888" : "#999999" + // 复制成功状态标记:true 时复制图标变绿并显示"已复制"提示, // 2 秒后由 copyResetTimer 复位 property bool copied: false @@ -39,13 +53,13 @@ Window { } } - // 窗口主体:白色圆角卡片,1px 浅灰边框模拟 DTK 窗口描边 + // 窗口主体:圆角卡片(背景与边框随 isDarkMode 切换深浅),1px 边框模拟 DTK 窗口描边 Rectangle { anchors.fill: parent - color: "#FFFFFF" + color: root.winBg radius: 12 border.width: 1 - border.color: "#E8E8E8" + border.color: root.lineColor ColumnLayout { anchors.fill: parent @@ -70,7 +84,7 @@ Window { text: qsTr("关于") font.pixelSize: 15 font.weight: Font.Bold - color: "#333333" + color: root.textPrimary } // 关闭按钮:28x28 圆形,hover 时淡红底 + 红色 ×(颜色由 accentColor 决定) @@ -88,7 +102,7 @@ Window { text: "×" font.pixelSize: 20 font.weight: Font.Bold - color: aboutCloseMouse.containsMouse ? accentColor : "#666666" + color: aboutCloseMouse.containsMouse ? accentColor : root.textSecondary } MouseArea { @@ -117,7 +131,7 @@ Window { text: qsTr("网络速度监控") font.pixelSize: 18 font.weight: Font.Bold - color: "#333333" + color: root.textPrimary Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter } @@ -125,7 +139,7 @@ Window { Text { text: "JNetApplet" font.pixelSize: 12 - color: "#999999" + color: root.textTertiary Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter } @@ -142,7 +156,7 @@ Window { Layout.leftMargin: 24 Layout.rightMargin: 24 Layout.preferredHeight: 1 - color: "#E8E8E8" + color: root.lineColor } // 信息区上方间距 @@ -173,7 +187,7 @@ Window { Text { text: version font.pixelSize: 12 - color: "#333333" + color: root.textPrimary Layout.fillWidth: true } } @@ -194,7 +208,7 @@ Window { Text { text: "Jokul" font.pixelSize: 12 - color: "#333333" + color: root.textPrimary Layout.fillWidth: true } } @@ -215,7 +229,7 @@ Window { Text { text: qsTr("监控网络速度和流量") font.pixelSize: 12 - color: "#333333" + color: root.textPrimary Layout.fillWidth: true wrapMode: Text.WordWrap } @@ -237,7 +251,7 @@ Window { Text { text: "git.jokul.space/Jokul/JNetApplet" font.pixelSize: 11 - color: "#999999" + color: root.textTertiary Layout.fillWidth: true wrapMode: Text.WrapAnywhere } @@ -248,7 +262,7 @@ Window { Layout.preferredHeight: 24 Layout.alignment: Qt.AlignTop - // "已复制"提示:白底绿字小标签,显示在按钮左侧, + // "已复制"提示:卡片底色绿字小标签,显示在按钮左侧, // 覆盖于 URL 文字上方,避免与值文本混排 Rectangle { anchors.right: parent.left @@ -258,7 +272,7 @@ Window { width: copiedHintText.implicitWidth + 10 height: 18 radius: 4 - color: "#FFFFFF" + color: root.cardBg border.width: 1 border.color: "#22A34A" @@ -286,7 +300,7 @@ Window { } // 复制图标:两个重叠的小圆角矩形 - // 前层矩形白底压住后层边框重叠区,形成"堆叠纸张"的视觉效果 + // 前层矩形以卡片底色压住后层边框重叠区,形成"堆叠纸张"的视觉效果 // 后层矩形(向右下偏移 2px) Rectangle { x: 8 @@ -294,9 +308,9 @@ Window { width: 10 height: 10 radius: 2 - color: "#FFFFFF" + color: root.cardBg border.width: 1 - border.color: copyRepoMouse.containsMouse ? "#333333" : "#999999" + border.color: copyRepoMouse.containsMouse ? root.textPrimary : root.textTertiary } // 前层矩形(左上),复制成功时边框短暂变绿 @@ -306,9 +320,9 @@ Window { width: 10 height: 10 radius: 2 - color: "#FFFFFF" + color: root.cardBg border.width: 1 - border.color: copied ? "#22A34A" : (copyRepoMouse.containsMouse ? "#333333" : "#999999") + border.color: copied ? "#22A34A" : (copyRepoMouse.containsMouse ? root.textPrimary : root.textTertiary) } } } diff --git a/package/components/NetworkPopup.qml b/package/components/NetworkPopup.qml index ea06080..fd855ba 100644 --- a/package/components/NetworkPopup.qml +++ b/package/components/NetworkPopup.qml @@ -32,14 +32,17 @@ Control { readonly property string ipv6Address: applet ? (applet.ipv6Address || "") : "" readonly property var networkInterfaces: applet ? (applet.networkInterfaces || []) : [] - // 弹窗的颜色全部派生自 DockPalette.iconTextPalette,与任务栏颜色独立 - // 设计原因:PanelPopup 不继承 AppletItem 的颜色属性,需要自行派生 - 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) + // 弹窗颜色派生自 DTK 系统调色板(windowText),而非 DockPalette.iconTextPalette。 + // 设计原因:PanelPopup 背景跟随系统主题(深色模式下变黑),但 DockPalette.iconTextPalette + // 反映的是任务栏图标文字色,不随系统深色主题变化,导致深色模式下弹窗黑底黑字。 + // DTK.palette.windowText 在深色模式下为浅色文字、浅色模式下为深色文字,与弹窗背景同步。 + // 主题切换时 DTK.paletteChanged 信号自动触发属性绑定重新求值。 + readonly property color baseTextColor: DTK.palette.windowText + readonly property color primaryText: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.95) + readonly property color secondaryText: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.80) + readonly property color tertiaryText: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.65) + readonly property color cardBackground: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.06) + readonly property color cardBorder: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.10) // 强调色:下载蓝、上传绿,是面板的主视觉区分 readonly property color accentBlue: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 1) diff --git a/package/components/SettingsWindow.qml b/package/components/SettingsWindow.qml index 3d0c320..582e8e0 100644 --- a/package/components/SettingsWindow.qml +++ b/package/components/SettingsWindow.qml @@ -3,9 +3,12 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // 设置窗口:独立顶层窗口,桌面居中弹出,提供网络接口选择、字体颜色选择、插件卸载功能 -// 设计要点:白色圆角卡片 + 自定义标题栏可拖动,接口单选切换,卸载按钮带复制命令功能 +// 设计要点:圆角卡片 + 自定义标题栏可拖动,接口单选切换,卸载按钮带复制命令功能; +// 深/浅主题由 isDarkMode 切换(networkview.qml 依据 DockPalette 检测任务栏深浅后传入), +// 默认浅色,保证组件独立预览时与原版视觉一致 // 对外依赖:applet(C++ 后端对象)、networkInterfaces(接口列表)、activeInterface(当前接口)、 -// accentColor(强调色),由 networkview.qml 实例化时传入,触发方式为 show()/raise()/requestActivate() +// accentColor(强调色)、isDarkMode(深色模式标记),由 networkview.qml 实例化时传入, +// 触发方式为 show()/raise()/requestActivate() import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 @@ -27,6 +30,26 @@ Window { // 默认值与 networkview.qml 中 root.accentRed 一致,确保独立可用 property color accentColor: Qt.rgba(220 / 255, 38 / 255, 38 / 255, 1) + // 深色模式标记:由 networkview.qml 根据 DockPalette 检测后传入; + // 默认 false(浅色)保证组件独立预览时与原版视觉一致 + property bool isDarkMode: false + + // ---- 主题感知颜色:isDarkMode 为 false 时取值与原浅色硬编码完全一致 ---- + readonly property color winBg: isDarkMode ? "#202020" : "#F5F5F5" + readonly property color cardBg: isDarkMode ? "#2A2A2A" : "#FFFFFF" + readonly property color lineColor: isDarkMode ? "#383838" : "#E0E0E0" + readonly property color textPrimary: isDarkMode ? "#E0E0E0" : "#333333" + readonly property color textSecondary: isDarkMode ? "#AAAAAA" : "#666666" + readonly property color textTertiary: isDarkMode ? "#888888" : "#999999" + readonly property color iconGray: isDarkMode ? "#AAAAAA" : "#777777" + readonly property color hoverBg: isDarkMode ? "#353535" : "#F0F0F0" + readonly property color codeBg: isDarkMode ? "#2A2A2A" : "#E8E8E8" + // 选中态蓝色系:深色模式下背景压暗、文字改浅蓝,保证深色卡片上的对比度 + readonly property color selBg: isDarkMode ? "#1A3A5C" : "#E3F2FD" + readonly property color selBgHover: isDarkMode ? "#1E4A6E" : "#D8ECFD" + readonly property color selBorder: isDarkMode ? "#2C5A8A" : "#90CAF9" + readonly property color selText: isDarkMode ? "#64B5F6" : "#1565C0" + // 卸载按钮文字:复制命令后临时显示提示,定时器到期后还原 property string uninstallButtonText: qsTr("卸载插件") @@ -82,13 +105,13 @@ Window { } } - // 主容器:提供不透明背景 + 圆角 + 边框 + // 主容器:提供不透明背景(随 isDarkMode 切换深浅)+ 圆角 + 边框 Rectangle { anchors.fill: parent - color: "#f5f5f5" + color: root.winBg radius: 12 border.width: 1 - border.color: "#e0e0e0" + border.color: root.lineColor ColumnLayout { anchors.fill: parent @@ -118,7 +141,7 @@ Window { text: qsTr("设置") font.pixelSize: 15 font.weight: Font.Bold - color: "#333333" + color: root.textPrimary } // 关闭按钮 @@ -137,7 +160,7 @@ Window { text: "×" font.pixelSize: 20 font.weight: Font.Bold - color: closeMouse.containsMouse ? accentColor : "#666666" + color: closeMouse.containsMouse ? accentColor : root.textSecondary } MouseArea { @@ -154,7 +177,7 @@ Window { Rectangle { Layout.fillWidth: true Layout.preferredHeight: 1 - color: "#e0e0e0" + color: root.lineColor } // 内容区域 @@ -172,7 +195,7 @@ Window { text: qsTr("网络接口") font.pixelSize: 13 font.weight: Font.Bold - color: "#666666" + color: root.textSecondary } // 接口列表容器:最多显示约 5 行(160px),超出部分垂直滚动 @@ -183,10 +206,10 @@ Window { Layout.preferredHeight: networkInterfaces.length > 0 ? Math.min(ifaceColumn.implicitHeight, 160) + 20 : 80 - color: "#ffffff" + color: root.cardBg radius: 10 border.width: 1 - border.color: "#e0e0e0" + border.color: root.lineColor // 用 ScrollView 而非裸 Flickable:自带滚轮滚动支持, // 且滚动条显示时自动让出内容宽度,不会遮挡行内文字 @@ -200,7 +223,7 @@ Window { contentWidth: availableWidth // 自定义滚动条样式:与 networkview.qml chip 滚动条一致(4px 圆角矩形), - // 颜色改用硬编码灰色系适配本窗口浅色主题; + // 颜色使用主题感知灰色系(随 isDarkMode 切换深浅); // AsNeeded 策略保证接口少时不显示滚动条 ScrollBar.vertical: ScrollBar { id: ifaceScrollBar @@ -210,9 +233,9 @@ Window { contentItem: Rectangle { implicitWidth: 4 radius: 2 - // 按压/悬停加深、默认稍淡,保证滚动条在白色卡片上可见 - color: ifaceScrollBar.pressed ? "#666666" - : (ifaceScrollBar.hovered ? "#666666" : "#999999") + // 按压/悬停加深、默认稍淡,保证滚动条在卡片背景上可见 + color: ifaceScrollBar.pressed ? root.textSecondary + : (ifaceScrollBar.hovered ? root.textSecondary : root.textTertiary) } } @@ -224,7 +247,7 @@ Window { // 接口列表:整行点击切换活动接口 // 行样式:类型图标 + 单选圆点 + 名称 + 右侧类型描述; - // hover 显示浅灰底,选中行浅蓝底 + 蓝色细边框 + // hover 底、选中底与边框颜色均随 isDarkMode 切换深浅 Repeater { model: networkInterfaces delegate: Rectangle { @@ -235,16 +258,16 @@ Window { // 当前行是否为活动接口(选中态) readonly property bool isActive: modelData === activeInterface readonly property bool hovered: rowMouse.containsMouse - // 选中态强调色用 #1565c0(较 #1976d2 更深): - // 保证 ▢ 等空心图标在浅蓝选中背景上仍有足够对比度 - readonly property color activeColor: "#1565c0" + // 选中态强调色由 root.selText 提供(浅色 #1565c0 / 深色 #64b5f6): + // 保证 ▢ 等空心图标在选中背景上仍有足够对比度 + readonly property color activeColor: root.selText // 背景优先级:选中 > hover;选中行 hover 时稍加深以保留交互反馈 color: isActive - ? (hovered ? "#d8ecfd" : "#e3f2fd") - : (hovered ? "#f0f0f0" : "transparent") - // 选中行加蓝色细边框增强视觉;未选中保持透明边框占位,避免切换时出现 1px 抖动 + ? (hovered ? root.selBgHover : root.selBg) + : (hovered ? root.hoverBg : "transparent") + // 选中行加细边框增强视觉;未选中保持透明边框占位,避免切换时出现 1px 抖动 border.width: 1 - border.color: isActive ? "#90caf9" : "transparent" + border.color: isActive ? root.selBorder : "transparent" // 整行 hover + 点击:行内元素均不处理鼠标事件, // 点击行任意位置即选中该接口 @@ -269,7 +292,7 @@ Window { horizontalAlignment: Text.AlignHCenter text: interfaceIcon(modelData) font.pixelSize: 14 - color: ifaceRow.isActive ? ifaceRow.activeColor : "#777777" + color: ifaceRow.isActive ? ifaceRow.activeColor : root.iconGray } // 自绘单选圆点:替代 RadioButton 作纯视觉指示器, @@ -283,7 +306,7 @@ Window { color: "transparent" border.width: 2 border.color: ifaceRow.isActive ? ifaceRow.activeColor - : (ifaceRow.hovered ? "#666666" : "#999999") + : (ifaceRow.hovered ? root.textSecondary : root.textTertiary) Rectangle { anchors.centerIn: parent @@ -302,7 +325,7 @@ Window { text: modelData font.pixelSize: 13 font.weight: ifaceRow.isActive ? Font.Medium : Font.Normal - color: ifaceRow.isActive ? ifaceRow.activeColor : "#333333" + color: ifaceRow.isActive ? ifaceRow.activeColor : root.textPrimary elide: Text.ElideRight } @@ -311,7 +334,7 @@ Window { Layout.alignment: Qt.AlignVCenter text: interfaceDescription(modelData) font.pixelSize: 12 - color: "#999999" + color: root.textTertiary } } } @@ -324,7 +347,7 @@ Window { anchors.centerIn: parent text: qsTr("未检测到网络接口") font.pixelSize: 12 - color: "#999999" + color: root.textTertiary visible: networkInterfaces.length === 0 } } @@ -336,12 +359,14 @@ Window { text: qsTr("字体颜色") font.pixelSize: 13 font.weight: Font.Bold - color: "#666666" + color: root.textSecondary } TextColorPicker { Layout.fillWidth: true currentColor: applet ? applet.textColor : "" + // 深色模式标记向下传递:TextColorPicker 内部色板卡片同样主题感知 + isDarkMode: root.isDarkMode onColorSelected: if (applet) applet.textColor = color } @@ -405,10 +430,10 @@ Window { y: (root.height - height) / 2 background: Rectangle { - color: "#f5f5f5" + color: root.winBg radius: 12 border.width: 1 - border.color: "#e0e0e0" + border.color: root.lineColor } contentItem: ColumnLayout { @@ -427,7 +452,7 @@ Window { Text { text: qsTr("以下命令用于卸载插件并重启 dde-shell,请复制到终端中执行:") font.pixelSize: 12 - color: "#333333" + color: root.textPrimary Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter @@ -438,7 +463,7 @@ Window { Rectangle { Layout.fillWidth: true Layout.preferredHeight: 60 - color: "#e8e8e8" + color: root.codeBg radius: 8 Text { @@ -447,7 +472,7 @@ Window { text: "sudo rm -rf /usr/share/dde-shell/space.jokul.JNetApplet/ && systemctl --user restart dde-shell@DDE" font.pixelSize: 10 font.family: "monospace" - color: "#666666" + color: root.textSecondary wrapMode: Text.WordWrap } } @@ -463,16 +488,16 @@ Window { Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 - color: cancelUninstallMouse.containsMouse ? "#f0f0f0" : "#ffffff" + color: cancelUninstallMouse.containsMouse ? root.hoverBg : root.cardBg radius: 8 border.width: 1 - border.color: "#e0e0e0" + border.color: root.lineColor Text { anchors.centerIn: parent text: qsTr("取消") font.pixelSize: 13 - color: "#333333" + color: root.textPrimary } MouseArea { diff --git a/package/components/TextColorPicker.qml b/package/components/TextColorPicker.qml index 8aa78cc..c3a5e60 100644 --- a/package/components/TextColorPicker.qml +++ b/package/components/TextColorPicker.qml @@ -3,9 +3,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // 字体颜色选择器:用于设置窗口,提供预设色板与"跟随系统"选项 -// 设计要点:白色圆角卡片与设置窗口其他分区视觉一致;圆形色块 22x22, -// 当前选中色加 2px 深色边框高亮;点击只发射 colorSelected 信号, -// 由父层(networkview.qml)回写 currentColor 并同步到 C++ 后端持久化 +// 设计要点:圆角卡片与设置窗口其他分区视觉一致;圆形色块 22x22, +// 当前选中色加 2px 边框高亮(颜色随主题切换);点击只发射 colorSelected 信号, +// 由父层(SettingsWindow)回写 currentColor 并同步到 C++ 后端持久化; +// 深/浅主题由 isDarkMode 切换(SettingsWindow 传入),默认浅色保证独立预览可用 // 属性语义: // currentColor - 当前选中颜色字符串,空串表示"跟随系统"(默认) // colorSelected(string) - 用户点击色块时发射,参数为空串或 #RRGGBB @@ -27,12 +28,25 @@ Rectangle { "#16A34A", "#1450A0", "#7C3AED", "#0891B2" ] + // 深色模式标记:由 SettingsWindow 传入;默认 false(浅色)保证独立预览可用 + property bool isDarkMode: false + + // ---- 主题感知颜色:isDarkMode 为 false 时取值与原浅色硬编码完全一致 ---- + readonly property color cardBg: isDarkMode ? "#2A2A2A" : "#FFFFFF" + readonly property color lineColor: isDarkMode ? "#383838" : "#E0E0E0" + readonly property color ringColor: isDarkMode ? "#E0E0E0" : "#333333" + // "跟随系统"色块底色:深色模式下用比卡片更深的灰保持可辨识 + readonly property color systemSwatchBg: isDarkMode ? "#202020" : "#F5F5F5" + Layout.fillWidth: true Layout.preferredHeight: 52 - color: "#ffffff" + color: root.cardBg radius: 10 border.width: 1 - border.color: "#e0e0e0" + border.color: root.lineColor + + // Canvas 不随属性绑定自动重绘,主题切换时主动触发"跟随系统"斜线重绘 + onIsDarkModeChanged: slashCanvas.requestPaint() RowLayout { anchors.fill: parent @@ -47,19 +61,20 @@ Rectangle { height: 26 radius: 13 color: "transparent" - // 当前选中态:2px 深色边框 + // 当前选中态:2px 边框高亮(颜色随主题切换) border.width: root.currentColor === modelData ? 2 : 0 - border.color: "#333333" + border.color: root.ringColor Rectangle { anchors.centerIn: parent width: 22 height: 22 radius: 11 - // 白色色块在白卡上需细边框可见 + // 白色色块在白卡上需细边框可见;深色模式下深色卡片上的黑色色块同理 color: modelData - border.width: modelData === "#FFFFFF" ? 1 : 0 - border.color: "#e0e0e0" + border.width: (modelData === "#FFFFFF" + || (root.isDarkMode && modelData === "#000000")) ? 1 : 0 + border.color: root.lineColor } MouseArea { @@ -78,24 +93,25 @@ Rectangle { color: "transparent" // currentColor 为空串时高亮(跟随系统选中态) border.width: root.currentColor === "" ? 2 : 0 - border.color: "#333333" + border.color: root.ringColor Rectangle { anchors.centerIn: parent width: 22 height: 22 radius: 11 - color: "#f5f5f5" + color: root.systemSwatchBg border.width: 1 - border.color: "#e0e0e0" + border.color: root.lineColor // 斜线:表示"无自定义色/跟随系统" Canvas { + id: slashCanvas anchors.fill: parent onPaint: { var ctx = getContext("2d") ctx.reset() - ctx.strokeStyle = "#999999" + ctx.strokeStyle = root.isDarkMode ? "#888888" : "#999999" ctx.lineWidth = 2 ctx.beginPath() ctx.moveTo(2, height - 2) diff --git a/package/components/TrafficChartWindow.qml b/package/components/TrafficChartWindow.qml index 008580f..f4b0fe0 100644 --- a/package/components/TrafficChartWindow.qml +++ b/package/components/TrafficChartWindow.qml @@ -5,9 +5,11 @@ // 流量波动图窗口:屏幕居中的独立顶层窗口,展示当前活动接口最近 5 分钟的 // 网速趋势(下载/上传双折线图),随 C++ 后端 speedHistoryChanged 信号每秒动态刷新 // 设计要点: -// - 视觉风格复用 AboutWindow.qml(白色圆角卡片 12px、1px #E8E8E8 边框、 -// 44px 标题栏、28x28 圆形关闭按钮 hover 淡红底) -// - 图表使用纯 QML Canvas 绘制,无 QtCharts 等外部依赖(与项目零依赖风格一致) +// - 视觉风格复用 AboutWindow.qml(圆角卡片 12px、1px 边框、44px 标题栏、 +// 28x28 圆形关闭按钮 hover 淡红底);深/浅主题由 isDarkMode 切换 +// (networkview.qml 依据 DockPalette 检测任务栏深浅后传入),默认浅色保证独立预览可用 +// - 图表使用纯 QML Canvas 绘制,无 QtCharts 等外部依赖(与项目零依赖风格一致); +// Canvas 不随属性绑定自动重绘,isDarkMode 变化时主动 requestPaint() // - 数据通过属性注入:applet 即 networkview.qml 的 root.applet(C++ 后端对象), // 为 null 时组件可独立预览(Canvas 显示"暂无历史数据"空状态) // 触发方式:右键菜单"流量波动图" -> show()/raise()/requestActivate() @@ -28,6 +30,27 @@ Window { // 为 null 时组件可独立预览(显示空状态),所有数据访问处均需做空值兜底 property var applet: null + // 深色模式标记:由 networkview.qml 根据 DockPalette 检测后传入; + // 默认 false(浅色)保证组件独立预览时与原版视觉一致 + property bool isDarkMode: false + + // ---- 主题感知颜色:isDarkMode 为 false 时取值与原浅色硬编码完全一致 ---- + readonly property color winBg: isDarkMode ? "#202020" : "#FFFFFF" + readonly property color lineColor: isDarkMode ? "#383838" : "#E8E8E8" + readonly property color textPrimary: isDarkMode ? "#E0E0E0" : "#333333" + readonly property color textSecondary: isDarkMode ? "#AAAAAA" : "#666666" + readonly property color textTertiary: isDarkMode ? "#888888" : "#999999" + // 置顶按钮 hover 底色:浅色用淡黑、深色用淡白,保证两种卡片背景上均可见 + readonly property color pinHoverBg: isDarkMode ? Qt.rgba(1, 1, 1, 0.10) : Qt.rgba(0, 0, 0, 0.06) + + // 图表绘制色:声明为 string 供 Canvas 2D 上下文直接使用 + //(QML color 类型含 alpha 时序列化为 #AARRGGBB,Canvas 无法正确解析,故不用 color 类型) + readonly property string gridBaselineColor: isDarkMode ? "#383838" : "#DDDDDD" + readonly property string gridLineColor: isDarkMode ? "#2A2A2A" : "#E5E5E5" + readonly property string axisTextColor: isDarkMode ? "#888888" : "#999999" + readonly property string hoverLineColor: isDarkMode ? "#555555" : "#BBBBBB" + readonly property string dotCoreColor: isDarkMode ? "#202020" : "#FFFFFF" + // Hover 状态:当前悬停采样点在历史缓冲中的索引,-1 表示无悬停 // 由图表区 MouseArea 根据 mouseX 反查最近采样点更新;Canvas 据此绘制 // 竖直辅助线与圆点标记,底部提示条据此显示该时刻数值 @@ -44,6 +67,12 @@ Window { // 置顶状态变化时重绘图钉图标(未置顶=灰色轮廓,置顶=蓝色填充) onPinnedChanged: pinIcon.requestPaint() + // 主题切换时重绘图表与图钉图标:Canvas 不随属性绑定自动重绘,需主动触发 + onIsDarkModeChanged: { + pinIcon.requestPaint() + canvas.requestPaint() + } + // 下载折线颜色(绿)与上传折线颜色(橙),与规范 §4.4 一致 readonly property color downloadLineColor: "#34C759" readonly property color uploadLineColor: "#FF9500" @@ -118,13 +147,13 @@ Window { return s + " " + unit.suffix } - // 窗口主体:白色圆角卡片,1px 浅灰边框模拟 DTK 窗口描边(与 AboutWindow 一致) + // 窗口主体:圆角卡片(背景与边框随 isDarkMode 切换深浅),1px 边框模拟 DTK 窗口描边(与 AboutWindow 一致) Rectangle { anchors.fill: parent - color: "#FFFFFF" + color: root.winBg radius: 12 border.width: 1 - border.color: "#E8E8E8" + border.color: root.lineColor ColumnLayout { anchors.fill: parent @@ -153,7 +182,7 @@ Window { text: qsTr("流量波动图") font.pixelSize: 15 font.weight: Font.Bold - color: "#333333" + color: root.textPrimary } // 置顶按钮:28x28 圆形(与关闭按钮一致),位于关闭按钮左侧 @@ -167,7 +196,7 @@ Window { height: 28 radius: 14 color: root.pinned ? Qt.rgba(0, 129 / 255, 1, 0.12) - : (pinMouse.containsMouse ? Qt.rgba(0, 0, 0, 0.06) : "transparent") + : (pinMouse.containsMouse ? root.pinHoverBg : "transparent") // 置顶图标:Canvas 绘制"上箭头触顶"(⤒ 风格)——向上箭头指向顶部横杠, // 是"置顶/移到顶部"最通用的视觉语言,与关闭按钮"×"形态区分明显 @@ -181,7 +210,7 @@ Window { onPaint: { var ctx = getContext("2d") ctx.clearRect(0, 0, width, height) - var c = root.pinned ? "#0081FF" : "#999999" + var c = root.pinned ? "#0081FF" : root.axisTextColor ctx.strokeStyle = c ctx.fillStyle = c ctx.lineCap = "round" @@ -246,7 +275,7 @@ Window { text: "×" font.pixelSize: 20 font.weight: Font.Bold - color: chartCloseMouse.containsMouse ? root.accentColor : "#666666" + color: chartCloseMouse.containsMouse ? root.accentColor : root.textSecondary } MouseArea { @@ -279,7 +308,7 @@ Window { // applet 为空时兜底为 0,保证独立预览不报错 text: root.formatSpeed(root.applet ? root.applet.downloadSpeed : 0) font.pixelSize: 12 - color: "#333333" + color: root.textPrimary } Item { Layout.preferredWidth: 10 } @@ -294,7 +323,7 @@ Window { Text { text: root.formatSpeed(root.applet ? root.applet.uploadSpeed : 0) font.pixelSize: 12 - color: "#333333" + color: root.textPrimary } Item { Layout.fillWidth: true } @@ -303,7 +332,7 @@ Window { text: (root.applet && root.applet.activeInterface ? root.applet.activeInterface : "—") + " · 5min" font.pixelSize: 12 - color: "#999999" + color: root.textTertiary } } @@ -362,15 +391,15 @@ Window { for (g = 0; g <= 4; g++) { gy = pt + ph - (ph * g / 4) // +0.5 让 1px 线条落在像素边界上,避免抗锯齿发虚 - // 基线(g=0)略深一档,其余 4 条网格线用规范色 #E5E5E5 - ctx.strokeStyle = g === 0 ? "#DDDDDD" : "#E5E5E5" + // 基线(g=0)略深一档;网格与刻度颜色均随 isDarkMode 切换深浅 + ctx.strokeStyle = g === 0 ? root.gridBaselineColor : root.gridLineColor ctx.lineWidth = 1 ctx.beginPath() ctx.moveTo(pl, gy + 0.5) ctx.lineTo(pl + pw, gy + 0.5) ctx.stroke() // 刻度标签右对齐于绘图区左侧 - ctx.fillStyle = "#999999" + ctx.fillStyle = root.axisTextColor ctx.textAlign = "right" ctx.fillText(root.formatAxisValue(yMax * g / 4, unit), pl - 8, gy) } @@ -379,7 +408,7 @@ Window { // 5 分钟窗口用 -5m/-4m/-3m/-2m/-1m/now 共 6 个标签,比 4 个三分点 // 的 -3m20s/-1m40s 更易读;循环按数组长度参数化,两端左/右对齐 var xLabels = ["-5m", "-4m", "-3m", "-2m", "-1m", "now"] - ctx.fillStyle = "#999999" + ctx.fillStyle = root.axisTextColor ctx.textBaseline = "alphabetic" for (i = 0; i < xLabels.length; i++) { var lx = pl + pw * i / (xLabels.length - 1) @@ -390,7 +419,7 @@ Window { // ---- 空状态:画完空网格后中央提示,不画折线(规范 §5)---- if (n === 0) { - ctx.fillStyle = "#999999" + ctx.fillStyle = root.axisTextColor ctx.font = "13px sans-serif" ctx.textAlign = "center" ctx.textBaseline = "middle" @@ -466,7 +495,7 @@ Window { if (hi >= 0 && hi < n) { var hx = pl + ((dl[hi].x - tMin) / tRange) * pw // 竖直辅助线:虚线,避免与实线折线混淆 - ctx.strokeStyle = "#BBBBBB" + ctx.strokeStyle = root.hoverLineColor ctx.lineWidth = 1 ctx.setLineDash([4, 3]) ctx.beginPath() @@ -474,12 +503,12 @@ Window { ctx.lineTo(hx, pt + ph) ctx.stroke() ctx.setLineDash([]) - // 圆点标记:白芯 + 2px 彩色描边,在折线上形成清晰锚点 + // 圆点标记:窗口底色芯 + 2px 彩色描边,在折线上形成清晰锚点 function drawDot(yVal, colorCss) { var hy = pt + ph - (yVal / yMax) * ph ctx.beginPath() ctx.arc(hx, hy, 4, 0, 2 * Math.PI) - ctx.fillStyle = "#FFFFFF" + ctx.fillStyle = root.dotCoreColor ctx.fill() ctx.strokeStyle = colorCss ctx.lineWidth = 2 @@ -553,7 +582,7 @@ Window { text: root.hoverHint.length > 0 ? root.hoverHint : qsTr("鼠标移到曲线上查看详情") font.pixelSize: 11 - color: root.hoverHint.length > 0 ? "#333333" : "#999999" + color: root.hoverHint.length > 0 ? root.textPrimary : root.textTertiary } } } diff --git a/package/networkview.qml b/package/networkview.qml index 11cc30f..1e89b68 100644 --- a/package/networkview.qml +++ b/package/networkview.qml @@ -105,14 +105,24 @@ AppletItem { } } - // 任务栏与面板的颜色全部派生自 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) + // 任务栏图标颜色派生自 DTK 系统调色板(windowText),跟随系统主题适配。 + // 设计原因:原用 DockPalette.iconTextPalette,但实测其不随系统深色主题变化, + // 深色模式下返回深色文字(黑底黑字)、浅色模式下返回浅色文字(白底白字),方向相反。 + // DTK.palette.windowText 在深色模式下为浅色、浅色模式下为深色,与任务栏背景匹配。 + // 注意:仅用于任务栏图标区(speedTextColor 回退),弹窗和独立窗口各自派生颜色。 + readonly property color baseTextColor: DTK.palette.windowText + readonly property color primaryText: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.95) + readonly property color secondaryText: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.80) + readonly property color tertiaryText: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.65) + readonly property color cardBackground: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.06) + readonly property color cardBorder: Qt.rgba(baseTextColor.r, baseTextColor.g, baseTextColor.b, 0.10) + + // 深色模式检测:用 DTK 系统调色板的窗口背景亮度判定,而非 DockPalette。 + // 设计原因:DockPalette.iconTextPalette 反映的是任务栏图标文字色,不随系统深色主题变化; + // DTK.palette.window 是系统主题的窗口背景色,深色模式下为深色(hslLightness < 0.5), + // 浅色模式下为浅色(hslLightness >= 0.5)。主题切换时 DTK.paletteChanged 自动触发重新求值。 + // 检测结果通过属性注入各独立窗口(AboutWindow 等独立 Window 无法访问 dock 上下文的 DockPalette) + readonly property bool isDarkMode: DTK.palette.window.hslLightness < 0.5 // 强调色:下载蓝、上传绿,是面板与任务栏的主视觉区分 readonly property color accentBlue: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 1) @@ -135,7 +145,7 @@ AppletItem { readonly property color uploadValueColor: accentGreen // 任务栏网速数值字体色:用户自定义色优先(持久化),未设置时跟随系统主题 - // 设计原因:primaryText 派生自 DockPalette,深浅色任务栏自动适配; + // 设计原因:primaryText 派生自 DTK.palette.windowText,深浅色系统主题自动适配; // 用户主动选色后覆盖,空串回退到 primaryText 保持自适应 readonly property color speedTextColor: (applet && applet.textColor.length > 0) ? applet.textColor : primaryText @@ -372,19 +382,23 @@ AppletItem { } // 关于窗口:抽取为独立组件 package/components/AboutWindow.qml - // 依赖通过属性传入:accentColor = root.accentRed,version 取自 C++ 后端 applet.version + // 依赖通过属性传入:accentColor = root.accentRed,version 取自 C++ 后端 applet.version, + // isDarkMode 取自上方 DockPalette 检测结果 AboutWindow { id: aboutWindow accentColor: root.accentRed version: root.applet ? root.applet.version : "1.0" + isDarkMode: root.isDarkMode } // 流量波动图窗口:屏幕居中独立窗口,展示活动接口最近 30 分钟网速趋势 - // 依赖通过属性传入:accentColor = root.accentRed,applet = root.applet + // 依赖通过属性传入:accentColor = root.accentRed,applet = root.applet, + // isDarkMode 取自上方 DockPalette 检测结果 TrafficChartWindow { id: trafficChartWindow accentColor: root.accentRed applet: root.applet + isDarkMode: root.isDarkMode } // 设置窗口:独立顶层窗口,在桌面中间弹出 @@ -396,6 +410,7 @@ AppletItem { networkInterfaces: root.networkInterfaces activeInterface: root.activeInterface accentColor: root.accentRed + isDarkMode: root.isDarkMode } // 点击处理