Qt: Add integrated configuration loader for the menu

This commit is contained in:
Jeffrey Pfau 2014-11-05 02:17:26 -08:00
parent 19346dff87
commit 8751f5cc0c
6 changed files with 189 additions and 49 deletions

View File

@ -2,12 +2,76 @@
#include "GameController.h" #include "GameController.h"
#include <QAction>
#include <QMenu>
extern "C" { extern "C" {
#include "platform/commandline.h" #include "platform/commandline.h"
} }
using namespace QGBA; using namespace QGBA;
ConfigOption::ConfigOption(QObject* parent)
: QObject(parent)
{
}
void ConfigOption::connect(std::function<void(const QVariant&)> slot) {
m_slot = slot;
}
QAction* ConfigOption::addValue(const QString& text, const QVariant& value, QMenu* parent) {
QAction* action = new QAction(text, parent);
action->setCheckable(true);
QObject::connect(action, &QAction::triggered, [this, value]() {
emit valueChanged(value);
});
parent->addAction(action);
m_actions.append(qMakePair(action, value));
return action;
}
QAction* ConfigOption::addValue(const QString& text, const char* value, QMenu* parent) {
return addValue(text, QString(value), parent);
}
QAction* ConfigOption::addBoolean(const QString& text, QMenu* parent) {
QAction* action = new QAction(text, parent);
action->setCheckable(true);
QObject::connect(action, &QAction::triggered, [this, action]() {
emit valueChanged(action->isChecked());
});
parent->addAction(action);
m_actions.append(qMakePair(action, true));
return action;
}
void ConfigOption::setValue(bool value) {
setValue(QVariant(value));
}
void ConfigOption::setValue(int value) {
setValue(QVariant(value));
}
void ConfigOption::setValue(unsigned value) {
setValue(QVariant(value));
}
void ConfigOption::setValue(const char* value) {
setValue(QVariant(QString(value)));
}
void ConfigOption::setValue(const QVariant& value) {
QPair<QAction*, QVariant> action;
foreach(action, m_actions) {
bool signalsEnabled = action.first->blockSignals(true);
action.first->setChecked(value == action.second);
action.first->blockSignals(signalsEnabled);
}
m_slot(value);
}
ConfigController::ConfigController(QObject* parent) ConfigController::ConfigController(QObject* parent)
: QObject(parent) : QObject(parent)
, m_opts() , m_opts()
@ -16,6 +80,8 @@ ConfigController::ConfigController(QObject* parent)
m_opts.audioSync = GameController::AUDIO_SYNC; m_opts.audioSync = GameController::AUDIO_SYNC;
m_opts.videoSync = GameController::VIDEO_SYNC; m_opts.videoSync = GameController::VIDEO_SYNC;
m_opts.fpsTarget = 60;
m_opts.audioBuffers = 768;
GBAConfigLoadDefaults(&m_config, &m_opts); GBAConfigLoadDefaults(&m_config, &m_opts);
GBAConfigLoad(&m_config); GBAConfigLoad(&m_config);
GBAConfigMap(&m_config, &m_opts); GBAConfigMap(&m_config, &m_opts);
@ -32,20 +98,68 @@ bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[]
return ::parseArguments(args, &m_config, argc, argv, 0); return ::parseArguments(args, &m_config, argc, argv, 0);
} }
ConfigOption* ConfigController::addOption(const char* key) {
QString optionName(key);
if (m_optionSet.contains(optionName)) {
return m_optionSet[optionName];
}
ConfigOption* newOption = new ConfigOption(this);
m_optionSet[optionName] = newOption;
connect(newOption, &ConfigOption::valueChanged, [this, key](const QVariant& value) {
setOption(key, value);
});
return newOption;
}
void ConfigController::updateOption(const char* key) {
if (!key) {
return;
}
QString optionName(key);
if (!m_optionSet.contains(optionName)) {
return;
}
m_optionSet[optionName]->setValue(GBAConfigGetValue(&m_config, key));
}
void ConfigController::setOption(const char* key, bool value) { void ConfigController::setOption(const char* key, bool value) {
setOption(key, (int) value); setOption(key, (int) value);
ConfigOption* option = m_optionSet[QString(key)];
if (option) {
option->setValue(value);
}
} }
void ConfigController::setOption(const char* key, int value) { void ConfigController::setOption(const char* key, int value) {
GBAConfigSetIntValue(&m_config, key, value); GBAConfigSetIntValue(&m_config, key, value);
ConfigOption* option = m_optionSet[QString(key)];
if (option) {
option->setValue(value);
}
} }
void ConfigController::setOption(const char* key, unsigned value) { void ConfigController::setOption(const char* key, unsigned value) {
GBAConfigSetUIntValue(&m_config, key, value); GBAConfigSetUIntValue(&m_config, key, value);
ConfigOption* option = m_optionSet[QString(key)];
if (option) {
option->setValue(value);
}
} }
void ConfigController::setOption(const char* key, const char* value) { void ConfigController::setOption(const char* key, const char* value) {
GBAConfigSetValue(&m_config, key, value); GBAConfigSetValue(&m_config, key, value);
ConfigOption* option = m_optionSet[QString(key)];
if (option) {
option->setValue(value);
}
}
void ConfigController::setOption(const char* key, const QVariant& value) {
QString stringValue(value.toString());
setOption(key, stringValue.toLocal8Bit().constData());
} }
void ConfigController::write() { void ConfigController::write() {

View File

@ -1,16 +1,50 @@
#ifndef QGBA_CONFIG_CONTROLLER #ifndef QGBA_CONFIG_CONTROLLER
#define QGBA_CONFIG_CONTROLLER #define QGBA_CONFIG_CONTROLLER
#include <QMap>
#include <QObject> #include <QObject>
#include <QScopedPointer>
#include <QVariant>
extern "C" { extern "C" {
#include "gba-config.h" #include "gba-config.h"
#include "util/configuration.h" #include "util/configuration.h"
} }
class QAction;
class QMenu;
struct GBAArguments; struct GBAArguments;
namespace QGBA { namespace QGBA {
class ConfigOption : public QObject {
Q_OBJECT
public:
ConfigOption(QObject* parent = nullptr);
void connect(std::function<void(const QVariant&)>);
QAction* addValue(const QString& text, const QVariant& value, QMenu* parent = 0);
QAction* addValue(const QString& text, const char* value, QMenu* parent = 0);
QAction* addBoolean(const QString& text, QMenu* parent = 0);
public slots:
void setValue(bool value);
void setValue(int value);
void setValue(unsigned value);
void setValue(const char* value);
void setValue(const QVariant& value);
signals:
void valueChanged(const QVariant& value);
private:
std::function<void(const QVariant&)> m_slot;
QList<QPair<QAction*, QVariant>> m_actions;
};
class ConfigController : public QObject { class ConfigController : public QObject {
Q_OBJECT Q_OBJECT
@ -23,17 +57,23 @@ public:
const GBAOptions* options() const { return &m_opts; } const GBAOptions* options() const { return &m_opts; }
bool parseArguments(GBAArguments* args, int argc, char* argv[]); bool parseArguments(GBAArguments* args, int argc, char* argv[]);
ConfigOption* addOption(const char* key);
void updateOption(const char* key);
public slots: public slots:
void setOption(const char* key, bool value); void setOption(const char* key, bool value);
void setOption(const char* key, int value); void setOption(const char* key, int value);
void setOption(const char* key, unsigned value); void setOption(const char* key, unsigned value);
void setOption(const char* key, const char* value); void setOption(const char* key, const char* value);
void setOption(const char* key, const QVariant& value);
void write(); void write();
private: private:
GBAConfig m_config; GBAConfig m_config;
GBAOptions m_opts; GBAOptions m_opts;
QMap<QString, ConfigOption*> m_optionSet;
}; };
} }

View File

@ -12,12 +12,12 @@ using namespace QGBA;
GBAApp::GBAApp(int& argc, char* argv[]) GBAApp::GBAApp(int& argc, char* argv[])
: QApplication(argc, argv) : QApplication(argc, argv)
, m_window(&m_configController)
{ {
QApplication::setApplicationName(PROJECT_NAME); QApplication::setApplicationName(PROJECT_NAME);
QApplication::setApplicationVersion(PROJECT_VERSION); QApplication::setApplicationVersion(PROJECT_VERSION);
GBAArguments args = {}; GBAArguments args = {};
m_window.setConfig(&m_configController);
if (m_configController.parseArguments(&args, argc, argv)) { if (m_configController.parseArguments(&args, argc, argv)) {
m_window.argumentsPassed(&args); m_window.argumentsPassed(&args);
} else { } else {

View File

@ -20,8 +20,8 @@ protected:
bool event(QEvent*); bool event(QEvent*);
private: private:
Window m_window;
ConfigController m_configController; ConfigController m_configController;
Window m_window;
}; };
} }

View File

@ -20,12 +20,13 @@ extern "C" {
using namespace QGBA; using namespace QGBA;
Window::Window(QWidget* parent) Window::Window(ConfigController* config, QWidget* parent)
: QMainWindow(parent) : QMainWindow(parent)
, m_logView(new LogView()) , m_logView(new LogView())
, m_stateWindow(nullptr) , m_stateWindow(nullptr)
, m_screenWidget(new WindowBackground()) , m_screenWidget(new WindowBackground())
, m_logo(":/res/mgba-1024.png") , m_logo(":/res/mgba-1024.png")
, m_config(config)
#ifdef USE_FFMPEG #ifdef USE_FFMPEG
, m_videoView(nullptr) , m_videoView(nullptr)
#endif #endif
@ -396,27 +397,16 @@ void Window::setupMenu(QMenuBar* menubar) {
emulationMenu->addAction(frameAdvance); emulationMenu->addAction(frameAdvance);
QMenu* target = emulationMenu->addMenu("FPS target"); QMenu* target = emulationMenu->addMenu("FPS target");
QAction* setTarget = new QAction(tr("15"), emulationMenu); ConfigOption* fpsTargetOption = m_config->addOption("fpsTarget");
connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(15); }); fpsTargetOption->connect([this](const QVariant& value) { emit fpsTargetChanged(value.toInt()); });
target->addAction(setTarget); fpsTargetOption->addValue(tr("15"), 15, target);
setTarget = new QAction(tr("30"), emulationMenu); fpsTargetOption->addValue(tr("30"), 30, target);
connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(30); }); fpsTargetOption->addValue(tr("45"), 45, target);
target->addAction(setTarget); fpsTargetOption->addValue(tr("60"), 60, target);
setTarget = new QAction(tr("45"), emulationMenu); fpsTargetOption->addValue(tr("90"), 90, target);
connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(45); }); fpsTargetOption->addValue(tr("120"), 120, target);
target->addAction(setTarget); fpsTargetOption->addValue(tr("240"), 240, target);
setTarget = new QAction(tr("60"), emulationMenu); m_config->updateOption("fpsTarget");
connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(60); });
target->addAction(setTarget);
setTarget = new QAction(tr("90"), emulationMenu);
connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(90); });
target->addAction(setTarget);
setTarget = new QAction(tr("120"), emulationMenu);
connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(120); });
target->addAction(setTarget);
setTarget = new QAction(tr("240"), emulationMenu);
connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(240); });
target->addAction(setTarget);
emulationMenu->addSeparator(); emulationMenu->addSeparator();
@ -427,17 +417,15 @@ void Window::setupMenu(QMenuBar* menubar) {
connect(turbo, SIGNAL(triggered(bool)), m_controller, SLOT(setTurbo(bool))); connect(turbo, SIGNAL(triggered(bool)), m_controller, SLOT(setTurbo(bool)));
emulationMenu->addAction(turbo); emulationMenu->addAction(turbo);
QAction* videoSync = new QAction(tr("Sync to &video"), emulationMenu); ConfigOption* videoSync = m_config->addOption("videoSync");
videoSync->setCheckable(true); videoSync->addBoolean(tr("Sync to &video"), emulationMenu);
videoSync->setChecked(m_controller->videoSync()); videoSync->connect([this](const QVariant& value) { m_controller->setVideoSync(value.toBool()); });
connect(videoSync, SIGNAL(triggered(bool)), m_controller, SLOT(setVideoSync(bool))); m_config->updateOption("videoSync");
emulationMenu->addAction(videoSync);
QAction* audioSync = new QAction(tr("Sync to &audio"), emulationMenu); ConfigOption* audioSync = m_config->addOption("audioSync");
audioSync->setCheckable(true); audioSync->addBoolean(tr("Sync to &audio"), emulationMenu);
audioSync->setChecked(m_controller->audioSync()); audioSync->connect([this](const QVariant& value) { m_controller->setAudioSync(value.toBool()); });
connect(audioSync, SIGNAL(triggered(bool)), m_controller, SLOT(setAudioSync(bool))); m_config->updateOption("audioSync");
emulationMenu->addAction(audioSync);
QMenu* videoMenu = menubar->addMenu(tr("&Video")); QMenu* videoMenu = menubar->addMenu(tr("&Video"));
QMenu* frameMenu = videoMenu->addMenu(tr("Frame size")); QMenu* frameMenu = videoMenu->addMenu(tr("Frame size"));
@ -468,25 +456,23 @@ void Window::setupMenu(QMenuBar* menubar) {
frameMenu->addAction(tr("Fullscreen"), this, SLOT(toggleFullScreen()), QKeySequence("Ctrl+F")); frameMenu->addAction(tr("Fullscreen"), this, SLOT(toggleFullScreen()), QKeySequence("Ctrl+F"));
QMenu* skipMenu = videoMenu->addMenu(tr("Frame&skip")); QMenu* skipMenu = videoMenu->addMenu(tr("Frame&skip"));
ConfigOption* skip = m_config->addOption("frameskip");
skip->connect([this](const QVariant& value) { m_controller->setFrameskip(value.toInt()); });
for (int i = 0; i <= 10; ++i) { for (int i = 0; i <= 10; ++i) {
QAction* setSkip = new QAction(QString::number(i), skipMenu); skip->addValue(QString::number(i), i, skipMenu);
connect(setSkip, &QAction::triggered, [this, i]() {
m_controller->setFrameskip(i);
});
skipMenu->addAction(setSkip);
} }
m_config->updateOption("frameskip");
QMenu* soundMenu = menubar->addMenu(tr("&Sound")); QMenu* soundMenu = menubar->addMenu(tr("&Sound"));
QMenu* buffersMenu = soundMenu->addMenu(tr("Buffer &size")); QMenu* buffersMenu = soundMenu->addMenu(tr("Buffer &size"));
QAction* setBuffer = new QAction(tr("512"), buffersMenu); ConfigOption* buffers = m_config->addOption("audioBuffers");
connect(setBuffer, &QAction::triggered, [this]() { emit audioBufferSamplesChanged(512); }); buffers->connect([this](const QVariant& value) { emit audioBufferSamplesChanged(value.toInt()); });
buffersMenu->addAction(setBuffer); buffers->addValue(tr("512"), 512, buffersMenu);
setBuffer = new QAction(tr("1024"), buffersMenu); buffers->addValue(tr("768"), 768, buffersMenu);
connect(setBuffer, &QAction::triggered, [this]() { emit audioBufferSamplesChanged(1024); }); buffers->addValue(tr("1024"), 1024, buffersMenu);
buffersMenu->addAction(setBuffer); buffers->addValue(tr("2048"), 2048, buffersMenu);
setBuffer = new QAction(tr("2048"), buffersMenu); buffers->addValue(tr("4096"), 4096, buffersMenu);
connect(setBuffer, &QAction::triggered, [this]() { emit audioBufferSamplesChanged(2048); }); m_config->updateOption("audioBuffers");
buffersMenu->addAction(setBuffer);
QMenu* debuggingMenu = menubar->addMenu(tr("&Debugging")); QMenu* debuggingMenu = menubar->addMenu(tr("&Debugging"));
QAction* viewLogs = new QAction(tr("View &logs..."), debuggingMenu); QAction* viewLogs = new QAction(tr("View &logs..."), debuggingMenu);

View File

@ -27,7 +27,7 @@ class Window : public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
Window(QWidget* parent = nullptr); Window(ConfigController* config, QWidget* parent = nullptr);
virtual ~Window(); virtual ~Window();
GameController* controller() { return m_controller; } GameController* controller() { return m_controller; }