Skip to content

Commit

Permalink
Fix and simplify creating scriptable objects
Browse files Browse the repository at this point in the history
  • Loading branch information
hluk committed Dec 13, 2023
1 parent b07cf41 commit c5447e6
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 35 deletions.
128 changes: 96 additions & 32 deletions src/scriptable/scriptable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ struct ScriptValueFactory<QVariant> {
return ::toScriptValue(variant.value<QVariantMap>(), scriptable);

if (variant.canConvert<QByteArray>())
return scriptable->engine()->newQObject(new ScriptableByteArray(variant));
return scriptable->newByteArray(new ScriptableByteArray(variant));

return scriptable->engine()->toScriptValue(variant);
}
Expand Down Expand Up @@ -467,17 +467,13 @@ QJSValue evaluateStrict(QJSEngine *engine, const QString &script)
return v;
}

void addScriptableClass(const QMetaObject *metaObject, const QString &name, QJSEngine *engine, const QString &initFunction = QString())
QJSValue addScriptableClass(const QMetaObject *metaObject, const QString &name, QJSEngine *engine)
{
auto cls = engine->newQMetaObject(metaObject);
const QString privateName(QStringLiteral("_copyq_") + name);
engine->globalObject().setProperty(privateName, cls);
// Only single argument constructors are supported.
// It's possible to use "...args" but it's not supported in Qt 5.9.
evaluateStrict(engine, QStringLiteral(
"function %1(arg) {return %3(arg === undefined ? new %2() : new %2(arg));}"
"%1.prototype = %2;"
).arg(name, privateName, initFunction));
auto fn = engine->globalObject().property(name);
Q_ASSERT(fn.isCallable());
fn.setProperty(QStringLiteral("prototype"), cls);
return cls;
}

QByteArray serializeScriptValue(const QJSValue &value, Scriptable *scriptable)
Expand Down Expand Up @@ -727,13 +723,18 @@ Scriptable::Scriptable(

installObject(this, &Scriptable::staticMetaObject, globalObject);

addScriptableClass(&ScriptableByteArray::staticMetaObject, QStringLiteral("ByteArray"), m_engine);
addScriptableClass(&ScriptableFile::staticMetaObject, QStringLiteral("File"), m_engine);
addScriptableClass(&ScriptableTemporaryFile::staticMetaObject, QStringLiteral("TemporaryFile"), m_engine);
addScriptableClass(&ScriptableDir::staticMetaObject, QStringLiteral("Dir"), m_engine);
addScriptableClass(&ScriptableItemSelection::staticMetaObject, QStringLiteral("ItemSelection"), m_engine,
QStringLiteral("global._initItemSelection = "));
addScriptableClass(&ScriptableSettings::staticMetaObject, QStringLiteral("Settings"), m_engine);
m_byteArrayPrototype = addScriptableClass(
&ScriptableByteArray::staticMetaObject, QStringLiteral("ByteArray"), m_engine);
m_filePrototype = addScriptableClass(
&ScriptableFile::staticMetaObject, QStringLiteral("File"), m_engine);
m_temporaryFilePrototype = addScriptableClass(
&ScriptableTemporaryFile::staticMetaObject, QStringLiteral("TemporaryFile"), m_engine);
m_dirPrototype = addScriptableClass(
&ScriptableDir::staticMetaObject, QStringLiteral("Dir"), m_engine);
m_itemSelectionPrototype = addScriptableClass(
&ScriptableItemSelection::staticMetaObject, QStringLiteral("ItemSelection"), m_engine);
m_settingsPrototype = addScriptableClass(
&ScriptableSettings::staticMetaObject, QStringLiteral("Settings"), m_engine);
}

QJSValue Scriptable::argumentsArray() const
Expand All @@ -753,7 +754,12 @@ QJSValue Scriptable::argument(int index) const

QJSValue Scriptable::newByteArray(const QByteArray &bytes) const
{
return m_engine->newQObject(new ScriptableByteArray(bytes));
return newQObject(new ScriptableByteArray(bytes), m_byteArrayPrototype);
}

QJSValue Scriptable::newByteArray(ScriptableByteArray *ba) const
{
return newQObject(ba, m_byteArrayPrototype);
}

QByteArray Scriptable::fromString(const QString &value) const
Expand Down Expand Up @@ -801,7 +807,14 @@ QJSValue Scriptable::fromDataMap(const QVariantMap &dataMap) const
QByteArray Scriptable::makeByteArray(const QJSValue &value) const
{
const QByteArray *data = getByteArray(value);
return data ? *data : fromString(value.toString());
if (data)
return *data;

const QVariant variant = value.toVariant();
if (variant.type() == QVariant::ByteArray)
return variant.toByteArray();

return fromString(value.toString());
}

bool Scriptable::toItemData(const QJSValue &value, const QString &mime, QVariantMap *data) const
Expand Down Expand Up @@ -948,6 +961,65 @@ QJSValue Scriptable::call(const QString &label, QJSValue *fn, const QJSValueList
return v;
}

QJSValue Scriptable::ByteArray() const
{
const auto arg = argument(0);
if (arg.isUndefined())
return newByteArray(new ScriptableByteArray());

if (arg.isNumber())
return newByteArray(new ScriptableByteArray(arg.toInt()));

const auto obj = arg.toQObject();
if (obj) {
const auto ba = qobject_cast<ScriptableByteArray*>(obj);
if (ba)
return newByteArray(new ScriptableByteArray(*ba));
}

return newByteArray(new ScriptableByteArray(makeByteArray(arg)));
}

QJSValue Scriptable::File() const
{
const auto arg = argument(0);
const auto path = arg.isUndefined() ? QString() : toString(arg);
return newQObject(new ScriptableFile(path), m_filePrototype);
}

