import QtQuick import QtQuick.Controls import QtQuick.Layouts import ISC import ISC.Wafer // ===== Run Chart ===== // Zoomable whole-run multi-sensor chart (C# parity: PopupChartForm.cs). // Lives in the Graph tab. Mouse wheel zooms around the cursor, drag pans; // the strip below shows the aggregate min/max/avg over the *visible* range // (not the whole run), matching PopupChartForm's recalc_stats. Cursor/ // playhead tracking is out of scope — see specs/plans/2026-07-10-popup-chart.md // and docs/adr/0004-graphquickitem-viewport-for-replay-chart.md. Item { id: root property alias sensorNames: chart.sensorNames property alias seriesData: chart.seriesData property alias title: chart.title // Same palette that colors the chart lines (GraphQuickItem.seriesColors, // theme-agnostic — see docs/adr/0004 for why); falls back to the default // text color when the sensor isn't found (e.g. a stale binding mid-swap). function sensorColor(name) { var idx = chart.sensorNames.indexOf(name) return (idx >= 0 && idx < chart.seriesColors.length) ? chart.seriesColors[idx] : Theme.headingColor } ColumnLayout { anchors.fill: parent spacing: 8 Item { Layout.fillWidth: true Layout.fillHeight: true GraphQuickItem { id: chart anchors.fill: parent showMinMaxMarkers: true xLabel: "Measurement Interval" yLabel: "Temperature (°C)" backgroundColor: Theme.cardBackground gridColor: Theme.toneBorder axisColor: Theme.toneMute textColor: Theme.headingColor } MouseArea { id: interaction anchors.fill: parent acceptedButtons: Qt.LeftButton property real lastX: 0 onWheel: function(wheel) { var frac = Math.max(0, Math.min(1, wheel.x / width)); var factor = wheel.angleDelta.y > 0 ? 0.8 : 1.25; chart.zoomAtFraction(frac, factor); wheel.accepted = true; } onPressed: function(mouse) { lastX = mouse.x; } onPositionChanged: function(mouse) { if (pressed && width > 0) { chart.panByFraction(-(mouse.x - lastX) / width); lastX = mouse.x; } } } Button { id: resetZoomBtn anchors.top: parent.top anchors.right: parent.right // Vertically centers on the chart title, which GraphQuickItem // paints in a y=4..28 band (see graph_quick_item.py paint()). anchors.topMargin: 2 anchors.rightMargin: 10 implicitHeight: 32 hoverEnabled: true text: "Reset Zoom" font.family: Theme.uiFontFamily font.pixelSize: Theme.fontSm font.weight: Font.Medium background: Rectangle { radius: Theme.radiusSm color: resetZoomBtn.pressed ? Theme.transportButtonHover : Theme.transportButtonBg border.color: Theme.sideBorder border.width: 1 opacity: 0.9 } contentItem: Text { text: resetZoomBtn.text color: Theme.headingColor font: resetZoomBtn.font horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } onClicked: chart.resetZoom() } } // ── Min/Max/Avg readout strip ─────────────────────────────────── RowLayout { Layout.fillWidth: true Layout.leftMargin: 8 spacing: 16 Label { text: "Min:" color: Theme.sensorLow font.family: Theme.uiFontFamily font.pixelSize: Theme.fontSm font.weight: Font.Medium } Row { Label { text: chart.viewMin.toFixed(2) color: Theme.headingColor font.family: Theme.codeFontFamily font.pixelSize: Theme.fontSm } Label { visible: !!chart.viewMinSensor text: chart.viewMinSensor ? " (" + chart.viewMinSensor + ")" : "" color: root.sensorColor(chart.viewMinSensor) font.family: Theme.codeFontFamily font.pixelSize: Theme.fontSm font.weight: Font.Medium } } Label { text: "Max:" color: Theme.sensorHigh font.family: Theme.uiFontFamily font.pixelSize: Theme.fontSm font.weight: Font.Medium } Row { Label { text: chart.viewMax.toFixed(2) color: Theme.headingColor font.family: Theme.codeFontFamily font.pixelSize: Theme.fontSm } Label { visible: !!chart.viewMaxSensor text: chart.viewMaxSensor ? " (" + chart.viewMaxSensor + ")" : "" color: root.sensorColor(chart.viewMaxSensor) font.family: Theme.codeFontFamily font.pixelSize: Theme.fontSm font.weight: Font.Medium } } Label { text: "Avg:" color: Theme.sideMutedText font.family: Theme.uiFontFamily font.pixelSize: Theme.fontSm font.weight: Font.Medium } Label { text: chart.viewAvg.toFixed(2) color: Theme.headingColor font.family: Theme.codeFontFamily font.pixelSize: Theme.fontSm } } } }