File indexing completed on 2024-11-24 03:56:26
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include <QQmlEngine> 0010 0011 #include "app/scripting/script_engine.hpp" 0012 0013 namespace app::scripting::js { 0014 0015 class JsContext : public ScriptExecutionContext 0016 { 0017 public: 0018 JsContext(const ScriptEngine* js_engine) : js_engine(js_engine) 0019 { 0020 qml_engine.installExtensions(QJSEngine::ConsoleExtension); 0021 } 0022 0023 QJSValue convert(const QVariant& val) 0024 { 0025 if ( val.userType() == QMetaType::QVariantMap ) 0026 { 0027 auto jsv = qml_engine.newObject(); 0028 auto cont = val.toMap(); 0029 for ( auto it = cont.begin(); it != cont.end(); ++it ) 0030 jsv.setProperty(it.key(), convert(*it)); 0031 return jsv; 0032 } 0033 else if ( val.userType() == QMetaType::QVariantList ) 0034 { 0035 auto cont = val.toList(); 0036 auto jsv = qml_engine.newArray(cont.size()); 0037 for ( int i = 0; i < cont.size(); i++ ) 0038 jsv.setProperty(i, convert(cont[i])); 0039 return jsv; 0040 } 0041 else if ( val.type() == QVariant::Int ) 0042 { 0043 return QJSValue(val.toInt()); 0044 } 0045 else if ( val.type() == QVariant::String ) 0046 { 0047 return QJSValue(val.toString()); 0048 } 0049 else if ( val.canConvert<QObject*>() ) 0050 { 0051 QObject* obj = val.value<QObject*>(); 0052 if ( obj ) 0053 { 0054 auto jsv = qml_engine.newQObject(obj); 0055 QQmlEngine::setObjectOwnership(obj, QQmlEngine::CppOwnership); 0056 return jsv; 0057 } 0058 } 0059 return QJSValue{QJSValue::NullValue}; 0060 } 0061 0062 void expose(const QString& name, const QVariant& val) override 0063 { 0064 qml_engine.globalObject().setProperty(name, convert(val)); 0065 } 0066 0067 QString eval_to_string(const QString& code) override 0068 { 0069 return qml_engine.evaluate(code+";").toString(); 0070 } 0071 0072 bool run_from_module ( 0073 const QDir& path, ///< Path containing the module file 0074 const QString& module, ///< Module name to load 0075 const QString& function,///< Function to call 0076 const QVariantList& args///< Arguments to pass the function 0077 ) override 0078 { 0079 QStringList import_path = qml_engine.importPathList(); 0080 qml_engine.addImportPath(path.path()); 0081 0082 QJSValue module_obj = qml_engine.importModule(module); 0083 if ( module_obj.isError() || !module_obj.hasProperty(function) ) 0084 return false; 0085 0086 QJSValue func = module_obj.property(function); 0087 if ( !func.isCallable() ) 0088 return false; 0089 0090 QJSValueList jsargs; 0091 for ( const auto& arg : args ) 0092 jsargs.push_back(convert(arg)); 0093 0094 QJSValue ret = func.call(jsargs); 0095 if ( ret.isError() ) 0096 throw ScriptError(ret.toString()); 0097 0098 qml_engine.setImportPathList(import_path); 0099 return true; 0100 } 0101 0102 const ScriptEngine* engine() const override 0103 { 0104 return js_engine; 0105 } 0106 0107 void app_module(const QString&) override {} 0108 0109 private: 0110 QQmlEngine qml_engine; 0111 const ScriptEngine* js_engine; 0112 }; 0113 0114 class JsEngine : public ScriptEngine 0115 { 0116 public: 0117 QString slug() const override { return "js"; } 0118 QString label() const override { return "ECMAScript"; } 0119 0120 ScriptContext create_context() const override 0121 { 0122 return std::make_unique<JsContext>(this); 0123 } 0124 0125 private: 0126 static Autoregister<JsEngine> autoreg; 0127 }; 0128 0129 } // namespace app::scripting::js