Qt: Add support for running scripts at startup (closes #3465)

This commit is contained in:
Vicki Pfau 2025-04-27 22:02:19 -07:00
parent c33a0d6534
commit 90057703b5
19 changed files with 573 additions and 48 deletions

View File

@ -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

View File

@ -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()

View File

@ -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:

View File

@ -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();

View File

@ -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);

View File

@ -63,6 +63,7 @@ signals:
void languageChanged();
void libraryCleared();
void saveSettingsRequested();
void openAutorunScripts();
public slots:
void selectPage(Page);

View File

@ -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>

View File

@ -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) {

View File

@ -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);

View 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);
}

View 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);

View 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);
}

View 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;
};
}

View 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>

View File

@ -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) {

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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();

View File

@ -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>&amp;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>