Skip to content
Merged
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
29 changes: 16 additions & 13 deletions apps/arstack_studio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ qt_add_qml_module(arstack_studio
qml/ConfigurationHub.qml
qml/PtpExpertPanel.qml
qml/StudioDarkComboBox.qml
qml/StudioDarkCheckBox.qml
qml/DeviceExpertPanel.qml
qml/FirmwarePanel.qml
qml/WaveformExpertPanel.qml
Expand Down Expand Up @@ -171,21 +172,23 @@ target_compile_definitions(arstack_session_harness PRIVATE
ARSTACK_STUDIO_VERSION="${PROJECT_VERSION}"
)

# Use the existing Studio-specific artwork for every branding surface.
# This is intentionally separate from root assets/ used by Workbench.
set(ARSTACK_STUDIO_BRAND_DIR "${CMAKE_CURRENT_LIST_DIR}/../../assets/studio_icon")
qt_add_resources(arstack_studio "studio_branding"
PREFIX "/studio"
BASE "${ARSTACK_STUDIO_BRAND_DIR}"
FILES
"${ARSTACK_STUDIO_BRAND_DIR}/android-chrome-192x192.png"
"${ARSTACK_STUDIO_BRAND_DIR}/android-chrome-512x512.png"
)

if(WIN32)
# One deterministic icon shared by executable, installer and shortcuts.
# Python is already available on supported Windows CI hosts. The icon is
# generated at configure time so RC.exe sees its file before compilation.
find_package(Python3 COMPONENTS Interpreter REQUIRED)
# The exact same embedded icon is copied to stage/ for Inno Setup.
# No Python icon generation or alternative branding paths.
set(ARSTACK_STUDIO_ICON "${CMAKE_CURRENT_BINARY_DIR}/ARStackStudio.ico")
execute_process(
COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_LIST_DIR}/assets/make_studio_icon.py"
"${ARSTACK_STUDIO_ICON}"
RESULT_VARIABLE icon_result
)
if(NOT icon_result EQUAL 0)
message(FATAL_ERROR "Could not generate native ARStack Studio icon")
endif()
configure_file("${ARSTACK_STUDIO_BRAND_DIR}/ARStackStudio.ico"
"${ARSTACK_STUDIO_ICON}" COPYONLY)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ARStackStudio.rc"
"IDI_ARSTACK ICON \"${ARSTACK_STUDIO_ICON}\"\n")
target_sources(arstack_studio PRIVATE
Expand Down
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 {
StudioDarkComboBox {
theme: panel.theme
uiFont: panel.uiFont
id: recoveryPort
Layout.preferredWidth: 170
model: panel.device.ports
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 {
StudioDarkComboBox {
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 {
StudioDarkComboBox {
theme: panel.theme
uiFont: panel.uiFont
id: profileFamilySelector
Layout.fillWidth: true
enabled: panel.session && panel.session.engineeringEditable
Expand Down
55 changes: 52 additions & 3 deletions apps/arstack_studio/qml/PtpExpertPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,53 @@ SurfacePanel {
}
}
Item { Layout.fillWidth: true }
Switch {
id: ptpPower
text: "PTP"
checked: Boolean(panel.device.ptpRunning)
enabled: panel.session && panel.device.ptpAvailable
&& !panel.startPending && !panel.session.updatingFirmware
font.family: panel.uiFont
font.pixelSize: 11
ToolTip.visible: hovered
ToolTip.text: "Start the selected lab PTP role, or stop PTP. State follows verified board telemetry."
onClicked: {
// A Switch flips locally before asynchronous firmware ACK.
// Restore its binding to the sole device-owned state first;
// rejected START/STOP must never advertise the wrong status.
const wantsRunning = checked
checked = Qt.binding(function() { return Boolean(panel.device.ptpRunning) })
if (wantsRunning) panel.startSelectedMode()
else if (panel.session && !panel.session.requestStopPtp() && panel.controller)
panel.controller.showMessage("PTP stop was not accepted. Copy diagnostics.", true)
}
indicator: Rectangle {
implicitWidth: 46
implicitHeight: 24
x: ptpPower.leftPadding
y: (ptpPower.height - height) / 2
radius: 12
color: ptpPower.checked ? "#195b44" : panel.theme.surface2
border.width: 1
border.color: ptpPower.checked ? panel.theme.green : panel.theme.line
Rectangle {
width: 16
height: 16
y: 4
x: ptpPower.checked ? parent.width - width - 4 : 4
radius: 8
color: ptpPower.checked ? panel.theme.green : panel.theme.textSoft
Behavior on x { NumberAnimation { duration: 120 } }
}
}
contentItem: Text {
text: ptpPower.text
color: ptpPower.enabled ? panel.theme.text : panel.theme.muted
font: ptpPower.font
leftPadding: ptpPower.indicator.width + ptpPower.spacing
verticalAlignment: Text.AlignVCenter
}
}
StateBadge {
theme: panel.theme
monoFont: panel.uiFont
Expand Down Expand Up @@ -279,7 +326,9 @@ SurfacePanel {
}
}
Item { Layout.fillWidth: true }
CheckBox {
StudioDarkCheckBox {
theme: panel.theme
uiFont: panel.uiFont
text: "Advanced settings"
checked: panel.advancedVisible
onToggled: panel.advancedVisible = checked
Expand Down Expand Up @@ -376,11 +425,11 @@ SurfacePanel {
NumericField { id: announceField; theme: panel.theme; monoFont: panel.uiFont; text: "1000"; validator: IntValidator { bottom: 100; top: 10000 } }
Label { text: "Sync interval (ms)"; color: panel.theme.muted; font.family: panel.uiFont; font.pixelSize: 9 }
NumericField { id: syncField; theme: panel.theme; monoFont: panel.uiFont; text: "1000"; validator: IntValidator { bottom: 20; top: 5000 } }
CheckBox { id: vlanCheck; text: "VLAN"; font.family: panel.uiFont; font.pixelSize: 10; palette.windowText: panel.theme.text }
StudioDarkCheckBox { id: vlanCheck; theme: panel.theme; uiFont: panel.uiFont; text: "VLAN" }
NumericField { id: vlanField; enabled: vlanCheck.checked; theme: panel.theme; monoFont: panel.uiFont; text: "100"; validator: IntValidator { bottom: 1; top: 4094 } }
Label { text: "VLAN PCP"; color: panel.theme.muted; font.family: panel.uiFont; font.pixelSize: 9 }
NumericField { id: pcpField; enabled: vlanCheck.checked; theme: panel.theme; monoFont: panel.uiFont; text: "4"; validator: IntValidator { bottom: 0; top: 7 } }
CheckBox { id: pdelayCheck; text: "Peer-delay response"; checked: true; font.family: panel.uiFont; font.pixelSize: 10; palette.windowText: panel.theme.text }
StudioDarkCheckBox { id: pdelayCheck; theme: panel.theme; uiFont: panel.uiFont; text: "Peer-delay response"; checked: true }
CalmButton {
theme: panel.theme
uiFont: panel.uiFont
Expand Down
41 changes: 41 additions & 0 deletions apps/arstack_studio/qml/StudioDarkCheckBox.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// One dark, legible checkbox shared across all Studio engineering panels.
import QtQuick
import QtQuick.Controls

CheckBox {
id: control
property var theme
property string uiFont: "Inter"
implicitHeight: 30
spacing: 9
font.family: uiFont
font.pixelSize: 11
indicator: Rectangle {
implicitWidth: 18
implicitHeight: 18
x: control.leftPadding
y: (control.height - height) / 2
radius: 5
color: control.checked ? control.theme.accent : control.theme.surface2
border.width: 1
border.color: control.checked ? control.theme.accent
: control.activeFocus ? control.theme.textSoft : control.theme.line
Text {
anchors.centerIn: parent
visible: control.checked
text: "\u2713"
color: control.theme.bg
font.pixelSize: 13
font.weight: Font.Bold
}
}
contentItem: Text {
leftPadding: control.indicator.width + control.spacing
text: control.text
font: control.font
color: control.enabled ? control.theme.textSoft : control.theme.muted
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
4 changes: 3 additions & 1 deletion apps/arstack_studio/qml/StudioDarkComboBox.qml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,13 @@ ComboBox {
}

delegate: ItemDelegate {
id: choice
required property int index
width: combo.width - 8
implicitHeight: 36
highlighted: combo.highlightedIndex === index
contentItem: Text {
text: modelData
text: combo.textAt(choice.index)
color: combo.theme.text
font: combo.font
verticalAlignment: Text.AlignVCenter
Expand Down
2 changes: 1 addition & 1 deletion apps/arstack_studio/qml/WaveformExpertPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ SurfacePanel {
RowLayout {
anchors.fill: parent
anchors.margins: 12
CheckBox { id: enabledCheck; checked: panel.controller.ctSaturationEnabled; text: "Enable CT saturation stress"; font.family: panel.uiFont; font.pixelSize: 10 }
StudioDarkCheckBox { id: enabledCheck; theme: panel.theme; uiFont: panel.uiFont; checked: panel.controller.ctSaturationEnabled; text: "Enable CT saturation stress" }
Item { Layout.fillWidth: true }
Label { text: panel.controller.ctSaturationEnabled ? "ACTIVE" : "CLEAN SINE"; color: panel.controller.ctSaturationEnabled ? panel.theme.amber : panel.theme.muted; font.family: panel.uiFont; font.pixelSize: 8; font.weight: Font.Bold }
}
Expand Down
5 changes: 5 additions & 0 deletions apps/arstack_studio/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <QEventLoop>
#include <QFile>
#include <QGuiApplication>
#include <QIcon>
#include <QPointer>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
Expand Down Expand Up @@ -438,6 +439,10 @@ int main(int argc, char* argv[]) {

QGuiApplication app(argc, argv);
app.setQuitOnLastWindowClosed(true);
QIcon studioIcon;
studioIcon.addFile(QStringLiteral(":/studio/android-chrome-192x192.png"));
studioIcon.addFile(QStringLiteral(":/studio/android-chrome-512x512.png"));
app.setWindowIcon(studioIcon);
QCoreApplication::setOrganizationName(QStringLiteral("ARStack61850"));
#ifdef ARSTACK_STUDIO_ENGINEERING_PREVIEW
QCoreApplication::setApplicationName(QStringLiteral("ARStack Studio Preview"));
Expand Down
Binary file added assets/studio_icon/ARStackStudio.ico
Binary file not shown.
Loading