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
+ 核顯
+