import QtQuick import ".." as M Column { id: root required property int temp required property int warm required property int hot required property var history required property var devices required property color accentColor property bool active: true property string deviceFilter: "" property color stateColor: temp > hot ? M.Theme.base08 : temp > warm ? M.Theme.base0A : root.accentColor Behavior on stateColor { ColorAnimation { duration: 300 } } 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); } // Header - current temp Item { width: root.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-100C), with warm/hot threshold markers Item { width: root.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.active 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 Canvas { id: _sparkline anchors.left: parent.left anchors.leftMargin: 12 anchors.right: parent.right anchors.rightMargin: 12 height: 40 property var _hist: root.history property color _col: root.stateColor on_HistChanged: if (root.active) requestPaint() on_ColChanged: 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 = 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: root.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 } } // Per-device breakdown Rectangle { width: root.width - 16 height: 1 anchors.horizontalCenter: parent.horizontalCenter color: M.Theme.base03 visible: root.devices.length > 0 } Repeater { model: root.devices delegate: Item { required property var modelData width: root.width height: 22 readonly property bool _isActive: root.deviceFilter === modelData.name Rectangle { anchors.fill: parent anchors.leftMargin: 8 anchors.rightMargin: 8 color: _isActive ? Qt.rgba(root.accentColor.r, root.accentColor.g, root.accentColor.b, 0.12) : "transparent" radius: 3 } Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.name color: _isActive ? root.accentColor : M.Theme.base04 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily elide: Text.ElideRight width: parent.width - 80 } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.celsius + "\u00B0C" color: root._tempColor(modelData.celsius) font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily font.bold: _isActive } } } Item { width: 1 height: 4 } }