aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2019-08-05 16:03:58 +0200
committerFabian Kosmale <fabian.kosmale@qt.io>2019-08-19 11:38:24 +0000
commit1c59558b03715c6be46650489547c5f9d60d3464 (patch)
treeed259466b692e52fd102cc0cbf2320361d3c9169
parent06f5b386e69a9e8c8ad3dfbdea44d18d912c23ea (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.cpp18
-rw-r--r--src/qml/qml/qqmlapplicationengine.h1
-rw-r--r--src/qml/qml/qqmlapplicationengine_p.h1
-rw-r--r--src/qml/qml/qqmlcomponent.cpp74
-rw-r--r--src/qml/qml/qqmlcomponent.h2
-rw-r--r--src/qml/qml/qqmlcomponent_p.h3
-rw-r--r--src/quick/items/qquickview.cpp18
-rw-r--r--src/quick/items/qquickview.h1
-rw-r--r--src/quick/items/qquickview_p.h2
-rw-r--r--tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp18
-rw-r--r--tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml9
-rw-r--r--tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml21
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp96
-rw-r--r--tests/auto/quick/qquickview/tst_qquickview.cpp12
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"