import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Io import "../../config" as Cfg Rectangle { id: root property real panelRadius: Cfg.Config.radius / 2 property int borderWidth: Cfg.Config.panelBorderWidth property int barHeight: Cfg.Config.barHeight property int barMargin: Cfg.Config.margin readonly property var t: Cfg.Config.theme width: statsLayout.implicitWidth + 20 height: Cfg.Config.moduleHeight color: t.statsBg radius: panelRadius border.color: t.statsBorder border.width: borderWidth Behavior on width { NumberAnimation { duration: 250; easing.type: Easing.OutCubic } } HoverHandler { cursorShape: Qt.PointingHandCursor } property real cpuVal: 0 property real memVal: 0 property real diskVal: 0 Timer { interval: 2000 running: true repeat: true triggeredOnStart: true onTriggered: { cpuProc.running = true memProc.running = true diskProc.running = true } } Process { id: cpuProc command: ["sh", "-c", "top -bn1 | awk '/^%Cpu/ {print 100-$8}'"] stdout: SplitParser { onRead: data => { let val = parseFloat(data.trim()) root.cpuVal = isNaN(val) ? 0 : val } } } Process { id: memProc command: ["sh", "-c", "free | awk '/Mem:/ {print ($2-$7)/$2 * 100}'"] stdout: SplitParser { onRead: data => { let val = parseFloat(data.trim()) root.memVal = isNaN(val) ? 0 : val } } } Process { id: diskProc command: ["sh", "-c", "df / --output=pcent | tail -1 | tr -dc '0-9'"] stdout: SplitParser { onRead: data => { let val = parseFloat(data.trim()) root.diskVal = isNaN(val) ? 0 : val } } } component StatRing: Item { id: ring property real value: 0.0 property string label: "" property color ringColor: Cfg.Config.theme.accent property color trackColor: Cfg.Config.theme.statsTrackColor property color labelColor: Cfg.Config.theme.textMain Behavior on value { NumberAnimation { duration: 800 easing.type: Easing.OutCubic } } readonly property real fraction: Math.max(0, Math.min(value, 100)) / 100 width: Cfg.Config.statsRingSize height: Cfg.Config.statsRingSize onFractionChanged: arc.requestPaint() onRingColorChanged: arc.requestPaint() Canvas { id: arc anchors.fill: parent antialiasing: true onPaint: { var ctx = getContext("2d") ctx.reset() ctx.clearRect(0, 0, width, height) var cx = width / 2 var cy = height / 2 var lw = 2.8 var r = Math.min(cx, cy) - lw / 2 - 0.5 ctx.beginPath() ctx.arc(cx, cy, r, 0, Math.PI * 2) ctx.strokeStyle = ring.trackColor ctx.lineWidth = lw ctx.stroke() if (ring.fraction > 0) { var start = -Math.PI / 2 var end = start + (ring.fraction * Math.PI * 2) ctx.beginPath() ctx.arc(cx, cy, r, start, end) ctx.strokeStyle = ring.ringColor ctx.lineWidth = lw ctx.lineCap = "round" ctx.stroke() } } } HoverHandler { id: hov } property bool showAlt: false onShowAltChanged: arc.requestPaint() Connections { target: hov function onHoveredChanged() { if (!hov.hovered) ring.showAlt = false } } TapHandler { enabled: !Cfg.Config.statsHideTextUntilHover || hov.hovered onTapped: ring.showAlt = !ring.showAlt } Text { anchors.centerIn: parent text: { if (Cfg.Config.statsHideTextUntilHover) { if (!hov.hovered) return "" var showPct = ring.showAlt ? !Cfg.Config.statsShowPercentByDefault : Cfg.Config.statsShowPercentByDefault return showPct ? Math.round(ring.value) + "%" : ring.label } else { var baseShowPct = Cfg.Config.statsShowPercentByDefault ? !hov.hovered : hov.hovered var finalShowPct = ring.showAlt ? !baseShowPct : baseShowPct return finalShowPct ? Math.round(ring.value) + "%" : ring.label } } color: ring.labelColor font.pixelSize: Cfg.Config.statsRingFontSize font.bold: true font.family: "monospace" opacity: (Cfg.Config.statsHideTextUntilHover && !hov.hovered) ? 0 : 1 Behavior on opacity { NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } } } RowLayout { id: statsLayout anchors.centerIn: parent spacing: 10 StatRing { value: root.cpuVal label: "cpu" ringColor: root.t.statsCpuColor trackColor: root.t.statsTrackColor labelColor: root.t.statsText } StatRing { value: root.memVal label: "ram" ringColor: root.t.statsMemColor trackColor: root.t.statsTrackColor labelColor: root.t.statsText } StatRing { value: root.diskVal label: "disk" ringColor: root.t.statsDiskColor trackColor: root.t.statsTrackColor labelColor: root.t.statsText } } }