refactor(qml): extract sidebar panels and data tab comparison cards to modular components
This commit is contained in:
+17
-349
@@ -17,20 +17,6 @@ Rectangle {
|
||||
border.color: Theme.outerFrameBorder
|
||||
border.width: Theme.borderStrong
|
||||
|
||||
// ===== Global ToolTip Styling =====
|
||||
ToolTip.toolTip.background: Rectangle {
|
||||
color: Theme.cardBackground
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
radius: Theme.radiusXs
|
||||
}
|
||||
ToolTip.toolTip.contentItem: Text {
|
||||
text: ToolTip.toolTip.text
|
||||
color: Theme.headingColor
|
||||
font.pixelSize: Theme.fontXs
|
||||
font.family: Theme.uiFontFamily
|
||||
}
|
||||
|
||||
// ===== View State =====
|
||||
property int selectedTabIndex: 0
|
||||
onSelectedTabIndexChanged: {
|
||||
@@ -420,43 +406,6 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
component PanelCheckBox: CheckBox {
|
||||
id: toggle
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 18
|
||||
implicitHeight: 18
|
||||
x: toggle.leftPadding
|
||||
y: parent.height / 2 - height / 2
|
||||
radius: Theme.radiusXs
|
||||
color: toggle.checked ? Theme.primaryAccent : "transparent"
|
||||
border.width: Theme.borderThin
|
||||
border.color: toggle.checked ? Theme.primaryAccent : Theme.fieldBorder
|
||||
|
||||
Behavior on color { ColorAnimation { duration: Theme.durationFast } }
|
||||
Behavior on border.color { ColorAnimation { duration: Theme.durationFast } }
|
||||
|
||||
IconImage {
|
||||
anchors.centerIn: parent
|
||||
source: "Tabs/icons/check.svg"
|
||||
width: 12; height: 12
|
||||
sourceSize.width: 12
|
||||
sourceSize.height: 12
|
||||
color: Theme.panelBackground
|
||||
opacity: toggle.checked ? 1.0 : 0.0
|
||||
Behavior on opacity { NumberAnimation { duration: Theme.durationFast } }
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
text: toggle.text
|
||||
color: Theme.headingColor
|
||||
font.family: Theme.uiFontFamily
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: toggle.indicator.width + toggle.spacing
|
||||
font.pixelSize: toggle.font.pixelSize || Theme.fontXs
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: runCompCol
|
||||
anchors.fill: parent
|
||||
@@ -498,8 +447,10 @@ Rectangle {
|
||||
color: Theme.bodyColor
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
ToolTip.visible: resetRunBtn.hovered
|
||||
ToolTip.text: "Reset comparison"
|
||||
AppToolTip {
|
||||
visible: resetRunBtn.hovered
|
||||
text: "Reset comparison"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,11 +513,13 @@ Rectangle {
|
||||
readonly property bool sameFile: !!dt && fileA !== "" && fileA === dt.compareFileB
|
||||
readonly property string gateError: !!dt ? dt.familyGateError : ""
|
||||
enabled: !!dt && fileA !== "" && dt.compareFileB !== "" && !sameFile && !dt.comparing && !masterMissing && gateError === ""
|
||||
ToolTip.visible: (sameFile || masterMissing || gateError !== "") && hovered
|
||||
ToolTip.text: masterMissing
|
||||
? "No master file registered for this wafer type (Settings → Master Files)"
|
||||
: gateError !== "" ? gateError
|
||||
: "Choose two different runs to compare"
|
||||
AppToolTip {
|
||||
visible: (compareRunBtn.sameFile || compareRunBtn.masterMissing || compareRunBtn.gateError !== "") && compareRunBtn.hovered
|
||||
text: compareRunBtn.masterMissing
|
||||
? "No master file registered for this wafer type (Settings → Master Files)"
|
||||
: compareRunBtn.gateError !== "" ? compareRunBtn.gateError
|
||||
: "Choose two different runs to compare"
|
||||
}
|
||||
onClicked: {
|
||||
dt.comparing = true
|
||||
dt.clearResults()
|
||||
@@ -617,100 +570,9 @@ Rectangle {
|
||||
currentIndex: root.selectedTabIndex === 0 ? 0 : 1
|
||||
|
||||
// ── Status tab: Hardware Actions + Log Actions ──────────
|
||||
ColumnLayout {
|
||||
spacing: Theme.sideRailSpacing
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: hwActionsCol.implicitHeight + 28
|
||||
radius: Theme.sidePanelRadius
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
color: Theme.sidePanelBackground
|
||||
|
||||
ColumnLayout {
|
||||
id: hwActionsCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: 14
|
||||
spacing: 6
|
||||
|
||||
Label {
|
||||
text: "HARDWARE ACTIONS"
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontSm
|
||||
font.letterSpacing: 1.5
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
RailActionButton {
|
||||
label: "DETECT WAFER"
|
||||
iconSource: "../icons/detect.svg"
|
||||
Layout.fillWidth: true
|
||||
enabled: !deviceController.operationInProgress
|
||||
onClicked: root._doDetect()
|
||||
}
|
||||
|
||||
RailActionButton {
|
||||
label: "READ MEMORY"
|
||||
iconSource: "../icons/read.svg"
|
||||
Layout.fillWidth: true
|
||||
enabled: root.waferDetected
|
||||
onClicked: deviceController.readMemoryAsync()
|
||||
}
|
||||
|
||||
RailActionButton {
|
||||
label: "ERASE MEMORY"
|
||||
iconSource: "../icons/erase.svg"
|
||||
Layout.fillWidth: true
|
||||
destructive: true
|
||||
enabled: root.waferDetected
|
||||
onClicked: deviceController.eraseMemory(
|
||||
deviceController.selectedPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: logActionsCol.implicitHeight + 28
|
||||
radius: Theme.sidePanelRadius
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
color: Theme.sidePanelBackground
|
||||
|
||||
ColumnLayout {
|
||||
id: logActionsCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: 14
|
||||
spacing: 6
|
||||
|
||||
Label {
|
||||
text: "LOG ACTIONS"
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontSm
|
||||
font.letterSpacing: 1.5
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
RailActionButton {
|
||||
label: "REFRESH SESSION"
|
||||
iconSource: "../icons/refresh.svg"
|
||||
Layout.fillWidth: true
|
||||
onClicked: deviceController.clearSession()
|
||||
}
|
||||
|
||||
RailActionButton {
|
||||
label: "CLEAR LOG"
|
||||
iconSource: "../icons/x.svg"
|
||||
Layout.fillWidth: true
|
||||
onClicked: deviceController.clearActivityLog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
StatusActionsPanel {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
// ── Map tab: Source file browser ──────────────
|
||||
@@ -728,208 +590,14 @@ Rectangle {
|
||||
}
|
||||
|
||||
// ── BOX 3: Hardware Status Footer ──────────────────────────
|
||||
// Not relevant on the Data tab — Run Comparison takes its place above.
|
||||
Rectangle {
|
||||
ConnectionFooter {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Theme.sideFooterConnection
|
||||
visible: root.selectedTabIndex !== 1
|
||||
radius: Theme.sidePanelRadius
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
color: Theme.sidePanelBackground
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 14
|
||||
spacing: 6
|
||||
|
||||
// Row 1: Title
|
||||
Label {
|
||||
text: "CONNECTION FLOW"
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontSm
|
||||
font.letterSpacing: 1.5
|
||||
font.bold: true
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
// Row 2: Port / descriptive text
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: {
|
||||
if (deviceController.connectionStatus === "Connected")
|
||||
return deviceController.selectedPort || "Connected"
|
||||
if (deviceController.selectedPort)
|
||||
return deviceController.selectedPort
|
||||
return "No port selected"
|
||||
}
|
||||
color: Theme.bodyColor
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontSm
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
// Row 3: Mode chip left, status badge bottom-right
|
||||
RowLayout {
|
||||
spacing: 6
|
||||
Layout.fillWidth: true
|
||||
|
||||
Rectangle {
|
||||
width: modeChipLabel.implicitWidth + 12
|
||||
height: 18
|
||||
radius: 4
|
||||
color: Theme.fieldBackground
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
|
||||
Label {
|
||||
id: modeChipLabel
|
||||
anchors.centerIn: parent
|
||||
text: streamController.mode
|
||||
? streamController.mode.toUpperCase() : "REVIEW"
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontXs
|
||||
font.bold: true
|
||||
font.letterSpacing: 0.8
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Rectangle {
|
||||
height: 18
|
||||
width: badgeLabel.implicitWidth + 14
|
||||
radius: 9
|
||||
color: {
|
||||
var s = deviceController.connectionStatus
|
||||
if (s === "Connected") return Theme.statusSuccessColor
|
||||
if (s === "Disconnected") return Theme.statusErrorColor
|
||||
return Theme.statusWarningColor
|
||||
}
|
||||
|
||||
Label {
|
||||
id: badgeLabel
|
||||
anchors.centerIn: parent
|
||||
text: deviceController.connectionStatus || "Disconnected"
|
||||
color: Theme.statusBadgeText
|
||||
font.pixelSize: Theme.fontXs
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── BOX 4: Utility Buttons ─────────────────────────────────
|
||||
Rectangle {
|
||||
UtilityFooter {
|
||||
id: utilFooter
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Theme.sideFooterUtility
|
||||
radius: Theme.sidePanelRadius
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
color: Theme.sidePanelBackground
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
spacing: 4
|
||||
|
||||
Button {
|
||||
id: settingsBtn
|
||||
flat: true
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
icon.source: "Tabs/icons/settings.svg"
|
||||
onClicked: settingsPopup.open()
|
||||
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
color: settingsBtn.hovered
|
||||
? Theme.sideActiveBackground : "transparent"
|
||||
}
|
||||
|
||||
contentItem: Row {
|
||||
spacing: 6
|
||||
anchors.centerIn: parent
|
||||
|
||||
IconImage {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 16
|
||||
height: 16
|
||||
source: "Tabs/icons/settings.svg"
|
||||
sourceSize.width: 16
|
||||
sourceSize.height: 16
|
||||
color: Theme.headingColor
|
||||
opacity: 0.75
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "SETTINGS"
|
||||
color: Theme.headingColor
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontXs
|
||||
font.bold: true
|
||||
font.letterSpacing: 1.2
|
||||
opacity: 0.8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 1
|
||||
height: 24
|
||||
color: Theme.sideBorder
|
||||
}
|
||||
|
||||
Button {
|
||||
id: aboutBtn
|
||||
flat: true
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
icon.source: "Tabs/icons/about.svg"
|
||||
onClicked: aboutDialog.open()
|
||||
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
color: aboutBtn.hovered
|
||||
? Theme.sideActiveBackground : "transparent"
|
||||
}
|
||||
|
||||
contentItem: Row {
|
||||
spacing: 6
|
||||
anchors.centerIn: parent
|
||||
|
||||
IconImage {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 16
|
||||
height: 16
|
||||
source: "Tabs/icons/about.svg"
|
||||
sourceSize.width: 16
|
||||
sourceSize.height: 16
|
||||
color: Theme.headingColor
|
||||
opacity: 0.75
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "ABOUT"
|
||||
color: Theme.headingColor
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontXs
|
||||
font.bold: true
|
||||
font.letterSpacing: 1.2
|
||||
opacity: 0.8
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user