import QtQuick import QtQuick.Controls import QtQuick.Layouts import QtQuick.Dialogs import ISC Item { id: root anchors.fill: parent // ===== CSV metadata dialog (must not live inside RowLayout: implicit size breaks the tab) ===== SelectFileDialog { id: selectFileDialog tableModel: file_browser.files onFileChosen: function (path) { root.handleCsvSelected(path); } } function handleEditCsvMetadata() { file_browser.refreshFiles(); selectFileDialog.open(); } function handleCsvSelected(filePath) { console.log("Doing work with:", filePath); // @todo: implement CSV metadata handling after file selection. } // ===== Settings Data Helpers ===== readonly property var masterFamilies: ["A", "B", "C", "D", "E", "F", "P", "X", "Z"] function masterPath(family) { const table = settingsModel.masters; return table && table[family] ? table[family] : ""; } function statusColor() { if (!settingsModel.isValid) { return Theme.statusWarningColor; } return settingsModel.saveStatus.indexOf("error:") === 0 ? Theme.statusErrorColor : Theme.statusSuccessColor; } // ===== Reusable Settings Components ===== component SettingsGroupBox: GroupBox { id: settingsGroup background: Rectangle { y: settingsGroup.topPadding - settingsGroup.bottomPadding width: settingsGroup.width height: settingsGroup.height - settingsGroup.topPadding + settingsGroup.bottomPadding radius: Theme.radiusMd color: Theme.panelBackground border.color: Theme.innerFrameBorder border.width: Theme.borderThin } label: Label { text: settingsGroup.title color: Theme.panelTitleText font.pixelSize: 16 font.bold: true } } component SettingsTextInput: TextField { id: textInput color: Theme.fieldText placeholderTextColor: Theme.fieldPlaceholder selectedTextColor: Theme.fieldBackground selectionColor: Theme.fieldText background: Rectangle { radius: Theme.radiusXs color: Theme.fieldBackground border.width: textInput.activeFocus ? Theme.borderStrong : Theme.borderThin border.color: textInput.activeFocus ? Theme.fieldBorderFocus : Theme.fieldBorder } } component SettingsActionButton: Button { id: actionButton hoverEnabled: true background: Rectangle { radius: Theme.radiusSm color: actionButton.down ? Theme.buttonNeutralPressed : (actionButton.hovered ? Theme.buttonNeutralHover : Theme.buttonNeutralBackground) border.width: Theme.borderThin border.color: Theme.fieldBorder } contentItem: Text { text: actionButton.text color: Theme.buttonNeutralText horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideRight font.bold: true } } component SettingsToggle: CheckBox { id: toggle indicator: Rectangle { implicitWidth: 20 implicitHeight: 20 x: toggle.leftPadding y: parent.height / 2 - height / 2 radius: Theme.radiusXs color: toggle.checked ? Theme.primaryAccent : Theme.fieldBackground border.width: Theme.borderThin border.color: toggle.checked ? Theme.primaryAccent : Theme.fieldBorder Rectangle { anchors.centerIn: parent width: 9 height: 9 radius: Theme.radiusXs visible: toggle.checked color: Theme.fieldBackground } } contentItem: Text { text: toggle.text color: Theme.checkboxText verticalAlignment: Text.AlignVCenter leftPadding: toggle.indicator.width + toggle.spacing } } // ===== File Picker Dialog ===== FileDialog { id: masterPicker title: "Choose Master CSV" nameFilters: ["CSV files (*.csv)"] property string targetFamily: "" onAccepted: settingsModel.setMaster(targetFamily, selectedFile.toLocalFile()) } // ===== Settings Page Layout ===== Item { anchors.fill: parent ScrollView { id: settingsScroll anchors.fill: parent anchors.margins: Theme.settingsOuterMargin contentWidth: availableWidth contentHeight: container.implicitHeight ScrollBar.vertical: ScrollBar { parent: settingsScroll anchors.top: settingsScroll.top anchors.right: settingsScroll.right anchors.bottom: settingsScroll.bottom policy: ScrollBar.AsNeeded contentItem: Rectangle { implicitWidth: 8 implicitHeight: 8 radius: Theme.radiusSm color: Theme.trackBackground } background: Rectangle { implicitWidth: 8 color: "transparent" } } ColumnLayout { id: container width: Math.min(settingsScroll.availableWidth, Theme.settingsPanelMaxWidth) x: (settingsScroll.availableWidth - width) / 2 spacing: Theme.settingsSectionSpacing // ===== Page Title ===== Label { text: "Settings" font.pixelSize: 30 font.bold: true color: Theme.headingColor } // ===== Chamber Settings ===== SettingsGroupBox { title: "Chamber" Layout.fillWidth: true RowLayout { anchors.fill: parent spacing: Theme.settingsRowSpacing Label { text: "Chamber ID" color: Theme.headingColor Layout.alignment: Qt.AlignVCenter } SettingsTextInput { id: chamberField Layout.fillWidth: true Layout.minimumWidth: Theme.settingsFieldMinWidth placeholderText: "Enter chamber ID" text: settingsModel.chamberId onEditingFinished: settingsModel.setChamberId(text) Connections { target: settingsModel function onChamberIdChanged() { if (!chamberField.activeFocus) { chamberField.text = settingsModel.chamberId; } } } } SettingsActionButton { text: "Set" Layout.preferredWidth: 90 Layout.preferredHeight: Theme.settingsButtonHeight onClicked: settingsModel.setChamberId(chamberField.text) } } } // ===== Master CSV Mapping ===== SettingsGroupBox { title: "Master Files" Layout.fillWidth: true ColumnLayout { anchors.fill: parent spacing: Theme.settingsGridSpacing Repeater { model: root.masterFamilies RowLayout { property string family: modelData Layout.fillWidth: true spacing: Theme.settingsRowSpacing Label { text: family font.bold: true color: Theme.headingColor Layout.preferredWidth: 24 } SettingsTextInput { Layout.fillWidth: true readOnly: true text: root.masterPath(family) placeholderText: "No master selected" color: Theme.bodyColor } SettingsActionButton { text: "Browse" Layout.preferredHeight: Theme.settingsButtonHeight onClicked: { masterPicker.targetFamily = family; masterPicker.open(); } } SettingsActionButton { text: "Clear" Layout.preferredHeight: Theme.settingsButtonHeight onClicked: settingsModel.clearMaster(family) } } } } } // ===== Wafer Behavior ===== SettingsGroupBox { title: "Wafer Behavior" Layout.fillWidth: true ColumnLayout { anchors.fill: parent spacing: Theme.settingsGridSpacing SettingsToggle { text: "Reverse Z Wafer" checked: settingsModel.reverseZWafer onToggled: settingsModel.setReverseZWafer(checked) } } } // ===== Appearance ===== SettingsGroupBox { title: "Appearance" Layout.fillWidth: true RowLayout { anchors.fill: parent spacing: Theme.settingsRowSpacing // Custom pill toggle Rectangle { id: toggleTrack implicitWidth: 52 implicitHeight: 28 radius: height / 2 color: Theme.trackBackground Layout.alignment: Qt.AlignVCenter Layout.preferredWidth: implicitWidth Layout.preferredHeight: implicitHeight Behavior on color { ColorAnimation { duration: 250 easing.type: Easing.InOutQuad } } // Thumb Rectangle { id: toggleThumb width: 22 height: 22 radius: height / 2 color: "white" anchors.verticalCenter: parent.verticalCenter x: Theme.isDarkMode ? parent.width - width - 3 : 3 Behavior on x { NumberAnimation { duration: 250 easing.type: Easing.InOutQuad } } // Drop shadow effect layer.enabled: true layer.effect: null // swap for DropShadow if Qt.labs.effects is available } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: Theme.isDarkMode = !Theme.isDarkMode } } // Label Label { text: Theme.isDarkMode ? "Dark" : "Light" color: Theme.bodyColor font.pixelSize: 13 Layout.alignment: Qt.AlignVCenter Behavior on color { ColorAnimation { duration: 200 } } } Item { Layout.fillWidth: true } } } // ===== Adjustment Entry Point ===== RowLayout { id: adjustmentRow Layout.fillWidth: true spacing: Theme.settingsRowSpacing SettingsActionButton { text: "Edit CSV Metadata" Layout.preferredWidth: Theme.settingsActionWidth Layout.preferredHeight: Theme.settingsButtonHeight onClicked: root.handleEditCsvMetadata() } Item { Layout.fillWidth: true } } } } // ===== Scroll hint overlay (fades out when scrolled) ===== Rectangle { id: scrollHint anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right height: 48 gradient: Gradient { GradientStop { position: 0.0 color: Theme.isDarkMode ? 'rgba(30,30,35,1)' : 'rgba(255,255,255,1)' } GradientStop { position: 1.0 color: Qt.transparent } } Label { anchors.centerIn: parent text: "↓ Scroll for more" font.pixelSize: 13 font.bold: true color: Theme.bodyColor opacity: (settingsScroll.ScrollBar.vertical.position <= 0 && settingsScroll.contentHeight > settingsScroll.height) ? (scrollHintTimer.running ? 1 : 0) : 0 Behavior on opacity { NumberAnimation { duration: 400 } } } } } // ===== Auto-hide scroll hint after first interaction ===== Timer { id: scrollHintTimer running: true repeat: false onTriggered: scrollHint.visible = false } }