diff --git a/shell/applets/GpuApplet.qml b/shell/applets/GpuApplet.qml new file mode 100644 index 0000000..7b2272f --- /dev/null +++ b/shell/applets/GpuApplet.qml @@ -0,0 +1,223 @@ +import QtQuick +import "../services" as S + +Column { + id: root + + required property color accentColor + + property bool active: true + + function _loadColor(pct) { + const t = Math.max(0, Math.min(100, pct)) / 100; + const a = t < 0.5 ? S.Theme.base0B : S.Theme.base0A; + const b = t < 0.5 ? S.Theme.base0A : S.Theme.base08; + const u = t < 0.5 ? t * 2 : (t - 0.5) * 2; + return Qt.rgba(a.r + (b.r - a.r) * u, a.g + (b.g - a.g) * u, a.b + (b.b - a.b) * u, 1); + } + + function _fmt(gb) { + return gb >= 10 ? gb.toFixed(1) + "G" : gb.toFixed(2) + "G"; + } + + // Header - vendor + usage% + Item { + width: parent.width + height: 28 + + Text { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: S.SystemStats.gpuVendor.toUpperCase() + color: S.Theme.base03 + font.pixelSize: S.Theme.fontSize - 3 + font.family: S.Theme.fontFamily + font.letterSpacing: 1 + } + + Text { + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: S.SystemStats.gpuUsage + "%" + color: root._loadColor(S.SystemStats.gpuUsage) + font.pixelSize: S.Theme.fontSize + font.family: S.Theme.fontFamily + font.bold: true + } + } + + // Usage bar + Item { + width: parent.width + height: 14 + + Item { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + height: 6 + + Rectangle { + anchors.fill: parent + color: S.Theme.base02 + radius: 3 + } + + Rectangle { + width: parent.width * Math.min(1, S.SystemStats.gpuUsage / 100) + height: parent.height + color: root._loadColor(S.SystemStats.gpuUsage) + radius: 3 + Behavior on width { + enabled: root.active + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + } + } + + // Usage history sparkline + Canvas { + id: _sparkline + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.right: parent.right + anchors.rightMargin: 12 + height: 36 + + property var _hist: S.SystemStats.gpuHistory + + on_HistChanged: if (root.active) + requestPaint() + + onVisibleChanged: if (visible) + requestPaint() + + onPaint: { + const ctx = getContext("2d"); + if (!ctx) + return; + ctx.clearRect(0, 0, width, height); + const d = _hist; + if (!d.length) + return; + const maxSamples = 60; + const bw = width / maxSamples; + const offset = maxSamples - d.length; + for (let i = 0; i < d.length; i++) { + const barH = Math.max(1, height * d[i] / 100); + const col = root._loadColor(d[i]); + ctx.fillStyle = col.toString(); + ctx.fillRect((offset + i) * bw, height - barH, Math.max(1, bw - 0.5), barH); + } + } + } + + // VRAM section + Rectangle { + width: parent.width - 16 + height: 1 + anchors.horizontalCenter: parent.horizontalCenter + color: S.Theme.base03 + } + + Item { + width: parent.width + height: 22 + + Text { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: "VRAM" + color: S.Theme.base03 + font.pixelSize: S.Theme.fontSize - 3 + font.family: S.Theme.fontFamily + font.letterSpacing: 1 + } + + Text { + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: root._fmt(S.SystemStats.gpuVramUsedGb) + " / " + root._fmt(S.SystemStats.gpuVramTotalGb) + color: root.accentColor + font.pixelSize: S.Theme.fontSize - 1 + font.family: S.Theme.fontFamily + font.bold: true + } + } + + Item { + width: parent.width + height: 12 + + Item { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + height: 5 + + Rectangle { + anchors.fill: parent + color: S.Theme.base02 + radius: 2 + } + + Rectangle { + width: S.SystemStats.gpuVramTotalGb > 0 ? parent.width * Math.min(1, S.SystemStats.gpuVramUsedGb / S.SystemStats.gpuVramTotalGb) : 0 + height: parent.height + color: root.accentColor + radius: 2 + Behavior on width { + enabled: root.active + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + } + } + + // Temperature row + Item { + width: parent.width + height: 22 + visible: S.SystemStats.gpuTempC > 0 + + Text { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: "Temp" + color: S.Theme.base04 + font.pixelSize: S.Theme.fontSize - 2 + font.family: S.Theme.fontFamily + } + + Text { + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: S.SystemStats.gpuTempC + "\u00B0C" + color: S.SystemStats.gpuTempC > 85 ? S.Theme.base08 : S.SystemStats.gpuTempC > 70 ? S.Theme.base0A : S.Theme.base05 + font.pixelSize: S.Theme.fontSize - 2 + font.family: S.Theme.fontFamily + } + } + + Item { + width: 1 + height: 4 + } +} diff --git a/shell/applets/qmldir b/shell/applets/qmldir index 9e72eb6..89c7fa7 100644 --- a/shell/applets/qmldir +++ b/shell/applets/qmldir @@ -3,6 +3,7 @@ module applets BacklightApplet 1.0 BacklightApplet.qml CpuApplet 1.0 CpuApplet.qml DiskApplet 1.0 DiskApplet.qml +GpuApplet 1.0 GpuApplet.qml HexWaveBackground 1.0 HexWaveBackground.qml MemoryApplet 1.0 MemoryApplet.qml MprisApplet 1.0 MprisApplet.qml diff --git a/shell/modules/GpuModule.qml b/shell/modules/GpuModule.qml index 8b471dd..106463b 100644 --- a/shell/modules/GpuModule.qml +++ b/shell/modules/GpuModule.qml @@ -2,6 +2,7 @@ import QtQuick import Quickshell import "." as M import "../services" as S +import "../applets" as C M.BarSection { id: root @@ -26,18 +27,6 @@ M.BarSection { onTriggered: root._pinned = false } - function _loadColor(pct) { - const t = Math.max(0, Math.min(100, pct)) / 100; - const a = t < 0.5 ? S.Theme.base0B : S.Theme.base0A; - const b = t < 0.5 ? S.Theme.base0A : S.Theme.base08; - const u = t < 0.5 ? t * 2 : (t - 0.5) * 2; - return Qt.rgba(a.r + (b.r - a.r) * u, a.g + (b.g - a.g) * u, a.b + (b.b - a.b) * u, 1); - } - - function _fmt(gb) { - return gb >= 10 ? gb.toFixed(1) + "G" : gb.toFixed(2) + "G"; - } - M.BarIcon { icon: "\uEB4C" anchors.verticalCenter: parent.verticalCenter @@ -64,210 +53,10 @@ M.BarSection { panelTitle: "GPU" contentWidth: 240 - // Header — vendor + usage% - Item { - width: parent.width - height: 28 - - Text { - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: S.SystemStats.gpuVendor.toUpperCase() - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily - font.letterSpacing: 1 - } - - Text { - anchors.right: parent.right - anchors.rightMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: S.SystemStats.gpuUsage + "%" - color: root._loadColor(S.SystemStats.gpuUsage) - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily - font.bold: true - } - } - - // Usage bar - Item { - width: parent.width - height: 14 - - Item { - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - anchors.verticalCenter: parent.verticalCenter - height: 6 - - Rectangle { - anchors.fill: parent - color: S.Theme.base02 - radius: 3 - } - - Rectangle { - width: parent.width * Math.min(1, S.SystemStats.gpuUsage / 100) - height: parent.height - color: root._loadColor(S.SystemStats.gpuUsage) - radius: 3 - Behavior on width { - enabled: root._showPanel - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - } - } - - // Usage history sparkline - Canvas { - id: _sparkline - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - height: 36 - - property var _hist: S.SystemStats.gpuHistory - - on_HistChanged: if (root._showPanel) - requestPaint() - - Connections { - target: root - function on_ShowPanelChanged() { - if (root._showPanel) - _sparkline.requestPaint(); - } - } - - onPaint: { - const ctx = getContext("2d"); - if (!ctx) - return; - ctx.clearRect(0, 0, width, height); - const d = _hist; - if (!d.length) - return; - const maxSamples = 60; - const bw = width / maxSamples; - const offset = maxSamples - d.length; - for (let i = 0; i < d.length; i++) { - const barH = Math.max(1, height * d[i] / 100); - const col = root._loadColor(d[i]); - ctx.fillStyle = col.toString(); - ctx.fillRect((offset + i) * bw, height - barH, Math.max(1, bw - 0.5), barH); - } - } - } - - // VRAM section - Rectangle { - width: parent.width - 16 - height: 1 - anchors.horizontalCenter: parent.horizontalCenter - color: S.Theme.base03 - } - - Item { - width: parent.width - height: 22 - - Text { - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: "VRAM" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily - font.letterSpacing: 1 - } - - Text { - anchors.right: parent.right - anchors.rightMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: root._fmt(S.SystemStats.gpuVramUsedGb) + " / " + root._fmt(S.SystemStats.gpuVramTotalGb) - color: root.accentColor - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily - font.bold: true - } - } - - Item { - width: parent.width - height: 12 - - Item { - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - anchors.verticalCenter: parent.verticalCenter - height: 5 - - Rectangle { - anchors.fill: parent - color: S.Theme.base02 - radius: 2 - } - - Rectangle { - width: S.SystemStats.gpuVramTotalGb > 0 ? parent.width * Math.min(1, S.SystemStats.gpuVramUsedGb / S.SystemStats.gpuVramTotalGb) : 0 - height: parent.height - color: root.accentColor - radius: 2 - Behavior on width { - enabled: root._showPanel - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - } - } - - // Temperature row - Item { - width: parent.width - height: 22 - visible: S.SystemStats.gpuTempC > 0 - - Text { - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: "Temp" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily - } - - Text { - anchors.right: parent.right - anchors.rightMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: S.SystemStats.gpuTempC + "\u00B0C" - color: S.SystemStats.gpuTempC > 85 ? S.Theme.base08 : S.SystemStats.gpuTempC > 70 ? S.Theme.base0A : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily - } - } - - Item { - width: 1 - height: 4 + C.GpuApplet { + width: hoverPanel.contentWidth + active: root._showPanel + accentColor: root.accentColor } } }