Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 33 additions & 13 deletions apps/arstack_studio/qml/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ARStack.Studio 1.0
ApplicationWindow {
id: root
width: 1150
height: 790
height: 800
minimumWidth: 960
minimumHeight: 620
visible: true
Expand Down Expand Up @@ -1065,11 +1065,30 @@ ApplicationWindow {
objectName: "studioInjectionSurface"
SplitView.fillWidth: true
SplitView.fillHeight: true
SplitView.minimumWidth: root.compactLayout ? 500 : 540
SplitView.preferredWidth: root.compactLayout ? 600 : 690
SplitView.maximumWidth: 750
// Hard width budget prevents input columns from stretching
// at desktop sizes. Visualization owns spare horizontal
// space; operator may still resize within useful bounds.
SplitView.minimumWidth: 445
SplitView.preferredWidth: root.width < 1100 ? 450 : 465
SplitView.maximumWidth: 490
theme: studioTheme

// Click on unoccupied injection panel space commits the
// currently focused table editor through editingFinished.
// Child input controls remain above this passive surface.
Item {
id: injectionFocusSink
width: 0
height: 0
focus: false
}
MouseArea {
anchors.fill: parent
z: 0
acceptedButtons: Qt.LeftButton
onClicked: injectionFocusSink.forceActiveFocus()
}

ColumnLayout {
anchors.fill: parent
anchors.margins: root.compactLayout ? 11 : 13
Expand Down Expand Up @@ -1296,9 +1315,9 @@ ApplicationWindow {
SplitView {
visible: (root.phasorDockVisible && !root.phasorDetached) ||
(root.waveformDockVisible && !root.waveformDetached)
SplitView.preferredWidth: root.compactLayout ? 450 : 470
SplitView.minimumWidth: root.compactLayout ? 300 : 340
SplitView.maximumWidth: 680
SplitView.preferredWidth: Math.max(440, root.width - 540)
SplitView.minimumWidth: 410
SplitView.fillWidth: true
SplitView.fillHeight: true
orientation: Qt.Vertical
handle: Rectangle {
Expand All @@ -1317,10 +1336,11 @@ ApplicationWindow {
DockFrame {
visible: root.phasorDockVisible && !root.phasorDetached
SplitView.fillWidth: true
SplitView.minimumHeight: 170
SplitView.preferredHeight: 225
SplitView.maximumHeight: 265
SplitView.fillHeight: false
// Phasor and waveform each own half of available
// height; no hidden 265px cap on the phasor plot.
SplitView.minimumHeight: 245

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep chart minima within the minimum window height

At the supported 620 px minimum window height with telemetry expanded, the outer layout leaves about 421 px for workspaceSplit after the 78 px workflow bar, margins, telemetry's 88 px minimum, and split handles. The nested visualization now requires 245 + 245 + 9 = 499 px for the two charts and its handle, so its constraints cannot be satisfied and one chart must shrink below its declared minimum or be clipped. Reduce these minima, raise the window minimum, or make this state scrollable.

Useful? React with 👍 / 👎.

SplitView.preferredHeight: 310
SplitView.fillHeight: true
theme: studioTheme
titleText: "Phasor"
statusText: ""
Expand Down Expand Up @@ -1357,8 +1377,8 @@ ApplicationWindow {
DockFrame {
visible: root.waveformDockVisible && !root.waveformDetached
SplitView.fillWidth: true
SplitView.minimumHeight: 170
SplitView.preferredHeight: 328
SplitView.minimumHeight: 245
SplitView.preferredHeight: 310
SplitView.fillHeight: true
theme: studioTheme
titleText: "Waveform"
Expand Down
11 changes: 9 additions & 2 deletions apps/arstack_studio/qml/SignalMatrix.qml
Original file line number Diff line number Diff line change
Expand Up @@ -313,10 +313,15 @@ Rectangle {
var value = matrix.controller.parseOperatorNumber(text)
if (matrix.controller.validMagnitude(matrix.groupIndex, value)) {
invalidInput = false
// Validate immediately, commit atomically only
// at Enter, Tab or focus-out. No 90ms race
// sends half-entered numeric values to board.
signalRow.pendingMagnitude = value
magnitudeApplyTimer.restart()
} else invalidInput = true
}
// Qt Quick editingFinished fires on Return AND loss of
// active focus (Tab, other field, empty workbench click).
// Both decimal separators are parsed by controller.
onEditingFinished: {
var value = matrix.controller.parseOperatorNumber(text)
if (!matrix.controller.validMagnitude(matrix.groupIndex, value)) {
Expand Down Expand Up @@ -377,9 +382,11 @@ Rectangle {
if (matrix.controller.validPhase(value)) {
invalidInput = false
signalRow.pendingPhase = value
phaseApplyTimer.restart()
} else invalidInput = true
}
// Qt Quick editingFinished fires on Return AND loss of
// active focus (Tab, other field, empty workbench click).
// Both decimal separators are parsed by controller.
onEditingFinished: {
var value = matrix.controller.parseOperatorNumber(text)
if (!matrix.controller.validPhase(value)) {
Expand Down
19 changes: 14 additions & 5 deletions apps/arstack_studio/tests/check_operator_workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ def main() -> None:
require("function flushPendingEdits()" in matrix
and "magnitudeApplyTimer.stop()" in matrix
and "phaseApplyTimer.stop()" in matrix
and "magnitudeApplyTimer.restart()" not in matrix
and "phaseApplyTimer.restart()" not in matrix
and matrix.count("onEditingFinished:") >= 2
and "parseOperatorNumber(text)" in matrix
and "currentMatrix.flushPendingEdits()" in main_qml
and "voltageMatrix.flushPendingEdits()" in main_qml
and "if (!scenario) return" in source("WorkflowBar.qml"),
Expand Down Expand Up @@ -377,9 +381,10 @@ def main() -> None:
assert "text: \"Generated setpoint · not independent on-wire proof\"" not in preview
assert "No ESP32 board detected · Preview mode only" in main_qml
require("Layout.preferredHeight: 57" in main_qml
and "SplitView.maximumHeight: 265" in main_qml
and "SplitView.fillHeight: false" in main_qml,
"compact timing strip and bounded phasor panel must avoid dead space")
and main_qml.count("SplitView.preferredHeight: 310") >= 2
and main_qml.count("SplitView.minimumHeight: 245") >= 2
and "SplitView.maximumHeight: 265" not in main_qml,
"compact timing strip and equal-height phasor/waveform must avoid dead space")
require("TIME SYNC / SV SIMULATION" not in tray
and "StateBadge {" not in tray,
"device/PTP status must not be duplicated in the control tray")
Expand Down Expand Up @@ -544,8 +549,12 @@ def main() -> None:
ribbon_layout = source("ModernRibbon.qml")
start_flow = source("WorkflowBar.qml")
require("width: 1150" in responsive_main
and "SplitView.maximumWidth: 750" in responsive_main
and "SplitView.preferredWidth: root.compactLayout ? 450 : 470" in responsive_main
and "SplitView.maximumWidth: 490" in responsive_main
and "SplitView.preferredWidth: root.width < 1100 ? 450 : 465" in responsive_main
and "SplitView.preferredHeight: 310" in responsive_main
and "SplitView.maximumHeight: 265" not in responsive_main

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the contradictory phasor-height assertion

This negative assertion conflicts with lines 383–385 of the same test, which still require SplitView.maximumHeight: 265 and SplitView.fillHeight: false. At this commit, python3 apps/arstack_studio/tests/check_operator_workspace.py therefore exits at the earlier assertion, and .github/workflows/arstack-studio-qt.yml:185 runs this script in Qt CI, so every such workflow run fails before reaching the new contract. Update or remove the stale earlier assertion together with this one.

Useful? React with 👍 / 👎.

and "id: injectionFocusSink" in responsive_main
and "onClicked: injectionFocusSink.forceActiveFocus()" in responsive_main
and "electric_bolt_run.svg" in ribbon_layout
and "electric_bolt_stop.svg" in ribbon_layout
and "assets/material/electric_bolt_run.svg" in
Expand Down
Loading