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
10 changes: 10 additions & 0 deletions apps/arstack_studio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ qt_add_qml_module(arstack_studio
qml/RibbonAction.qml
qml/CalmButton.qml
qml/NumericField.qml
qml/DarkComboBox.qml
qml/DarkCheckBox.qml
qml/SignalQuickMenu.qml
qml/StateBadge.qml
qml/ProfileInspector.qml
qml/ConfigurationHub.qml
Expand Down Expand Up @@ -170,6 +173,13 @@ target_compile_definitions(arstack_session_harness PRIVATE
ARSTACK_STUDIO_VERSION="${PROJECT_VERSION}"
)

# Dedicated ARStack Studio icon shared by Qt window, Windows PE, and Inno.
qt_add_resources(arstack_studio "studio_branding"
PREFIX "/studio"
BASE "${CMAKE_CURRENT_LIST_DIR}/../../assets/studio_icon"
FILES "${CMAKE_CURRENT_LIST_DIR}/../../assets/studio_icon/favicon.ico"
)
if(WIN32)
target_sources(arstack_studio PRIVATE src/StudioIcon.rc)
set_target_properties(arstack_studio PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
2 changes: 2 additions & 0 deletions apps/arstack_studio/packaging/ARStackStudio.iss
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ UninstallDisplayName={#ProductName}
DefaultDirName={localappdata}\Programs\{#ProductFolder}
DefaultGroupName={#ProductName}
OutputBaseFilename={#ProductOutput}
SetupIconFile=..\..\..\assets\studio_icon\favicon.ico
UninstallDisplayIcon={app}\ARStackStudio.exe
PrivilegesRequired=lowest
ArchitecturesAllowed=x64compatible
CloseApplications=yes
Expand Down
8 changes: 4 additions & 4 deletions apps/arstack_studio/qml/ConfigurationHub.qml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Item {
font.weight: Font.DemiBold
}
Label {
text: "Firmware recovery, engineering profile, waveform and timing tools"
text: "SIMULATION LAB / 4I+4V · PTP · Firmware · Diagnostics"
color: hub.theme.muted
font.family: hub.uiFont
font.pixelSize: 11
Expand Down Expand Up @@ -121,10 +121,10 @@ Item {
border.width: 1
border.color: hub.theme.lineSoft
}
ExpertTab { text: "Injection" }
ExpertTab { text: "Signal profile" }
ExpertTab { text: "Firmware" }
ExpertTab { text: "Waveform" }
ExpertTab { text: "Timing" }
ExpertTab { text: "Wave shaping" }
ExpertTab { text: "PTP / Sync" }
ExpertTab { text: "Device" }
}

Expand Down
38 changes: 38 additions & 0 deletions apps/arstack_studio/qml/DarkCheckBox.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls

CheckBox {
id: control
property var theme
property string uiFont: "Inter"
font.family: uiFont
font.pixelSize: 11
implicitHeight: 29
spacing: 8
indicator: Rectangle {
implicitWidth: 17
implicitHeight: 17
x: 1
y: (control.height - height) / 2
radius: 4
color: control.checked ? control.theme.accent : control.theme.surface2
border.width: 1
border.color: control.checked ? control.theme.accent : control.theme.line
Text {
anchors.centerIn: parent
text: "✓"
visible: control.checked
color: control.theme.bg
font.pixelSize: 12
font.weight: Font.Bold
}
}
contentItem: Text {
text: control.text
font: control.font
color: control.enabled ? control.theme.textSoft : control.theme.muted
verticalAlignment: Text.AlignVCenter
leftPadding: control.indicator.width + control.spacing
}
}
77 changes: 77 additions & 0 deletions apps/arstack_studio/qml/DarkComboBox.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls

ComboBox {
id: control
property var theme
property string uiFont: "Inter"
implicitWidth: 180
implicitHeight: 35
font.family: uiFont
font.pixelSize: 11
hoverEnabled: true
contentItem: Text {
text: control.displayText
color: control.enabled ? control.theme.text : control.theme.muted
font: control.font
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
leftPadding: 11
rightPadding: 30
}
indicator: Text {
text: "⌄"
color: control.theme.textSoft
font.pixelSize: 18
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 12
}
background: Rectangle {
radius: 7
color: control.hovered ? control.theme.raisedHover : control.theme.surface2
border.width: 1
border.color: control.activeFocus ? control.theme.accent : control.theme.line
}
delegate: ItemDelegate {
id: item
required property int index
// textAt resolves QStringList and QAbstractItemModel textRole.
width: control.width
height: 35
highlighted: control.highlightedIndex === index
contentItem: Text {
text: control.textAt(item.index)
font: control.font
color: control.theme.text
leftPadding: 9
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
radius: 5
color: item.highlighted ? control.theme.accentSoft
: item.hovered ? control.theme.raisedHover : "transparent"
}
}
popup: Popup {
y: control.height + 3
width: control.width
implicitHeight: Math.min(contentItem.implicitHeight + 10, 220)
padding: 5
contentItem: ListView {
implicitHeight: contentHeight
clip: true
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator {}
}
background: Rectangle {
radius: 8
color: control.theme.raised
border.width: 1
border.color: control.theme.line
}
}
}
4 changes: 3 additions & 1 deletion apps/arstack_studio/qml/DeviceExpertPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ SurfacePanel {

RowLayout {
Layout.fillWidth: true
ComboBox {
DarkComboBox {
theme: panel.theme
uiFont: panel.uiFont
id: recoveryPort
Layout.preferredWidth: 170
model: panel.device.ports
Expand Down
81 changes: 59 additions & 22 deletions apps/arstack_studio/qml/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,52 @@ ApplicationWindow {
return true
}

// Three-phase lab shortcuts; neutral is deliberately untouched.
// Existing live setpoint session owns the serial writer and ordering.
function quickSetGroup(group, row, action) {
if (row < 0 || row > 2) return false
var model = groupModel(group)
var anchor = model.get(row)
var i
if (action === "equal_value") {
if (!validMagnitude(group, anchor.magnitude)) return false
for (i = 0; i < 3; ++i) model.setProperty(i, "magnitude", anchor.magnitude)
} else if (action === "equal_angle") {
if (signalFrequency <= 0 || !validPhase(anchor.phase)) return false
for (i = 0; i < 3; ++i) model.setProperty(i, "phase", anchor.phase)
} else if (action === "balance_angle") {
if (signalFrequency <= 0 || !validPhase(anchor.phase)) return false
var a = anchor.phase + (row === 1 ? 120 : row === 2 ? -120 : 0)
var angles = [normalizedAngle(a), normalizedAngle(a - 120), normalizedAngle(a + 120)]
for (i = 0; i < 3; ++i) model.setProperty(i, "phase", angles[i])
} else return false
selectSignal(group, row)
var submitted = false
if (workflowBar.session && workflowBar.session.liveControlReady) {
var batch = []
for (i = 0; i < 3; ++i) {
var channel = model.get(i)
batch.push({
signalId: channel.signalId,
magnitude: channel.magnitude,
phase: channel.phase,
quality: Number(channel.quality) >>> 0
})
}
submitted = workflowBar.session.requestSetSignalsBatch(
batch, currentScale, voltageScale)
if (!submitted) {
showMessage("ABC changed locally; complete live batch rejected. Copy diagnostics.", true)
return false
}
}
showMessage((group === 0 ? "Current" : "Voltage") + " ABC: " +
(action === "equal_value" ? "Equal Value" : action === "equal_angle" ? "Equal Angle" : "Balance 120°") +
(submitted ? " · 3 commands submitted together" : " · offline preview") +
" · neutral unchanged", false)
return true
}

function applyAllSignals() {
if (!workflowBar.session || !workflowBar.session.liveControlReady) return
workflowBar.session.requestSetFrequency(signalFrequency)
Expand Down Expand Up @@ -472,10 +518,10 @@ ApplicationWindow {

Window {
id: configurationWindow
width: 760
height: 780
minimumWidth: 640
minimumHeight: 660
width: 860
height: 750
minimumWidth: 700
minimumHeight: 620
visible: false
title: "ARStack Studio · Advanced"
color: studioTheme.bg
Expand All @@ -485,27 +531,17 @@ ApplicationWindow {
configurationWindow.hide()
}

DockFrame {
ConfigurationHub {
id: profilePanel
anchors.fill: parent
anchors.margins: 10
anchors.margins: 18
theme: studioTheme
titleText: "Advanced"
statusText: device.deviceVerified ? "DEVICE CONNECTED" : "OFFLINE"
controller: root
device: device
profiles: sclProfiles
session: workflowBar.session
uiFont: root.uiFont
monoFont: root.monoFont
closable: false

ConfigurationHub {
id: profilePanel
anchors.fill: parent
theme: studioTheme
controller: root
device: device
profiles: sclProfiles
session: workflowBar.session
uiFont: root.uiFont
monoFont: root.monoFont
}
}
}

Expand Down Expand Up @@ -726,7 +762,8 @@ ApplicationWindow {
CalmButton { theme: studioTheme; uiFont: root.uiFont; text: "50"; implicitWidth: 44; onClicked: root.setFrequencyValue(50) }
CalmButton { theme: studioTheme; uiFont: root.uiFont; text: "60"; implicitWidth: 44; onClicked: root.setFrequencyValue(60) }
Rectangle { width: 1; height: 24; color: studioTheme.lineSoft }
CheckBox {
DarkCheckBox {
theme: studioTheme
enabled: root.signalFrequency > 0
checked: root.phaseLink
text: "3-phase link"
Expand Down
8 changes: 6 additions & 2 deletions apps/arstack_studio/qml/ProfileInspector.qml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ SurfacePanel {
font.letterSpacing: 1.0
}

ComboBox {
DarkComboBox {
theme: panel.theme
uiFont: panel.uiFont
Layout.fillWidth: true
model: panel.profiles
textRole: "control"
Expand Down Expand Up @@ -308,7 +310,9 @@ SurfacePanel {
wrapMode: Text.WordWrap
}

ComboBox {
DarkComboBox {
theme: panel.theme
uiFont: panel.uiFont
id: profileFamilySelector
Layout.fillWidth: true
enabled: panel.session && panel.session.engineeringEditable
Expand Down
Loading
Loading