feat(wafer-map): add threshold legend overlay and scrollbar polish

- Add color legend (Low/In-range/High + Auto/Manual mode)
  as a floating overlay in WaferMapView
- Expose thresholdAuto on SessionController/SessionModel
  to drive the legend's Auto · / Manual · label
- Style ReadoutPanel scrollbar (always-visible, 8px thumb)
  and reserve 14px gap so cards don't overlap it
This commit is contained in:
jack
2026-07-11 21:57:57 -07:00
parent f9c6639a66
commit 35cee98579
6 changed files with 216 additions and 120 deletions
+119 -116
View File
@@ -5,10 +5,13 @@ import QtQuick.Layouts
import ISC
// ===== Readout Panel =====
// Right-rail panel with three bento-tile sections: READOUT, DISPLAY,
// THRESHOLDS. Uses the same PanelBox / SectionTitle / ReadoutStat /
// PanelCheckBox / PanelSlider components as the Data tab's
// ComparisonSidePanel, so both right rails share one visual language.
// Right-rail panel with three bento-tile sections: THRESHOLDS, DISPLAY,
// READOUT. Thresholds comes first so Set Point/Margin/Auto are visible
// without scrolling on first use. The whole panel scrolls as one column
// (see WaferMapTab.qml) when content outgrows the available height.
// Uses the same PanelBox / SectionTitle / ReadoutStat / PanelCheckBox /
// PanelSlider components as the Data tab's ComparisonSidePanel, so both
// right rails share one visual language.
ColumnLayout {
id: root
spacing: 6
@@ -47,63 +50,85 @@ ColumnLayout {
}
}
// ── READOUT ─────────────────────────────────────────────────────
// ── THRESHOLDS ──────────────────────────────────────────────────
PanelBox {
Layout.fillWidth: true
Layout.preferredHeight: readoutCol.implicitHeight + 24
Layout.preferredHeight: thresholdCard.implicitHeight + 24
ColumnLayout {
id: readoutCol
id: thresholdCard
anchors.fill: parent
anchors.margins: 12
spacing: 8
spacing: 6
SectionTitle { text: "READOUT" }
SectionTitle { text: "THRESHOLDS" }
ReadoutStat {
label: "Min Temp"
value: s.min !== undefined
? s.min + ((s.minIndex !== undefined && s.minIndex >= 0) ? " #" + s.minIndex : "")
: "—"
valueColor: Theme.sensorLow
Label {
text: "Set Point (°C)"
color: Theme.bodyColor
font.pixelSize: Theme.fontSm
}
ReadoutStat {
label: "Max Temp"
value: s.max !== undefined
? s.max + ((s.maxIndex !== undefined && s.maxIndex >= 0) ? " #" + s.maxIndex : "")
: "—"
valueColor: Theme.sensorHigh
TextField {
id: spField
Layout.fillWidth: true
text: "149.0"
font.pixelSize: Theme.fontSm
inputMethodHints: Qt.ImhFormattedNumbersOnly
color: Theme.fieldText
placeholderTextColor: Theme.fieldPlaceholder
selectedTextColor: Theme.fieldBackground
selectionColor: Theme.fieldText
background: Rectangle {
radius: Theme.radiusXs
color: Theme.fieldBackground
border.width: spField.activeFocus ? Theme.borderStrong : Theme.borderThin
border.color: spField.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder
Behavior on color {
ColorAnimation { duration: Theme.durationFast }
}
Behavior on border.color {
ColorAnimation { duration: Theme.durationFast }
}
}
onEditingFinished: pushThresholds()
}
ReadoutStat {
label: "Diff"
value: s.diff !== undefined ? s.diff : "—"
valueColor: Theme.diffAccent
Label {
text: "Margin (±°C)"
color: Theme.bodyColor
font.pixelSize: Theme.fontSm
}
ReadoutStat {
label: "Average"
value: s.avg !== undefined ? s.avg : "—"
TextField {
id: mgField
Layout.fillWidth: true
text: "1.0"
font.pixelSize: Theme.fontSm
inputMethodHints: Qt.ImhFormattedNumbersOnly
color: Theme.fieldText
placeholderTextColor: Theme.fieldPlaceholder
selectedTextColor: Theme.fieldBackground
selectionColor: Theme.fieldText
background: Rectangle {
radius: Theme.radiusXs
color: Theme.fieldBackground
border.width: mgField.activeFocus ? Theme.borderStrong : Theme.borderThin
border.color: mgField.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder
Behavior on color {
ColorAnimation { duration: Theme.durationFast }
}
Behavior on border.color {
ColorAnimation { duration: Theme.durationFast }
}
}
onEditingFinished: pushThresholds()
}
ReadoutStat {
label: "Sigma (Σ)"
value: s.sigma !== undefined ? s.sigma : "—"
}
ReadoutStat {
label: "3Σ Value"
value: s.threeSigma !== undefined ? s.threeSigma : "—"
}
ReadoutStat {
visible: s.ecMinDelta !== undefined
label: "E-C Δ Min"
value: s.ecMinDelta !== undefined
? s.ecMinDelta.toFixed(2) + " (#" + s.ecMinEdgeIndex + "→#" + s.ecMinCenterIndex + ")"
: "—"
}
ReadoutStat {
visible: s.ecMaxDelta !== undefined
label: "E-C Δ Max"
value: s.ecMaxDelta !== undefined
? s.ecMaxDelta.toFixed(2) + " (#" + s.ecMaxEdgeIndex + "→#" + s.ecMaxCenterIndex + ")"
: "—"
PanelCheckBox {
id: autoCheck
text: "Auto range (mean ± 1σ)"
checked: true
font.pixelSize: Theme.fontSm
onCheckedChanged: pushThresholds()
}
}
}
@@ -203,85 +228,63 @@ ColumnLayout {
}
}
// ── THRESHOLDS ──────────────────────────────────────────────────
// ── READOUT ─────────────────────────────────────────────────────
PanelBox {
Layout.fillWidth: true
Layout.preferredHeight: thresholdCard.implicitHeight + 24
Layout.preferredHeight: readoutCol.implicitHeight + 24
ColumnLayout {
id: thresholdCard
id: readoutCol
anchors.fill: parent
anchors.margins: 12
spacing: 6
spacing: 8
SectionTitle { text: "THRESHOLDS" }
SectionTitle { text: "READOUT" }
Label {
text: "Set Point (°C)"
color: Theme.bodyColor
font.pixelSize: Theme.fontSm
ReadoutStat {
label: "Min Temp"
value: s.min !== undefined
? s.min + ((s.minIndex !== undefined && s.minIndex >= 0) ? " #" + s.minIndex : "")
: "—"
valueColor: Theme.sensorLow
}
TextField {
id: spField
Layout.fillWidth: true
text: "149.0"
font.pixelSize: Theme.fontSm
inputMethodHints: Qt.ImhFormattedNumbersOnly
color: Theme.fieldText
placeholderTextColor: Theme.fieldPlaceholder
selectedTextColor: Theme.fieldBackground
selectionColor: Theme.fieldText
background: Rectangle {
radius: Theme.radiusXs
color: Theme.fieldBackground
border.width: spField.activeFocus ? Theme.borderStrong : Theme.borderThin
border.color: spField.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder
Behavior on color {
ColorAnimation { duration: Theme.durationFast }
}
Behavior on border.color {
ColorAnimation { duration: Theme.durationFast }
}
}
onEditingFinished: pushThresholds()
ReadoutStat {
label: "Max Temp"
value: s.max !== undefined
? s.max + ((s.maxIndex !== undefined && s.maxIndex >= 0) ? " #" + s.maxIndex : "")
: "—"
valueColor: Theme.sensorHigh
}
Label {
text: "Margin (±°C)"
color: Theme.bodyColor
font.pixelSize: Theme.fontSm
ReadoutStat {
label: "Diff"
value: s.diff !== undefined ? s.diff : "—"
valueColor: Theme.diffAccent
}
TextField {
id: mgField
Layout.fillWidth: true
text: "1.0"
font.pixelSize: Theme.fontSm
inputMethodHints: Qt.ImhFormattedNumbersOnly
color: Theme.fieldText
placeholderTextColor: Theme.fieldPlaceholder
selectedTextColor: Theme.fieldBackground
selectionColor: Theme.fieldText
background: Rectangle {
radius: Theme.radiusXs
color: Theme.fieldBackground
border.width: mgField.activeFocus ? Theme.borderStrong : Theme.borderThin
border.color: mgField.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder
Behavior on color {
ColorAnimation { duration: Theme.durationFast }
}
Behavior on border.color {
ColorAnimation { duration: Theme.durationFast }
}
}
onEditingFinished: pushThresholds()
ReadoutStat {
label: "Average"
value: s.avg !== undefined ? s.avg : "—"
}
PanelCheckBox {
id: autoCheck
text: "Auto range (mean ± 1σ)"
checked: true
font.pixelSize: Theme.fontSm
onCheckedChanged: pushThresholds()
ReadoutStat {
label: "Sigma (Σ)"
value: s.sigma !== undefined ? s.sigma : "—"
}
ReadoutStat {
label: "3Σ Value"
value: s.threeSigma !== undefined ? s.threeSigma : "—"
}
ReadoutStat {
visible: s.ecMinDelta !== undefined
label: "E-C Δ Min"
value: s.ecMinDelta !== undefined
? s.ecMinDelta.toFixed(2) + " (#" + s.ecMinEdgeIndex + "→#" + s.ecMinCenterIndex + ")"
: "—"
}
ReadoutStat {
visible: s.ecMaxDelta !== undefined
label: "E-C Δ Max"
value: s.ecMaxDelta !== undefined
? s.ecMaxDelta.toFixed(2) + " (#" + s.ecMaxEdgeIndex + "→#" + s.ecMaxCenterIndex + ")"
: "—"
}
}
}