diff options
author | Lars Knoll <lars.knoll@qt.io> | 2020-08-22 17:21:36 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2020-09-02 22:44:29 +0200 |
commit | ad32ac5b4f05c9eed1fb7a93ee7947050d840a19 (patch) | |
tree | 5f14ae7a6a588ad3c9400058943f675556a403a9 /tests | |
parent | 3e6c09279304fbde1860288717958e28377b9a9c (diff) |
Make bindings introspectable through moc
Add a new BINDABLE declaration to the Q_PROPERTY() macro that tells moc
where to find the QBindable for the property.
Add a QUntypedBindable base class to QBindable<T> that gives access to
generic functionality and checks argument compatibility at runtime.
QBindable<T> will still do static checking at compile time.
Add QMetaProperty::isBindable() and QMetaProperty::bindable()
to be able to dynamically access the binding functionality.
Change-Id: Ic7b08ae2cde83fd43e627d813a886e1de01fa3dc
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp | 52 | ||||
-rw-r--r-- | tests/auto/tools/moc/allmocs_baseline_in.json | 20 | ||||
-rw-r--r-- | tests/auto/tools/moc/tst_moc.cpp | 222 |
3 files changed, 151 insertions, 143 deletions
diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index b00adc6620..f5652eb599 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -77,6 +77,7 @@ private slots: void testNewStuff(); void qobjectObservers(); void compatBindings(); + void metaProperty(); }; void tst_QProperty::functorBinding() @@ -966,8 +967,8 @@ void tst_QProperty::bindingValueReplacement() class MyQObject : public QObject { Q_OBJECT - Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged) // Use Q_BINDABLE_PROPERTY and generate iface API - Q_PROPERTY(int bar READ bar WRITE setBar NOTIFY barChanged) + Q_PROPERTY(int foo READ foo WRITE setFoo BINDABLE bindableFoo NOTIFY fooChanged) + Q_PROPERTY(int bar READ bar WRITE setBar BINDABLE bindableBar NOTIFY barChanged) Q_PROPERTY(int read READ read NOTIFY readChanged) Q_PROPERTY(int computed READ computed STORED false) Q_PROPERTY(int compat READ compat WRITE setCompat NOTIFY compatChanged) @@ -1162,6 +1163,53 @@ void tst_QProperty::compatBindings() QCOMPARE(object.setCompatCalled, 4); } +void tst_QProperty::metaProperty() +{ + MyQObject object; + QObject::connect(&object, &MyQObject::fooChanged, &object, &MyQObject::fooHasChanged); + QObject::connect(&object, &MyQObject::barChanged, &object, &MyQObject::barHasChanged); + QObject::connect(&object, &MyQObject::compatChanged, &object, &MyQObject::compatHasChanged); + + QCOMPARE(object.fooChangedCount, 0); + object.setFoo(10); + QCOMPARE(object.fooChangedCount, 1); + QCOMPARE(object.foo(), 10); + + auto f = [&object]() -> int { + return object.barData; + }; + QCOMPARE(object.barChangedCount, 0); + object.setBar(42); + QCOMPARE(object.barChangedCount, 1); + QCOMPARE(object.fooChangedCount, 1); + int fooIndex = object.metaObject()->indexOfProperty("foo"); + QVERIFY(fooIndex >= 0); + QMetaProperty fooProp = object.metaObject()->property(fooIndex); + QVERIFY(fooProp.isValid()); + auto fooBindable = fooProp.bindable(&object); + QVERIFY(fooBindable.isValid()); + QVERIFY(fooBindable.isBindable()); + QVERIFY(!fooBindable.hasBinding()); + fooBindable.setBinding(Qt::makePropertyBinding(f)); + QVERIFY(fooBindable.hasBinding()); + QCOMPARE(object.fooChangedCount, 2); + QCOMPARE(object.fooData.value(), 42); + object.setBar(666); + QCOMPARE(object.fooChangedCount, 3); + QCOMPARE(object.barChangedCount, 2); + QCOMPARE(object.fooData.value(), 666); + QCOMPARE(object.fooChangedCount, 3); + + fooBindable.setBinding(QUntypedPropertyBinding()); + QVERIFY(!fooBindable.hasBinding()); + QCOMPARE(object.fooData.value(), 666); + + object.setBar(0); + QCOMPARE(object.fooData.value(), 666); + object.setFoo(1); + QCOMPARE(object.fooData.value(), 1); +} + QTEST_MAIN(tst_QProperty); #include "tst_qproperty.moc" diff --git a/tests/auto/tools/moc/allmocs_baseline_in.json b/tests/auto/tools/moc/allmocs_baseline_in.json index 85751845c4..b477aa01f1 100644 --- a/tests/auto/tools/moc/allmocs_baseline_in.json +++ b/tests/auto/tools/moc/allmocs_baseline_in.json @@ -1063,7 +1063,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "prop1", "read": "getProp1", "required": false, @@ -1077,7 +1076,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "prop2", "read": "getProp2", "required": false, @@ -1091,7 +1089,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "prop3", "read": "getProp3", "required": false, @@ -1204,7 +1201,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "flags", "read": "flags", "required": false, @@ -1231,7 +1227,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "flags", "read": "flags", "required": false, @@ -1245,7 +1240,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "flagsList", "read": "flagsList", "required": false, @@ -1593,8 +1587,8 @@ "outputRevision": 68 }, { - "classes": [ - { + "classes": [ + { "className": "TestPointeeCanBeIncomplete", "object": true, "qualifiedClassName": "TestPointeeCanBeIncomplete", @@ -1787,7 +1781,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "blah", "read": "blah", "required": false, @@ -1840,7 +1833,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "blah", "read": "blah", "required": false, @@ -2016,7 +2008,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "gadgetPoperty", "read": "gadgetPoperty", "required": false, @@ -2029,7 +2020,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "objectPoperty", "read": "objectPoperty", "required": false, @@ -2055,7 +2045,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "nestedGadgetPoperty", "read": "nestedGadgetPoperty", "required": false, @@ -2081,7 +2070,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "nestedObjectPoperty", "read": "nestedObjectPoperty", "required": false, @@ -2215,7 +2203,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "gadgetPoperty", "read": "gadgetPoperty", "required": false, @@ -2228,7 +2215,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "objectPoperty", "read": "objectPoperty", "required": false, @@ -2254,7 +2240,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "nestedGadgetPoperty", "read": "nestedGadgetPoperty", "required": false, @@ -2280,7 +2265,6 @@ "constant": false, "designable": true, "final": false, - "isQProperty": false, "name": "nestedObjectPoperty", "read": "nestedObjectPoperty", "required": false, diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index e17e2283fa..8d405d997e 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -4071,15 +4071,16 @@ void tst_Moc::requiredProperties() class ClassWithQPropertyMembers : public QObject { Q_OBJECT - Q_PROPERTY(int publicProperty NOTIFY publicPropertyChanged) - Q_PROPERTY(int privateExposedProperty) + Q_PROPERTY(int publicProperty MEMBER publicProperty BINDABLE bindablePublicProperty NOTIFY publicPropertyChanged) + Q_PROPERTY(int privateExposedProperty MEMBER privateExposedProperty) public: signals: void publicPropertyChanged(); public: -// QNotifiedProperty<int, &ClassWithQPropertyMembers::publicPropertyChanged> publicProperty; + QBindable<int> bindablePublicProperty() { return QBindable<int>(&publicProperty); } + Q_OBJECT_BINDABLE_PROPERTY(ClassWithQPropertyMembers, int, publicProperty, &ClassWithQPropertyMembers::publicPropertyChanged); QProperty<int> notExposed; @@ -4093,188 +4094,163 @@ private: void tst_Moc::qpropertyMembers() { -// const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; + const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; -// QCOMPARE(metaObject->propertyCount() - metaObject->superClass()->propertyCount(), 2); + QCOMPARE(metaObject->propertyCount() - metaObject->superClass()->propertyCount(), 2); -// QCOMPARE(metaObject->indexOfProperty("notExposed"), -1); + QCOMPARE(metaObject->indexOfProperty("notExposed"), -1); -// QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); -// QVERIFY(prop.isValid()); + QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); + QVERIFY(prop.isValid()); -// QVERIFY(metaObject->property(metaObject->indexOfProperty("privateExposedProperty")).isValid()); + QVERIFY(metaObject->property(metaObject->indexOfProperty("privateExposedProperty")).isValid()); -// ClassWithQPropertyMembers instance; + ClassWithQPropertyMembers instance; -// prop.write(&instance, 42); -// QCOMPARE(instance.publicProperty.value(), 42); + prop.write(&instance, 42); + QCOMPARE(instance.publicProperty.value(), 42); -// QSignalSpy publicPropertySpy(&instance, SIGNAL(publicPropertyChanged())); + QSignalSpy publicPropertySpy(&instance, SIGNAL(publicPropertyChanged())); -// instance.publicProperty.setValue(&instance, 100); -// QCOMPARE(prop.read(&instance).toInt(), 100); -// QCOMPARE(publicPropertySpy.count(), 1); + instance.publicProperty.setValue(100); + QCOMPARE(prop.read(&instance).toInt(), 100); + QCOMPARE(publicPropertySpy.count(), 1); -// QCOMPARE(prop.metaType(), QMetaType(QMetaType::Int)); + QCOMPARE(prop.metaType(), QMetaType(QMetaType::Int)); -// QVERIFY(prop.notifySignal().isValid()); + QVERIFY(prop.notifySignal().isValid()); } void tst_Moc::observerMetaCall() { -// const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; -// QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); -// QVERIFY(prop.isValid()); + const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; + QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); + QVERIFY(prop.isValid()); -// ClassWithQPropertyMembers instance; + ClassWithQPropertyMembers instance; -// int observerCallCount = 0; + int observerCallCount = 0; -// auto handler = QPropertyChangeHandler([&observerCallCount]() { -// ++observerCallCount; -// }); + auto observer = [&observerCallCount]() { + ++observerCallCount; + }; + + auto bindable = prop.bindable(&instance); + QVERIFY(bindable.isBindable()); -// { -// void *argv[] = { &handler }; -// instance.qt_metacall(QMetaObject::RegisterQPropertyObserver, prop.propertyIndex(), argv); -// } + auto handler = bindable.onValueChanged(observer); -// QCOMPARE(observerCallCount, 0); -// instance.publicProperty.setValue(100); -// QCOMPARE(observerCallCount, 1); -// instance.publicProperty.setValue(&instance, 101); -// QCOMPARE(observerCallCount, 2); + QCOMPARE(observerCallCount, 0); + instance.publicProperty.setValue(100); + QCOMPARE(observerCallCount, 1); + instance.publicProperty.setValue(101); + QCOMPARE(observerCallCount, 2); } void tst_Moc::setQPRopertyBinding() { -// const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; -// QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); -// QVERIFY(prop.isValid()); + const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; + QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); + QVERIFY(prop.isValid()); -// ClassWithQPropertyMembers instance; + ClassWithQPropertyMembers instance; -// bool bindingCalled = false; -// auto binding = Qt::makePropertyBinding([&bindingCalled]() { -// bindingCalled = true; -// return 42; -// }); + bool bindingCalled = false; + auto binding = Qt::makePropertyBinding([&bindingCalled]() { + bindingCalled = true; + return 42; + }); -// { -// void *argv[] = { &binding }; -// instance.qt_metacall(QMetaObject::SetQPropertyBinding, prop.propertyIndex(), argv); -// } + auto bindable = prop.bindable(&instance); + QVERIFY(bindable.isBindable()); + bindable.setBinding(binding); -// QCOMPARE(instance.publicProperty.value(), 42); -// QVERIFY(bindingCalled); // but now it should've been called :) + QCOMPARE(instance.publicProperty.value(), 42); + QVERIFY(bindingCalled); // but now it should've been called :) } -#if 0 class ClassWithPrivateQPropertyShim :public QObject { Q_OBJECT public: - Q_PRIVATE_QPROPERTY(d_func(), int, testProperty, setTestProperty, NOTIFY testPropertyChanged) - Q_PRIVATE_QPROPERTY(d_func(), int, testProperty2, setTestProperty2, NOTIFY false) - Q_PRIVATE_QPROPERTY(d_func(), int, lazyTestProperty, setLazyTestProperty, - NOTIFY lazyTestPropertyChanged STORED false) - - Q_PRIVATE_QPROPERTIES_BEGIN - Q_PRIVATE_QPROPERTY_IMPL(testProperty) - Q_PRIVATE_QPROPERTY_IMPL(testProperty2) - Q_PRIVATE_QPROPERTY_IMPL(lazyTestProperty) - Q_PRIVATE_QPROPERTIES_END + Q_PROPERTY(int testProperty READ testProperty WRITE setTestProperty BINDABLE bindableTestProperty NOTIFY testPropertyChanged) + Q_PROPERTY(int testProperty2 READ testProperty2 WRITE setTestProperty2 BINDABLE bindableTestProperty2) + //Q_PROPERTY(d_func(), int, lazyTestProperty, setLazyTestProperty, NOTIFY lazyTestPropertyChanged) signals: void testPropertyChanged(); void lazyTestPropertyChanged(); public: + int testProperty() const { return priv.testProperty; } + void setTestProperty(int val) { priv.testProperty = val; } + int testProperty2() const { return priv.testProperty2; } + void setTestProperty2(int val) { priv.testProperty2 = val; } + + QBindable<int> bindableTestProperty() { return QBindable<int>(&priv.testProperty); } + QBindable<int> bindableTestProperty2() { return QBindable<int>(&priv.testProperty2); } + struct Private { Private(ClassWithPrivateQPropertyShim *pub) : q(pub) {} + QBindingStorage bindingStorage; + ClassWithPrivateQPropertyShim *q = nullptr; void onTestPropertyChanged() { q->testPropertyChanged(); } - QNotifiedProperty<int, &Private::onTestPropertyChanged> testProperty; + Q_OBJECT_BINDABLE_PROPERTY(Private, int, testProperty, &Private::onTestPropertyChanged); QProperty<int> testProperty2; - - void onLazyTestPropertyChanged() { q->lazyTestPropertyChanged(); } - - const QNotifiedProperty<int, &Private::onLazyTestPropertyChanged> *lazyTestProperty() const { - // Mind that this prevents the property read from being recorded. - // For real-world use cases some more logic is necessary here. - return lazyTestPropertyStorage.data(); - } - - QNotifiedProperty<int, &Private::onLazyTestPropertyChanged> *lazyTestProperty() { - if (!lazyTestPropertyStorage) - lazyTestPropertyStorage.reset(new QNotifiedProperty<int, &Private::onLazyTestPropertyChanged>); - return lazyTestPropertyStorage.data(); - } - - QScopedPointer<QNotifiedProperty<int, &Private::onLazyTestPropertyChanged>> lazyTestPropertyStorage; }; Private priv{this}; Private *d_func() { return &priv; } const Private *d_func() const { return &priv; } }; -#endif + +inline const QBindingStorage *qGetBindingStorage(const ClassWithPrivateQPropertyShim::Private *o) +{ + return &o->bindingStorage; +} +inline QBindingStorage *qGetBindingStorage(ClassWithPrivateQPropertyShim::Private *o) +{ + return &o->bindingStorage; +} void tst_Moc::privateQPropertyShim() { -// ClassWithPrivateQPropertyShim testObject; - -// { -// auto metaObject = &ClassWithPrivateQPropertyShim::staticMetaObject; -// QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("testProperty")); -// QVERIFY(prop.isValid()); -// QVERIFY(prop.notifySignal().isValid()); -// } - -// testObject.priv.testProperty.setValue(&testObject.priv, 42); -// QCOMPARE(testObject.property("testProperty").toInt(), 42); - -// // Behave like a QProperty -// QVERIFY(!testObject.testProperty.hasBinding()); -// testObject.testProperty.setBinding([]() { return 100; }); -// QCOMPARE(testObject.testProperty.value(), 100); -// QVERIFY(testObject.testProperty.hasBinding()); - -// // Old style setter getters -// testObject.setTestProperty(400); -// QVERIFY(!testObject.testProperty.hasBinding()); -// QCOMPARE(testObject.testProperty(), 400); - -// // Created and default-initialized, without nullptr access -// QCOMPARE(testObject.lazyTestProperty(), 0); - -// // Explicitly set to something -// testObject.priv.lazyTestProperty()->setValue(&testObject.priv, 42); -// QCOMPARE(testObject.property("lazyTestProperty").toInt(), 42); - -// // Behave like a QProperty -// QVERIFY(!testObject.lazyTestProperty.hasBinding()); -// testObject.lazyTestProperty.setBinding([]() { return 100; }); -// QCOMPARE(testObject.lazyTestProperty.value(), 100); -// QVERIFY(testObject.lazyTestProperty.hasBinding()); - -// // Old style setter getters -// testObject.setLazyTestProperty(400); -// QVERIFY(!testObject.lazyTestProperty.hasBinding()); -// QCOMPARE(testObject.lazyTestProperty(), 400); - -// // mo generates correct code for plain QProperty in PIMPL -// testObject.testProperty2.setValue(42); -// QCOMPARE(testObject.testProperty2.value(), 42); + ClassWithPrivateQPropertyShim testObject; + + { + auto metaObject = &ClassWithPrivateQPropertyShim::staticMetaObject; + QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("testProperty")); + QVERIFY(prop.isValid()); + QVERIFY(prop.notifySignal().isValid()); + } + + testObject.priv.testProperty.setValue(42); + QCOMPARE(testObject.property("testProperty").toInt(), 42); + + // Behave like a QProperty + QVERIFY(!testObject.bindableTestProperty().hasBinding()); + testObject.bindableTestProperty().setBinding([]() { return 100; }); + QCOMPARE(testObject.testProperty(), 100); + QVERIFY(testObject.bindableTestProperty().hasBinding()); + + // Old style setter getters + testObject.setTestProperty(400); + QVERIFY(!testObject.bindableTestProperty().hasBinding()); + QCOMPARE(testObject.testProperty(), 400); + + // moc generates correct code for plain QProperty in PIMPL + testObject.setTestProperty2(42); + QCOMPARE(testObject.priv.testProperty2.value(), 42); } QTEST_MAIN(tst_Moc) |