feat(panels): redesign ReadoutPanel and SourcePanel to match mockup

ReadoutPanel:
- Move section headers inside card containers
- Redesign readout table with uppercase labels, code fonts, color mapping
- Add full-width dividers, fix vertical spacing with fillHeight spacer
- Unify typography with Theme tokens

SourcePanel:
- Bump font sizes to new standard (fontMd primary, fontSm secondary)
- Replace unicode button text with edit.svg/refresh.svg IconImages
- Add styled hover tooltips on edit and refresh buttons
This commit is contained in:
jack
2026-06-29 14:57:13 -07:00
parent 5d079174e0
commit c9cb7ab8d8
2 changed files with 498 additions and 247 deletions
+286 -79
View File
@@ -3,8 +3,11 @@ import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import ISC import ISC
// ===== Readout Panel =====
// Right-rail panel with three bordered sections: READOUT, DISPLAY, THRESHOLDS.
// Cards match the left-rail SOURCE / CONNECTION FLOW style.
ColumnLayout { ColumnLayout {
spacing: 0 spacing: 6
property var s: streamController.stats property var s: streamController.stats
property alias showLabels: labelsToggle.checked property alias showLabels: labelsToggle.checked
property alias heatmapBlend: heatmapSlider.value property alias heatmapBlend: heatmapSlider.value
@@ -21,13 +24,23 @@ ColumnLayout {
border.width: Theme.borderThin border.width: Theme.borderThin
border.color: toggle.checked ? Theme.primaryAccent : Theme.fieldBorder border.color: toggle.checked ? Theme.primaryAccent : Theme.fieldBorder
Behavior on color {
ColorAnimation { duration: Theme.durationFast }
}
Behavior on border.color {
ColorAnimation { duration: Theme.durationFast }
}
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "✓" text: "✓"
font.pixelSize: 13 font.pixelSize: Theme.fontSm
font.bold: true font.bold: true
color: Theme.panelBackground color: Theme.panelBackground
visible: toggle.checked opacity: toggle.checked ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation { duration: Theme.durationFast }
}
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
@@ -36,150 +49,290 @@ ColumnLayout {
contentItem: Text { contentItem: Text {
text: toggle.text text: toggle.text
color: Theme.headingColor color: Theme.headingColor
font.family: Theme.uiFontFamily
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
leftPadding: toggle.indicator.width + toggle.spacing leftPadding: toggle.indicator.width + toggle.spacing
font.pixelSize: toggle.font.pixelSize || 11 font.pixelSize: toggle.font.pixelSize || Theme.fontXs
} }
} }
// ── READOUT ─────────────────────────────────────────────────────────── component BadgePill: Rectangle {
implicitWidth: badgeLabel.implicitWidth + 10
implicitHeight: 10
radius: 9
color: Theme.fieldBackground
border.color: Theme.cardBorder
border.width: 1
property alias text: badgeLabel.text
Label { Label {
text: "READOUT" id: badgeLabel
anchors.centerIn: parent
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 10 font.pixelSize: Theme.font2xs
font.letterSpacing: 1.8
font.weight: Font.Medium font.weight: Font.Medium
topPadding: 6 }
bottomPadding: 8
} }
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: statsLayout.implicitHeight + 20 implicitHeight: readoutCard.implicitHeight + 24
color: Theme.subtleSectionBackground color: Theme.sidePanelBackground
radius: Theme.radiusSm radius: Theme.sidePanelRadius
border.color: Theme.cardBorder border.color: Theme.sideBorder
border.width: 1 border.width: 1
ColumnLayout { ColumnLayout {
id: statsLayout id: readoutCard
anchors { fill: parent; margins: 10 } anchors {
spacing: 8 left: parent.left
right: parent.right
top: parent.top
topMargin: 12
}
spacing: 0
// Header Inside the Box
Label {
text: "READOUT"
color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.fontXs
font.letterSpacing: 1.5
font.bold: true
Layout.leftMargin: 14
Layout.rightMargin: 14
Layout.topMargin: 2
Layout.bottomMargin: 10
}
// Min Temp Row // Min Temp Row
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Label { text: "Min Temp"; color: Theme.bodyColor; font.pixelSize: 11 } Layout.leftMargin: 14
Layout.rightMargin: 14
Layout.preferredHeight: 32
Label {
text: "MIN TEMP"
color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.fontXs
font.bold: true
}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
RowLayout { RowLayout {
spacing: 4 spacing: 4
Label { Label {
text: s.min !== undefined ? s.min : "—" text: s.min !== undefined ? s.min : "—"
color: Theme.sensorLow color: Theme.sensorLow
font.pixelSize: 13; font.weight: Font.Bold font.family: Theme.codeFontFamily
font.pixelSize: Theme.fontMd
font.bold: true
} }
Label { Label {
text: s.minIndex !== undefined ? "#" + s.minIndex : "" visible: (s.minIndex !== undefined) && s.minIndex >= 0
color: Theme.bodyColor text: "#" + (s.minIndex !== undefined ? s.minIndex : "")
font.pixelSize: 10 color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.font2xs
Layout.alignment: Qt.AlignBottom
bottomPadding: 3
} }
} }
} }
Rectangle {
Layout.fillWidth: true
height: 1
color: Theme.cardBorder
}
// Max Temp Row // Max Temp Row
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Label { text: "Max Temp"; color: Theme.bodyColor; font.pixelSize: 11 } Layout.leftMargin: 14
Layout.rightMargin: 14
Layout.preferredHeight: 32
Label {
text: "MAX TEMP"
color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.fontXs
font.bold: true
}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
RowLayout { RowLayout {
spacing: 4 spacing: 4
Label { Label {
text: s.max !== undefined ? s.max : "—" text: s.max !== undefined ? s.max : "—"
color: Theme.sensorHigh color: Theme.sensorHigh
font.pixelSize: 13; font.weight: Font.Bold font.family: Theme.codeFontFamily
font.pixelSize: Theme.fontMd
font.bold: true
} }
Label { Label {
text: s.maxIndex !== undefined ? "#" + s.maxIndex : "" visible: (s.maxIndex !== undefined) && s.maxIndex >= 0
color: Theme.bodyColor text: "#" + (s.maxIndex !== undefined ? s.maxIndex : "")
font.pixelSize: 10 color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.font2xs
Layout.alignment: Qt.AlignBottom
bottomPadding: 3
} }
} }
} }
Rectangle { Layout.fillWidth: true; height: 1; color: Theme.cardBorder } Rectangle {
Layout.fillWidth: true
height: 1
color: Theme.cardBorder
}
// Differential Row // Differential Row
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Label { text: "Differential"; color: Theme.bodyColor; font.pixelSize: 11 } Layout.leftMargin: 14
Layout.rightMargin: 14
Layout.preferredHeight: 32
Label {
text: "DIFF"
color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.fontXs
font.bold: true
}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
Label { Label {
text: s.diff !== undefined ? s.diff : "—" text: s.diff !== undefined ? s.diff : "—"
color: Theme.headingColor color: Theme.isDarkMode ? "#A78BFA" : "#8B5CF6"
font.pixelSize: 13; font.weight: Font.Bold font.family: Theme.codeFontFamily
font.pixelSize: Theme.fontMd
font.bold: true
} }
} }
Rectangle {
Layout.fillWidth: true
height: 1
color: Theme.cardBorder
}
// Average Row // Average Row
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Label { text: "Average"; color: Theme.bodyColor; font.pixelSize: 11 } Layout.leftMargin: 14
Layout.rightMargin: 14
Layout.preferredHeight: 32
Label {
text: "AVERAGE"
color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.fontXs
font.bold: true
}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
Label { Label {
text: s.avg !== undefined ? s.avg : "—" text: s.avg !== undefined ? s.avg : "—"
color: Theme.headingColor color: Theme.headingColor
font.pixelSize: 13; font.weight: Font.Bold font.family: Theme.codeFontFamily
font.pixelSize: Theme.fontMd
font.bold: true
} }
} }
Rectangle { Layout.fillWidth: true; height: 1; color: Theme.cardBorder } Rectangle {
Layout.fillWidth: true
height: 1
color: Theme.cardBorder
}
// Sigma Row // Sigma Row
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Label { text: "Sigma (σ)"; color: Theme.bodyColor; font.pixelSize: 11 } Layout.leftMargin: 14
Layout.rightMargin: 14
Layout.preferredHeight: 32
Label {
text: "SIGMA (Σ)"
color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.fontXs
font.bold: true
}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
Label { Label {
text: s.sigma !== undefined ? s.sigma : "—" text: s.sigma !== undefined ? s.sigma : "—"
color: Theme.headingColor color: Theme.headingColor
font.pixelSize: 13; font.weight: Font.Bold font.family: Theme.codeFontFamily
font.pixelSize: Theme.fontMd
font.bold: true
} }
} }
Rectangle {
Layout.fillWidth: true
height: 1
color: Theme.cardBorder
}
// 3-Sigma Row // 3-Sigma Row
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Label { text: "3σ Value"; color: Theme.bodyColor; font.pixelSize: 11 } Layout.leftMargin: 14
Layout.rightMargin: 14
Layout.preferredHeight: 32
Label {
text: "3Σ VALUE"
color: Theme.sideMutedText
font.family: Theme.uiFontFamily
font.pixelSize: Theme.fontXs
font.bold: true
}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
Label { Label {
text: s.threeSigma !== undefined ? s.threeSigma : "—" text: s.threeSigma !== undefined ? s.threeSigma : "—"
color: Theme.bodyColor color: Theme.headingColor
font.pixelSize: 12; font.weight: Font.Medium font.family: Theme.codeFontFamily
font.pixelSize: Theme.fontMd
font.bold: true
} }
} }
} }
} }
Item { height: 12 } Rectangle {
Rectangle { Layout.fillWidth: true; height: 1; color: Theme.cardBorder } Layout.fillWidth: true
Item { height: 12 } implicitHeight: displayCard.implicitHeight + 24
color: Theme.sidePanelBackground
radius: Theme.sidePanelRadius
border.color: Theme.sideBorder
border.width: 1
ColumnLayout {
id: displayCard
anchors {
left: parent.left
right: parent.right
top: parent.top
margins: 12
}
spacing: 8
// ── DISPLAY ───────────────────────────────────────────────────────────
Label { Label {
text: "DISPLAY" text: "DISPLAY"
color: Theme.bodyColor color: Theme.sideMutedText
font.pixelSize: 10 font.family: Theme.uiFontFamily
font.letterSpacing: 1.8 font.pixelSize: Theme.fontXs
font.weight: Font.Medium font.letterSpacing: 1.5
topPadding: 6 font.bold: true
bottomPadding: 8 Layout.bottomMargin: 4
} }
PanelCheckBox { PanelCheckBox {
id: thicknessToggle id: thicknessToggle
text: "Show Thickness" text: "Show Thickness"
checked: false checked: false
font.pixelSize: 11 font.pixelSize: Theme.fontXs
onCheckedChanged: waferMapItem.showThickness = checked onCheckedChanged: waferMapItem.showThickness = checked
} }
@@ -187,61 +340,104 @@ ColumnLayout {
id: labelsToggle id: labelsToggle
text: "Labels" text: "Labels"
checked: true checked: true
font.pixelSize: 11 font.pixelSize: Theme.fontXs
} }
PanelCheckBox { PanelCheckBox {
id: clusterAverageToggle id: clusterAverageToggle
text: "Average Clusters" text: "Average Clusters"
checked: streamController.clusterAveragingEnabled checked: streamController.clusterAveragingEnabled
font.pixelSize: 11 font.pixelSize: Theme.fontXs
onCheckedChanged: streamController.clusterAveragingEnabled = checked onCheckedChanged: streamController.clusterAveragingEnabled = checked
} }
Rectangle {
Layout.fillWidth: true
height: 1
color: Theme.cardBorder
}
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
spacing: 6 spacing: 6
Label { Label {
text: "Heatmap" text: "Heatmap"
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 11 font.pixelSize: Theme.fontXs
Layout.preferredWidth: 52 Layout.preferredWidth: 52
} }
Slider { Slider {
id: heatmapSlider id: heatmapSlider
from: 0; to: 1; value: 0 from: 0
to: 1
value: 0
Layout.fillWidth: true Layout.fillWidth: true
ToolTip.visible: hovered ToolTip.visible: hovered
ToolTip.text: Math.round(value * 100) + "%" ToolTip.text: Math.round(value * 100) + "%"
handle: Rectangle {
x: heatmapSlider.leftPadding + heatmapSlider.visualPosition * (heatmapSlider.availableWidth - width)
y: heatmapSlider.topPadding + heatmapSlider.availableHeight / 2 - height / 2
implicitWidth: 18
implicitHeight: 18
radius: 9
color: Theme.primaryAccent
scale: heatmapSlider.pressed ? 1.15 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.durationFast; easing.type: Theme.easeStandard }
}
}
}
Label {
text: Math.round(heatmapSlider.value * 100) + "%"
color: Theme.bodyColor
font.pixelSize: Theme.fontXs
font.weight: Font.Medium
Layout.preferredWidth: 32
horizontalAlignment: Text.AlignRight
}
}
} }
} }
Item { height: 12 } Rectangle {
Rectangle { Layout.fillWidth: true; height: 1; color: Theme.cardBorder } Layout.fillWidth: true
Item { height: 12 } implicitHeight: thresholdCard.implicitHeight + 24
color: Theme.sidePanelBackground
radius: Theme.sidePanelRadius
border.color: Theme.sideBorder
border.width: 1
ColumnLayout {
id: thresholdCard
anchors {
left: parent.left
right: parent.right
top: parent.top
margins: 12
}
spacing: 6
// ── THRESHOLDS ────────────────────────────────────────────────────────
Label { Label {
text: "THRESHOLDS" text: "THRESHOLDS"
color: Theme.bodyColor color: Theme.sideMutedText
font.pixelSize: 10 font.family: Theme.uiFontFamily
font.letterSpacing: 1.8 font.pixelSize: Theme.fontXs
font.weight: Font.Medium font.letterSpacing: 1.5
topPadding: 6 font.bold: true
bottomPadding: 8 Layout.bottomMargin: 4
} }
Label { Label {
text: "Set Point (°C)" text: "Set Point (°C)"
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 11 font.pixelSize: Theme.fontXs
bottomPadding: 3
} }
TextField { TextField {
id: spField id: spField
Layout.fillWidth: true Layout.fillWidth: true
text: "149.0" text: "149.0"
font.pixelSize: 12 font.pixelSize: Theme.fontSm
inputMethodHints: Qt.ImhFormattedNumbersOnly inputMethodHints: Qt.ImhFormattedNumbersOnly
color: Theme.fieldText color: Theme.fieldText
placeholderTextColor: Theme.fieldPlaceholder placeholderTextColor: Theme.fieldPlaceholder
@@ -252,23 +448,26 @@ ColumnLayout {
color: Theme.fieldBackground color: Theme.fieldBackground
border.width: spField.activeFocus ? Theme.borderStrong : Theme.borderThin border.width: spField.activeFocus ? Theme.borderStrong : Theme.borderThin
border.color: spField.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder 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() onEditingFinished: pushThresholds()
} }
Item { height: 10 }
Label { Label {
text: "Margin (±°C)" text: "Margin (±°C)"
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 11 font.pixelSize: Theme.fontXs
bottomPadding: 3
} }
TextField { TextField {
id: mgField id: mgField
Layout.fillWidth: true Layout.fillWidth: true
text: "1.0" text: "1.0"
font.pixelSize: 12 font.pixelSize: Theme.fontSm
inputMethodHints: Qt.ImhFormattedNumbersOnly inputMethodHints: Qt.ImhFormattedNumbersOnly
color: Theme.fieldText color: Theme.fieldText
placeholderTextColor: Theme.fieldPlaceholder placeholderTextColor: Theme.fieldPlaceholder
@@ -279,25 +478,33 @@ ColumnLayout {
color: Theme.fieldBackground color: Theme.fieldBackground
border.width: mgField.activeFocus ? Theme.borderStrong : Theme.borderThin border.width: mgField.activeFocus ? Theme.borderStrong : Theme.borderThin
border.color: mgField.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder 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() onEditingFinished: pushThresholds()
} }
Item { height: 10 }
PanelCheckBox { PanelCheckBox {
id: autoCheck id: autoCheck
text: "Auto range (mean ± 1σ)" text: "Auto range (mean ± 1σ)"
checked: true checked: true
font.pixelSize: 11 font.pixelSize: Theme.fontXs
onCheckedChanged: pushThresholds() onCheckedChanged: pushThresholds()
} }
}
}
Item { Layout.fillHeight: true } Item {
Layout.fillHeight: true
}
function pushThresholds() { function pushThresholds() {
var sp = parseFloat(spField.text) || 149.0 var sp = parseFloat(spField.text) || 149.0;
var mg = parseFloat(mgField.text) || 1.0 var mg = parseFloat(mgField.text) || 1.0;
streamController.setThresholds(sp, mg, autoCheck.checked) streamController.setThresholds(sp, mg, autoCheck.checked);
} }
} }
+121 -77
View File
@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls.impl
import ISC import ISC
import ".." import ".."
@@ -14,10 +15,11 @@ ColumnLayout {
Label { Label {
text: "SOURCE" text: "SOURCE"
color: Theme.bodyColor color: Theme.sideMutedText
font.pixelSize: 10 font.family: Theme.uiFontFamily
font.letterSpacing: 1.8 font.pixelSize: Theme.fontSm
font.weight: Font.Medium font.letterSpacing: 1.5
font.bold: true
topPadding: 6 topPadding: 6
bottomPadding: 8 bottomPadding: 8
Layout.fillWidth: true Layout.fillWidth: true
@@ -26,51 +28,79 @@ ColumnLayout {
Button { Button {
id: editBtn id: editBtn
text: "✎" implicitWidth: 28
implicitWidth: 26 implicitHeight: 28
implicitHeight: 26
hoverEnabled: true hoverEnabled: true
font.pixelSize: 14
background: Rectangle { background: Rectangle {
color: editBtn.pressed ? Theme.buttonPressed color: editBtn.pressed ? Theme.buttonPressed : editBtn.hovered ? Theme.buttonNeutralHover : "transparent"
: editBtn.hovered ? Theme.buttonNeutralHover
: "transparent"
radius: Theme.radiusSm radius: Theme.radiusSm
} }
contentItem: Text { contentItem: IconImage {
text: parent.text source: "../icons/edit.svg"
sourceSize.width: 16
sourceSize.height: 16
color: Theme.bodyColor color: Theme.bodyColor
font: parent.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
} }
onClicked: { onClicked: {
file_browser.refreshFiles() file_browser.refreshFiles();
csvEditorDialog.open() csvEditorDialog.open();
}
ToolTip {
visible: editBtn.hovered
text: "Edit"
delay: 400
font.pixelSize: Theme.fontXs
font.family: Theme.uiFontFamily
contentItem: Text {
text: parent.text
color: Theme.headingColor
font: parent.font
}
background: Rectangle {
color: Theme.cardBackground
border.color: Theme.sideBorder
border.width: 1
radius: Theme.radiusXs
}
} }
} }
Button { Button {
id: refreshBtn id: refreshBtn
text: "⟳" implicitWidth: 28
implicitWidth: 26 implicitHeight: 28
implicitHeight: 26
hoverEnabled: true hoverEnabled: true
font.pixelSize: 14
background: Rectangle { background: Rectangle {
color: refreshBtn.pressed ? Theme.buttonPressed color: refreshBtn.pressed ? Theme.buttonPressed : refreshBtn.hovered ? Theme.buttonNeutralHover : "transparent"
: refreshBtn.hovered ? Theme.buttonNeutralHover
: "transparent"
radius: Theme.radiusSm radius: Theme.radiusSm
} }
contentItem: Text { contentItem: IconImage {
text: parent.text source: "../icons/refresh.svg"
sourceSize.width: 16
sourceSize.height: 16
color: Theme.bodyColor color: Theme.bodyColor
font: parent.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
} }
onClicked: file_browser.refreshFiles() onClicked: file_browser.refreshFiles()
ToolTip {
visible: refreshBtn.hovered
text: "Refresh"
delay: 400
font.pixelSize: Theme.fontXs
font.family: Theme.uiFontFamily
contentItem: Text {
text: parent.text
color: Theme.headingColor
font: parent.font
}
background: Rectangle {
color: Theme.cardBackground
border.color: Theme.sideBorder
border.width: 1
radius: Theme.radiusXs
}
}
} }
} }
@@ -79,14 +109,12 @@ ColumnLayout {
id: dirBtn id: dirBtn
Layout.fillWidth: true Layout.fillWidth: true
hoverEnabled: true hoverEnabled: true
implicitHeight: 32 implicitHeight: 36
leftPadding: 8 leftPadding: 8
rightPadding: 8 rightPadding: 8
background: Rectangle { background: Rectangle {
radius: Theme.radiusSm radius: Theme.radiusSm
color: dirBtn.pressed ? Theme.buttonNeutralPressed color: dirBtn.pressed ? Theme.buttonNeutralPressed : dirBtn.hovered ? Theme.buttonNeutralHover : Theme.buttonNeutralBackground
: dirBtn.hovered ? Theme.buttonNeutralHover
: Theme.buttonNeutralBackground
border.width: Theme.borderThin border.width: Theme.borderThin
border.color: Theme.fieldBorder border.color: Theme.fieldBorder
} }
@@ -95,12 +123,12 @@ ColumnLayout {
Label { Label {
text: "▤" text: "▤"
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 12 font.pixelSize: Theme.fontSm
} }
Label { Label {
text: file_browser.currentDirectory.split("/").pop() || file_browser.currentDirectory text: file_browser.currentDirectory.split("/").pop() || file_browser.currentDirectory
color: Theme.headingColor color: Theme.headingColor
font.pixelSize: 11 font.pixelSize: Theme.fontSm
elide: Text.ElideMiddle elide: Text.ElideMiddle
Layout.fillWidth: true Layout.fillWidth: true
} }
@@ -113,7 +141,7 @@ ColumnLayout {
id: filter id: filter
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "Filter…" placeholderText: "Filter…"
font.pixelSize: 11 font.pixelSize: Theme.fontSm
color: Theme.fieldText color: Theme.fieldText
placeholderTextColor: Theme.fieldPlaceholder placeholderTextColor: Theme.fieldPlaceholder
selectedTextColor: Theme.fieldBackground selectedTextColor: Theme.fieldBackground
@@ -123,6 +151,12 @@ ColumnLayout {
color: Theme.fieldBackground color: Theme.fieldBackground
border.width: filter.activeFocus ? Theme.borderStrong : Theme.borderThin border.width: filter.activeFocus ? Theme.borderStrong : Theme.borderThin
border.color: filter.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder border.color: filter.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder
Behavior on color {
ColorAnimation { duration: Theme.durationFast }
}
Behavior on border.color {
ColorAnimation { duration: Theme.durationFast }
}
} }
} }
@@ -146,17 +180,14 @@ ColumnLayout {
focusPolicy: Qt.NoFocus focusPolicy: Qt.NoFocus
readonly property bool matchesFilter: { readonly property bool matchesFilter: {
if (filter.text === "") return true if (filter.text === "")
var q = filter.text.toLowerCase() return true;
return (modelData.baseName + modelData.waferType + modelData.date) var q = filter.text.toLowerCase();
.toLowerCase().indexOf(q) >= 0 return (modelData.baseName + modelData.waferType + modelData.date).toLowerCase().indexOf(q) >= 0;
} }
// Active only in review mode; no highlight during live streaming // Active only in review mode; no highlight during live streaming
readonly property bool isActive: readonly property bool isActive: streamController.mode === "review" && streamController.loadedFile !== "" && modelData.fileName === streamController.loadedFile
streamController.mode === "review" &&
streamController.loadedFile !== "" &&
modelData.fileName === streamController.loadedFile
visible: matchesFilter visible: matchesFilter
height: matchesFilter ? implicitHeight : 0 height: matchesFilter ? implicitHeight : 0
@@ -164,10 +195,11 @@ ColumnLayout {
onClicked: streamController.loadFile(modelData.fileName) onClicked: streamController.loadFile(modelData.fileName)
background: Rectangle { background: Rectangle {
color: fileItem.isActive color: fileItem.isActive ? Qt.rgba(1, 1, 1, 0.06) : fileItem.hovered ? Qt.rgba(1, 1, 1, 0.04) : "transparent"
? Qt.rgba(1, 1, 1, 0.06)
: fileItem.hovered ? Qt.rgba(1, 1, 1, 0.04) : "transparent"
radius: Theme.radiusSm radius: Theme.radiusSm
Behavior on color {
ColorAnimation { duration: Theme.durationFast }
}
// Left accent bar — shown only when active // Left accent bar — shown only when active
Rectangle { Rectangle {
@@ -175,15 +207,21 @@ ColumnLayout {
width: 3 width: 3
radius: 1.5 radius: 1.5
anchors { anchors {
top: parent.top; bottom: parent.bottom; left: parent.left top: parent.top
topMargin: 5; bottomMargin: 5 bottom: parent.bottom
left: parent.left
topMargin: 5
bottomMargin: 5
} }
color: { color: {
var t = modelData.waferType var t = modelData.waferType;
if (t === "A" || t === "E" || t === "P") return "#3B82F6" if (t === "A" || t === "E" || t === "P")
if (t === "B" || t === "C" || t === "D") return "#10B981" return "#3B82F6";
if (t === "Z") return "#8B5CF6" if (t === "B" || t === "C" || t === "D")
return Theme.headingColor return "#10B981";
if (t === "Z")
return "#8B5CF6";
return Theme.headingColor;
} }
} }
} }
@@ -193,22 +231,26 @@ ColumnLayout {
// Wafer-type avatar (circle) // Wafer-type avatar (circle)
Rectangle { Rectangle {
implicitWidth: 28; implicitHeight: 28 implicitWidth: 28
implicitHeight: 28
Layout.preferredWidth: 28 Layout.preferredWidth: 28
Layout.preferredHeight: 28 Layout.preferredHeight: 28
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
radius: 14 radius: 14
color: { color: {
var t = modelData.waferType var t = modelData.waferType;
if (t === "A" || t === "E" || t === "P") return "#1D4ED8" if (t === "A" || t === "E" || t === "P")
if (t === "B" || t === "C" || t === "D") return "#065F46" return "#1D4ED8";
if (t === "Z") return "#7C3AED" if (t === "B" || t === "C" || t === "D")
return "#374151" return "#065F46";
if (t === "Z")
return "#7C3AED";
return "#374151";
} }
Label { Label {
anchors.centerIn: parent anchors.centerIn: parent
text: modelData.waferType || "?" text: modelData.waferType || "?"
font.pixelSize: 12 font.pixelSize: Theme.fontMd
font.weight: Font.Bold font.weight: Font.Bold
color: "#FFFFFF" color: "#FFFFFF"
} }
@@ -223,7 +265,7 @@ ColumnLayout {
Label { Label {
text: modelData.serialNumber ? (modelData.waferType + modelData.serialNumber) : modelData.baseName text: modelData.serialNumber ? (modelData.waferType + modelData.serialNumber) : modelData.baseName
color: Theme.headingColor color: Theme.headingColor
font.pixelSize: 13 font.pixelSize: Theme.fontMd
font.weight: Font.Bold font.weight: Font.Bold
elide: Text.ElideRight elide: Text.ElideRight
Layout.fillWidth: true Layout.fillWidth: true
@@ -235,24 +277,24 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Label { Label {
text: { text: {
var d = modelData.date || "" var d = modelData.date || "";
return d.length >= 10 ? d.substring(0, 10) : (d || "—") return d.length >= 10 ? d.substring(0, 10) : (d || "—");
} }
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 10 font.pixelSize: Theme.fontSm
} }
Label { Label {
visible: (modelData.timeStr || "") !== "" visible: (modelData.timeStr || "") !== ""
text: "·" text: "·"
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 10 font.pixelSize: Theme.fontSm
opacity: 0.45 opacity: 0.45
} }
Label { Label {
visible: (modelData.timeStr || "") !== "" visible: (modelData.timeStr || "") !== ""
text: modelData.timeStr || "" text: modelData.timeStr || ""
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 10 font.pixelSize: Theme.fontSm
opacity: 0.65 opacity: 0.65
} }
} }
@@ -260,18 +302,20 @@ ColumnLayout {
// Tertiary: edited metadata (chamber · notes) if any // Tertiary: edited metadata (chamber · notes) if any
Label { Label {
visible: { visible: {
var c = modelData.chamber || "" var c = modelData.chamber || "";
var n = modelData.notes || "" var n = modelData.notes || "";
return c !== "" || n !== "" return c !== "" || n !== "";
} }
text: { text: {
var parts = [] var parts = [];
if (modelData.chamber) parts.push(modelData.chamber) if (modelData.chamber)
if (modelData.notes) parts.push(modelData.notes) parts.push(modelData.chamber);
return parts.join(" · ") if (modelData.notes)
parts.push(modelData.notes);
return parts.join(" · ");
} }
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 9 font.pixelSize: Theme.fontXs
opacity: 0.7 opacity: 0.7
elide: Text.ElideRight elide: Text.ElideRight
Layout.fillWidth: true Layout.fillWidth: true
@@ -285,7 +329,7 @@ ColumnLayout {
Label { Label {
text: file_browser.files.length + " files · CSV only" text: file_browser.files.length + " files · CSV only"
color: Theme.bodyColor color: Theme.bodyColor
font.pixelSize: 10 font.pixelSize: Theme.fontSm
} }
SelectFileDialog { SelectFileDialog {