diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2019-08-05 16:03:58 +0200 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2019-08-19 11:38:24 +0000 |
commit | 1c59558b03715c6be46650489547c5f9d60d3464 (patch) | |
tree | ed259466b692e52fd102cc0cbf2320361d3c9169 | |
parent | 06f5b386e69a9e8c8ad3dfbdea44d18d912c23ea (diff) |
Introduce functions to set properties during creation
Change-Id: Idd4c8ab9e34b9bc3e00f21d7cf1e4f1a70586e7f
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | src/qml/qml/qqmlapplicationengine.cpp | 18 | ||||
-rw-r--r-- | src/qml/qml/qqmlapplicationengine.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlapplicationengine_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent.cpp | 74 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent_p.h | 3 | ||||
-rw-r--r-- | src/quick/items/qquickview.cpp | 18 | ||||
-rw-r--r-- | src/quick/items/qquickview.h | 1 | ||||
-rw-r--r-- | src/quick/items/qquickview_p.h | 2 | ||||
-rw-r--r-- | tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp | 18 | ||||
-rw-r--r-- | tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml | 9 | ||||
-rw-r--r-- | tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml | 21 | ||||
-rw-r--r-- | tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp | 96 | ||||
-rw-r--r-- | tests/auto/quick/qquickview/tst_qquickview.cpp | 12 |
14 files changed, 265 insertions, 11 deletions
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index e93cfcadb9..d04a89b514 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -131,7 +131,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) q->objectCreated(nullptr, c->url()); break; case QQmlComponent::Ready: { - auto newObj = c->create(); + auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties); objects << newObj; QObject::connect(newObj, &QObject::destroyed, q, [&](QObject *obj) { objects.removeAll(obj); }); q->objectCreated(objects.constLast(), c->url()); @@ -279,6 +279,22 @@ void QQmlApplicationEngine::load(const QString &filePath) } /*! + Sets the initial properties with which the QML component gets initialized after + it gets loaded. + + + \sa QQmlComponent::setInitialProperties + \sa QQmlApplicationEngine::load + \sa QQmlApplicationEngine::loadData + \since 5.14 +*/ +void QQmlApplicationEngine::setInitialProperties(const QVariantMap &initialProperties) +{ + Q_D(QQmlApplicationEngine); + d->initialProperties = initialProperties; +} + +/*! Loads the QML given in \a data. The object tree defined by \a data is instantiated immediately. diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h index bb5d6b5d68..2b4de91154 100644 --- a/src/qml/qml/qqmlapplicationengine.h +++ b/src/qml/qml/qqmlapplicationengine.h @@ -66,6 +66,7 @@ public: public Q_SLOTS: void load(const QUrl &url); void load(const QString &filePath); + void setInitialProperties(const QVariantMap &initialProperties); void loadData(const QByteArray &data, const QUrl &url = QUrl()); Q_SIGNALS: diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 7a341847bd..1279e400e8 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -73,6 +73,7 @@ public: void loadTranslations(const QUrl &rootFile); void finishLoad(QQmlComponent *component); QList<QObject *> objects; + QVariantMap initialProperties; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index fefe2bc685..ed8c41a582 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -350,6 +350,32 @@ void QQmlComponentPrivate::clear() compilationUnit = nullptr; } +QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context) +{ + if (!engine) { + // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check + qWarning("QQmlComponent: Must provide an engine before calling create"); + return nullptr; + } + if (!context) + context = engine->rootContext(); + return q->beginCreate(context); +} + +bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString& name, const QVariant &value) +{ + QQmlProperty prop(component, name); + auto privProp = QQmlPropertyPrivate::get(prop); + if (!prop.isValid() || !privProp->writeValueProperty(value, nullptr)) { + QQmlError error{}; + error.setUrl(url); + error.setDescription(QLatin1String("Could not set property %1").arg(name)); + state.errors.push_back(error); + return false; + } else + return true; +} + /*! \internal */ @@ -780,18 +806,28 @@ QObject *QQmlComponent::create(QQmlContext *context) { Q_D(QQmlComponent); - if (!d->engine) { - // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check - qWarning("QQmlComponent: Must provide an engine before calling create"); - return nullptr; - } + QObject *rv = d->doBeginCreate(this, context); + if (rv) + completeCreate(); + return rv; +} - if (!context) - context = d->engine->rootContext(); +/*! + Create an object instance of this component, and initialize its toplevel properties according to initalPropertyValues. - QObject *rv = beginCreate(context); - if (rv) + + \sa QQmlComponent::create + \since 5.14 +*/ +QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context) +{ + Q_D(QQmlComponent); + + QObject *rv = d->doBeginCreate(this, context); + if (rv) { + setInitialProperties(rv, initialProperties); completeCreate(); + } return rv; } @@ -1067,6 +1103,26 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, enginePriv->incubate(incubator, forContextData); } +/*! + Set toplevel properties of the component. + + + This method provides advanced control over component instance creation. + In general, programmers should use + \l QQmlComponent::createWithInitialProperties to create a component. + + Use this method after beginCreate and before completeCreate has been called. + If a provided property does not exist, a warning is issued. + + \since 5.14 +*/ +void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties) +{ + Q_D(QQmlComponent); + for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) + d->setInitialProperty(component, it.key(), it.value()); +} + /* This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData arguments instead of QQmlContext which means we don't have to construct the rather weighty diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index 39b6d4526f..f259c99b08 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -100,6 +100,8 @@ public: QUrl url() const; virtual QObject *create(QQmlContext *context = nullptr); + QObject *createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context = nullptr); + void setInitialProperties(QObject *component, const QVariantMap &properties); virtual QObject *beginCreate(QQmlContext *); virtual void completeCreate(); diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 9a967501c9..2170646b89 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -143,6 +143,9 @@ public: static QQmlComponentPrivate *get(QQmlComponent *c) { return static_cast<QQmlComponentPrivate *>(QObjectPrivate::get(c)); } + + QObject *doBeginCreate(QQmlComponent *q, QQmlContext *context); + bool setInitialProperty(QObject *component, const QString &name, const QVariant& value); }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 61477360bf..17fc16d44b 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -240,6 +240,22 @@ void QQuickView::setSource(const QUrl& url) } /*! + Sets the initial properties with which the QML component gets initialized after + calling \l QQuickView::setSource. + + + Note that you can only use this function to initialize toplevel properties. + + \sa QQmlComponent::createWithInitialProperties + \since 5.14 +*/ +void QQuickView::setInitialProperties(const QVariantMap &initialProperties) +{ + Q_D(QQuickView); + d->initialProperties = initialProperties; +} + +/*! \internal Set the source \a url, \a component and content \a item (root of the QML object hierarchy) directly. @@ -471,7 +487,7 @@ void QQuickView::continueExecute() return; } - QObject *obj = d->component->create(); + QObject *obj = d->initialProperties.empty() ? d->component->create() : d->component->createWithInitialProperties(d->initialProperties); if (d->component->isError()) { const QList<QQmlError> errorList = d->component->errors(); diff --git a/src/quick/items/qquickview.h b/src/quick/items/qquickview.h index ecae25e90b..4122fcac79 100644 --- a/src/quick/items/qquickview.h +++ b/src/quick/items/qquickview.h @@ -88,6 +88,7 @@ public: public Q_SLOTS: void setSource(const QUrl&); + void setInitialProperties(const QVariantMap &initialProperties); void setContent(const QUrl& url, QQmlComponent *component, QObject *item); Q_SIGNALS: diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h index 3f284c0519..b1ab8d8e8c 100644 --- a/src/quick/items/qquickview_p.h +++ b/src/quick/items/qquickview_p.h @@ -108,6 +108,8 @@ public: QQuickView::ResizeMode resizeMode; QSize initialSize; QElapsedTimer frameTimer; + + QVariantMap initialProperties; }; QT_END_NAMESPACE diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index 8787a43884..0f5eea8b95 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -52,6 +52,7 @@ private slots: void removeObjectsWhenDestroyed(); void loadTranslation_data(); void loadTranslation(); + void setInitialProperties(); private: QString buildDir; @@ -275,6 +276,23 @@ void tst_qqmlapplicationengine::loadTranslation() QCOMPARE(rootObject->property("translation").toString(), translation); } +void tst_qqmlapplicationengine::setInitialProperties() +{ + QQmlApplicationEngine test {}; + { + test.setInitialProperties(QVariantMap{{"success", false}}); + test.load(testFileUrl("basicTest.qml")); + QVERIFY(!test.rootObjects().empty()); + QCOMPARE(test.rootObjects().first()->property("success").toBool(), false); + } + { + test.setInitialProperties({{"success", true}}); + test.load(testFileUrl("basicTest.qml")); + QCOMPARE(test.rootObjects().size(), 2); + QCOMPARE(test.rootObjects().at(1)->property("success").toBool(), true); + } +} + QTEST_MAIN(tst_qqmlapplicationengine) #include "tst_qqmlapplicationengine.moc" diff --git a/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml b/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml new file mode 100644 index 0000000000..0541c9b104 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml @@ -0,0 +1,9 @@ +import QtQuick 2.14 + +Item { + property int i + property bool b + property double d + property string s + property var nothing +} diff --git a/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml b/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml new file mode 100644 index 0000000000..acf08e94d2 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml @@ -0,0 +1,21 @@ +import QtQuick 2.14 + +Item { + property int i + property bool b + property double d + property string s + property var nothing + property url myurl + property color c + property font myfont + property date mydate + property point mypoint + property size mysize + property rect myrect + property matrix4x4 matrix + property quaternion quat + property vector2d vec2 + property vector3d vec3 + property vector4d vec4 +} diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index 71d3e8fe5f..79ec507388 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -121,6 +121,7 @@ private slots: void relativeUrl_data(); void relativeUrl(); void setDataNoEngineNoSegfault(); + void testSetInitialProperties(); private: QQmlEngine engine; @@ -667,6 +668,101 @@ void tst_qqmlcomponent::setDataNoEngineNoSegfault() QVERIFY(!c); } +void tst_qqmlcomponent::testSetInitialProperties() +{ + QQmlEngine eng; + { + // JSON based initialization + QQmlComponent comp(&eng); + comp.loadUrl(testFileUrl("allJSONTypes.qml")); + QScopedPointer<QObject> obj { comp.beginCreate(eng.rootContext()) }; + QVERIFY(obj); + comp.setInitialProperties(obj.get(), QVariantMap { + {QLatin1String("i"), 42}, + {QLatin1String("b"), true}, + {QLatin1String("d"), 3.1416}, + {QLatin1String("s"), QLatin1String("hello world")}, + {QLatin1String("nothing"), QVariant::fromValue(nullptr)} + }); + comp.completeCreate(); + if (!comp.errors().empty()) + qDebug() << comp.errorString() << comp.errors(); + QVERIFY(comp.errors().empty()); + QCOMPARE(obj->property("i"), 42); + QCOMPARE(obj->property("b"), true); + QCOMPARE(obj->property("d"), 3.1416); + QCOMPARE(obj->property("s"), QLatin1String("hello world")); + QCOMPARE(obj->property("nothing"), QVariant::fromValue(nullptr)); + } + { + // QVariant + QQmlComponent comp(&eng); + comp.loadUrl(testFileUrl("variantBasedInitialization.qml")); + QScopedPointer<QObject> obj { comp.beginCreate(eng.rootContext()) }; + QVERIFY(obj); + QUrl myurl = comp.url(); + QFont myfont; + QDateTime mydate = QDateTime::currentDateTime(); + QPoint mypoint {1,2}; + QSizeF mysize {0.5, 0.3}; + QMatrix4x4 matrix {}; + QQuaternion quat {5.0f, 0.3f, 0.2f, 0.1f}; + QVector2D vec2 {2.0f, 3.1f}; + QVector3D vec3 {1.0f, 2.0, 3.0f}; + QVector4D vec4 {1.0f, 2.0f, 3.0f, 4.0f}; +#define ASJSON(NAME) {QLatin1String(#NAME), NAME} + comp.setInitialProperties(obj.get(), QVariantMap { + {QLatin1String("i"), 42}, + {QLatin1String("b"), true}, + {QLatin1String("d"), 3.1416}, + {QLatin1String("s"), QLatin1String("hello world")}, + {QLatin1String("nothing"), QVariant::fromValue( nullptr)}, + ASJSON(myurl), + ASJSON(myfont), + ASJSON(mydate), + ASJSON(mypoint), + ASJSON(mysize), + ASJSON(matrix), + ASJSON(quat), + ASJSON(vec2), ASJSON(vec3), ASJSON(vec4) + }); +#undef ASJSON + comp.completeCreate(); + if (!comp.errors().empty()) + qDebug() << comp.errorString() << comp.errors(); + QVERIFY(comp.errors().empty()); + QCOMPARE(obj->property("i"), 42); + QCOMPARE(obj->property("b"), true); + QCOMPARE(obj->property("d"), 3.1416); + QCOMPARE(obj->property("s"), QLatin1String("hello world")); + QCOMPARE(obj->property("nothing"), QVariant::fromValue(nullptr)); +#define COMPARE(NAME) QCOMPARE(obj->property(#NAME), NAME) + COMPARE(myurl); + COMPARE(myfont); + COMPARE(mydate); + COMPARE(mypoint); + COMPARE(mysize); + COMPARE(matrix); + COMPARE(quat); + COMPARE(vec2); + COMPARE(vec3); + COMPARE(vec4); +#undef COMPARE + + } + { + // createWithInitialProperties: setting a nonexistent property + QQmlComponent comp(&eng); + comp.loadUrl(testFileUrl("allJSONTypes.qml")); + QScopedPointer<QObject> obj { + comp.createWithInitialProperties(QVariantMap { {"notThePropertiesYoureLookingFor", 42} }) + }; + qDebug() << comp.errorString(); + QVERIFY(obj); + QVERIFY(comp.errorString().contains("Could not set property notThePropertiesYoureLookingFor")); + } +} + QTEST_MAIN(tst_qqmlcomponent) #include "tst_qqmlcomponent.moc" diff --git a/tests/auto/quick/qquickview/tst_qquickview.cpp b/tests/auto/quick/qquickview/tst_qquickview.cpp index e259ed1ae7..dbce0d308c 100644 --- a/tests/auto/quick/qquickview/tst_qquickview.cpp +++ b/tests/auto/quick/qquickview/tst_qquickview.cpp @@ -49,6 +49,7 @@ private slots: void errors(); void engine(); void findChild(); + void setInitialProperties(); }; @@ -283,6 +284,17 @@ void tst_QQuickView::findChild() QVERIFY(!view.rootObject()->findChild<QObject *>("rootObject")); // self } +void tst_QQuickView::setInitialProperties() +{ + QQuickView view; + view.setInitialProperties({{"z", 4}, {"width", 100}}); + view.setSource(testFileUrl("resizemodeitem.qml")); + QObject *rootObject = view.rootObject(); + QVERIFY(rootObject); + QCOMPARE(rootObject->property("z").toInt(), 4); + QCOMPARE(rootObject->property("width").toInt(), 100); +} + QTEST_MAIN(tst_QQuickView) #include "tst_qquickview.moc" |