From 0146bcb6f2bde0fb9ea90f0de2531e540ac82f50 Mon Sep 17 00:00:00 2001 From: Jon ESA Date: Wed, 1 Apr 2026 19:43:28 +0100 Subject: [PATCH] Fixed games tab - went rogue but its looks better --- main.cpp | 370 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 331 insertions(+), 39 deletions(-) diff --git a/main.cpp b/main.cpp index 3c5dea0..261888f 100644 --- a/main.cpp +++ b/main.cpp @@ -1,9 +1,15 @@ #include +#include +#include #include +#include #include #include -#include #include +#include +#include +#include +#include #include #include #include @@ -36,11 +42,198 @@ static QWidget *makeSettingsTab(WebSocketController *ctrl, QWidget *parent) auto *page = new QWidget(parent); auto *layout = new QVBoxLayout(page); layout->setContentsMargins(2, 2, 2, 2); + layout->setSpacing(4); + + // --- Sliders row: brightness and volume side by side (top) --- + auto *slidersRow = new QHBoxLayout(); + + auto *brightnessLabel = new QLabel("Brightness:", page); + auto *brightnessSlider = new QSlider(Qt::Horizontal, page); + brightnessSlider->setMinimum(1); + brightnessSlider->setMaximum(12); + brightnessSlider->setValue(1); + brightnessSlider->setEnabled(false); + auto *brightnessVal = new QLabel("–", page); + brightnessVal->setFixedWidth(28); + + auto *volumeLabel = new QLabel("Volume:", page); + auto *volumeSlider = new QSlider(Qt::Horizontal, page); + volumeSlider->setMinimum(1); + volumeSlider->setMaximum(6); + volumeSlider->setValue(1); + volumeSlider->setEnabled(false); + auto *volumeVal = new QLabel("–", page); + volumeVal->setFixedWidth(28); + + slidersRow->addWidget(brightnessLabel); + slidersRow->addWidget(brightnessSlider, 1); + slidersRow->addWidget(brightnessVal); + slidersRow->addSpacing(16); + slidersRow->addWidget(volumeLabel); + slidersRow->addWidget(volumeSlider, 1); + slidersRow->addWidget(volumeVal); + layout->addLayout(slidersRow); + + // --- Tree (takes all spare vertical space) --- auto *tree = new SettingsTree(page); auto *ph = new QTreeWidgetItem(tree); ph->setText(0, "Connect to load settings..."); layout->addWidget(tree, 1); ctrl->setSettingsTree(tree); + + // --- Single compact toolbar row: name input + 3 GBL buttons + checkbox (below tree) --- + auto *toolbar = new QHBoxLayout(); + auto *nameEdit = new QLineEdit(page); + nameEdit->setPlaceholderText("Config name..."); + nameEdit->setMaximumWidth(160); + auto *saveBtn = new QPushButton("Save", page); + auto *loadBtn = new QPushButton("Restore From File", page); + auto *resetBtn = new QPushButton("Restore Defaults", page); + resetBtn->setStyleSheet( + "QPushButton { color: white; background-color: #c62828; border-radius: 4px; padding: 3px 8px; }" + "QPushButton:hover { background-color: #b71c1c; }" + "QPushButton:disabled { background-color: #888888; color: #cccccc; }"); + auto *autoPowerOff = new QCheckBox("Auto Power Off", page); + toolbar->addWidget(nameEdit); + toolbar->addWidget(saveBtn); + toolbar->addWidget(loadBtn); + toolbar->addWidget(resetBtn); + toolbar->addStretch(1); + toolbar->addWidget(autoPowerOff); + layout->addLayout(toolbar); + + // --- Reset to defaults (inline confirm row — no QMessageBox::exec on WASM) --- + auto *confirmRow = new QHBoxLayout(); + auto *confirmLabel = new QLabel(page); + confirmLabel->setText("Click again to confirm reset:"); + confirmLabel->setStyleSheet("color: #c62828;"); + confirmLabel->setVisible(false); + auto *confirmYesBtn = new QPushButton("Yes, Reset", page); + confirmYesBtn->setStyleSheet( + "QPushButton { color: white; background-color: #c62828; border-radius: 4px; padding: 2px 8px; }"); + confirmYesBtn->setVisible(false); + auto *confirmNoBtn = new QPushButton("Cancel", page); + confirmNoBtn->setVisible(false); + confirmRow->addWidget(confirmLabel); + confirmRow->addWidget(confirmYesBtn); + confirmRow->addWidget(confirmNoBtn); + confirmRow->addStretch(1); + layout->addLayout(confirmRow); + + // ---------------------------------------------------------------- + // Connections + // ---------------------------------------------------------------- + + // Save config + auto doSave = [ctrl, nameEdit]() { + QString name = nameEdit->text().trimmed(); + name.remove(QRegularExpression("[^A-Za-z0-9]")); + if (name.isEmpty()) return; + ctrl->sendCommand(QString("GBL SaveToFile %1").arg(name)); + nameEdit->clear(); + }; + QObject::connect(saveBtn, &QPushButton::clicked, page, doSave); + QObject::connect(nameEdit, &QLineEdit::returnPressed, page, doSave); + + // Restore from file + QObject::connect(loadBtn, &QPushButton::clicked, page, [ctrl, nameEdit]() { + QString name = nameEdit->text().trimmed(); + name.remove(QRegularExpression("[^A-Za-z0-9]")); + if (name.isEmpty()) return; + ctrl->sendCommand(QString("GBL LoadFromFile %1").arg(name)); + nameEdit->clear(); + }); + + // Reset confirm + QObject::connect(resetBtn, &QPushButton::clicked, page, + [confirmLabel, confirmYesBtn, confirmNoBtn]() { + confirmLabel->setVisible(true); + confirmYesBtn->setVisible(true); + confirmNoBtn->setVisible(true); + }); + QObject::connect(confirmYesBtn, &QPushButton::clicked, page, + [ctrl, confirmLabel, confirmYesBtn, confirmNoBtn]() { + ctrl->sendCommand(QStringLiteral("GBL ResetAllToDefaults")); + confirmLabel->setVisible(false); + confirmYesBtn->setVisible(false); + confirmNoBtn->setVisible(false); + }); + QObject::connect(confirmNoBtn, &QPushButton::clicked, page, + [confirmLabel, confirmYesBtn, confirmNoBtn]() { + confirmLabel->setVisible(false); + confirmYesBtn->setVisible(false); + confirmNoBtn->setVisible(false); + }); + + // Auto power off + QObject::connect(autoPowerOff, &QCheckBox::toggled, page, [ctrl](bool checked) { + ctrl->sendCommand(checked ? QStringLiteral("POW EnableAutoOff") + : QStringLiteral("POW KeepOn")); + }); + + // Query brightness/volume ranges+values on connect; disable on disconnect + QObject::connect(ctrl->socket(), &QWebSocket::connected, page, [ctrl]() { + ctrl->sendCommand(QStringLiteral("GBL brightnessMin")); + ctrl->sendCommand(QStringLiteral("GBL brightnessMax")); + ctrl->sendCommand(QStringLiteral("GBL brightness")); + ctrl->sendCommand(QStringLiteral("GBL sound/volumeMin")); + ctrl->sendCommand(QStringLiteral("GBL sound/volumeMax")); + ctrl->sendCommand(QStringLiteral("GBL sound/volume")); + }); + QObject::connect(ctrl->socket(), &QWebSocket::disconnected, page, + [brightnessSlider, brightnessVal, volumeSlider, volumeVal]() { + brightnessSlider->setEnabled(false); + brightnessVal->setText(QStringLiteral("–")); + volumeSlider->setEnabled(false); + volumeVal->setText(QStringLiteral("–")); + }); + + // Parse incoming GBL brightness / sound/volume responses + QObject::connect(ctrl->socket(), &QWebSocket::textMessageReceived, page, + [brightnessSlider, brightnessVal, volumeSlider, volumeVal](const QString &msg) { + // helper: parse "GBL =" and return {ok, value} + auto parseGbl = [&](const QString &key) -> std::pair { + const QString prefix = QString("GBL %1=").arg(key); + if (!msg.startsWith(prefix)) return {false, 0}; + bool ok = false; + int v = msg.mid(prefix.length()).toInt(&ok); + return {ok, v}; + }; + + if (auto [ok, v] = parseGbl("brightness"); ok) { + brightnessSlider->setValue(v); + brightnessVal->setText(QString::number(v)); + brightnessSlider->setEnabled(true); + } else if (auto [ok, v] = parseGbl("brightnessMin"); ok) { + brightnessSlider->setMinimum(v); + } else if (auto [ok, v] = parseGbl("brightnessMax"); ok) { + brightnessSlider->setMaximum(v); + } else if (auto [ok, v] = parseGbl("sound/volume"); ok) { + volumeSlider->setValue(v); + volumeVal->setText(QString::number(v)); + volumeSlider->setEnabled(true); + } else if (auto [ok, v] = parseGbl("sound/volumeMin"); ok) { + volumeSlider->setMinimum(v); + } else if (auto [ok, v] = parseGbl("sound/volumeMax"); ok) { + volumeSlider->setMaximum(v); + } + }); + + // Live label update while dragging; send command only on release + QObject::connect(brightnessSlider, &QSlider::valueChanged, page, + [brightnessVal](int v) { brightnessVal->setText(QString::number(v)); }); + QObject::connect(brightnessSlider, &QSlider::sliderReleased, page, + [ctrl, brightnessSlider]() { + ctrl->sendCommand(QString("GBL brightness=%1").arg(brightnessSlider->value())); + }); + + QObject::connect(volumeSlider, &QSlider::valueChanged, page, + [volumeVal](int v) { volumeVal->setText(QString::number(v)); }); + QObject::connect(volumeSlider, &QSlider::sliderReleased, page, + [ctrl, volumeSlider]() { + ctrl->sendCommand(QString("GBL sound/volume=%1").arg(volumeSlider->value())); + }); + return page; } @@ -67,9 +260,9 @@ static QWidget *makeManualTab(WebSocketController *ctrl, QWidget *parent) const QString cmd = cmdEdit->text().trimmed(); if (!cmd.isEmpty()) { ctrl->sendCommand(cmd); cmdEdit->clear(); } }; - QObject::connect(sendBtn, &QPushButton::clicked, page, send); - QObject::connect(cmdEdit, &QLineEdit::returnPressed, page, send); - QObject::connect(clearBtn, &QPushButton::clicked, log, &QTextEdit::clear); + QObject::connect(sendBtn, &QPushButton::clicked, page, send); + QObject::connect(cmdEdit, &QLineEdit::returnPressed, page, send); + QObject::connect(clearBtn, &QPushButton::clicked, log, &QTextEdit::clear); return page; } @@ -109,68 +302,167 @@ int main(int argc, char *argv[]) mainLayout->setContentsMargins(6, 6, 6, 6); mainLayout->setSpacing(4); - // --- Header bar --- - auto *headerRow = new QHBoxLayout(); - auto *urlEdit = new QLineEdit(&window); - urlEdit->setPlaceholderText("ws://127.0.0.1:3491/"); - urlEdit->setText("ws://127.0.0.1:3491/"); + // --- ICON → IP helper (formula + hardcoded overrides) --- + // Returns the IP string for a given ICON number. + static const auto iconToIp = [](int icon) -> QString { + static const QHash overrides = { + {280, QStringLiteral("10.10.1.30")}, + }; + if (overrides.contains(icon)) + return overrides[icon]; + const int subnet = (icon - 1) / 254; + const int host = ((icon - 1) % 254) + 1; + return QString("10.10.%1.%2").arg(subnet).arg(host); + }; + + // --- Hidden urlEdit still used by WebSocketController::startConnection() --- + auto *urlEdit = new QLineEdit(&window); + urlEdit->setVisible(false); + + auto *statusLabel = new QLabel("Disconnected", &window); + + // --- Network connection row --- + auto *headerRow = new QHBoxLayout(); + headerRow->addWidget(new QLabel("Network connection:", &window)); + + // Option 1 – fixed IP + auto *radioFixed = new QRadioButton("10.110.110.10", &window); + + // Option 2 – ICON number → derived IP + auto *radioIcon = new QRadioButton(&window); + auto *iconSpin = new QSpinBox(&window); + iconSpin->setRange(1, 9999); + iconSpin->setValue(280); + iconSpin->setFixedWidth(68); + iconSpin->setAlignment(Qt::AlignRight); + auto *iconIpLabel = new QLabel(iconToIp(280), &window); + iconIpLabel->setStyleSheet("color: #555; font-style: italic;"); + + // Option 3 – free text (hostname or raw IP) + auto *radioOther = new QRadioButton("other:", &window); + auto *otherEdit = new QLineEdit(&window); + otherEdit->setText("ftsDevkit4.local"); + otherEdit->setFixedWidth(160); + otherEdit->setEnabled(false); // only active when radioOther selected + + auto *btnGroup = new QButtonGroup(&window); + btnGroup->addButton(radioFixed, 0); + btnGroup->addButton(radioIcon, 1); + btnGroup->addButton(radioOther, 2); + radioIcon->setChecked(true); // default selection + + headerRow->addWidget(radioFixed); + headerRow->addWidget(radioIcon); + headerRow->addWidget(iconSpin); + headerRow->addWidget(iconIpLabel); + headerRow->addSpacing(8); + headerRow->addWidget(radioOther); + headerRow->addWidget(otherEdit); + headerRow->addStretch(1); + + // Buttons auto *connectBtn = new QPushButton("Connect", &window); auto *disconnectBtn = new QPushButton("Disconnect", &window); auto *shutdownBtn = new QPushButton("Shutdown", &window); disconnectBtn->setEnabled(false); shutdownBtn->setEnabled(false); shutdownBtn->setStyleSheet( - "QPushButton { color: white; background-color: #c62828;" - " border-radius: 4px; padding: 3px 10px; }" - "QPushButton:hover { background-color: #b71c1c; }" + "QPushButton { color: white; background-color: #c62828;" + " border-radius: 4px; padding: 3px 10px; }" + "QPushButton:hover { background-color: #b71c1c; }" "QPushButton:disabled { background-color: #888888; color: #cccccc; }"); - auto *statusLabel = new QLabel("Disconnected", &window); - - headerRow->addWidget(new QLabel("WebSocket URL:", &window)); - headerRow->addWidget(urlEdit, 1); headerRow->addWidget(connectBtn); headerRow->addWidget(disconnectBtn); headerRow->addWidget(shutdownBtn); headerRow->addWidget(statusLabel); mainLayout->addLayout(headerRow); + // --- Build URL from current selection into the hidden urlEdit --- + auto updateUrl = [=]() { + const int port = 5424; + QString host; + if (radioFixed->isChecked()) { + host = QStringLiteral("10.110.110.10"); + } else if (radioIcon->isChecked()) { + host = iconToIp(iconSpin->value()); + } else { + host = otherEdit->text().trimmed(); + if (host.isEmpty()) host = QStringLiteral("127.0.0.1"); + } + urlEdit->setText(QString("ws://%1:%2/").arg(host).arg(port)); + }; + updateUrl(); // set initial value + + // Keep iconIpLabel in sync and rebuild URL whenever ICON changes + QObject::connect(iconSpin, QOverload::of(&QSpinBox::valueChanged), &window, + [=](int v) { iconIpLabel->setText(iconToIp(v)); updateUrl(); }); + QObject::connect(btnGroup, QOverload::of(&QButtonGroup::idClicked), &window, + [=](int) { + otherEdit->setEnabled(radioOther->isChecked()); + updateUrl(); + }); + QObject::connect(otherEdit, &QLineEdit::textChanged, &window, [=]() { + if (radioOther->isChecked()) updateUrl(); + }); + // --- Controller --- auto *ctrl = new WebSocketController(urlEdit, statusLabel, &window); // --- Tabs --- auto *tabs = new QTabWidget(&window); - tabs->addTab(makeGamesTab (ctrl, &window), "games"); - tabs->addTab(makeVersionsTab(ctrl, &window), "versions"); - tabs->addTab(makeManualTab (ctrl, &window), "manual"); - tabs->addTab(makeSettingsTab(ctrl, &window), "settings"); - tabs->addTab(makePowerTab (ctrl, &window), "power"); - tabs->addTab(makeLogsTab (ctrl, &window), "logs"); - tabs->addTab(makePanelsTab (ctrl, &window), "panels"); + tabs->addTab(makeGamesTab (ctrl, &window), "Games"); + tabs->addTab(makeVersionsTab(ctrl, &window), "Versions"); + tabs->addTab(makeManualTab (ctrl, &window), "Manual"); + tabs->addTab(makeSettingsTab(ctrl, &window), "Settings"); + tabs->addTab(makePowerTab (ctrl, &window), "Power"); + tabs->addTab(makeLogsTab (ctrl, &window), "Logs"); + tabs->addTab(makePanelsTab (ctrl, &window), "Panels"); mainLayout->addWidget(tabs, 1); - // --- Shutdown: confirm then send --- - QObject::connect(shutdownBtn, &QPushButton::clicked, &window, [ctrl, &window]() { - QMessageBox msgBox(&window); - msgBox.setWindowTitle("Confirm Shutdown"); - msgBox.setText("Are you sure you want to shut down the machine?"); - msgBox.setInformativeText("This will send the POW ShutDown command immediately."); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Cancel); - if (msgBox.exec() == QMessageBox::Yes) - ctrl->sendCommand(QStringLiteral("POW ShutDown")); + // --- Shutdown: inline confirm (no QMessageBox::exec on WASM) --- + auto *shutdownConfirmLabel = new QLabel("Confirm shutdown:", &window); + auto *shutdownConfirmYes = new QPushButton("Yes, Shutdown", &window); + auto *shutdownConfirmNo = new QPushButton("Cancel", &window); + shutdownConfirmLabel->setStyleSheet("color: #c62828; font-weight: bold;"); + shutdownConfirmYes->setStyleSheet( + "QPushButton { color: white; background-color: #c62828; border-radius: 4px; padding: 2px 8px; }"); + shutdownConfirmLabel->setVisible(false); + shutdownConfirmYes->setVisible(false); + shutdownConfirmNo->setVisible(false); + headerRow->addWidget(shutdownConfirmLabel); + headerRow->addWidget(shutdownConfirmYes); + headerRow->addWidget(shutdownConfirmNo); + + QObject::connect(shutdownBtn, &QPushButton::clicked, &window, + [shutdownConfirmLabel, shutdownConfirmYes, shutdownConfirmNo]() { + shutdownConfirmLabel->setVisible(true); + shutdownConfirmYes->setVisible(true); + shutdownConfirmNo->setVisible(true); + }); + QObject::connect(shutdownConfirmYes, &QPushButton::clicked, &window, + [ctrl, shutdownConfirmLabel, shutdownConfirmYes, shutdownConfirmNo]() { + ctrl->sendCommand(QStringLiteral("POW ShutDown")); + shutdownConfirmLabel->setVisible(false); + shutdownConfirmYes->setVisible(false); + shutdownConfirmNo->setVisible(false); + }); + QObject::connect(shutdownConfirmNo, &QPushButton::clicked, &window, + [shutdownConfirmLabel, shutdownConfirmYes, shutdownConfirmNo]() { + shutdownConfirmLabel->setVisible(false); + shutdownConfirmYes->setVisible(false); + shutdownConfirmNo->setVisible(false); }); // --- Wire up buttons --- QObject::connect(connectBtn, &QPushButton::clicked, ctrl, &WebSocketController::startConnection); QObject::connect(disconnectBtn, &QPushButton::clicked, ctrl, &WebSocketController::closeConnection); - QObject::connect(ctrl->socket(), &QWebSocket::connected, ctrl, &WebSocketController::onConnected); - QObject::connect(ctrl->socket(), &QWebSocket::disconnected, ctrl, &WebSocketController::onDisconnected); - QObject::connect(ctrl->socket(), &QWebSocket::textMessageReceived, ctrl, &WebSocketController::onTextMessageReceived); - QObject::connect(ctrl->socket(), &QWebSocket::errorOccurred, ctrl, &WebSocketController::onErrorOccurred); + QObject::connect(ctrl->socket(), &QWebSocket::connected, ctrl, &WebSocketController::onConnected); + QObject::connect(ctrl->socket(), &QWebSocket::disconnected, ctrl, &WebSocketController::onDisconnected); + QObject::connect(ctrl->socket(), &QWebSocket::textMessageReceived, ctrl, &WebSocketController::onTextMessageReceived); + QObject::connect(ctrl->socket(), &QWebSocket::errorOccurred, ctrl, &WebSocketController::onErrorOccurred); - QObject::connect(ctrl->socket(), &QWebSocket::connected, &window, + QObject::connect(ctrl->socket(), &QWebSocket::connected, &window, [connectBtn, disconnectBtn, shutdownBtn]() { connectBtn->setEnabled(false); disconnectBtn->setEnabled(true);