nova-shell/modules/Temperature.qml

265 lines
8.3 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 (0100°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
}
}
}