summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-02-26 09:01:25 +0100
committerUlf Hermann <ulf.hermann@qt.io>2021-03-01 13:15:16 +0100
commit681725dbb132ca7ea3ac91ea643d556bfb8b60ac (patch)
tree9509ae24e5750415f18e2fa9eafaf7780a2404fc
parente7e3808092001588d0d2fe1fb52b4d6f89739c90 (diff)
Use the correct metaObject in captureProperty()
QObject::staticMetaObject is not very useful. Change-Id: Ifc40e1fa08755c59ff6b8ae23a7a1257f34507da Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 89ebac46d7bde1df265b8970132bf09dc790eca2)
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp53
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp73
2 files changed, 103 insertions, 23 deletions
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index c711f0ab70..1a13095e88 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -299,33 +299,40 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotif
return;
Q_ASSERT(expression);
- const QQmlData *ddata = QQmlData::get(o, /*create=*/false);
- bool isBindable = false;
- if (auto const propCache = ddata ? ddata->propertyCache : nullptr; propCache) {
- Q_ASSERT(propCache->property(c));
- isBindable = propCache->property(c)->isBindable();
- } else {
- auto metaProp = o->staticMetaObject.property(c);
- isBindable = metaProp.isBindable();
- }
- if (isBindable) {
- // if the property is a QPropery, and we're binding to a QProperty
- // the automatic capturing process already takes care of everything
- if (typeid(QQmlPropertyBinding) == typeid(*expression))
+
+ // If c < 0 we won't find any property. We better leave the metaobjects alone in that case.
+ // QQmlListModel expects us _not_ to trigger the creation of dynamic metaobjects from here.
+ if (c >= 0) {
+ const QQmlData *ddata = QQmlData::get(o, /*create=*/false);
+ const QMetaObject *metaObjectForBindable = nullptr;
+ if (auto const propCache = ddata ? ddata->propertyCache : nullptr; propCache) {
+ Q_ASSERT(propCache->property(c));
+ if (propCache->property(c)->isBindable())
+ metaObjectForBindable = propCache->metaObject();
+ } else {
+ const QMetaObject *m = o->metaObject();
+ if (m->property(c).isBindable())
+ metaObjectForBindable = m;
+ }
+ if (metaObjectForBindable) {
+ // if the property is a QPropery, and we're binding to a QProperty
+ // the automatic capturing process already takes care of everything
+ if (typeid(QQmlPropertyBinding) == typeid(*expression))
+ return;
+ for (auto trigger = expression->qpropertyChangeTriggers; trigger; trigger = trigger->next) {
+ if (trigger->target == o && trigger->propertyIndex == c)
+ return; // already installed
+ }
+ auto trigger = expression->allocatePropertyChangeTrigger(o, c);
+ QUntypedBindable bindable;
+ void *argv[] = { &bindable };
+ metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
+ bindable.observe(trigger);
return;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger; trigger = trigger->next) {
- if (trigger->target == o && trigger->propertyIndex == c)
- return; // already installed
}
- auto trigger = expression->allocatePropertyChangeTrigger(o, c);
- QUntypedBindable bindable;
- void *argv[] = { &bindable };
- o->qt_metacall(QMetaObject::BindableProperty, c, argv);
- bindable.observe(trigger);
- return;
}
- if (n == -1) {
+ if (n == -1) {
if (!errorString) {
errorString = new QStringList;
QString preamble = QLatin1String("QQmlExpression: Expression ") +
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index 6c00494189..c1239265ac 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -85,6 +85,7 @@ private slots:
void cachedGetterLookup_qtbug_75335();
void createComponentOnSingletonDestruction();
void uiLanguage();
+ void captureQProperty();
public slots:
QObject *createAQObjectForOwnershipTest ()
@@ -1238,6 +1239,78 @@ void tst_qqmlengine::uiLanguage()
}
}
+class WithQProperty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int foo READ foo WRITE setFoo BINDABLE fooBindable)
+
+public:
+ WithQProperty(QObject *parent = nullptr) : QObject(parent) { m_foo.setValue(12); }
+
+ int foo() const { return m_foo.value(); }
+ void setFoo(int foo) { m_foo.setValue(foo); }
+ QBindable<int> fooBindable() { return QBindable<int>(&m_foo); }
+
+ int getFooWithCapture()
+ {
+ const QMetaObject *m = metaObject();
+ currentEngine->captureProperty(this, m->property(m->indexOfProperty("foo")));
+ return m_foo.value();
+ }
+
+ static QQmlEngine *currentEngine;
+
+private:
+ QProperty<int> m_foo;
+};
+
+class WithoutQProperty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged)
+public:
+ WithoutQProperty(QObject *parent = nullptr) : QObject(parent), m_foo(new WithQProperty(this)) {}
+
+ int foo() const { return m_foo->getFooWithCapture(); }
+
+ void setFoo(int foo) {
+ if (foo != m_foo->foo()) {
+ m_foo->setFoo(foo);
+ emit fooChanged();
+ }
+ }
+
+ void triggerBinding(int val)
+ {
+ m_foo->setFoo(val);
+ }
+
+signals:
+ void fooChanged();
+
+private:
+ WithQProperty *m_foo;
+};
+
+QQmlEngine *WithQProperty::currentEngine = nullptr;
+
+void tst_qqmlengine::captureQProperty()
+{
+ qmlRegisterType<WithoutQProperty>("Foo", 1, 0, "WithoutQProperty");
+ QQmlEngine engine;
+ WithQProperty::currentEngine = &engine;
+ QQmlComponent c(&engine);
+ c.setData("import Foo\n"
+ "WithoutQProperty {\n"
+ " property int x: foo\n"
+ "}", QUrl());
+ QVERIFY2(c.isReady(), c.errorString().toUtf8());
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->property("x").toInt(), 12);
+ static_cast<WithoutQProperty *>(o.data())->triggerBinding(13);
+ QCOMPARE(o->property("x").toInt(), 13);
+}
+
QTEST_MAIN(tst_qqmlengine)
#include "tst_qqmlengine.moc"