diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2022-03-31 16:28:24 +0200 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2022-11-28 17:10:07 +0100 |
commit | 6656567a4085e3d6de01226fb7b1ec16ee7ba08c (patch) | |
tree | 9d021e9f3c17d468dcc6bc1be52c0e595019a662 /src/qml/qml | |
parent | 6cd8d209ec472a658a330f25b84f92cd61e0d4cf (diff) |
Introduce type based overload of Qt.createComponent
Add type based createComponent overloads based on
QQmlComponent::loadFromModule. They provide a way to instantiate a
component by its type, specified by a URI and typename pair.
This enables creating Components for inline component types and C++ only
types.
Support for directly passing the type is deferred to a later commit, as
it needs some care to work together with the compiler.
As the string based overload matches a mistaken call to the URL based
createComponent with parent and mode swapped (due to anything being
convertible to string), we add a check to detect this specific error
case and give a helpful error message.
[ChangeLog][QtQml] Qt.createComponent now supports creating a Component
from its module and type name (passed as strings).
Task-number: QTBUG-97156
Fixes: QTBUG-26278
Change-Id: I89e7430fe02d52f57230bfa1b0bfbcbfd0ead4b7
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/qml')
-rw-r--r-- | src/qml/qml/qqmlbuiltinfunctions.cpp | 85 | ||||
-rw-r--r-- | src/qml/qml/qqmlbuiltinfunctions_p.h | 12 |
2 files changed, 91 insertions, 6 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; |