import QtQuick import Quickshell import "." as M M.BarSection { id: root spacing: Math.max(1, M.Theme.moduleSpacing - 2) tooltip: "" readonly property int _warm: M.Modules.temperature.warm || 80 readonly property int _hot: M.Modules.temperature.hot || 90 readonly property int _temp: M.SystemStats.tempCelsius property color _stateColor: _temp > _hot ? M.Theme.base08 : _temp > _warm ? M.Theme.base0A : root.accentColor Behavior on _stateColor { ColorAnimation { duration: 300 } } property bool _pinned: false readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered readonly property bool _showPanel: _anyHover || _pinned on_AnyHoverChanged: { if (_anyHover) _unpinTimer.stop(); else if (_pinned) _unpinTimer.start(); } Timer { id: _unpinTimer interval: 500 onTriggered: root._pinned = false } // Returns a color interpolated green→yellow→red for a given celsius value function _tempColor(celsius) { const t = Math.max(0, Math.min(100, celsius)) / 100; const a = t < 0.5 ? M.Theme.base0B : M.Theme.base0A; const b = t < 0.5 ? M.Theme.base0A : M.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); } M.BarIcon { icon: "\uF2C9" color: root._stateColor anchors.verticalCenter: parent.verticalCenter TapHandler { cursorShape: Qt.PointingHandCursor onTapped: root._pinned = !root._pinned } } M.BarLabel { label: root._temp + "\u00B0C" minText: "100\u00B0C" color: root._stateColor anchors.verticalCenter: parent.verticalCenter TapHandler { cursorShape: Qt.PointingHandCursor onTapped: root._pinned = !root._pinned } } M.HoverPanel { id: hoverPanel showPanel: root._showPanel screen: QsWindow.window?.screen ?? null anchorItem: root accentColor: root.accentColor panelNamespace: "nova-temperature" panelTitle: "Temperature" contentWidth: 220 // Header — current temp Item { width: parent.width height: 28 Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root._temp + "\u00B0C" color: root._stateColor font.pixelSize: M.Theme.fontSize font.family: M.Theme.fontFamily font.bold: true width: _tempSizer.implicitWidth horizontalAlignment: Text.AlignRight Text { id: _tempSizer visible: false text: "100\u00B0C" font: parent.font } } } // Gauge bar (0–100°C), with warm/hot threshold markers Item { width: parent.width height: 16 Item { id: _gaugeBar 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: M.Theme.base02 radius: 3 } Rectangle { width: parent.width * Math.min(1, root._temp / 100) height: parent.height color: root._stateColor radius: 3 Behavior on width { enabled: root._showPanel NumberAnimation { duration: 300 easing.type: Easing.OutCubic } } } // Warm threshold marker Rectangle { x: parent.width * (root._warm / 100) - 1 width: 1 height: parent.height + 4 anchors.verticalCenter: parent.verticalCenter color: M.Theme.base0A opacity: 0.6 } // Hot threshold marker Rectangle { x: parent.width * (root._hot / 100) - 1 width: 1 height: parent.height + 4 anchors.verticalCenter: parent.verticalCenter color: M.Theme.base08 opacity: 0.6 } } } // History sparkline (~10 min @ 4s per sample) Canvas { id: _sparkline anchors.left: parent.left anchors.leftMargin: 12 anchors.right: parent.right anchors.rightMargin: 12 height: 40 property var _hist: M.SystemStats.tempHistory property color _col: root._stateColor on_HistChanged: if (root._showPanel) requestPaint() on_ColChanged: 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 = 150; const bw = width / maxSamples; // Background tint ctx.fillStyle = Qt.rgba(_col.r, _col.g, _col.b, 0.08).toString(); ctx.fillRect(0, 0, width, height); // Warm threshold line const warmY = height - height * (root._warm / 100); ctx.strokeStyle = M.Theme.base0A.toString(); ctx.globalAlpha = 0.3; ctx.lineWidth = 1; ctx.setLineDash([3, 3]); ctx.beginPath(); ctx.moveTo(0, warmY); ctx.lineTo(width, warmY); ctx.stroke(); // Hot threshold line const hotY = height - height * (root._hot / 100); ctx.strokeStyle = M.Theme.base08.toString(); ctx.beginPath(); ctx.moveTo(0, hotY); ctx.lineTo(width, hotY); ctx.stroke(); ctx.setLineDash([]); ctx.globalAlpha = 1.0; // Bars const offset = maxSamples - d.length; for (let i = 0; i < d.length; i++) { const barH = Math.max(1, height * d[i] / 100); const barColor = d[i] > root._hot ? M.Theme.base08 : d[i] > root._warm ? M.Theme.base0A : _col; ctx.fillStyle = barColor.toString(); ctx.fillRect((offset + i) * bw, height - barH, Math.max(1, bw - 0.5), barH); } } } // Threshold labels Item { width: parent.width height: 16 Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "warm " + root._warm + "\u00B0 hot " + root._hot + "\u00B0" color: M.Theme.base03 font.pixelSize: M.Theme.fontSize - 3 font.family: M.Theme.fontFamily font.letterSpacing: 0.5 } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "10 min" color: M.Theme.base03 font.pixelSize: M.Theme.fontSize - 3 font.family: M.Theme.fontFamily } } Item { width: 1 height: 4 } } }