From ad32ac5b4f05c9eed1fb7a93ee7947050d840a19 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 22 Aug 2020 17:21:36 +0200 Subject: 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 that gives access to generic functionality and checks argument compatibility at runtime. QBindable 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 --- src/corelib/kernel/qmetaobject.cpp | 29 ++- src/corelib/kernel/qmetaobject.h | 6 +- src/corelib/kernel/qmetaobject_p.h | 2 +- src/corelib/kernel/qobjectdefs.h | 3 +- src/corelib/kernel/qproperty.h | 87 ++++++-- src/tools/moc/generator.cpp | 59 +----- src/tools/moc/moc.cpp | 29 ++- src/tools/moc/moc.h | 3 +- .../corelib/kernel/qproperty/tst_qproperty.cpp | 52 ++++- tests/auto/tools/moc/allmocs_baseline_in.json | 20 +- tests/auto/tools/moc/tst_moc.cpp | 222 +++++++++------------ 11 files changed, 271 insertions(+), 241 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index f0cef22b6f..ef042317c1 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -3251,6 +3251,23 @@ bool QMetaProperty::reset(QObject *object) const QMetaObject::metacall(object, QMetaObject::ResetProperty, data.index(mobj) + mobj->propertyOffset(), argv); return true; } + +/*! + \since 6.0 + Returns the bindable interface for the property on a given \a object. + + If the property doesn't support bindings, the returned interface will be + invalid. + + \sa QUntypedBindable, QProperty, isBindable() +*/ +QUntypedBindable QMetaProperty::bindable(QObject *object) const +{ + QUntypedBindable bindable; + void * argv[1] { &bindable }; + mobj->metacall(object, QMetaObject::BindableProperty, data.index(mobj) + mobj->propertyOffset(), argv); + return bindable; +} /*! \since 5.5 @@ -3509,16 +3526,18 @@ bool QMetaProperty::isRequired() const /*! \since 6.0 - Returns \c true if the property is implemented using a QProperty member; otherwise returns \c false. + Returns \c true if the \c{Q_PROPERTY()} exposes binding functionality; otherwise returns false. + + This implies that you can create bindings that use this property as a dependency or install QPropertyObserver + objects on this property. Unless the property is readonly, you can also set a binding on this property. - This can be used to detect the availability of QProperty related meta-call types ahead of - performing the call itself. + \sa QProperty, isReadOnly(), bindable() */ -bool QMetaProperty::isQProperty() const +bool QMetaProperty::isBindable() const { if (!mobj) return false; - return data.flags() & IsQProperty; + return (data.flags() & Bindable); } /*! diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 8136e04dc9..08c57d5f28 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -46,6 +46,8 @@ QT_BEGIN_NAMESPACE +class QUntypedBindable; + #define Q_METAMETHOD_INVOKE_MAX_ARGS 10 class Q_CORE_EXPORT QMetaMethod @@ -294,7 +296,7 @@ public: bool isConstant() const; bool isFinal() const; bool isRequired() const; - bool isQProperty() const; + bool isBindable() const; bool isFlagType() const; bool isEnumType() const; @@ -310,6 +312,8 @@ public: bool write(QObject *obj, const QVariant &value) const; bool reset(QObject *obj) const; + QUntypedBindable bindable(QObject *object) const; + QVariant readOnGadget(const void *gadget) const; bool writeOnGadget(void *gadget, const QVariant &value) const; bool resetOnGadget(void *gadget) const; diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 0d6cd7396d..d205034d77 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -81,7 +81,7 @@ enum PropertyFlags { Stored = 0x00010000, User = 0x00100000, Required = 0x01000000, - IsQProperty = 0x02000000 + Bindable = 0x02000000 }; enum MethodFlags { diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 2013af1695..6c6e4e5a8e 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -386,8 +386,7 @@ struct Q_CORE_EXPORT QMetaObject IndexOfMethod, RegisterPropertyMetaType, RegisterMethodArgumentMetaType, - RegisterQPropertyObserver, - SetQPropertyBinding + BindableProperty }; int static_metacall(Call, int, void **) const; diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index b3f4644031..27ad20d51a 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -615,10 +615,12 @@ struct QBindableInterface using BindingSetter = QUntypedPropertyBinding (*)(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding); using MakeBinding = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location); using SetObserver = void (*)(const QUntypedPropertyData *d, QPropertyObserver *observer); + using GetMetaType = QMetaType (*)(); BindingGetter getBinding; BindingSetter setBinding; MakeBinding makeBinding; SetObserver setObserver; + GetMetaType metaType; }; template @@ -634,7 +636,8 @@ public: [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding { return Qt::makePropertyBinding([d]() -> T { return static_cast(d)->value(); }, location); }, [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void - { observer->setSource(static_cast(d)->bindingData()); } + { observer->setSource(static_cast(d)->bindingData()); }, + []() { return QMetaType::fromType(); } }; }; @@ -651,36 +654,45 @@ public: [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding { return Qt::makePropertyBinding([d]() -> T { return static_cast(d)->value(); }, location); }, [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void - { observer->setSource(static_cast(d)->bindingData()); } + { observer->setSource(static_cast(d)->bindingData()); }, + []() { return QMetaType::fromType(); } }; }; } -template -class QBindable +class QUntypedBindable { protected: - QUntypedPropertyData *data; - const QtPrivate::QBindableInterface *iface; + QUntypedPropertyData *data = nullptr; + const QtPrivate::QBindableInterface *iface = nullptr; public: + constexpr QUntypedBindable() = default; template - QBindable(Property *p) + QUntypedBindable(Property *p) : data(const_cast *>(p)), iface(&QtPrivate::QBindableInterfaceForProperty::iface) - {} + { Q_ASSERT(data && iface); } - QPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) + bool isValid() const { return data != nullptr; } + bool isBindable() const { return iface && iface->getBinding; } + + QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) + { + return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding(); + } + void observe(QPropertyObserver *observer) { - return static_cast &&>(iface->makeBinding(data, location)); + if (iface) + iface->setObserver(data, observer); } template QPropertyChangeHandler onValueChanged(Functor f) { QPropertyChangeHandler handler(f); - iface->setObserver(data, &handler); + observe(&handler); return handler; } @@ -691,17 +703,54 @@ public: return onValueChanged(f); } - QPropertyBinding binding() const + QUntypedPropertyBinding binding() const { if (!iface->getBinding) - return QPropertyBinding(); - return static_cast &&>(iface->getBinding(data)); + return QUntypedPropertyBinding(); + return iface->getBinding(data); } - QPropertyBinding setBinding(const QPropertyBinding &binding) + bool setBinding(const QUntypedPropertyBinding &binding) { if (!iface->setBinding) - return QPropertyBinding(); - return static_cast &&>(iface->setBinding(data, binding)); + return false; + if (!binding.isNull() && binding.valueMetaType() != iface->metaType()) + return false; + iface->setBinding(data, binding); + return true; + } + bool hasBinding() const + { + return !binding().isNull(); + } + +}; + +template +class QBindable : public QUntypedBindable +{ +public: + using QUntypedBindable::QUntypedBindable; + explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b) + { + if (iface && iface->metaType() != QMetaType::fromType()) { + data = nullptr; + iface = nullptr; + } + } + + QPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) + { + return static_cast &&>(QUntypedBindable::makeBinding(location)); + } + QPropertyBinding binding() const + { + return static_cast &&>(QUntypedBindable::binding()); + } + using QUntypedBindable::setBinding; + QPropertyBinding setBinding(const QPropertyBinding &binding) + { + Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == iface->metaType()); + return iface ? static_cast &&>(iface->setBinding(data, binding)) : QPropertyBinding(); } #ifndef Q_CLANG_QDOC template @@ -715,10 +764,6 @@ public: template QPropertyBinding setBinding(Functor f); #endif - bool hasBinding() const - { - return !binding().isNull(); - } }; struct QBindingStatus; diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 1d2fa5d1d7..155a98209d 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -906,8 +906,8 @@ void Generator::generateProperties() if (p.required) flags |= Required; - if (p.isQProperty) - flags |= IsQProperty; + if (!p.bind.isEmpty()) + flags |= Bindable; fprintf(out, " %4d, ", stridx(p.name)); generateTypeInfo(p.type); @@ -1025,9 +1025,8 @@ void Generator::generateMetacall() fprintf(out, "else "); fprintf(out, "if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty\n" - " || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType\n" - " || _c == QMetaObject::RegisterQPropertyObserver\n" - " || _c == QMetaObject::SetQPropertyBinding) {\n" + " || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty\n" + " || _c == QMetaObject::RegisterPropertyMetaType) {\n" " qt_static_metacall(this, _c, _id, _a);\n" " _id -= %d;\n }", int(cdef->propertyList.count())); fprintf(out, "\n#endif // QT_NO_PROPERTIES"); @@ -1268,7 +1267,7 @@ void Generator::generateStaticMetacall() bool needTempVarForGet = false; bool needSet = false; bool needReset = false; - bool haveQProperties = false; + bool hasBindableProperties = false; for (int i = 0; i < cdef->propertyList.size(); ++i) { const PropertyDef &p = cdef->propertyList.at(i); needGet |= !p.read.isEmpty() || !p.member.isEmpty(); @@ -1278,7 +1277,7 @@ void Generator::generateStaticMetacall() needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant); needReset |= !p.reset.isEmpty(); - haveQProperties |= p.isQProperty; + hasBindableProperties |= !p.bind.isEmpty(); } fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); @@ -1404,59 +1403,21 @@ void Generator::generateStaticMetacall() } fprintf(out, " }"); -#if 0 fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::RegisterQPropertyObserver) {\n"); - if (haveQProperties) { + fprintf(out, "if (_c == QMetaObject::BindableProperty) {\n"); + if (hasBindableProperties) { setupMemberAccess(); - fprintf(out, " QPropertyObserver *observer = reinterpret_cast(_a[0]);\n"); fprintf(out, " switch (_id) {\n"); for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.isQProperty) + if (p.bind.isEmpty()) continue; - QByteArray prefix = "_t->"; - if (p.qpropertyname.isEmpty() || p.stored == "true") { - fprintf(out, " case %d: observer->setSource(%s%s); break;\n", - propindex, prefix.constData(), p.bindingAccessor.constData()); - } else { - fprintf(out, " case %d: if (auto *source = %s%s) observer->setSource(*source); break; \n", - propindex, prefix.constData(), p.bindingAccessor.constData()); - } + fprintf(out, " case %d: *static_cast(_a[0]) = _t->%s(); break;\n", propindex, p.bind.constData()); } fprintf(out, " default: break;\n"); fprintf(out, " }\n"); } fprintf(out, " }"); - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::SetQPropertyBinding) {\n"); - if (haveQProperties) { - setupMemberAccess(); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.isQProperty) - continue; - QByteArray prefix = "_t->"; - - if (p.qpropertyname.isEmpty() || p.stored == "true") { - fprintf(out, " case %d: %s%s.setBinding(*reinterpret_cast *>(_a[0])); break;\n", - propindex, prefix.constData(), - p.bindingAccessor.constData(), - p.type.constData()); - } else { - fprintf(out, " case %d: if (auto *source = %s%s) source->setBinding(*reinterpret_cast *>(_a[0])); break;\n", - propindex, prefix.constData(), p.bindingAccessor.constData(), - p.type.constData()); - } - - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - fprintf(out, " }"); -#endif fprintf(out, "\n#endif // QT_NO_PROPERTIES"); needElse = true; } diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 1a20d32d7c..516b231cd5 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -1022,7 +1022,7 @@ static QByteArrayList requiredQtContainers(const QList &classes) for (const auto &c : classes) { for (const auto &p : c.propertyList) - needsQProperty |= p.isQProperty; + needsQProperty |= !p.bind.isEmpty(); if (any_type_contains(c.propertyList, pattern) || any_arg_contains(c.slotList, pattern) || any_arg_contains(c.signalList, pattern) || @@ -1219,23 +1219,9 @@ void Moc::createPropertyDef(PropertyDef &propDef) { propDef.location = index; - const bool isPrivateProperty = !propDef.inPrivateClass.isEmpty(); - bool typeWrappedInQProperty = false; - if (isPrivateProperty) { - const int rewind = index; - if (test(IDENTIFIER) && lexem() == "QProperty" && test(LANGLE)) { - typeWrappedInQProperty = true; - propDef.isQProperty = true; - } else { - index = rewind; - } - } - QByteArray type = parseType().name; if (type.isEmpty()) error(); - if (typeWrappedInQProperty) - next(RANGLE); propDef.designable = propDef.scriptable = propDef.stored = "true"; propDef.user = "false"; /* @@ -1346,6 +1332,9 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef) case 'W': if (l != "WRITE") error(2); propDef.write = v; break; + case 'B': if (l != "BINDABLE") error(2); + propDef.bind = v; + break; case 'D': if (l != "DESIGNABLE") error(2); propDef.designable = v + v2; checkIsFunction(propDef.designable, "DESIGNABLE"); @@ -1373,6 +1362,12 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef) propDef.constant = false; warning(msg.constData()); } + if (propDef.constant && !propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is both BINDable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } } void Moc::parseProperty(ClassDef *def) @@ -1808,7 +1803,7 @@ void Moc::checkProperties(ClassDef *cdef) warning(msg.constData()); } - if (p.read.isEmpty() && p.member.isEmpty() && !p.isQProperty) { + if (p.read.isEmpty() && p.member.isEmpty() && p.bind.isEmpty()) { const int rewind = index; if (p.location >= 0) index = p.location; @@ -2012,6 +2007,7 @@ QJsonObject PropertyDef::toJson() const jsonify("member", member); jsonify("read", read); jsonify("write", write); + jsonify("bindable", bind); jsonify("reset", reset); jsonify("notify", notify); jsonify("privateClass", inPrivateClass); @@ -2035,7 +2031,6 @@ QJsonObject PropertyDef::toJson() const prop[QLatin1String("constant")] = constant; prop[QLatin1String("final")] = final; prop[QLatin1String("required")] = required; - prop[QLatin1String("isQProperty")] = isQProperty; if (revision > 0) prop[QLatin1String("revision")] = revision; diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index def5a8e82d..7f63402aca 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -132,7 +132,7 @@ struct PropertyDef return (s == write); } - QByteArray name, type, member, read, write, reset, designable, scriptable, stored, user, notify, inPrivateClass; + QByteArray name, type, member, read, write, bind, reset, designable, scriptable, stored, user, notify, inPrivateClass; int notifyId = -1; // -1 means no notifyId, >= 0 means signal defined in this class, < -1 means signal not defined in this class enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; Specification gspec = ValueSpec; @@ -140,7 +140,6 @@ struct PropertyDef bool constant = false; bool final = false; bool required = false; - bool isQProperty = false; int location = -1; // token index, used for error reporting 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 publicProperty; + QBindable bindablePublicProperty() { return QBindable(&publicProperty); } + Q_OBJECT_BINDABLE_PROPERTY(ClassWithQPropertyMembers, int, publicProperty, &ClassWithQPropertyMembers::publicPropertyChanged); QProperty 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 bindableTestProperty() { return QBindable(&priv.testProperty); } + QBindable bindableTestProperty2() { return QBindable(&priv.testProperty2); } + struct Private { Private(ClassWithPrivateQPropertyShim *pub) : q(pub) {} + QBindingStorage bindingStorage; + ClassWithPrivateQPropertyShim *q = nullptr; void onTestPropertyChanged() { q->testPropertyChanged(); } - QNotifiedProperty testProperty; + Q_OBJECT_BINDABLE_PROPERTY(Private, int, testProperty, &Private::onTestPropertyChanged); QProperty testProperty2; - - void onLazyTestPropertyChanged() { q->lazyTestPropertyChanged(); } - - const QNotifiedProperty *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 *lazyTestProperty() { - if (!lazyTestPropertyStorage) - lazyTestPropertyStorage.reset(new QNotifiedProperty); - return lazyTestPropertyStorage.data(); - } - - QScopedPointer> 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) -- cgit v1.2.3