From 64408bf84273f034e6987d3d330cfdd5af73add2 Mon Sep 17 00:00:00 2001 From: jokul Date: Tue, 14 Jul 2026 21:50:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20GPU=E5=88=87=E6=8D=A2=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E3=80=81=E6=8C=89=E9=92=AE=E9=80=89=E4=B8=AD?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E3=80=81=E6=96=87=E5=AD=97=E5=AF=B9=E6=AF=94?= =?UTF-8?q?=E5=BA=A6=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GPU切换按钮:核显厂商自动识别(AMD/Intel),当前模式高亮选中 - 切换确认覆盖层:15秒倒计时圆环,确认/取消按钮,超时自动取消 - 文字对比度提升:primaryText 0.95, secondaryText 0.8, tertiaryText 0.65 - 图表刻度文字 0.25->0.5,网格线 0.06->0.1 - 删除卡片内频率/功耗/风扇行,驱动名版本号两端对齐 - 卡片高度78,进度条宽度180,弹窗宽度480 - 翻译更新:切换确认相关8条新增 --- package/driverview.qml | 287 +++++++++++++++--- .../org.deepin.ds.graphics-driver_zh_CN.ts | 40 +++ .../org.deepin.ds.graphics-driver_zh_TW.ts | 40 +++ 3 files changed, 329 insertions(+), 38 deletions(-) diff --git a/package/driverview.qml b/package/driverview.qml index 4223d11..45222fb 100644 --- a/package/driverview.qml +++ b/package/driverview.qml @@ -26,6 +26,55 @@ AppletItem { readonly property string currentDriver: applet ? (applet.currentDriver || qsTr("Unknown")) : qsTr("Unknown") readonly property string gpuMode: applet ? (applet.gpuMode || "") : "" + // 当前 PRIME 模式(nvidia/on-demand/intel) + readonly property string currentPrimeMode: { + if (root.gpuMode !== "PRIME" || !root.applet || !root.applet.gpuPrimary) return "" + var lines = root.deviceInfo.split("\n").filter(function(l) { return l.trim().length > 0 }) + for (var i = 0; i < lines.length; i++) { + if (/nvidia/i.test(lines[i]) && root.applet.gpuPrimary[i] === "true") { + return "nvidia" + } + } + return "on-demand" + } + + // GPU 切换确认 + property string pendingSwitchMode: "" + property int switchCountdown: 15 + + // 切换模式描述 + function switchModeDesc(mode) { + if (mode === "nvidia") return qsTr("Switch to NVIDIA discrete GPU for maximum performance. Higher power consumption.") + if (mode === "intel") return qsTr("Switch to integrated GPU for power saving. NVIDIA GPU will be disabled.") + if (mode === "on-demand") return qsTr("Hybrid mode: integrated GPU for display, NVIDIA for on-demand rendering.") + return "" + } + + function switchModeLabel(mode) { + if (mode === "nvidia") return "NVIDIA" + if (mode === "intel") return getIgpuVendorName() + if (mode === "on-demand") return qsTr("On-Demand") + return mode + } + + function requestSwitch(mode) { + root.pendingSwitchMode = mode + root.switchCountdown = 15 + switchConfirmTimer.start() + } + + function confirmSwitch() { + if (root.pendingSwitchMode && root.applet) { + root.applet.switchGpu(root.pendingSwitchMode) + } + cancelSwitch() + } + + function cancelSwitch() { + root.pendingSwitchMode = "" + switchConfirmTimer.stop() + } + // 所有 GPU 中的最高温度,用于任务栏图标变色 readonly property int maxTemp: { if (!applet || !applet.gpuStats) return -1 @@ -45,9 +94,9 @@ AppletItem { } property Palette basePalette: DockPalette.iconTextPalette - readonly property color primaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.9) - readonly property color secondaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.65) - readonly property color tertiaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.5) + 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.8) + 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.1) readonly property color accentBlue: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 1) @@ -92,6 +141,19 @@ AppletItem { } } + // 切换确认倒计时 + Timer { + id: switchConfirmTimer + interval: 1000 + repeat: true + onTriggered: { + root.switchCountdown-- + if (root.switchCountdown <= 0) { + root.cancelSwitch() + } + } + } + Timer { id: toolTipShowTimer interval: 50 @@ -522,7 +584,7 @@ AppletItem { } Rectangle { - Layout.preferredWidth: 140 + Layout.preferredWidth: 180 height: 4 color: root.cardBorder radius: 2 @@ -667,12 +729,12 @@ AppletItem { // 1. 绘制网格线和刻度 ctx.font = "9px sans-serif" - ctx.fillStyle = Qt.rgba(1, 1, 1, 0.25) + ctx.fillStyle = Qt.rgba(1, 1, 1, 0.5) var temps = [40, 60, 80] for (var ti = 0; ti < temps.length; ti++) { var t = temps[ti] var y = h - ((t - minT) / (maxT - minT)) * (h - pad * 2) - pad - ctx.strokeStyle = Qt.rgba(1, 1, 1, 0.06) + ctx.strokeStyle = Qt.rgba(1, 1, 1, 0.1) ctx.lineWidth = 1 ctx.setLineDash(t === 80 ? [3, 3] : []) ctx.beginPath() @@ -806,67 +868,204 @@ AppletItem { spacing: 8 visible: root.gpuMode === "PRIME" + // 核显按钮 Button { Layout.fillWidth: true - Layout.preferredHeight: 30 + Layout.preferredHeight: 32 font.pixelSize: 11 - text: qsTr("NVIDIA") + text: getIgpuVendorName() background: Rectangle { - color: parent.hovered ? root.accentBlueLight : root.cardBackground + color: root.currentPrimeMode === "intel" ? root.accentBlue : (parent.hovered ? root.accentBlueLight : root.cardBackground) radius: 6 border.width: 1 - border.color: root.cardBorder + border.color: root.currentPrimeMode === "intel" ? root.accentBlue : root.cardBorder } contentItem: Text { text: parent.text - color: root.accentBlue + color: root.currentPrimeMode === "intel" ? "white" : root.accentBlue font.pixelSize: 11 + font.bold: root.currentPrimeMode === "intel" horizontalAlignment: Text.AlignHCenter } - onClicked: if (root.applet) root.applet.switchGpu("nvidia") + onClicked: root.requestSwitch("intel") } + // 按需按钮 Button { Layout.fillWidth: true - Layout.preferredHeight: 30 - font.pixelSize: 11 - text: qsTr("Intel") - - background: Rectangle { - color: parent.hovered ? root.accentBlueLight : root.cardBackground - radius: 6 - border.width: 1 - border.color: root.cardBorder - } - contentItem: Text { - text: parent.text - color: root.accentBlue - font.pixelSize: 11 - horizontalAlignment: Text.AlignHCenter - } - onClicked: if (root.applet) root.applet.switchGpu("intel") - } - - Button { - Layout.fillWidth: true - Layout.preferredHeight: 30 + Layout.preferredHeight: 32 font.pixelSize: 11 text: qsTr("On-Demand") background: Rectangle { - color: parent.hovered ? root.accentBlueLight : root.cardBackground + color: root.currentPrimeMode === "on-demand" ? root.accentBlue : (parent.hovered ? root.accentBlueLight : root.cardBackground) radius: 6 border.width: 1 - border.color: root.cardBorder + border.color: root.currentPrimeMode === "on-demand" ? root.accentBlue : root.cardBorder } contentItem: Text { text: parent.text - color: root.accentBlue + color: root.currentPrimeMode === "on-demand" ? "white" : root.accentBlue font.pixelSize: 11 + font.bold: root.currentPrimeMode === "on-demand" horizontalAlignment: Text.AlignHCenter } - onClicked: if (root.applet) root.applet.switchGpu("on-demand") + onClicked: root.requestSwitch("on-demand") + } + + // NVIDIA 按钮 + Button { + Layout.fillWidth: true + Layout.preferredHeight: 32 + font.pixelSize: 11 + text: qsTr("NVIDIA") + + background: Rectangle { + color: root.currentPrimeMode === "nvidia" ? root.accentBlue : (parent.hovered ? root.accentBlueLight : root.cardBackground) + radius: 6 + border.width: 1 + border.color: root.currentPrimeMode === "nvidia" ? root.accentBlue : root.cardBorder + } + contentItem: Text { + text: parent.text + color: root.currentPrimeMode === "nvidia" ? "white" : root.accentBlue + font.pixelSize: 11 + font.bold: root.currentPrimeMode === "nvidia" + horizontalAlignment: Text.AlignHCenter + } + onClicked: root.requestSwitch("nvidia") + } + } + } + } + + // GPU 切换确认覆盖层 + Rectangle { + id: switchOverlay + anchors.fill: parent + color: Qt.rgba(0, 0, 0, 0.7) + radius: 12 + visible: root.pendingSwitchMode.length > 0 + z: 100 + + ColumnLayout { + anchors.centerIn: parent + width: parent.width - 48 + spacing: 16 + + // 模式标题 + Text { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Switch to") + " " + switchModeLabel(root.pendingSwitchMode) + font.pixelSize: 16 + font.bold: true + color: "#e8e8e8" + } + + // 模式描述 + Text { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + text: switchModeDesc(root.pendingSwitchMode) + font.pixelSize: 12 + color: "#a0a0a0" + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + // 倒计时圆环 + Item { + Layout.alignment: Qt.AlignHCenter + width: 64 + height: 64 + + // 背景圆环 + Rectangle { + anchors.fill: parent + radius: 32 + color: "transparent" + border.width: 3 + border.color: Qt.rgba(1, 1, 1, 0.1) + } + + // 进度圆环 + Rectangle { + anchors.fill: parent + radius: 32 + color: "transparent" + border.width: 3 + border.color: root.accentBlue + // 用 ConicGradient 效果模拟进度(简化用 opacity) + opacity: root.switchCountdown / 15 + + Behavior on opacity { + NumberAnimation { duration: 1000 } + } + } + + // 倒计时数字 + Text { + anchors.centerIn: parent + text: root.switchCountdown + font.pixelSize: 24 + font.bold: true + color: "#e8e8e8" + } + } + + // 提示文字 + Text { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Auto-cancel if not confirmed") + font.pixelSize: 10 + color: "#666666" + } + + // 按钮行 + RowLayout { + Layout.fillWidth: true + spacing: 12 + + // 取消按钮 + Button { + Layout.fillWidth: true + Layout.preferredHeight: 34 + font.pixelSize: 12 + + background: Rectangle { + color: parent.hovered ? Qt.rgba(1, 1, 1, 0.1) : Qt.rgba(1, 1, 1, 0.05) + radius: 8 + border.width: 1 + border.color: Qt.rgba(1, 1, 1, 0.15) + } + contentItem: Text { + text: qsTr("Cancel") + color: "#cccccc" + font.pixelSize: 12 + horizontalAlignment: Text.AlignHCenter + } + onClicked: root.cancelSwitch() + } + + // 确认按钮 + Button { + Layout.fillWidth: true + Layout.preferredHeight: 34 + font.pixelSize: 12 + + background: Rectangle { + color: parent.hovered ? Qt.darker(root.accentBlue, 1.2) : root.accentBlue + radius: 8 + } + contentItem: Text { + text: qsTr("Confirm") + color: "white" + font.pixelSize: 12 + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + onClicked: root.confirmSwitch() } } } @@ -928,6 +1127,18 @@ AppletItem { return "GPU" } + // 获取核显厂商简称 + function getIgpuVendorName() { + if (!root.ready) return qsTr("Integrated") + var lines = root.deviceInfo.split("\n").filter(function(l) { return l.trim().length > 0 }) + for (var i = 0; i < lines.length; i++) { + if (!/nvidia/i.test(lines[i])) { + return parseGpuVendorShort(lines[i]) + } + } + return qsTr("Integrated") + } + // 构建 tooltip 文本 function buildToolTipText() { if (!root.applet || !root.applet.gpuStats) { diff --git a/translations/org.deepin.ds.graphics-driver_zh_CN.ts b/translations/org.deepin.ds.graphics-driver_zh_CN.ts index 49cd5e9..ec2460e 100644 --- a/translations/org.deepin.ds.graphics-driver_zh_CN.ts +++ b/translations/org.deepin.ds.graphics-driver_zh_CN.ts @@ -153,5 +153,45 @@ Temperature Trend 温度趋势 + + + Switch to + 切换到 + + + + Switch to NVIDIA discrete GPU for maximum performance. Higher power consumption. + 切换到 NVIDIA 独显以获得最高性能,功耗较高。 + + + + Switch to integrated GPU for power saving. NVIDIA GPU will be disabled. + 切换到核显以节省功耗,NVIDIA 独显将被禁用。 + + + + Hybrid mode: integrated GPU for display, NVIDIA for on-demand rendering. + 混合模式:核显负责显示,NVIDIA 按需渲染。 + + + + Auto-cancel if not confirmed + 未确认将自动取消 + + + + Cancel + 取消 + + + + Confirm + 确认 + + + + Integrated + 核显 + diff --git a/translations/org.deepin.ds.graphics-driver_zh_TW.ts b/translations/org.deepin.ds.graphics-driver_zh_TW.ts index 0c16911..b67bb6a 100644 --- a/translations/org.deepin.ds.graphics-driver_zh_TW.ts +++ b/translations/org.deepin.ds.graphics-driver_zh_TW.ts @@ -153,5 +153,45 @@ Temperature Trend 溫度趨勢 + + + Switch to + 切換到 + + + + Switch to NVIDIA discrete GPU for maximum performance. Higher power consumption. + 切換到 NVIDIA 獨顯以獲得最高性能,功耗較高。 + + + + Switch to integrated GPU for power saving. NVIDIA GPU will be disabled. + 切換到核顯以節省功耗,NVIDIA 獨顯將被禁用。 + + + + Hybrid mode: integrated GPU for display, NVIDIA for on-demand rendering. + 混合模式:核顯負責顯示,NVIDIA 按需渲染。 + + + + Auto-cancel if not confirmed + 未確認將自動取消 + + + + Cancel + 取消 + + + + Confirm + 確認 + + + + Integrated + 核顯 +