Qt: Add support for running scripts at startup (closes #3465)
This commit is contained in:
parent
c33a0d6534
commit
90057703b5
1
CHANGES
1
CHANGES
@ -10,6 +10,7 @@ Features:
|
||||
- Scripting: New `storage` API for saving data for a script, e.g. settings
|
||||
- Scripting: New `image` and `canvas` APIs for drawing images and displaying on-screen
|
||||
- Scripting: Debugger integration to allow for breakpoints and watchpoints
|
||||
- Scripting: Add support for running scripts at startup
|
||||
- New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81, Sintax
|
||||
- Initial support for bootleg GBA multicarts
|
||||
- Debugger: Add range watchpoints
|
||||
|
@ -297,12 +297,15 @@ endif()
|
||||
|
||||
if(ENABLE_SCRIPTING)
|
||||
list(APPEND SOURCE_FILES
|
||||
scripting/AutorunScriptModel.cpp
|
||||
scripting/AutorunScriptView.cpp
|
||||
scripting/ScriptingController.cpp
|
||||
scripting/ScriptingTextBuffer.cpp
|
||||
scripting/ScriptingTextBufferModel.cpp
|
||||
scripting/ScriptingView.cpp)
|
||||
|
||||
list(APPEND UI_FILES
|
||||
scripting/AutorunScriptView.ui
|
||||
scripting/ScriptingView.ui)
|
||||
endif()
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "ActionMapper.h"
|
||||
#include "CoreController.h"
|
||||
#include "scripting/AutorunScriptModel.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QMenu>
|
||||
@ -122,6 +123,8 @@ QString ConfigController::s_configDir;
|
||||
ConfigController::ConfigController(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
qRegisterMetaType<AutorunScriptModel::ScriptInfo>();
|
||||
|
||||
QString fileName = configDir();
|
||||
fileName.append(QDir::separator());
|
||||
fileName.append("qt.ini");
|
||||
@ -379,6 +382,36 @@ void ConfigController::setMRU(const QStringList& mru, ConfigController::MRU mruT
|
||||
m_settings->endGroup();
|
||||
}
|
||||
|
||||
QList<QVariant> ConfigController::getList(const QString& group) const {
|
||||
QList<QVariant> list;
|
||||
m_settings->beginGroup(group);
|
||||
for (int i = 0; ; ++i) {
|
||||
QVariant item = m_settings->value(QString::number(i));
|
||||
if (item.isNull() || !item.isValid()) {
|
||||
break;
|
||||
}
|
||||
list.append(item);
|
||||
}
|
||||
m_settings->endGroup();
|
||||
return list;
|
||||
}
|
||||
|
||||
void ConfigController::setList(const QString& group, const QList<QVariant>& list) {
|
||||
int i = 0;
|
||||
m_settings->beginGroup(group);
|
||||
QStringList keys = m_settings->childKeys();
|
||||
for (const QVariant& item : list) {
|
||||
QString key = QString::number(i);
|
||||
keys.removeAll(key);
|
||||
m_settings->setValue(key, item);
|
||||
++i;
|
||||
}
|
||||
for (const auto& key: keys) {
|
||||
m_settings->remove(key);
|
||||
}
|
||||
m_settings->endGroup();
|
||||
}
|
||||
|
||||
constexpr const char* ConfigController::mruName(ConfigController::MRU mru) {
|
||||
switch (mru) {
|
||||
case MRU::ROM:
|
||||
|
@ -90,7 +90,7 @@ public:
|
||||
QVariant takeArgvOption(const QString& key);
|
||||
|
||||
QStringList getMRU(MRU = MRU::ROM) const;
|
||||
void setMRU(const QStringList& mru, MRU = MRU::ROM);
|
||||
QList<QVariant> getList(const QString& group) const;
|
||||
|
||||
Configuration* overrides() { return mCoreConfigGetOverrides(&m_config); }
|
||||
void saveOverride(const Override&);
|
||||
@ -116,6 +116,8 @@ public slots:
|
||||
void setOption(const char* key, const char* value);
|
||||
void setOption(const char* key, const QVariant& value);
|
||||
void setQtOption(const QString& key, const QVariant& value, const QString& group = QString());
|
||||
void setMRU(const QStringList& mru, MRU = MRU::ROM);
|
||||
void setList(const QString& group, const QList<QVariant>& list);
|
||||
|
||||
void makePortable();
|
||||
void write();
|
||||
|
@ -69,6 +69,8 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
||||
|
||||
reloadConfig();
|
||||
|
||||
connect(m_ui.autorunScripts, &QAbstractButton::pressed, this, &SettingsView::openAutorunScripts);
|
||||
|
||||
connect(m_ui.volume, static_cast<void (QSlider::*)(int)>(&QSlider::valueChanged), [this](int v) {
|
||||
if (v < m_ui.volumeFf->value()) {
|
||||
m_ui.volumeFf->setValue(v);
|
||||
|
@ -63,6 +63,7 @@ signals:
|
||||
void languageChanged();
|
||||
void libraryCleared();
|
||||
void saveSettingsRequested();
|
||||
void openAutorunScripts();
|
||||
|
||||
public slots:
|
||||
void selectPage(Page);
|
||||
|
@ -569,14 +569,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<item row="6" column="1">
|
||||
<widget class="QPushButton" name="autorunScripts">
|
||||
<property name="text">
|
||||
<string>Edit autorun scripts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="Line" name="line_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="autosave">
|
||||
<property name="text">
|
||||
<string>Periodically autosave state</string>
|
||||
@ -586,7 +593,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="cheatAutosave">
|
||||
<property name="text">
|
||||
<string>Save entered cheats</string>
|
||||
@ -596,21 +603,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<item row="10" column="0" colspan="2">
|
||||
<widget class="Line" name="line_21">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_51">
|
||||
<property name="text">
|
||||
<string>Save state extra data:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="11" column="1">
|
||||
<widget class="QCheckBox" name="saveStateScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
@ -620,7 +627,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<item row="12" column="1">
|
||||
<widget class="QCheckBox" name="saveStateSave">
|
||||
<property name="text">
|
||||
<string>Save game</string>
|
||||
@ -630,7 +637,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<item row="13" column="1">
|
||||
<widget class="QCheckBox" name="saveStateCheats">
|
||||
<property name="text">
|
||||
<string>Cheat codes</string>
|
||||
@ -640,21 +647,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0" colspan="2">
|
||||
<item row="14" column="0" colspan="2">
|
||||
<widget class="Line" name="line_22">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<item row="15" column="0">
|
||||
<widget class="QLabel" name="label_52">
|
||||
<property name="text">
|
||||
<string>Load state extra data:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<item row="15" column="1">
|
||||
<widget class="QCheckBox" name="loadStateScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
@ -664,28 +671,28 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1">
|
||||
<item row="16" column="1">
|
||||
<widget class="QCheckBox" name="loadStateSave">
|
||||
<property name="text">
|
||||
<string>Save game</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<item row="17" column="1">
|
||||
<widget class="QCheckBox" name="loadStateCheats">
|
||||
<property name="text">
|
||||
<string>Cheat codes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="0" colspan="2">
|
||||
<item row="18" column="0" colspan="2">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<item row="19" column="1">
|
||||
<widget class="QCheckBox" name="useDiscordPresence">
|
||||
<property name="text">
|
||||
<string>Enable Discord Rich Presence</string>
|
||||
|
@ -305,6 +305,10 @@ void Window::loadConfig() {
|
||||
updateMRU();
|
||||
|
||||
m_inputController.setConfiguration(m_config);
|
||||
|
||||
if (!m_config->getList("autorunSettings").isEmpty()) {
|
||||
ensureScripting();
|
||||
}
|
||||
}
|
||||
|
||||
void Window::reloadConfig() {
|
||||
@ -550,6 +554,10 @@ void Window::openSettingsWindow(SettingsView::Page page) {
|
||||
#ifdef USE_SQLITE3
|
||||
connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear);
|
||||
#endif
|
||||
connect(settingsWindow, &SettingsView::openAutorunScripts, this, [this]() {
|
||||
ensureScripting();
|
||||
m_scripting->openAutorunEdit();
|
||||
});
|
||||
connect(this, &Window::shaderSelectorAdded, settingsWindow, &SettingsView::setShaderSelector);
|
||||
openView(settingsWindow);
|
||||
settingsWindow->selectPage(page);
|
||||
@ -639,17 +647,7 @@ void Window::consoleOpen() {
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
void Window::scriptingOpen() {
|
||||
if (!m_scripting) {
|
||||
m_scripting = std::make_unique<ScriptingController>();
|
||||
m_scripting->setInputController(&m_inputController);
|
||||
m_shortcutController->setScriptingController(m_scripting.get());
|
||||
if (m_controller) {
|
||||
m_scripting->setController(m_controller);
|
||||
m_display->installEventFilter(m_scripting.get());
|
||||
}
|
||||
|
||||
m_scripting->setVideoBackend(m_display->videoBackend());
|
||||
}
|
||||
ensureScripting();
|
||||
ScriptingView* view = new ScriptingView(m_scripting.get(), m_config);
|
||||
openView(view);
|
||||
}
|
||||
@ -2057,6 +2055,25 @@ void Window::updateMRU() {
|
||||
m_actions.rebuildMenu(menuBar(), this, *m_shortcutController);
|
||||
}
|
||||
|
||||
void Window::ensureScripting() {
|
||||
if (m_scripting) {
|
||||
return;
|
||||
}
|
||||
m_scripting = std::make_unique<ScriptingController>(m_config);
|
||||
m_scripting->setInputController(&m_inputController);
|
||||
m_shortcutController->setScriptingController(m_scripting.get());
|
||||
if (m_controller) {
|
||||
m_scripting->setController(m_controller);
|
||||
m_display->installEventFilter(m_scripting.get());
|
||||
}
|
||||
|
||||
if (m_display) {
|
||||
m_scripting->setVideoBackend(m_display->videoBackend());
|
||||
}
|
||||
|
||||
connect(m_scripting.get(), &ScriptingController::autorunScriptsOpened, this, &Window::openView);
|
||||
}
|
||||
|
||||
std::shared_ptr<Action> Window::addGameAction(const QString& visibleName, const QString& name, Action::Function function, const QString& menu, const QKeySequence& shortcut) {
|
||||
auto action = m_actions.addAction(visibleName, name, [this, function = std::move(function)]() {
|
||||
if (m_controller) {
|
||||
|
@ -109,6 +109,8 @@ public slots:
|
||||
|
||||
void startVideoLog();
|
||||
|
||||
void openView(QWidget* widget);
|
||||
|
||||
#ifdef ENABLE_DEBUGGERS
|
||||
void consoleOpen();
|
||||
#endif
|
||||
@ -173,7 +175,7 @@ private:
|
||||
void clearMRU();
|
||||
void updateMRU();
|
||||
|
||||
void openView(QWidget* widget);
|
||||
void ensureScripting();
|
||||
|
||||
template <typename T, typename... A> std::function<void()> openTView(A... arg);
|
||||
template <typename T, typename... A> std::function<void()> openControllerTView(A... arg);
|
||||
|
131
src/platform/qt/scripting/AutorunScriptModel.cpp
Normal file
131
src/platform/qt/scripting/AutorunScriptModel.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
/* Copyright (c) 2013-2025 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "scripting/AutorunScriptModel.h"
|
||||
|
||||
#include "ConfigController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
AutorunScriptModel::AutorunScriptModel(ConfigController* config, QObject* parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_config(config)
|
||||
{
|
||||
QList<QVariant> autorun = m_config->getList("autorunSettings");
|
||||
for (const auto& item: autorun) {
|
||||
if (!item.canConvert<ScriptInfo>()) {
|
||||
continue;
|
||||
}
|
||||
m_scripts.append(qvariant_cast<ScriptInfo>(item));
|
||||
}
|
||||
}
|
||||
|
||||
int AutorunScriptModel::rowCount(const QModelIndex& parent) const {
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return m_scripts.count();
|
||||
}
|
||||
|
||||
bool AutorunScriptModel::setData(const QModelIndex& index, const QVariant& data, int role) {
|
||||
if (!index.isValid() || index.parent().isValid() || index.row() >= m_scripts.count()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Qt::CheckStateRole:
|
||||
m_scripts[index.row()].active = data.value<Qt::CheckState>() == Qt::Checked;
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
QVariant AutorunScriptModel::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid() || index.parent().isValid() || index.row() >= m_scripts.count()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return m_scripts.at(index.row()).filename;
|
||||
case Qt::CheckStateRole:
|
||||
return m_scripts.at(index.row()).active ? Qt::Checked : Qt::Unchecked;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Qt::ItemFlags AutorunScriptModel::flags(const QModelIndex& index) const {
|
||||
if (!index.isValid() || index.parent().isValid()) {
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
|
||||
}
|
||||
|
||||
bool AutorunScriptModel::removeRows(int row, int count, const QModelIndex& parent) {
|
||||
if (parent.isValid()) {
|
||||
return false;
|
||||
}
|
||||
if (m_scripts.size() < row) {
|
||||
return false;
|
||||
}
|
||||
if (m_scripts.size() < row + count) {
|
||||
count = m_scripts.size() - row;
|
||||
}
|
||||
m_scripts.erase(m_scripts.begin() + row, m_scripts.begin() + row + count);
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AutorunScriptModel::moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, int destinationChild) {
|
||||
if (sourceParent.isValid() || destinationParent.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceRow < 0 || destinationChild < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceRow >= m_scripts.size() || destinationChild >= m_scripts.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
qWarning() << tr("Moving more than one row at once is not yet supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto item = m_scripts.takeAt(sourceRow);
|
||||
m_scripts.insert(destinationChild, item);
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutorunScriptModel::addScript(const QString& filename) {
|
||||
beginInsertRows({}, m_scripts.count(), m_scripts.count());
|
||||
m_scripts.append(ScriptInfo { filename, true });
|
||||
endInsertRows();
|
||||
save();
|
||||
}
|
||||
|
||||
QList<QString> AutorunScriptModel::activeScripts() const {
|
||||
QList<QString> scripts;
|
||||
for (const auto& pair: m_scripts) {
|
||||
if (!pair.active) {
|
||||
continue;
|
||||
}
|
||||
scripts.append(pair.filename);
|
||||
}
|
||||
return scripts;
|
||||
}
|
||||
|
||||
void AutorunScriptModel::save() {
|
||||
QList<QVariant> list;
|
||||
for (const auto& script : m_scripts) {
|
||||
list.append(QVariant::fromValue(script));
|
||||
}
|
||||
m_config->setList("autorunSettings", list);
|
||||
}
|
57
src/platform/qt/scripting/AutorunScriptModel.h
Normal file
57
src/platform/qt/scripting/AutorunScriptModel.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* Copyright (c) 2013-2025 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class ConfigController;
|
||||
|
||||
class AutorunScriptModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct ScriptInfo {
|
||||
QString filename;
|
||||
bool active;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& stream, const ScriptInfo& object) {
|
||||
stream << object.filename;
|
||||
stream << object.active;
|
||||
return stream;
|
||||
}
|
||||
|
||||
friend QDataStream& operator>>(QDataStream& stream, ScriptInfo& object) {
|
||||
stream >> object.filename;
|
||||
stream >> object.active;
|
||||
return stream;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
AutorunScriptModel(ConfigController* config, QObject* parent = nullptr);
|
||||
|
||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual bool setData(const QModelIndex& index, const QVariant& data, int role = Qt::DisplayRole) override;
|
||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||
virtual bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, int destinationChild) override;
|
||||
|
||||
void addScript(const QString& filename);
|
||||
QList<QString> activeScripts() const;
|
||||
|
||||
private:
|
||||
ConfigController* m_config;
|
||||
QList<ScriptInfo> m_scripts;
|
||||
|
||||
void save();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QGBA::AutorunScriptModel::ScriptInfo);
|
52
src/platform/qt/scripting/AutorunScriptView.cpp
Normal file
52
src/platform/qt/scripting/AutorunScriptView.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/* Copyright (c) 2013-2025 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "scripting/AutorunScriptView.h"
|
||||
|
||||
#include "GBAApp.h"
|
||||
#include "scripting/AutorunScriptModel.h"
|
||||
#include "scripting/ScriptingController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
AutorunScriptView::AutorunScriptView(AutorunScriptModel* model, ScriptingController* controller, QWidget* parent)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||
, m_controller(controller)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
m_ui.autorunList->setModel(model);
|
||||
}
|
||||
|
||||
void AutorunScriptView::addScript() {
|
||||
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select a script"), m_controller->getFilenameFilters());
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutorunScriptModel* model = static_cast<AutorunScriptModel*>(m_ui.autorunList->model());
|
||||
model->addScript(filename);
|
||||
}
|
||||
|
||||
void AutorunScriptView::removeScript(const QModelIndex& index) {
|
||||
QAbstractItemModel* model = m_ui.autorunList->model();
|
||||
model->removeRow(index.row(), index.parent());
|
||||
}
|
||||
|
||||
void AutorunScriptView::removeScript() {
|
||||
removeScript(m_ui.autorunList->currentIndex());
|
||||
}
|
||||
|
||||
void AutorunScriptView::moveUp() {
|
||||
QModelIndex index = m_ui.autorunList->currentIndex();
|
||||
QAbstractItemModel* model = m_ui.autorunList->model();
|
||||
model->moveRows(index.parent(), index.row(), 1, index.parent(), index.row() - 1);
|
||||
}
|
||||
|
||||
void AutorunScriptView::moveDown() {
|
||||
QModelIndex index = m_ui.autorunList->currentIndex();
|
||||
QAbstractItemModel* model = m_ui.autorunList->model();
|
||||
model->moveRows(index.parent(), index.row(), 1, index.parent(), index.row() + 1);
|
||||
}
|
33
src/platform/qt/scripting/AutorunScriptView.h
Normal file
33
src/platform/qt/scripting/AutorunScriptView.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* Copyright (c) 2013-2025 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#pragma once
|
||||
|
||||
#include "ui_AutorunScriptView.h"
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class AutorunScriptModel;
|
||||
class ScriptingController;
|
||||
|
||||
class AutorunScriptView : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AutorunScriptView(AutorunScriptModel* model, ScriptingController* controller, QWidget* parent = nullptr);
|
||||
void removeScript(const QModelIndex&);
|
||||
|
||||
private slots:
|
||||
void addScript();
|
||||
void removeScript();
|
||||
void moveUp();
|
||||
void moveDown();
|
||||
|
||||
private:
|
||||
Ui::AutorunScriptView m_ui;
|
||||
ScriptingController* m_controller;
|
||||
};
|
||||
|
||||
}
|
157
src/platform/qt/scripting/AutorunScriptView.ui
Normal file
157
src/platform/qt/scripting/AutorunScriptView.ui
Normal file
@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QGBA::AutorunScriptView</class>
|
||||
<widget class="QDialog" name="QGBA::AutorunScriptView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Autorun scripts</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="add">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="remove">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QPushButton" name="up">
|
||||
<property name="text">
|
||||
<string>Move up</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-up"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QPushButton" name="down">
|
||||
<property name="text">
|
||||
<string>Move down</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-down"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="5">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Run scripts when starting a game</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QListView" name="autorunList"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>add</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>QGBA::AutorunScriptView</receiver>
|
||||
<slot>addScript()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>47</x>
|
||||
<y>276</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>remove</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>QGBA::AutorunScriptView</receiver>
|
||||
<slot>removeScript()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>138</x>
|
||||
<y>276</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>up</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>QGBA::AutorunScriptView</receiver>
|
||||
<slot>moveUp()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>238</x>
|
||||
<y>276</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>down</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>QGBA::AutorunScriptView</receiver>
|
||||
<slot>moveDown()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>341</x>
|
||||
<y>276</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>addScript()</slot>
|
||||
<slot>removeScript()</slot>
|
||||
<slot>moveUp()</slot>
|
||||
<slot>moveDown()</slot>
|
||||
</slots>
|
||||
</ui>
|
@ -10,14 +10,17 @@
|
||||
#include <QMouseEvent>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ConfigController.h"
|
||||
#include "CoreController.h"
|
||||
#include "Display.h"
|
||||
#include "input/Gamepad.h"
|
||||
#include "input/GamepadButtonEvent.h"
|
||||
#include "input/GamepadHatEvent.h"
|
||||
#include "InputController.h"
|
||||
#include "scripting/AutorunScriptView.h"
|
||||
#include "scripting/ScriptingTextBuffer.h"
|
||||
#include "scripting/ScriptingTextBufferModel.h"
|
||||
#include "Window.h"
|
||||
|
||||
#include <mgba/script.h>
|
||||
#include <mgba-util/math.h>
|
||||
@ -25,8 +28,9 @@
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
ScriptingController::ScriptingController(QObject* parent)
|
||||
ScriptingController::ScriptingController(ConfigController* config, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_model(config)
|
||||
{
|
||||
m_logger.p = this;
|
||||
m_logger.log = [](mLogger* log, int, enum mLogLevel level, const char* format, va_list args) {
|
||||
@ -154,9 +158,6 @@ void ScriptingController::reset() {
|
||||
m_engines.clear();
|
||||
m_activeEngine = nullptr;
|
||||
init();
|
||||
if (m_controller && m_controller->hasStarted()) {
|
||||
attach();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingController::runCode(const QString& code) {
|
||||
@ -164,6 +165,11 @@ void ScriptingController::runCode(const QString& code) {
|
||||
load(vf, "*prompt");
|
||||
}
|
||||
|
||||
void ScriptingController::openAutorunEdit() {
|
||||
AutorunScriptView* view = new AutorunScriptView(&m_model, this);
|
||||
emit autorunScriptsOpened(view);
|
||||
}
|
||||
|
||||
void ScriptingController::flushStorage() {
|
||||
#ifdef USE_JSON_C
|
||||
mScriptStorageFlushAll(&m_scriptContext);
|
||||
@ -259,6 +265,15 @@ void ScriptingController::scriptingEvent(QObject* obj, QEvent* event) {
|
||||
}
|
||||
}
|
||||
|
||||
QString ScriptingController::getFilenameFilters() const {
|
||||
QStringList filters;
|
||||
#ifdef USE_LUA
|
||||
filters.append(tr("Lua scripts (*.lua)"));
|
||||
#endif
|
||||
filters.append(tr("All files (*.*)"));
|
||||
return filters.join(";;");
|
||||
}
|
||||
|
||||
void ScriptingController::updateGamepad() {
|
||||
InputDriver* driver = m_inputController->gamepadDriver();
|
||||
if (!driver) {
|
||||
@ -354,6 +369,14 @@ void ScriptingController::init() {
|
||||
#ifdef USE_JSON_C
|
||||
m_storageFlush.start();
|
||||
#endif
|
||||
|
||||
if (m_controller && m_controller->hasStarted()) {
|
||||
attach();
|
||||
}
|
||||
|
||||
for (const auto& script: m_model.activeScripts()) {
|
||||
loadFile(script);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ScriptingController::qtToScriptingKey(const QKeyEvent* event) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <mgba/script/input.h>
|
||||
#include <mgba/core/scripting.h>
|
||||
|
||||
#include "scripting/AutorunScriptModel.h"
|
||||
#include "VFileDevice.h"
|
||||
|
||||
#include <memory>
|
||||
@ -24,6 +25,7 @@ struct VideoBackend;
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class ConfigController;
|
||||
class CoreController;
|
||||
class InputController;
|
||||
class ScriptingTextBuffer;
|
||||
@ -33,7 +35,7 @@ class ScriptingController : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptingController(QObject* parent = nullptr);
|
||||
ScriptingController(ConfigController* config, QObject* parent = nullptr);
|
||||
~ScriptingController();
|
||||
|
||||
void setController(std::shared_ptr<CoreController> controller);
|
||||
@ -48,17 +50,22 @@ public:
|
||||
mScriptContext* context() { return &m_scriptContext; }
|
||||
ScriptingTextBufferModel* textBufferModel() const { return m_bufferModel; }
|
||||
|
||||
QString getFilenameFilters() const;
|
||||
|
||||
signals:
|
||||
void log(const QString&);
|
||||
void warn(const QString&);
|
||||
void error(const QString&);
|
||||
void textBufferCreated(ScriptingTextBuffer*);
|
||||
|
||||
void autorunScriptsOpened(QWidget* view);
|
||||
|
||||
public slots:
|
||||
void clearController();
|
||||
void updateVideoScale();
|
||||
void reset();
|
||||
void runCode(const QString& code);
|
||||
void openAutorunEdit();
|
||||
|
||||
void flushStorage();
|
||||
|
||||
@ -91,6 +98,7 @@ private:
|
||||
|
||||
mScriptGamepad m_gamepad;
|
||||
|
||||
AutorunScriptModel m_model;
|
||||
std::shared_ptr<CoreController> m_controller;
|
||||
InputController* m_inputController = nullptr;
|
||||
|
||||
|
@ -40,6 +40,7 @@ ScriptingView::ScriptingView(ScriptingController* controller, ConfigController*
|
||||
connect(m_ui.buffers->selectionModel(), &QItemSelectionModel::currentChanged, this, &ScriptingView::selectBuffer);
|
||||
connect(m_ui.load, &QAction::triggered, this, &ScriptingView::load);
|
||||
connect(m_ui.loadMostRecent, &QAction::triggered, this, &ScriptingView::loadMostRecent);
|
||||
connect(m_ui.editAutorunScripts, &QAction::triggered, controller, &ScriptingController::openAutorunEdit);
|
||||
connect(m_ui.reset, &QAction::triggered, controller, &ScriptingController::reset);
|
||||
|
||||
m_mruFiles = m_config->getMRU(ConfigController::MRU::Script);
|
||||
@ -58,7 +59,7 @@ void ScriptingView::submitRepl() {
|
||||
}
|
||||
|
||||
void ScriptingView::load() {
|
||||
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select script to load"), getFilters());
|
||||
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select script to load"), m_controller->getFilenameFilters());
|
||||
if (!filename.isEmpty()) {
|
||||
if (!m_controller->loadFile(filename)) {
|
||||
return;
|
||||
@ -84,15 +85,6 @@ void ScriptingView::selectBuffer(const QModelIndex& current, const QModelIndex&)
|
||||
}
|
||||
}
|
||||
|
||||
QString ScriptingView::getFilters() const {
|
||||
QStringList filters;
|
||||
#ifdef USE_LUA
|
||||
filters.append(tr("Lua scripts (*.lua)"));
|
||||
#endif
|
||||
filters.append(tr("All files (*.*)"));
|
||||
return filters.join(";;");
|
||||
}
|
||||
|
||||
void ScriptingView::appendMRU(const QString& fname) {
|
||||
int index = m_mruFiles.indexOf(fname);
|
||||
if (index >= 0) {
|
||||
@ -121,4 +113,4 @@ void ScriptingView::updateMRU() {
|
||||
|
||||
void ScriptingView::checkEmptyMRU() {
|
||||
m_ui.loadMostRecent->setEnabled(!m_mruFiles.isEmpty());
|
||||
}
|
||||
}
|
||||
|
@ -28,8 +28,6 @@ private slots:
|
||||
void selectBuffer(const QModelIndex& current, const QModelIndex& = QModelIndex());
|
||||
|
||||
private:
|
||||
QString getFilters() const;
|
||||
|
||||
void appendMRU(const QString&);
|
||||
void updateMRU();
|
||||
void checkEmptyMRU();
|
||||
|
@ -98,9 +98,10 @@
|
||||
</widget>
|
||||
<addaction name="load"/>
|
||||
<addaction name="mru"/>
|
||||
<addaction name="loadMostRecent"/>
|
||||
<addaction name="loadMostRecent"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="reset"/>
|
||||
<addaction name="editAutorunScripts"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
</widget>
|
||||
@ -110,7 +111,7 @@
|
||||
<string>Load script...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="loadMostRecent">
|
||||
<action name="loadMostRecent">
|
||||
<property name="text">
|
||||
<string>&Load most recent</string>
|
||||
</property>
|
||||
@ -125,6 +126,11 @@
|
||||
<string>0</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="editAutorunScripts">
|
||||
<property name="text">
|
||||
<string>Edit autorun scripts...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
Loading…
x
Reference in New Issue
Block a user