Added games installer and now its amazing
This commit is contained in:
162
GamesPanel.cpp
162
GamesPanel.cpp
@@ -3,9 +3,40 @@
|
|||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSet>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Helper: parse "GAM <cmd> CODE1Name1 CODE2Name2 ..." → name->code map
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
static QMap<QString,QString> parseGamResponse(const QString &response,
|
||||||
|
const QString &subCmd)
|
||||||
|
{
|
||||||
|
QMap<QString,QString> result;
|
||||||
|
const QStringList tokens = response.split(' ', Qt::SkipEmptyParts);
|
||||||
|
if (tokens.size() < 3 || tokens[0] != "GAM" || tokens[1] != subCmd)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
for (const QString &entry : tokens.mid(2)) {
|
||||||
|
if (entry.isEmpty()) continue;
|
||||||
|
// First up-to-4 uppercase chars = code, remainder = name
|
||||||
|
int splitPos = 0;
|
||||||
|
while (splitPos < entry.size() && entry[splitPos].isUpper())
|
||||||
|
++splitPos;
|
||||||
|
const QString code = entry.left(qMin(splitPos, 4));
|
||||||
|
QString name = entry.mid(code.size());
|
||||||
|
name.replace("_S", " ");
|
||||||
|
if (code.isEmpty() || name.isEmpty()) continue;
|
||||||
|
result[name] = code;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constructor
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
GamesPanel::GamesPanel(QWidget *parent) : QWidget(parent)
|
GamesPanel::GamesPanel(QWidget *parent) : QWidget(parent)
|
||||||
{
|
{
|
||||||
auto *layout = new QHBoxLayout(this);
|
auto *layout = new QHBoxLayout(this);
|
||||||
@@ -13,74 +44,97 @@ GamesPanel::GamesPanel(QWidget *parent) : QWidget(parent)
|
|||||||
|
|
||||||
auto *splitter = new QSplitter(Qt::Horizontal, this);
|
auto *splitter = new QSplitter(Qt::Horizontal, this);
|
||||||
|
|
||||||
m_gameList = new QListWidget(splitter);
|
// ---- Left pane: Available (not installed) games ----
|
||||||
m_gameList->setMinimumWidth(180);
|
auto *leftWidget = new QWidget(splitter);
|
||||||
m_gameList->setMaximumWidth(300);
|
auto *leftLayout = new QVBoxLayout(leftWidget);
|
||||||
|
leftLayout->setContentsMargins(4, 4, 4, 4);
|
||||||
|
leftLayout->setSpacing(4);
|
||||||
|
|
||||||
m_detailWidget = new QWidget(splitter);
|
auto *availLabel = new QLabel("<b>Available Games</b>", leftWidget);
|
||||||
auto *detailLayout = new QVBoxLayout(m_detailWidget);
|
availLabel->setStyleSheet("color: #555;");
|
||||||
detailLayout->setContentsMargins(8, 8, 8, 8);
|
m_availableList = new QListWidget(leftWidget);
|
||||||
|
m_availableList->addItem("Connect to load available games...");
|
||||||
|
m_addBtn = new QPushButton("Add to Installed →", leftWidget);
|
||||||
|
m_addBtn->setEnabled(false);
|
||||||
|
|
||||||
m_nameLabel = new QLabel(m_detailWidget);
|
leftLayout->addWidget(availLabel);
|
||||||
m_nameLabel->setStyleSheet("font-size: 14pt; font-weight: bold;");
|
leftLayout->addWidget(m_availableList, 1);
|
||||||
m_codeLabel = new QLabel(m_detailWidget);
|
leftLayout->addWidget(m_addBtn);
|
||||||
m_codeLabel->setStyleSheet("font-size: 11pt; color: gray;");
|
|
||||||
m_hintLabel = new QLabel("Select a game to view details.", m_detailWidget);
|
|
||||||
m_hintLabel->setAlignment(Qt::AlignTop);
|
|
||||||
|
|
||||||
detailLayout->addWidget(m_nameLabel);
|
// ---- Right pane: Installed games ----
|
||||||
detailLayout->addWidget(m_codeLabel);
|
auto *rightWidget = new QWidget(splitter);
|
||||||
detailLayout->addWidget(m_hintLabel);
|
auto *rightLayout = new QVBoxLayout(rightWidget);
|
||||||
detailLayout->addStretch(1);
|
rightLayout->setContentsMargins(4, 4, 4, 4);
|
||||||
|
rightLayout->setSpacing(4);
|
||||||
|
|
||||||
splitter->addWidget(m_gameList);
|
auto *instLabel = new QLabel("<b>Installed Games</b>", rightWidget);
|
||||||
splitter->addWidget(m_detailWidget);
|
instLabel->setStyleSheet("color: #555;");
|
||||||
splitter->setStretchFactor(0, 0);
|
m_installedList = new QListWidget(rightWidget);
|
||||||
|
m_installedList->addItem("Connect to load installed games...");
|
||||||
|
|
||||||
|
rightLayout->addWidget(instLabel);
|
||||||
|
rightLayout->addWidget(m_installedList, 1);
|
||||||
|
|
||||||
|
splitter->addWidget(leftWidget);
|
||||||
|
splitter->addWidget(rightWidget);
|
||||||
|
splitter->setStretchFactor(0, 1);
|
||||||
splitter->setStretchFactor(1, 1);
|
splitter->setStretchFactor(1, 1);
|
||||||
splitter->setSizes({200, 600});
|
splitter->setSizes({420, 420});
|
||||||
|
|
||||||
layout->addWidget(splitter);
|
layout->addWidget(splitter);
|
||||||
|
|
||||||
connect(m_gameList, &QListWidget::currentTextChanged,
|
// Enable Add button only when something is selected in available list
|
||||||
this, &GamesPanel::onGameSelected);
|
connect(m_availableList, &QListWidget::currentRowChanged, this,
|
||||||
|
[this](int row) { m_addBtn->setEnabled(row >= 0); });
|
||||||
|
connect(m_addBtn, &QPushButton::clicked, this, &GamesPanel::onAddClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Load installed games (GAM list response)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
void GamesPanel::loadFromResponse(const QString &response)
|
void GamesPanel::loadFromResponse(const QString &response)
|
||||||
{
|
{
|
||||||
// Format: "GAM list LEADVision ARCHArchitect ..."
|
m_installedGames = parseGamResponse(response, "list");
|
||||||
// Each token: 4 uppercase letters = code, rest = name (_S = space)
|
m_installedList->clear();
|
||||||
const QStringList tokens = response.split(' ', Qt::SkipEmptyParts);
|
for (const QString &name : m_installedGames.keys())
|
||||||
if (tokens.size() < 3 || tokens[0] != "GAM" || tokens[1] != "list") return;
|
m_installedList->addItem(name);
|
||||||
|
rebuildAvailable();
|
||||||
m_games.clear();
|
|
||||||
m_gameList->clear();
|
|
||||||
|
|
||||||
for (const QString &entry : tokens.mid(2)) {
|
|
||||||
if (entry.isEmpty()) continue;
|
|
||||||
|
|
||||||
int splitPos = 0;
|
|
||||||
while (splitPos < entry.size() && entry[splitPos].isUpper())
|
|
||||||
++splitPos;
|
|
||||||
|
|
||||||
const QString code = entry.left(qMin(splitPos, 4));
|
|
||||||
QString name = entry.mid(code.size());
|
|
||||||
name.replace("_S", " ");
|
|
||||||
|
|
||||||
if (code.isEmpty() || name.isEmpty()) continue;
|
|
||||||
m_games[name] = code;
|
|
||||||
m_gameList->addItem(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GamesPanel::onGameSelected(const QString &name)
|
// ---------------------------------------------------------------------------
|
||||||
|
// Load all available games (GAM listall response)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
void GamesPanel::loadAllFromResponse(const QString &response)
|
||||||
{
|
{
|
||||||
if (name.isEmpty() || !m_games.contains(name)) {
|
m_allGames = parseGamResponse(response, "listall");
|
||||||
m_nameLabel->clear();
|
rebuildAvailable();
|
||||||
m_codeLabel->clear();
|
|
||||||
m_hintLabel->setText("Select a game to view details.");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
m_nameLabel->setText(name);
|
|
||||||
m_codeLabel->setText(QString("Code: %1").arg(m_games[name]));
|
// ---------------------------------------------------------------------------
|
||||||
m_hintLabel->setText("Game selected. Future controls will appear here.");
|
// Rebuild available list = allGames minus installedGames (by code)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
void GamesPanel::rebuildAvailable()
|
||||||
|
{
|
||||||
|
const QSet<QString> installedCodes(m_installedGames.values().begin(),
|
||||||
|
m_installedGames.values().end());
|
||||||
|
m_availableList->clear();
|
||||||
|
for (auto it = m_allGames.constBegin(); it != m_allGames.constEnd(); ++it) {
|
||||||
|
if (!installedCodes.contains(it.value()))
|
||||||
|
m_availableList->addItem(it.key());
|
||||||
|
}
|
||||||
|
if (m_availableList->count() == 0 && !m_allGames.isEmpty())
|
||||||
|
m_availableList->addItem("(all available games are installed)");
|
||||||
|
m_addBtn->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Add button clicked → emit GAM add <code>
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
void GamesPanel::onAddClicked()
|
||||||
|
{
|
||||||
|
auto *item = m_availableList->currentItem();
|
||||||
|
if (!item) return;
|
||||||
|
const QString name = item->text();
|
||||||
|
if (!m_allGames.contains(name)) return;
|
||||||
|
emit commandRequested(QString("GAM add %1").arg(m_allGames[name]));
|
||||||
}
|
}
|
||||||
|
|||||||
25
GamesPanel.h
25
GamesPanel.h
@@ -1,26 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <QWidget>
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QListWidget;
|
class QListWidget;
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
class GamesPanel : public QWidget
|
class GamesPanel : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit GamesPanel(QWidget *parent = nullptr);
|
explicit GamesPanel(QWidget *parent = nullptr);
|
||||||
void loadFromResponse(const QString &response);
|
void loadFromResponse(const QString &response); // GAM list (installed)
|
||||||
|
void loadAllFromResponse(const QString &response); // GAM listall (all available)
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void commandRequested(const QString &cmd);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onGameSelected(const QString &name);
|
void onAddClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QListWidget *m_gameList = nullptr;
|
void rebuildAvailable();
|
||||||
QWidget *m_detailWidget = nullptr;
|
|
||||||
QLabel *m_nameLabel = nullptr;
|
QListWidget *m_availableList = nullptr;
|
||||||
QLabel *m_codeLabel = nullptr;
|
QListWidget *m_installedList = nullptr;
|
||||||
QLabel *m_hintLabel = nullptr;
|
QPushButton *m_addBtn = nullptr;
|
||||||
QMap<QString, QString> m_games; // display name -> 4-letter code
|
|
||||||
|
QMap<QString,QString> m_allGames; // name -> code (GAM listall)
|
||||||
|
QMap<QString,QString> m_installedGames; // name -> code (GAM list)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QUrl>
|
#include <QAbstractSocket>
|
||||||
|
|
||||||
WebSocketController::WebSocketController(QLineEdit *urlEdit,
|
WebSocketController::WebSocketController(QLineEdit *urlEdit,
|
||||||
QLabel *statusLabel,
|
QLabel *statusLabel,
|
||||||
@@ -28,7 +28,7 @@ void WebSocketController::setSettingsTree(SettingsTree *tree)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketController::setGamesPanel(GamesPanel *panel) { m_gamesPanel = panel; }
|
void WebSocketController::setGamesPanel(GamesPanel *panel) { m_gamesPanel = panel; }
|
||||||
void WebSocketController::setVersionsPanel(VersionsPanel *panel) { m_versionsPanel = panel; }
|
void WebSocketController::setVersionsPanel(VersionsPanel *p) { m_versionsPanel = p; }
|
||||||
void WebSocketController::setPowerPanel(PowerPanel *panel) { m_powerPanel = panel; }
|
void WebSocketController::setPowerPanel(PowerPanel *panel) { m_powerPanel = panel; }
|
||||||
void WebSocketController::setLogPanel(LogPanel *panel) { m_logPanel = panel; }
|
void WebSocketController::setLogPanel(LogPanel *panel) { m_logPanel = panel; }
|
||||||
|
|
||||||
@@ -71,6 +71,7 @@ void WebSocketController::onConnected()
|
|||||||
|
|
||||||
sendCommand(QStringLiteral("GBL List"));
|
sendCommand(QStringLiteral("GBL List"));
|
||||||
sendCommand(QStringLiteral("GAM list"));
|
sendCommand(QStringLiteral("GAM list"));
|
||||||
|
sendCommand(QStringLiteral("GAM listall")); // <-- fetch full catalogue
|
||||||
sendCommand(QStringLiteral("NAM"));
|
sendCommand(QStringLiteral("NAM"));
|
||||||
sendCommand(QStringLiteral("VER"));
|
sendCommand(QStringLiteral("VER"));
|
||||||
sendCommand(QStringLiteral("UID"));
|
sendCommand(QStringLiteral("UID"));
|
||||||
@@ -123,38 +124,35 @@ void WebSocketController::handleProtocol(const QString &msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---- LOG channel list ----
|
// ---- LOG channel list ----
|
||||||
// Response: LOG name1 name2* name3 ... (* = enabled)
|
|
||||||
if (cmd == "LOG" && tokens.size() > 1 && m_logPanel) {
|
if (cmd == "LOG" && tokens.size() > 1 && m_logPanel) {
|
||||||
// Only handle the full list response (no '=' in any token)
|
|
||||||
bool isList = true;
|
bool isList = true;
|
||||||
for (int i = 1; i < tokens.size(); ++i) {
|
for (int i = 1; i < tokens.size(); ++i)
|
||||||
if (tokens[i].contains('=')) { isList = false; break; }
|
if (tokens[i].contains('=')) { isList = false; break; }
|
||||||
}
|
|
||||||
if (isList) m_logPanel->applyLogResponse(msg);
|
if (isList) m_logPanel->applyLogResponse(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NAM
|
// ---- NAM ----
|
||||||
if (cmd == "NAM" && tokens.size() >= 2) {
|
if (cmd == "NAM" && tokens.size() >= 2) {
|
||||||
if (m_versionsPanel) m_versionsPanel->setDeviceName(tokens[1]);
|
if (m_versionsPanel) m_versionsPanel->setDeviceName(tokens[1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VER
|
// ---- VER ----
|
||||||
if (cmd == "VER" && tokens.size() >= 3) {
|
if (cmd == "VER" && tokens.size() >= 3) {
|
||||||
if (m_versionsPanel)
|
if (m_versionsPanel)
|
||||||
m_versionsPanel->setVersion(tokens[1], tokens.mid(2).join(' '));
|
m_versionsPanel->setVersion(tokens[1], tokens.mid(2).join(' '));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// UID
|
// ---- UID ----
|
||||||
if (cmd == "UID" && tokens.size() >= 3) {
|
if (cmd == "UID" && tokens.size() >= 3) {
|
||||||
if (m_versionsPanel)
|
if (m_versionsPanel)
|
||||||
m_versionsPanel->setUid(tokens[1], tokens.mid(2).join(' '));
|
m_versionsPanel->setUid(tokens[1], tokens.mid(2).join(' '));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RNP
|
// ---- RNP ----
|
||||||
if (cmd == "RNP" && tokens.size() >= 2) {
|
if (cmd == "RNP" && tokens.size() >= 2) {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
const int count = tokens[1].toInt(&ok);
|
const int count = tokens[1].toInt(&ok);
|
||||||
@@ -169,13 +167,19 @@ void WebSocketController::handleProtocol(const QString &msg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GAM list
|
// ---- GAM listall — full catalogue ----
|
||||||
|
if (cmd == "GAM" && tokens.size() >= 2 && tokens[1] == "listall") {
|
||||||
|
if (m_gamesPanel) m_gamesPanel->loadAllFromResponse(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- GAM list — installed games ----
|
||||||
if (cmd == "GAM" && tokens.size() >= 2 && tokens[1] == "list") {
|
if (cmd == "GAM" && tokens.size() >= 2 && tokens[1] == "list") {
|
||||||
if (m_gamesPanel) m_gamesPanel->loadFromResponse(msg);
|
if (m_gamesPanel) m_gamesPanel->loadFromResponse(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GBL List key1 key2 ...
|
// ---- GBL List key1 key2 ... ----
|
||||||
if (cmd == "GBL" && tokens.size() > 2 && tokens[1] == "List") {
|
if (cmd == "GBL" && tokens.size() > 2 && tokens[1] == "List") {
|
||||||
m_settingsKeys = tokens.mid(2);
|
m_settingsKeys = tokens.mid(2);
|
||||||
if (m_settingsTree) {
|
if (m_settingsTree) {
|
||||||
@@ -186,7 +190,7 @@ void WebSocketController::handleProtocol(const QString &msg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GBL key=value
|
// ---- GBL key=value ----
|
||||||
if (cmd == "GBL" && tokens.size() >= 2 && m_settingsTree) {
|
if (cmd == "GBL" && tokens.size() >= 2 && m_settingsTree) {
|
||||||
const QString payload = tokens[1];
|
const QString payload = tokens[1];
|
||||||
const int eqIdx = payload.indexOf('=');
|
const int eqIdx = payload.indexOf('=');
|
||||||
|
|||||||
3
main.cpp
3
main.cpp
@@ -27,6 +27,9 @@ static QWidget *makeGamesTab(WebSocketController *ctrl, QWidget *parent)
|
|||||||
{
|
{
|
||||||
auto *panel = new GamesPanel(parent);
|
auto *panel = new GamesPanel(parent);
|
||||||
ctrl->setGamesPanel(panel);
|
ctrl->setGamesPanel(panel);
|
||||||
|
// Route GAM add commands back through the controller
|
||||||
|
QObject::connect(panel, &GamesPanel::commandRequested,
|
||||||
|
ctrl, &WebSocketController::sendCommand);
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user