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:
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls.impl
|
||||
import ISC
|
||||
import ".."
|
||||
|
||||
@@ -14,10 +15,11 @@ ColumnLayout {
|
||||
|
||||
Label {
|
||||
text: "SOURCE"
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: 10
|
||||
font.letterSpacing: 1.8
|
||||
font.weight: Font.Medium
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontSm
|
||||
font.letterSpacing: 1.5
|
||||
font.bold: true
|
||||
topPadding: 6
|
||||
bottomPadding: 8
|
||||
Layout.fillWidth: true
|
||||
@@ -26,51 +28,79 @@ ColumnLayout {
|
||||
|
||||
Button {
|
||||
id: editBtn
|
||||
text: "✎"
|
||||
implicitWidth: 26
|
||||
implicitHeight: 26
|
||||
implicitWidth: 28
|
||||
implicitHeight: 28
|
||||
hoverEnabled: true
|
||||
font.pixelSize: 14
|
||||
background: Rectangle {
|
||||
color: editBtn.pressed ? Theme.buttonPressed
|
||||
: editBtn.hovered ? Theme.buttonNeutralHover
|
||||
: "transparent"
|
||||
color: editBtn.pressed ? Theme.buttonPressed : editBtn.hovered ? Theme.buttonNeutralHover : "transparent"
|
||||
radius: Theme.radiusSm
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
contentItem: IconImage {
|
||||
source: "../icons/edit.svg"
|
||||
sourceSize.width: 16
|
||||
sourceSize.height: 16
|
||||
color: Theme.bodyColor
|
||||
font: parent.font
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
onClicked: {
|
||||
file_browser.refreshFiles()
|
||||
csvEditorDialog.open()
|
||||
file_browser.refreshFiles();
|
||||
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 {
|
||||
id: refreshBtn
|
||||
text: "⟳"
|
||||
implicitWidth: 26
|
||||
implicitHeight: 26
|
||||
implicitWidth: 28
|
||||
implicitHeight: 28
|
||||
hoverEnabled: true
|
||||
font.pixelSize: 14
|
||||
background: Rectangle {
|
||||
color: refreshBtn.pressed ? Theme.buttonPressed
|
||||
: refreshBtn.hovered ? Theme.buttonNeutralHover
|
||||
: "transparent"
|
||||
color: refreshBtn.pressed ? Theme.buttonPressed : refreshBtn.hovered ? Theme.buttonNeutralHover : "transparent"
|
||||
radius: Theme.radiusSm
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
contentItem: IconImage {
|
||||
source: "../icons/refresh.svg"
|
||||
sourceSize.width: 16
|
||||
sourceSize.height: 16
|
||||
color: Theme.bodyColor
|
||||
font: parent.font
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
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
|
||||
Layout.fillWidth: true
|
||||
hoverEnabled: true
|
||||
implicitHeight: 32
|
||||
implicitHeight: 36
|
||||
leftPadding: 8
|
||||
rightPadding: 8
|
||||
background: Rectangle {
|
||||
radius: Theme.radiusSm
|
||||
color: dirBtn.pressed ? Theme.buttonNeutralPressed
|
||||
: dirBtn.hovered ? Theme.buttonNeutralHover
|
||||
: Theme.buttonNeutralBackground
|
||||
color: dirBtn.pressed ? Theme.buttonNeutralPressed : dirBtn.hovered ? Theme.buttonNeutralHover : Theme.buttonNeutralBackground
|
||||
border.width: Theme.borderThin
|
||||
border.color: Theme.fieldBorder
|
||||
}
|
||||
@@ -95,12 +123,12 @@ ColumnLayout {
|
||||
Label {
|
||||
text: "▤"
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: 12
|
||||
font.pixelSize: Theme.fontSm
|
||||
}
|
||||
Label {
|
||||
text: file_browser.currentDirectory.split("/").pop() || file_browser.currentDirectory
|
||||
color: Theme.headingColor
|
||||
font.pixelSize: 11
|
||||
font.pixelSize: Theme.fontSm
|
||||
elide: Text.ElideMiddle
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
@@ -113,7 +141,7 @@ ColumnLayout {
|
||||
id: filter
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Filter…"
|
||||
font.pixelSize: 11
|
||||
font.pixelSize: Theme.fontSm
|
||||
color: Theme.fieldText
|
||||
placeholderTextColor: Theme.fieldPlaceholder
|
||||
selectedTextColor: Theme.fieldBackground
|
||||
@@ -123,6 +151,12 @@ ColumnLayout {
|
||||
color: Theme.fieldBackground
|
||||
border.width: filter.activeFocus ? Theme.borderStrong : Theme.borderThin
|
||||
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
|
||||
|
||||
readonly property bool matchesFilter: {
|
||||
if (filter.text === "") return true
|
||||
var q = filter.text.toLowerCase()
|
||||
return (modelData.baseName + modelData.waferType + modelData.date)
|
||||
.toLowerCase().indexOf(q) >= 0
|
||||
if (filter.text === "")
|
||||
return true;
|
||||
var q = filter.text.toLowerCase();
|
||||
return (modelData.baseName + modelData.waferType + modelData.date).toLowerCase().indexOf(q) >= 0;
|
||||
}
|
||||
|
||||
// Active only in review mode; no highlight during live streaming
|
||||
readonly property bool isActive:
|
||||
streamController.mode === "review" &&
|
||||
streamController.loadedFile !== "" &&
|
||||
modelData.fileName === streamController.loadedFile
|
||||
readonly property bool isActive: streamController.mode === "review" && streamController.loadedFile !== "" && modelData.fileName === streamController.loadedFile
|
||||
|
||||
visible: matchesFilter
|
||||
height: matchesFilter ? implicitHeight : 0
|
||||
@@ -164,10 +195,11 @@ ColumnLayout {
|
||||
onClicked: streamController.loadFile(modelData.fileName)
|
||||
|
||||
background: Rectangle {
|
||||
color: fileItem.isActive
|
||||
? Qt.rgba(1, 1, 1, 0.06)
|
||||
: fileItem.hovered ? Qt.rgba(1, 1, 1, 0.04) : "transparent"
|
||||
color: fileItem.isActive ? Qt.rgba(1, 1, 1, 0.06) : fileItem.hovered ? Qt.rgba(1, 1, 1, 0.04) : "transparent"
|
||||
radius: Theme.radiusSm
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.durationFast }
|
||||
}
|
||||
|
||||
// Left accent bar — shown only when active
|
||||
Rectangle {
|
||||
@@ -175,15 +207,21 @@ ColumnLayout {
|
||||
width: 3
|
||||
radius: 1.5
|
||||
anchors {
|
||||
top: parent.top; bottom: parent.bottom; left: parent.left
|
||||
topMargin: 5; bottomMargin: 5
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
topMargin: 5
|
||||
bottomMargin: 5
|
||||
}
|
||||
color: {
|
||||
var t = modelData.waferType
|
||||
if (t === "A" || t === "E" || t === "P") return "#3B82F6"
|
||||
if (t === "B" || t === "C" || t === "D") return "#10B981"
|
||||
if (t === "Z") return "#8B5CF6"
|
||||
return Theme.headingColor
|
||||
var t = modelData.waferType;
|
||||
if (t === "A" || t === "E" || t === "P")
|
||||
return "#3B82F6";
|
||||
if (t === "B" || t === "C" || t === "D")
|
||||
return "#10B981";
|
||||
if (t === "Z")
|
||||
return "#8B5CF6";
|
||||
return Theme.headingColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,22 +231,26 @@ ColumnLayout {
|
||||
|
||||
// Wafer-type avatar (circle)
|
||||
Rectangle {
|
||||
implicitWidth: 28; implicitHeight: 28
|
||||
implicitWidth: 28
|
||||
implicitHeight: 28
|
||||
Layout.preferredWidth: 28
|
||||
Layout.preferredHeight: 28
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
radius: 14
|
||||
color: {
|
||||
var t = modelData.waferType
|
||||
if (t === "A" || t === "E" || t === "P") return "#1D4ED8"
|
||||
if (t === "B" || t === "C" || t === "D") return "#065F46"
|
||||
if (t === "Z") return "#7C3AED"
|
||||
return "#374151"
|
||||
var t = modelData.waferType;
|
||||
if (t === "A" || t === "E" || t === "P")
|
||||
return "#1D4ED8";
|
||||
if (t === "B" || t === "C" || t === "D")
|
||||
return "#065F46";
|
||||
if (t === "Z")
|
||||
return "#7C3AED";
|
||||
return "#374151";
|
||||
}
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
text: modelData.waferType || "?"
|
||||
font.pixelSize: 12
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.weight: Font.Bold
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
@@ -223,7 +265,7 @@ ColumnLayout {
|
||||
Label {
|
||||
text: modelData.serialNumber ? (modelData.waferType + modelData.serialNumber) : modelData.baseName
|
||||
color: Theme.headingColor
|
||||
font.pixelSize: 13
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.weight: Font.Bold
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
@@ -235,24 +277,24 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: {
|
||||
var d = modelData.date || ""
|
||||
return d.length >= 10 ? d.substring(0, 10) : (d || "—")
|
||||
var d = modelData.date || "";
|
||||
return d.length >= 10 ? d.substring(0, 10) : (d || "—");
|
||||
}
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: 10
|
||||
font.pixelSize: Theme.fontSm
|
||||
}
|
||||
Label {
|
||||
visible: (modelData.timeStr || "") !== ""
|
||||
text: "·"
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: 10
|
||||
font.pixelSize: Theme.fontSm
|
||||
opacity: 0.45
|
||||
}
|
||||
Label {
|
||||
visible: (modelData.timeStr || "") !== ""
|
||||
text: modelData.timeStr || ""
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: 10
|
||||
font.pixelSize: Theme.fontSm
|
||||
opacity: 0.65
|
||||
}
|
||||
}
|
||||
@@ -260,18 +302,20 @@ ColumnLayout {
|
||||
// Tertiary: edited metadata (chamber · notes) if any
|
||||
Label {
|
||||
visible: {
|
||||
var c = modelData.chamber || ""
|
||||
var n = modelData.notes || ""
|
||||
return c !== "" || n !== ""
|
||||
var c = modelData.chamber || "";
|
||||
var n = modelData.notes || "";
|
||||
return c !== "" || n !== "";
|
||||
}
|
||||
text: {
|
||||
var parts = []
|
||||
if (modelData.chamber) parts.push(modelData.chamber)
|
||||
if (modelData.notes) parts.push(modelData.notes)
|
||||
return parts.join(" · ")
|
||||
var parts = [];
|
||||
if (modelData.chamber)
|
||||
parts.push(modelData.chamber);
|
||||
if (modelData.notes)
|
||||
parts.push(modelData.notes);
|
||||
return parts.join(" · ");
|
||||
}
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: 9
|
||||
font.pixelSize: Theme.fontXs
|
||||
opacity: 0.7
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
@@ -285,7 +329,7 @@ ColumnLayout {
|
||||
Label {
|
||||
text: file_browser.files.length + " files · CSV only"
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: 10
|
||||
font.pixelSize: Theme.fontSm
|
||||
}
|
||||
|
||||
SelectFileDialog {
|
||||
|
||||
Reference in New Issue
Block a user