diff options
-rw-r--r-- | src/qml/qml/qqmlbuiltinfunctions.cpp | 85 | ||||
-rw-r--r-- | src/qml/qml/qqmlbuiltinfunctions_p.h | 12 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/data/componentCreationForType.qml | 48 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 4 |
4 files changed, 141 insertions, 8 deletions
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp index 79475435bf..4a1bb5c8c2 100644 --- a/src/qml/qml/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/qqmlbuiltinfunctions.cpp @@ -271,6 +271,22 @@ QtObject::QtObject(ExecutionEngine *engine) { } +QtObject::Contexts QtObject::getContexts() const +{ + QQmlEngine *engine = qmlEngine(); + if (!engine) + return {}; + + QQmlRefPointer<QQmlContextData> context = v4Engine()->callingQmlContext(); + if (!context) + context = QQmlContextData::get(QQmlEnginePrivate::get(engine)->rootContext); + + Q_ASSERT(context); + QQmlRefPointer<QQmlContextData> effectiveContext + = context->isPragmaLibraryContext() ? nullptr : context; + return {context, effectiveContext}; +} + QtObject *QtObject::create(QQmlEngine *, QJSEngine *jsEngine) { QV4::ExecutionEngine *v4 = jsEngine->handle(); @@ -1288,6 +1304,32 @@ See \l {Dynamic QML Object Creation from JavaScript} for more information on usi To create a QML object from an arbitrary string of QML (instead of a file), use \l{QtQml::Qt::createQmlObject()}{Qt.createQmlObject()}. */ + +/*! +\qmlmethod Component Qt::createComponent(string moduleUri, string typeName, enumeration mode, QtObject parent) +\overload +Returns a \l Component object created for the type specified by \a moduleUri and \a typeName. +\qml +import QtQuick +QtObject { + id: root + property Component myComponent: Qt.createComponent(Rectangle, root) +} +\endqml +This overload mostly behaves as the \c url based version, but can be used +to instantiate types which do not have an URL (e.g. C++ types registered +via \l {QML_ELEMENT}). +\note In some cases, passing \c Component.Asynchronous won't have any +effect: +\list +\li The type is implemented in C++ +\li The type is an inline component. +\endlist +If the optional \a parent parameter is given, it should refer to the object +that will become the parent for the created \l Component object. If no mode +was passed, this can be the second argument. +*/ + QQmlComponent *QtObject::createComponent(const QUrl &url, QObject *parent) const { return createComponent(url, QQmlComponent::PreferSynchronous, parent); @@ -1308,13 +1350,9 @@ QQmlComponent *QtObject::createComponent(const QUrl &url, QQmlComponent::Compila if (!engine) return nullptr; - QQmlRefPointer<QQmlContextData> context = v4Engine()->callingQmlContext(); + auto [context, effectiveContext] = getContexts(); if (!context) - context = QQmlContextData::get(QQmlEnginePrivate::get(engine)->rootContext); - - Q_ASSERT(context); - QQmlRefPointer<QQmlContextData> effectiveContext - = context->isPragmaLibraryContext() ? nullptr : context; + return nullptr; QQmlComponent *c = new QQmlComponent(engine, context->resolvedUrl(url), mode, parent); QQmlComponentPrivate::get(c)->creationContext = effectiveContext; @@ -1323,6 +1361,41 @@ QQmlComponent *QtObject::createComponent(const QUrl &url, QQmlComponent::Compila return c; } +QQmlComponent *QtObject::createComponent(const QString &moduleUri, const QString &typeName, + QObject *parent) const +{ + return createComponent(moduleUri, typeName, QQmlComponent::PreferSynchronous, parent); +} + +QQmlComponent *QtObject::createComponent(const QString &moduleUri, const QString &typeName, QQmlComponent::CompilationMode mode, QObject *parent) const +{ + if (mode != QQmlComponent::Asynchronous && mode != QQmlComponent::PreferSynchronous) { + v4Engine()->throwError(QStringLiteral("Invalid compilation mode %1").arg(mode)); + return nullptr; + } + + QQmlEngine *engine = qmlEngine(); + if (!engine) + return nullptr; + + if (moduleUri.isEmpty() || typeName.isEmpty()) + return nullptr; + + auto [context, effectiveContext] = getContexts(); + if (!context) + return nullptr; + + QQmlComponent *c = new QQmlComponent(engine, moduleUri, typeName, mode, parent); + if (c->isError() && !parent && moduleUri.endsWith(u".qml")) { + v4Engine()->throwTypeError( + QStringLiteral("Invalid arguments; did you swap mode and parent")); + } + QQmlComponentPrivate::get(c)->creationContext = effectiveContext; + QQmlData::get(c, true)->explicitIndestructibleSet = false; + QQmlData::get(c)->indestructible = false; + return c; +} + #if QT_CONFIG(translation) QString QtObject::uiLanguage() const { diff --git a/src/qml/qml/qqmlbuiltinfunctions_p.h b/src/qml/qml/qqmlbuiltinfunctions_p.h index b961fe6e56..2739524516 100644 --- a/src/qml/qml/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/qqmlbuiltinfunctions_p.h @@ -131,6 +131,12 @@ public: const QUrl &url, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous, QObject *parent = nullptr) const; + Q_INVOKABLE QQmlComponent *createComponent(const QString &moduleUri, + const QString &typeName, QObject *parent) const; + Q_INVOKABLE QQmlComponent *createComponent(const QString &moduleUri, const QString &typeName, + QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous, + QObject *parent = nullptr) const; + Q_INVOKABLE QJSValue binding(const QJSValue &function) const; Q_INVOKABLE void callLater(QQmlV4Function *args); @@ -156,6 +162,12 @@ private: QJSEngine *jsEngine() const { return m_engine->jsEngine(); } QV4::ExecutionEngine *v4Engine() const { return m_engine; } + struct Contexts { + QQmlRefPointer<QQmlContextData> context; + QQmlRefPointer<QQmlContextData> effectiveContext; + }; + Contexts getContexts() const; + QQmlPlatform *m_platform = nullptr; QQmlApplication *m_application = nullptr; diff --git a/tests/auto/qml/qqmlecmascript/data/componentCreationForType.qml b/tests/auto/qml/qqmlecmascript/data/componentCreationForType.qml new file mode 100644 index 0000000000..66b06d4f5c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/componentCreationForType.qml @@ -0,0 +1,48 @@ +import QtQml + +QtObject { + id: root + property int status: -1 + Component.onCompleted: { + let comp + comp = Qt.createComponent("QtQml", "QtObject") + if (comp.status !== Component.Ready) { + root.status = 1 + return + } + + comp = Qt.createComponent("QtQml", "QtObject", root) + if (comp.status !== Component.Ready) { + root.status = 2 + return + } + + comp = Qt.createComponent("QtQml", "QtObject", Component.PreferSynchronous, root) + if (comp.status !== Component.Ready) { + root.status = 3 + return + } + + + comp = Qt.createComponent("QtQml", "QtObject", Component.Asynchronous) + // C++ component will _always_ be ready, even if we request async loading + if (comp.status !== Component.Ready) { + root.status = 4 + return + } + + comp = Qt.createComponent("QtQml", "DoesNotExist") + if (comp.status !== Component.Error) { + root.status = 5 + return + } + + comp = Qt.createComponent("NoSuchModule", "QtObject") + if (comp.status !== Component.Error) { + root.status = 6 + return + } + + root.status = 0 + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 2922b02643..f591a87dcb 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -1964,11 +1964,11 @@ void tst_qqmlecmascript::componentCreation_data() << "null"; QTest::newRow("invalidSecondArg") << "invalidSecondArg" - << "" // We cannot catch this case as coercing a string to a number is valid in JavaScript + << ":40: TypeError: Invalid arguments; did you swap mode and parent" << ""; QTest::newRow("invalidThirdArg") << "invalidThirdArg" - << ":45: TypeError: Passing incompatible arguments to C++ functions from JavaScript is not allowed." + << ":45: TypeError: Invalid arguments; did you swap mode and parent" << ""; QTest::newRow("invalidMode") << "invalidMode" |