QJSValue Scriptable::TemporaryFile() const
{
const auto arg = argument(0);
const auto path = arg.isUndefined() ? QString() : toString(arg);
return newQObject(new ScriptableTemporaryFile(path), m_temporaryFilePrototype);
}

QJSValue Scriptable::Dir() const
{
const auto arg = argument(0);
const auto path = arg.isUndefined() ? QString() : toString(arg);
return newQObject(new ScriptableDir(path), m_dirPrototype);
}

QJSValue Scriptable::ItemSelection() const
{
const auto arg = argument(0);
const auto tabName = arg.isUndefined() ? QString() : toString(arg);
auto sel = new ScriptableItemSelection(tabName);
auto obj = newQObject(sel, m_itemSelectionPrototype);
sel->init(obj, m_proxy, m_tabName);
return obj;
}

QJSValue Scriptable::Settings() const
{
const auto arg = argument(0);
if (arg.isUndefined())
return newQObject(new ScriptableSettings(), m_settingsPrototype);

return newQObject(new ScriptableSettings(toString(arg)), m_settingsPrototype);
}

QJSValue Scriptable::version()
{
m_skipArguments = 0;
Expand Down Expand Up @@ -3742,6 +3814,7 @@ QJSValue Scriptable::readInput()
}

inputReaderThread->deleteLater();
COPYQ_LOG(QStringLiteral("Read Input %1 bytes").arg(inputReaderThread->input.length()));
return newByteArray(inputReaderThread->input);
}

Expand Down Expand Up @@ -3784,20 +3857,11 @@ NetworkReply *Scriptable::networkPostHelper()
return NetworkReply::post(url, postData, this);
}

QJSValue Scriptable::initItemSelection(const QJSValue &obj)
QJSValue Scriptable::newQObject(QObject *obj, const QJSValue &prototype) const
{
QObject *qobj = obj.toQObject();
Q_ASSERT(qobj);
if (!qobj)
return {};

auto sel = qobject_cast<ScriptableItemSelection*>(qobj);
Q_ASSERT(sel);
if (!sel)
return {};

sel->init(obj, m_proxy, m_tabName);
return obj;
auto value = m_engine->newQObject(obj);
value.setPrototype(prototype);
return value;
}

void Scriptable::installObject(QObject *fromObj, const QMetaObject *metaObject, QJSValue &toObject)
Expand Down
20 changes: 17 additions & 3 deletions src/scriptable/scriptable.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "platform/platformnativeinterface.h"

class Action;
class ScriptableByteArray;
class ClipboardBrowser;
class ItemFactory;
class NetworkReply;
Expand Down Expand Up @@ -56,7 +57,6 @@ class Scriptable final : public QObject

Q_PROPERTY(QJSValue _copyqUncaughtException READ uncaughtException WRITE setUncaughtException)
Q_PROPERTY(QJSValue _copyqHasUncaughtException READ hasUncaughtException)
Q_PROPERTY(QJSValue _initItemSelection READ getUndefined WRITE initItemSelection)

public:
Scriptable(
Expand All @@ -76,6 +76,7 @@ class Scriptable final : public QObject
QJSValue argument(int index) const;

QJSValue newByteArray(const QByteArray &bytes) const;
QJSValue newByteArray(ScriptableByteArray *ba) const;

QByteArray fromString(const QString &value) const;
QVariant toVariant(const QJSValue &value);
Expand Down Expand Up @@ -153,6 +154,13 @@ class Scriptable final : public QObject
void abortEvaluation(Abort abort = Abort::AllEvaluations);

public slots:
QJSValue ByteArray() const;
QJSValue File() const;
QJSValue TemporaryFile() const;
QJSValue Dir() const;
QJSValue ItemSelection() const;
QJSValue Settings() const;

QJSValue version();
QJSValue help();

Expand Down Expand Up @@ -451,8 +459,7 @@ public slots:
NetworkReply *networkGetHelper();
NetworkReply *networkPostHelper();

QJSValue getUndefined () { return {}; }
QJSValue initItemSelection(const QJSValue &obj);
QJSValue newQObject(QObject *obj, const QJSValue &prototype) const;

ScriptableProxy *m_proxy;
QJSEngine *m_engine;
Expand Down Expand Up @@ -494,6 +501,13 @@ public slots:
QJSValue m_createFn;
QJSValue m_createFnB;
QJSValue m_createProperty;

QJSValue m_byteArrayPrototype;
QJSValue m_filePrototype;
QJSValue m_temporaryFilePrototype;
QJSValue m_dirPrototype;
QJSValue m_itemSelectionPrototype;
QJSValue m_settingsPrototype;
};

class NetworkReply final : public QObject {
Expand Down
2 changes: 2 additions & 0 deletions src/tests/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1971,6 +1971,7 @@ void Tests::classByteArray()
{
RUN("ByteArray", "");
RUN("ByteArray('test')", "test");
RUN("ByteArray(ByteArray('test'))", "test");
RUN("typeof(ByteArray('test'))", "object\n");
RUN("ByteArray('test') instanceof ByteArray", "true\n");
RUN("b = ByteArray('0123'); b.chop(2); b", "01");
Expand All @@ -1979,6 +1980,7 @@ void Tests::classByteArray()
RUN("ByteArray('0123').mid(1, 2)", "12");
RUN("ByteArray('0123').mid(1)", "123");
RUN("ByteArray('0123').right(3)", "123");
RUN("ByteArray('0123').remove(1, 2)", "03");
RUN("ByteArray(' 01 23 ').simplified()", "01 23");
RUN("ByteArray('0123').toBase64()", "MDEyMw==");
RUN("ByteArray('ABCd').toLower()", "abcd");
Expand Down

0 comments on commit c5447e6

Please sign in to comment.