// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import org.deepin.ds 1.0 import org.deepin.ds.dock 1.0 import org.deepin.dtk 1.0 AppletItem { id: root objectName: "graphics driver applet" property int dockOrder: 21 property int dockSize: Panel.rootObject.dockItemMaxSize || 48 implicitWidth: dockSize implicitHeight: dockSize readonly property var applet: Applet readonly property bool ready: applet ? applet.ready : false readonly property string gpuSummary: applet ? (applet.gpuSummary || qsTr("No GPU detected")) : qsTr("No GPU detected") readonly property string deviceInfo: applet ? (applet.deviceInfo || qsTr("Unknown")) : qsTr("Unknown") readonly property string currentDriver: applet ? (applet.currentDriver || qsTr("Unknown")) : qsTr("Unknown") 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 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) readonly property color accentBlueLight: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 0.12) // 图标区域 Rectangle { anchors.centerIn: parent width: dockSize * 0.7 height: dockSize * 0.7 color: "transparent" radius: width * 0.45 border.width: 1 border.color: root.secondaryText Text { anchors.centerIn: parent text: "GPU" font.pixelSize: root.dockSize * 0.22 font.bold: true color: root.primaryText } } // 悬停提示 PanelToolTip { id: toolTip text: root.ready ? root.deviceInfo.replace(/\n/g, "\n") : qsTr("No GPU detected") toolTipX: DockPanelPositioner.x toolTipY: DockPanelPositioner.y } Timer { id: toolTipShowTimer interval: 50 onTriggered: { const point = root.mapToItem(null, root.width / 2, root.height / 2) toolTip.DockPanelPositioner.bounding = Qt.rect(point.x, point.y, toolTip.width, toolTip.height) toolTip.open() } } HoverHandler { onHoveredChanged: { if (hovered && !driverPopup.popupVisible) { toolTipShowTimer.start() } else { if (toolTipShowTimer.running) { toolTipShowTimer.stop() } toolTip.close() } } } // 弹出窗口 PanelPopup { id: driverPopup width: 360 height: 320 popupX: DockPanelPositioner.x popupY: DockPanelPositioner.y onPopupVisibleChanged: { if (popupVisible) { toolTip.close() } } Control { id: popupContainer anchors.fill: parent padding: 16 contentItem: ColumnLayout { spacing: 14 // 标题区域 RowLayout { Layout.fillWidth: true spacing: 10 Item { Layout.fillWidth: true } Rectangle { width: 28 height: 28 color: accentBlueLight radius: 8 Text { anchors.centerIn: parent text: "GPU" font.pixelSize: 12 font.bold: true color: accentBlue } } Text { text: qsTr("Graphics Driver Manager") font.pixelSize: 16 font.bold: true color: root.primaryText } Item { Layout.fillWidth: true } } // 分隔线 Rectangle { Layout.fillWidth: true Layout.preferredHeight: 1 color: root.cardBorder } // 当前驱动信息 RowLayout { Layout.fillWidth: true spacing: 8 visible: root.ready Text { text: qsTr("Current Driver:") font.pixelSize: 12 color: root.secondaryText } Text { text: root.currentDriver font.pixelSize: 12 font.bold: true color: root.primaryText Layout.fillWidth: true elide: Text.ElideRight } } // GPU 列表区域 Flickable { id: gpuListFlick Layout.fillWidth: true Layout.fillHeight: true clip: true contentWidth: width contentHeight: gpuColumn.height boundsBehavior: Flickable.StopAtBounds Column { id: gpuColumn width: gpuListFlick.width spacing: 10 visible: root.ready Repeater { model: root.ready ? root.deviceInfo.split("\n").filter(function(line) { return line.trim().length > 0 }) : [] delegate: Rectangle { id: gpuCard width: gpuColumn.width height: 72 color: root.cardBackground radius: 10 border.width: 1 border.color: root.cardBorder ToolTip.text: qsTr("GPU: ") + parseGpuName(modelData) + "\n" + qsTr("Driver: ") + parseDriverName(modelData) + "\n" + qsTr("Version: ") + (parseDriverVersion(modelData) || qsTr("Unknown")) ToolTip.visible: cardHover.hovered ToolTip.delay: 300 HoverHandler { id: cardHover } RowLayout { anchors.fill: parent anchors.margins: 12 spacing: 12 // GPU 图标 Rectangle { width: 44 height: 44 color: root.accentBlueLight radius: 8 Text { anchors.centerIn: parent text: parseGpuVendorShort(modelData) font.pixelSize: 14 font.bold: true color: root.accentBlue } } // GPU 信息 ColumnLayout { spacing: 4 Layout.fillWidth: true Text { text: parseGpuName(modelData) font.pixelSize: 13 font.bold: true color: root.primaryText Layout.fillWidth: true elide: Text.ElideRight } Text { text: parseDriverInfo(modelData) font.pixelSize: 11 color: root.secondaryText Layout.fillWidth: true elide: Text.ElideRight } } } } } } // 未检测到 GPU 提示 Rectangle { anchors.centerIn: parent width: gpuListFlick.width - 32 height: 80 color: root.cardBackground radius: 10 border.width: 1 border.color: root.cardBorder visible: !root.ready ColumnLayout { anchors.centerIn: parent spacing: 6 Text { text: "⚠" font.pixelSize: 24 } Text { text: qsTr("No GPU detected") font.pixelSize: 13 color: root.secondaryText } } } } // 刷新按钮 Button { id: refreshBtn text: qsTr("Refresh") Layout.fillWidth: true Layout.preferredHeight: 36 font.pixelSize: 13 background: Rectangle { color: refreshBtn.hovered ? Qt.darker(root.accentBlue, 1.2) : root.accentBlue radius: 8 } contentItem: Text { text: refreshBtn.text color: "white" font.pixelSize: refreshBtn.font.pixelSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } onClicked: { if (root.applet) { root.applet.refreshDeviceInfo() } } } } } Component.onCompleted: { DockPanelPositioner.bounding = Qt.binding(function () { const point = root.mapToItem(null, root.width / 2, root.height / 2) return Qt.rect(point.x, point.y, driverPopup.width, driverPopup.height) }) } } // 解析 GPU 名称的辅助函数 function parseGpuName(info) { var parts = info.split("(") if (parts.length > 0) { return parts[0].trim() } return info } // 解析驱动信息的辅助函数 function parseDriverInfo(info) { var startIndex = info.indexOf("(") var endIndex = info.lastIndexOf(")") if (startIndex !== -1 && endIndex !== -1) { return info.substring(startIndex + 1, endIndex) } return info } // 解析驱动名称 function parseDriverName(info) { var driver = parseDriverInfo(info) var parts = driver.split(" ") return parts[0] || qsTr("Unknown") } // 解析驱动版本号 function parseDriverVersion(info) { var driver = parseDriverInfo(info) var parts = driver.split(" ") if (parts.length > 1) { return parts[1] } return "" } // 识别 GPU 厂商简称 function parseGpuVendorShort(info) { var name = info.toLowerCase() if (name.indexOf("nvidia") !== -1) return "NV" if (name.indexOf("amd") !== -1 || name.indexOf("ati") !== -1 || name.indexOf("advanced micro") !== -1) return "AMD" if (name.indexOf("intel") !== -1) return "Intel" if (name.indexOf("qualcomm") !== -1) return "QC" if (name.indexOf("microsoft") !== -1) return "MS" return "GPU" } // 点击处理 TapHandler { acceptedButtons: Qt.LeftButton gesturePolicy: TapHandler.ReleaseWithinBounds onTapped: { if (driverPopup.popupVisible) { driverPopup.close() } else { Panel.requestClosePopup() const point = root.mapToItem(null, root.width / 2, root.height / 2) driverPopup.DockPanelPositioner.bounding = Qt.rect(point.x, point.y, driverPopup.width, driverPopup.height) driverPopup.open() } toolTip.close() } } }