mirror of https://github.com/mgba-emu/mgba.git
Scripting: Add prototype "buffer" pseudo-TUI interface
This commit is contained in:
parent
f570786d78
commit
28d7bfdffc
|
@ -21,8 +21,10 @@ mLOG_DECLARE_CATEGORY(SCRIPT);
|
|||
|
||||
struct mCore;
|
||||
struct mLogger;
|
||||
struct mScriptTextBuffer;
|
||||
mSCRIPT_DECLARE_STRUCT(mCore);
|
||||
mSCRIPT_DECLARE_STRUCT(mLogger);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptTextBuffer);
|
||||
|
||||
struct mScriptBridge;
|
||||
struct VFile;
|
||||
|
@ -41,6 +43,24 @@ struct mScriptEngine {
|
|||
#endif
|
||||
};
|
||||
|
||||
struct mScriptTextBuffer {
|
||||
void (*init)(struct mScriptTextBuffer*, const char* name);
|
||||
void (*deinit)(struct mScriptTextBuffer*);
|
||||
|
||||
void (*setName)(struct mScriptTextBuffer*, const char* text);
|
||||
|
||||
uint32_t (*getX)(const struct mScriptTextBuffer*);
|
||||
uint32_t (*getY)(const struct mScriptTextBuffer*);
|
||||
uint32_t (*cols)(const struct mScriptTextBuffer*);
|
||||
uint32_t (*rows)(const struct mScriptTextBuffer*);
|
||||
|
||||
void (*print)(struct mScriptTextBuffer*, const char* text);
|
||||
void (*clear)(struct mScriptTextBuffer*);
|
||||
void (*setSize)(struct mScriptTextBuffer*, uint32_t cols, uint32_t rows);
|
||||
void (*moveCursor)(struct mScriptTextBuffer*, uint32_t x, uint32_t y);
|
||||
void (*advance)(struct mScriptTextBuffer*, int32_t);
|
||||
};
|
||||
|
||||
struct mScriptBridge* mScriptBridgeCreate(void);
|
||||
void mScriptBridgeDestroy(struct mScriptBridge*);
|
||||
|
||||
|
@ -64,6 +84,9 @@ void mScriptContextDetachCore(struct mScriptContext*);
|
|||
void mScriptContextAttachLogger(struct mScriptContext*, struct mLogger*);
|
||||
void mScriptContextDetachLogger(struct mScriptContext*);
|
||||
|
||||
typedef struct mScriptTextBuffer* (*mScriptContextBufferFactory)(void*);
|
||||
void mScriptContextSetTextBufferFactory(struct mScriptContext*, mScriptContextBufferFactory factory, void* cbContext);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -324,7 +324,9 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME)
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init)
|
||||
#define mSCRIPT_DEFINE_STRUCT_INIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, NAME)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, NAME)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set)
|
||||
|
||||
|
|
|
@ -161,6 +161,11 @@ struct mScriptCoreAdapter {
|
|||
struct mScriptValue memory;
|
||||
};
|
||||
|
||||
struct mScriptUILibrary {
|
||||
mScriptContextBufferFactory textBufferFactory;
|
||||
void* textBufferContext;
|
||||
};
|
||||
|
||||
static uint32_t mScriptMemoryAdapterRead8(struct mScriptMemoryAdapter* adapter, uint32_t address) {
|
||||
uint32_t segmentSize = adapter->block.end - adapter->block.start;
|
||||
uint32_t segmentAddress = address % segmentSize;
|
||||
|
@ -522,3 +527,59 @@ void mScriptContextAttachLogger(struct mScriptContext* context, struct mLogger*
|
|||
void mScriptContextDetachLogger(struct mScriptContext* context) {
|
||||
mScriptContextRemoveGlobal(context, "console");
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, deinit, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, getX, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, getY, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, cols, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, rows, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, print, 1, CHARP, text);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, clear, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setSize, 2, U32, cols, U32, rows);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, moveCursor, 2, U32, x, U32, y);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, advance, 1, S32, adv);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setName, 1, CHARP, name);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mScriptTextBuffer)
|
||||
mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(mScriptTextBuffer, deinit)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getX)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getY)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, cols)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, rows)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, print)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, clear)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setSize)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, moveCursor)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, advance)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Set the user-visible name of this buffer")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setName)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
struct mScriptTextBuffer* _mScriptUICreateBuffer(struct mScriptUILibrary* lib, const char* name) {
|
||||
struct mScriptTextBuffer* buffer = lib->textBufferFactory(lib->textBufferContext);
|
||||
buffer->init(buffer, name);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptUILibrary);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptUILibrary, S(mScriptTextBuffer), createBuffer, _mScriptUICreateBuffer, 1, CHARP, name);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mScriptUILibrary)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptUILibrary, createBuffer)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptUILibrary, createBuffer)
|
||||
mSCRIPT_MAKE_CHARP(NULL)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
void mScriptContextSetTextBufferFactory(struct mScriptContext* context, mScriptContextBufferFactory factory, void* cbContext) {
|
||||
struct mScriptValue* value = mScriptContextEnsureGlobal(context, "ui", mSCRIPT_TYPE_MS_S(mScriptUILibrary));
|
||||
struct mScriptUILibrary* uiLib = value->value.opaque;
|
||||
if (!uiLib) {
|
||||
uiLib = calloc(1, sizeof(*uiLib));
|
||||
value->value.opaque = uiLib;
|
||||
value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
}
|
||||
uiLib->textBufferFactory = factory;
|
||||
uiLib->textBufferContext = cbContext;
|
||||
}
|
||||
|
|
|
@ -250,6 +250,7 @@ endif()
|
|||
if(ENABLE_SCRIPTING)
|
||||
list(APPEND SOURCE_FILES
|
||||
ScriptingController.cpp
|
||||
ScriptingTextBuffer.cpp
|
||||
ScriptingView.cpp)
|
||||
|
||||
list(APPEND UI_FILES
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "ScriptingController.h"
|
||||
|
||||
#include "CoreController.h"
|
||||
#include "ScriptingTextBuffer.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
|
@ -37,6 +38,7 @@ ScriptingController::ScriptingController(QObject* parent)
|
|||
};
|
||||
|
||||
mScriptContextAttachLogger(&m_scriptContext, &m_logger);
|
||||
mScriptContextSetTextBufferFactory(&m_scriptContext, &ScriptingController::createTextBuffer, this);
|
||||
|
||||
HashTableEnumerate(&m_scriptContext.engines, [](const char* key, void* engine, void* context) {
|
||||
ScriptingController* self = static_cast<ScriptingController*>(context);
|
||||
|
@ -100,3 +102,10 @@ void ScriptingController::runCode(const QString& code) {
|
|||
VFileDevice vf(code.toUtf8());
|
||||
load(vf);
|
||||
}
|
||||
|
||||
mScriptTextBuffer* ScriptingController::createTextBuffer(void* context) {
|
||||
ScriptingController* self = static_cast<ScriptingController*>(context);
|
||||
ScriptingTextBuffer* buffer = new ScriptingTextBuffer(self);
|
||||
emit self->textBufferCreated(buffer);
|
||||
return buffer->textBuffer();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
namespace QGBA {
|
||||
|
||||
class CoreController;
|
||||
class ScriptingTextBuffer;
|
||||
|
||||
class ScriptingController : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -36,12 +38,15 @@ signals:
|
|||
void log(const QString&);
|
||||
void warn(const QString&);
|
||||
void error(const QString&);
|
||||
void textBufferCreated(ScriptingTextBuffer*);
|
||||
|
||||
public slots:
|
||||
void clearController();
|
||||
void runCode(const QString& code);
|
||||
|
||||
private:
|
||||
static mScriptTextBuffer* createTextBuffer(void* context);
|
||||
|
||||
struct Logger : mLogger {
|
||||
ScriptingController* p;
|
||||
} m_logger{};
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/* Copyright (c) 2013-2022 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 "ScriptingTextBuffer.h"
|
||||
|
||||
#include "GBAApp.h"
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QPlainTextDocumentLayout>
|
||||
#include <QTextBlock>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
ScriptingTextBuffer::ScriptingTextBuffer(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_shim.init = &ScriptingTextBuffer::init;
|
||||
m_shim.deinit = &ScriptingTextBuffer::deinit;
|
||||
m_shim.setName = &ScriptingTextBuffer::setName;
|
||||
m_shim.getX = &ScriptingTextBuffer::getX;
|
||||
m_shim.getY = &ScriptingTextBuffer::getY;
|
||||
m_shim.cols = &ScriptingTextBuffer::cols;
|
||||
m_shim.rows = &ScriptingTextBuffer::rows;
|
||||
m_shim.print = &ScriptingTextBuffer::print;
|
||||
m_shim.clear = &ScriptingTextBuffer::clear;
|
||||
m_shim.setSize = &ScriptingTextBuffer::setSize;
|
||||
m_shim.moveCursor = &ScriptingTextBuffer::moveCursor;
|
||||
m_shim.advance = &ScriptingTextBuffer::advance;
|
||||
m_shim.p = this;
|
||||
m_shim.cursor = QTextCursor(&m_document);
|
||||
|
||||
auto layout = new QPlainTextDocumentLayout(&m_document);
|
||||
m_document.setDocumentLayout(layout);
|
||||
m_document.setDefaultFont(GBAApp::app()->monospaceFont());
|
||||
m_document.setMaximumBlockCount(m_dims.height());
|
||||
|
||||
QTextOption textOption;
|
||||
textOption.setWrapMode(QTextOption::NoWrap);
|
||||
m_document.setDefaultTextOption(textOption);
|
||||
setBufferName(tr("Untitled buffer"));
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::setBufferName(const QString& name) {
|
||||
m_name = name;
|
||||
m_document.setMetaInformation(QTextDocument::DocumentTitle, name);
|
||||
emit bufferNameChanged(name);
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::print(const QString& text) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
QString split(text);
|
||||
m_shim.cursor.beginEditBlock();
|
||||
while (m_shim.cursor.positionInBlock() + split.length() > m_dims.width()) {
|
||||
int cut = m_dims.width() - m_shim.cursor.positionInBlock();
|
||||
if (!m_shim.cursor.atBlockEnd()) {
|
||||
m_shim.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
}
|
||||
m_shim.cursor.insertText(split.left(cut));
|
||||
if (m_shim.cursor.atEnd()) {
|
||||
m_shim.cursor.insertBlock();
|
||||
} else {
|
||||
m_shim.cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, 1);
|
||||
}
|
||||
split = split.mid(cut);
|
||||
}
|
||||
if (!m_shim.cursor.atBlockEnd()) {
|
||||
m_shim.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, split.length());
|
||||
}
|
||||
m_shim.cursor.insertText(split);
|
||||
m_shim.cursor.endEditBlock();
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::clear() {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_document.clear();
|
||||
m_document.setMetaInformation(QTextDocument::DocumentTitle, m_name);
|
||||
m_shim.cursor = QTextCursor(&m_document);
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::setSize(const QSize& size) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_dims = size;
|
||||
m_document.setMaximumBlockCount(m_dims.height());
|
||||
for (int i = 0; i < m_document.blockCount(); ++i) {
|
||||
if (m_document.findBlockByNumber(i).length() - 1 > m_dims.width()) {
|
||||
QTextCursor deleter(m_document.findBlockByNumber(i));
|
||||
deleter.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, size.width());
|
||||
deleter.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
deleter.removeSelectedText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::moveCursor(const QPoint& pos) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_shim.cursor.movePosition(QTextCursor::Start);
|
||||
int y = pos.y();
|
||||
if (y >= m_dims.height()) {
|
||||
y = m_dims.height() - 1;
|
||||
}
|
||||
if (y >= m_document.blockCount()) {
|
||||
m_shim.cursor.movePosition(QTextCursor::End);
|
||||
while (y >= m_document.blockCount()) {
|
||||
m_shim.cursor.insertBlock();
|
||||
}
|
||||
} else {
|
||||
m_shim.cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, y);
|
||||
}
|
||||
|
||||
int x = pos.x();
|
||||
if (x >= m_dims.width()) {
|
||||
x = m_dims.width() - 1;
|
||||
}
|
||||
|
||||
if (x >= m_shim.cursor.block().length()) {
|
||||
m_shim.cursor.movePosition(QTextCursor::EndOfBlock);
|
||||
m_shim.cursor.insertText(QString(x - m_shim.cursor.block().length() + 1, QChar(' ')));
|
||||
} else {
|
||||
m_shim.cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, x);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::advance(int increment) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
int x = m_shim.cursor.positionInBlock();
|
||||
int y = m_shim.cursor.blockNumber();
|
||||
x += increment;
|
||||
if (x > 0) {
|
||||
y += x / m_dims.width();
|
||||
x %= m_dims.width();
|
||||
} else if (x < 0) {
|
||||
y += (x - m_dims.width() + 1) / m_dims.width();
|
||||
x %= m_dims.width();
|
||||
if (x) {
|
||||
x += m_dims.width();
|
||||
}
|
||||
}
|
||||
locker.unlock();
|
||||
moveCursor({x, y});
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::init(struct mScriptTextBuffer* buffer, const char* name) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
if (name) {
|
||||
QMetaObject::invokeMethod(self, "setBufferName", Q_ARG(const QString&, QString::fromUtf8(name)));
|
||||
}
|
||||
QMetaObject::invokeMethod(self, "clear");
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::deinit(struct mScriptTextBuffer*) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::setName(struct mScriptTextBuffer* buffer, const char* name) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMetaObject::invokeMethod(self, "setBufferName", Q_ARG(const QString&, QString::fromUtf8(name)));
|
||||
}
|
||||
|
||||
uint32_t ScriptingTextBuffer::getX(const struct mScriptTextBuffer* buffer) {
|
||||
const ScriptingBufferShim* self = static_cast<const ScriptingTextBuffer::ScriptingBufferShim*>(buffer);
|
||||
QMutexLocker locker(&self->p->m_mutex);
|
||||
return self->cursor.positionInBlock();
|
||||
}
|
||||
|
||||
uint32_t ScriptingTextBuffer::getY(const struct mScriptTextBuffer* buffer) {
|
||||
const ScriptingBufferShim* self = static_cast<const ScriptingTextBuffer::ScriptingBufferShim*>(buffer);
|
||||
QMutexLocker locker(&self->p->m_mutex);
|
||||
return self->cursor.blockNumber();
|
||||
}
|
||||
|
||||
uint32_t ScriptingTextBuffer::cols(const struct mScriptTextBuffer* buffer) {
|
||||
ScriptingTextBuffer* self = static_cast<const ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMutexLocker locker(&self->m_mutex);
|
||||
return self->m_dims.width();
|
||||
}
|
||||
|
||||
uint32_t ScriptingTextBuffer::rows(const struct mScriptTextBuffer* buffer) {
|
||||
ScriptingTextBuffer* self = static_cast<const ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMutexLocker locker(&self->m_mutex);
|
||||
return self->m_dims.height();
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::print(struct mScriptTextBuffer* buffer, const char* text) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMetaObject::invokeMethod(self, "print", Q_ARG(const QString&, QString::fromUtf8(text)));
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::clear(struct mScriptTextBuffer* buffer) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMetaObject::invokeMethod(self, "clear");
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::setSize(struct mScriptTextBuffer* buffer, uint32_t cols, uint32_t rows) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
if (cols > 500) {
|
||||
cols = 500;
|
||||
}
|
||||
if (rows > 10000) {
|
||||
rows = 10000;
|
||||
}
|
||||
QMetaObject::invokeMethod(self, "setSize", Q_ARG(QSize, QSize(cols, rows)));
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::moveCursor(struct mScriptTextBuffer* buffer, uint32_t x, uint32_t y) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
if (x > INT_MAX) {
|
||||
x = INT_MAX;
|
||||
}
|
||||
if (y > INT_MAX) {
|
||||
y = INT_MAX;
|
||||
}
|
||||
QMetaObject::invokeMethod(self, "moveCursor", Q_ARG(QPoint, QPoint(x, y)));
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::advance(struct mScriptTextBuffer* buffer, int32_t adv) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
emit self->advance(adv);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* Copyright (c) 2013-2022 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 <QMutex>
|
||||
#include <QObject>
|
||||
#include <QTextCursor>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include <mgba/core/scripting.h>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class ScriptingTextBuffer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptingTextBuffer(QObject* parent);
|
||||
|
||||
QTextDocument* document() { return &m_document; };
|
||||
mScriptTextBuffer* textBuffer() { return &m_shim; }
|
||||
|
||||
public slots:
|
||||
void setBufferName(const QString&);
|
||||
void print(const QString&);
|
||||
void clear();
|
||||
void setSize(const QSize&);
|
||||
void moveCursor(const QPoint&);
|
||||
void advance(int);
|
||||
|
||||
signals:
|
||||
void bufferNameChanged(const QString&);
|
||||
|
||||
private:
|
||||
struct ScriptingBufferShim : public mScriptTextBuffer {
|
||||
ScriptingTextBuffer* p;
|
||||
QTextCursor cursor;
|
||||
} m_shim;
|
||||
QTextDocument m_document;
|
||||
QMutex m_mutex;
|
||||
QString m_name;
|
||||
|
||||
static void init(struct mScriptTextBuffer*, const char* name);
|
||||
static void deinit(struct mScriptTextBuffer*);
|
||||
|
||||
static void setName(struct mScriptTextBuffer*, const char* name);
|
||||
|
||||
static uint32_t getX(const struct mScriptTextBuffer*);
|
||||
static uint32_t getY(const struct mScriptTextBuffer*);
|
||||
static uint32_t cols(const struct mScriptTextBuffer*);
|
||||
static uint32_t rows(const struct mScriptTextBuffer*);
|
||||
|
||||
static void print(struct mScriptTextBuffer*, const char* text);
|
||||
static void clear(struct mScriptTextBuffer*);
|
||||
static void setSize(struct mScriptTextBuffer*, uint32_t cols, uint32_t rows);
|
||||
static void moveCursor(struct mScriptTextBuffer*, uint32_t x, uint32_t y);
|
||||
static void advance(struct mScriptTextBuffer*, int32_t);
|
||||
|
||||
QSize m_dims{80, 24};
|
||||
};
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "GBAApp.h"
|
||||
#include "ScriptingController.h"
|
||||
#include "ScriptingTextBuffer.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
|
@ -24,7 +25,9 @@ ScriptingView::ScriptingView(ScriptingController* controller, QWidget* parent)
|
|||
connect(m_controller, &ScriptingController::log, m_ui.log, &LogWidget::log);
|
||||
connect(m_controller, &ScriptingController::warn, m_ui.log, &LogWidget::warn);
|
||||
connect(m_controller, &ScriptingController::error, m_ui.log, &LogWidget::error);
|
||||
connect(m_controller, &ScriptingController::textBufferCreated, this, &ScriptingView::addTextBuffer);
|
||||
|
||||
connect(m_ui.buffers, &QListWidget::currentRowChanged, this, &ScriptingView::selectBuffer);
|
||||
connect(m_ui.load, &QAction::triggered, this, &ScriptingView::load);
|
||||
}
|
||||
|
||||
|
@ -41,6 +44,25 @@ void ScriptingView::load() {
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptingView::addTextBuffer(ScriptingTextBuffer* buffer) {
|
||||
QTextDocument* document = buffer->document();
|
||||
m_textBuffers.append(buffer);
|
||||
QListWidgetItem* item = new QListWidgetItem(document->metaInformation(QTextDocument::DocumentTitle));
|
||||
connect(buffer, &ScriptingTextBuffer::bufferNameChanged, this, [item](const QString& name) {
|
||||
item->setText(name);
|
||||
});
|
||||
connect(buffer, &QObject::destroyed, this, [this, buffer, item]() {
|
||||
m_textBuffers.removeAll(buffer);
|
||||
m_ui.buffers->removeItemWidget(item);
|
||||
});
|
||||
m_ui.buffers->addItem(item);
|
||||
m_ui.buffers->setCurrentItem(item);
|
||||
}
|
||||
|
||||
void ScriptingView::selectBuffer(int index) {
|
||||
m_ui.buffer->setDocument(m_textBuffers[index]->document());
|
||||
}
|
||||
|
||||
QString ScriptingView::getFilters() const {
|
||||
QStringList filters;
|
||||
#ifdef USE_LUA
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
namespace QGBA {
|
||||
|
||||
class ScriptingController;
|
||||
class ScriptingTextBuffer;
|
||||
|
||||
class ScriptingView : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
@ -21,11 +22,16 @@ private slots:
|
|||
void submitRepl();
|
||||
void load();
|
||||
|
||||
void addTextBuffer(ScriptingTextBuffer*);
|
||||
void selectBuffer(int);
|
||||
|
||||
private:
|
||||
QString getFilters() const;
|
||||
|
||||
Ui::ScriptingView m_ui;
|
||||
|
||||
ScriptingController* m_controller;
|
||||
QList<ScriptingTextBuffer*> m_textBuffers;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -15,31 +15,8 @@
|
|||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="prompt"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="runButton">
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QGBA::LogWidget" name="log">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QListWidget" name="loadedScripts">
|
||||
<item row="0" column="0" rowspan="3">
|
||||
<widget class="QListWidget" name="buffers">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
|
@ -52,11 +29,39 @@
|
|||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Console</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QPlainTextEdit" name="buffer">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QGBA::LogWidget" name="log">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="prompt"/>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="runButton">
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
Loading…
Reference in New Issue