Repository navigation
P0 Studio UX: exact 4I+4V START, vertical matrices, default PTP and centered controls #236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c3f91fb
8843d9c
a2d938c
284e114
bc0be48
e2fe937
f50c376
72b5592
7b96dad
25c7a1a
c72cf68
cd85669
5d7e3da
7f4bd26
620ff15
5c2b72e
651076a
b4337c6
5dbc499
db7558d
d02bb03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,7 +54,8 @@ ApplicationWindow { | |
| saveScenarioTimer.restart() | ||
| // While STOPPED, a new edit belongs to the next supervised Start batch. | ||
| // Only a genuinely RUNNING board receives incremental live SETs. | ||
| if (!workflowBar.injectionRunning) | ||
| if (!workflowBar.injectionRunning || | ||
| !workflowBar.session || !workflowBar.session.liveControlReady) | ||
| pendingApply = true | ||
| } | ||
| function restoreLocalScenario() { | ||
|
|
@@ -119,6 +120,8 @@ ApplicationWindow { | |
| // an explicit PTP ON can be restored after a firmware reflash/reconnect | ||
| // without introducing an uncontrolled power-on publisher. | ||
| property bool ptpDesiredForSession: false | ||
| property bool ptpDefaultStartPending: true | ||
| property bool ptpExplicitOff: false | ||
| property string ptpDesiredRole: "SOURCE" | ||
| property bool ptpRestoreAfterFirmware: false | ||
| property int ptpRestoreChecks: 0 | ||
|
|
@@ -198,6 +201,8 @@ ApplicationWindow { | |
| function onDeviceVerifiedChanged() { | ||
| root.pendingApply = true | ||
| root.offlinePreviewRunning = false | ||
| if (device.deviceVerified && !root.ptpExplicitOff) | ||
| root.ptpDefaultStartPending = true | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -215,6 +220,28 @@ ApplicationWindow { | |
| } | ||
| } | ||
|
|
||
| // One automatic SOURCE start on verified connection. An explicit OFF | ||
| // cancels this for the remainder of the Studio session; never send PTP | ||
| // commands without a verified board or while firmware is updating. | ||
| Timer { | ||
| interval: 400 | ||
| repeat: true | ||
| running: root.ptpDefaultStartPending && !root.ptpExplicitOff && device.deviceVerified | ||
| onTriggered: { | ||
| if (device.ptpRunning) { | ||
| root.ptpDefaultStartPending = false | ||
| return | ||
| } | ||
| const session = workflowBar.session | ||
| if (!session || !session.startReady || session.updatingFirmware || | ||
| device.ptpStarting || !device.ptpAvailable) | ||
| return | ||
| root.ptpDefaultStartPending = false | ||
| if (!root.startPtpRole("SOURCE")) | ||
| root.showMessage("PTP auto-start was not accepted. Use PTP On to retry.", true) | ||
|
Comment on lines
+239
to
+241
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
After an operator starts RECEIVER or MONITOR, a serial disconnect/reconnect re-arms Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
|
|
||
| Timer { | ||
| interval: 300 | ||
| repeat: true | ||
|
|
@@ -317,13 +344,17 @@ ApplicationWindow { | |
| const accepted = profilePanel.startPtpRole(role) | ||
| if (accepted) { | ||
| root.ptpDesiredForSession = true | ||
| root.ptpExplicitOff = false | ||
| root.ptpDefaultStartPending = false | ||
| root.ptpDesiredRole = role | ||
| } | ||
| return accepted | ||
| } | ||
|
|
||
| function requestStopPtp() { | ||
| root.ptpDesiredForSession = false | ||
| root.ptpDefaultStartPending = false | ||
| root.ptpExplicitOff = true | ||
| root.ptpRestoreAfterFirmware = false | ||
| return workflowBar.session && workflowBar.session.requestStopPtp() | ||
| } | ||
|
|
@@ -468,8 +499,11 @@ ApplicationWindow { | |
| selectSignal(group, row) | ||
| markScenarioDirty() | ||
| if (workflowBar.session && workflowBar.session.liveControlReady && !pendingApply) { | ||
| if (phaseLink && row < 3) sendLinkedGroup(group) | ||
| else sendSignal(group, row) | ||
| const sent = phaseLink && row < 3 ? sendLinkedGroup(group) : sendSignal(group, row) | ||
| if (!sent) { | ||
| pendingApply = true | ||
| showMessage("Setpoint not queued. STOP and START to apply all displayed 4I+4V values.", true) | ||
|
Comment on lines
+502
to
+505
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the worker queue is full during a live edit, AGENTS.md reference: AGENTS.md:L116-L116 Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
| return true | ||
| } | ||
|
|
@@ -503,8 +537,9 @@ ApplicationWindow { | |
| frequencyField.invalidInput = false | ||
| refreshPreview() | ||
| markScenarioDirty() | ||
| if (workflowBar.session && workflowBar.session.liveControlReady && !pendingApply) | ||
| workflowBar.session.requestSetFrequency(value) | ||
| if (workflowBar.session && workflowBar.session.liveControlReady && !pendingApply && | ||
| !workflowBar.session.requestSetFrequency(value)) | ||
| pendingApply = true | ||
| return true | ||
| } | ||
|
|
||
|
|
@@ -610,17 +645,19 @@ ApplicationWindow { | |
| selectSignal(activeGroup, activeRow) | ||
| refreshPreview() | ||
| markScenarioDirty() | ||
| if (workflowBar.session && workflowBar.session.liveControlReady && !pendingApply) | ||
| workflowBar.session.requestZero() | ||
| if (workflowBar.session && workflowBar.session.liveControlReady && !pendingApply && | ||
| !workflowBar.session.requestZero()) | ||
| pendingApply = true | ||
| } | ||
|
|
||
| function setActiveQuality(value) { | ||
| var unsignedValue = Number(value) >>> 0 | ||
| groupModel(activeGroup).setProperty(activeRow, "quality", unsignedValue) | ||
| activeQuality = unsignedValue | ||
| markScenarioDirty() | ||
| if (workflowBar.session && workflowBar.session.liveControlReady && !pendingApply) | ||
| workflowBar.session.requestSetQuality(activeSignal, unsignedValue) | ||
| if (workflowBar.session && workflowBar.session.liveControlReady && !pendingApply && | ||
| !workflowBar.session.requestSetQuality(activeSignal, unsignedValue)) | ||
| pendingApply = true | ||
| } | ||
|
|
||
| Shortcut { sequence: "Ctrl+B"; enabled: !workflowBar.startQueued; onActivated: root.balanced() } | ||
|
|
@@ -698,11 +735,12 @@ ApplicationWindow { | |
| } | ||
| Rectangle { width: 1; height: 12; color: studioTheme.lineSoft } | ||
| Label { | ||
| text: root.pendingApply && !workflowBar.injectionRunning | ||
| ? "4I + 4V · Changes sent on Start" | ||
| text: root.pendingApply | ||
| ? (workflowBar.injectionRunning | ||
| ? "4I + 4V · Changes pending · restart to apply" | ||
| : "4I + 4V · Changes sent on Start") | ||
| : "4I + 4V · 4000 samples/s" | ||
| color: root.pendingApply && !workflowBar.injectionRunning | ||
| ? studioTheme.amber : studioTheme.muted | ||
| color: root.pendingApply ? studioTheme.amber : studioTheme.muted | ||
| font.family: root.uiFont | ||
| font.pixelSize: 9 | ||
| } | ||
|
|
@@ -930,10 +968,31 @@ ApplicationWindow { | |
| SplitView.preferredWidth: root.compactLayout ? 700 : 860 | ||
| theme: studioTheme | ||
|
|
||
| ColumnLayout { | ||
| // A narrow Windows viewport cannot display two full | ||
| // engineering tables plus timing/quality at once. Scroll | ||
| // the injection editor as ONE surface at small heights; | ||
| // normal desktop sizes fully expand the two matrices. | ||
| ScrollView { | ||
| id: injectionScroll | ||
| objectName: "studioInjectionScroll" | ||
| anchors.fill: parent | ||
| anchors.margins: root.compactLayout ? 11 : 13 | ||
| spacing: root.compactLayout ? 6 : 8 | ||
| clip: true | ||
| ScrollBar.horizontal.policy: ScrollBar.AlwaysOff | ||
| ScrollBar.vertical.policy: ScrollBar.AsNeeded | ||
| contentWidth: availableWidth | ||
| contentHeight: injectionEditor.y + injectionEditor.height | ||
| + (root.compactLayout ? 8 : 9) | ||
|
|
||
| ColumnLayout { | ||
| id: injectionEditor | ||
| x: root.compactLayout ? 8 : 9 | ||
| y: root.compactLayout ? 8 : 9 | ||
| width: Math.max(1, injectionScroll.availableWidth - | ||
| 2 * (root.compactLayout ? 8 : 9)) | ||
| height: Math.max(implicitHeight, | ||
| injectionScroll.availableHeight - | ||
| 2 * (root.compactLayout ? 8 : 9)) | ||
| spacing: root.compactLayout ? 4 : 5 | ||
|
|
||
| RowLayout { | ||
| Layout.fillWidth: true | ||
|
|
@@ -1049,11 +1108,15 @@ ApplicationWindow { | |
| } | ||
| } | ||
|
|
||
| RowLayout { | ||
| // Stack voltage above current. Both tables share the | ||
| // available height instead of leaving an empty lower | ||
| // half; the inner rows stretch with the desktop. | ||
| ColumnLayout { | ||
| Layout.fillWidth: true | ||
| Layout.preferredHeight: root.compactLayout ? 214 : 224 | ||
| Layout.maximumHeight: root.compactLayout ? 214 : 224 | ||
| spacing: 9 | ||
| Layout.fillHeight: true | ||
| Layout.minimumHeight: 408 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
At the declared 620 px window minimum, the footer, outer margins, workflow bar, and panel margins leave about 474 px inside the injection surface. This 408 px matrix minimum plus the 57 px timing tray and spacing already consumes essentially all of that space before the title, 48 px frequency row, separator, and quality row are laid out, so the editor must overflow or clip at a supported window size. Either raise the window minimum, make this section scrollable, or allow the matrices to shrink further. Useful? React with 👍 / 👎. |
||
| Layout.preferredHeight: 422 | ||
| spacing: 7 | ||
| SignalMatrix { | ||
| id: voltageMatrix | ||
| enabled: !workflowBar.startQueued | ||
|
|
@@ -1136,7 +1199,7 @@ ApplicationWindow { | |
| compact: root.compactLayout | ||
| } | ||
| } | ||
| Item { Layout.fillHeight: true } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The replacement
Itemhas an implicit width of zero and does not inherit the inner Row's implicit size, while the button still computes its width fromcontentItem.implicitWidth. Consequently every CalmButton without an explicit width now resolves to the 64 px minimum, causing captions such as “Start PTP Receiver” and “Copy diagnostics” to overflow or overlap adjacent controls. Give the content item the Row's implicit dimensions or retain a content item that propagates child sizing.Useful? React with 👍 / 👎.