summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2020-08-22 17:21:36 +0200
committerLars Knoll <lars.knoll@qt.io>2020-09-02 22:44:29 +0200
commitad32ac5b4f05c9eed1fb7a93ee7947050d840a19 (patch)
tree5f14ae7a6a588ad3c9400058943f675556a403a9
parent3e6c09279304fbde1860288717958e28377b9a9c (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>
-rw-r--r--src/corelib/kernel/qmetaobject.cpp29
-rw-r--r--src/corelib/kernel/qmetaobject.h6
-rw-r--r--src/corelib/kernel/qmetaobject_p.h2
-rw-r--r--src/corelib/kernel/qobjectdefs.h3
-rw-r--r--src/corelib/kernel/qproperty.h87
-rw-r--r--src/tools/moc/generator.cpp59
-rw-r--r--src/tools/moc/moc.cpp29
-rw-r--r--src/tools/moc/moc.h3
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp52
-rw-r--r--tests/auto/tools/moc/allmocs_baseline_in.json20
-rw-r--r--tests/auto/tools/moc/tst_moc.cpp222
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<typename Property, typename = void>
@@ -634,7 +636,8 @@ public:
[](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
{ return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
[](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
- { observer->setSource(static_cast<const Property *>(d)->bindingData()); }
+ { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
+ []() { return QMetaType::fromType<T>(); }
};
};
@@ -651,36 +654,45 @@ public:
[](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
{ return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
[](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
- { observer->setSource(static_cast<const Property *>(d)->bindingData()); }
+ { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
+ []() { return QMetaType::fromType<T>(); }
};
};
}
-template<typename T>
-class QBindable
+class QUntypedBindable
{
protected:
- QUntypedPropertyData *data;
- const QtPrivate::QBindableInterface *iface;
+ QUntypedPropertyData *data = nullptr;
+ const QtPrivate::QBindableInterface *iface = nullptr;
public:
+ constexpr QUntypedBindable() = default;
template<typename Property>
- QBindable(Property *p)
+ QUntypedBindable(Property *p)
: data(const_cast<std::remove_cv_t<Property> *>(p)),
iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
- {}
+ { Q_ASSERT(data && iface); }
- QPropertyBinding<T> 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<QPropertyBinding<T> &&>(iface->makeBinding(data, location));
+ if (iface)
+ iface->setObserver(data, observer);
}
template<typename Functor>
QPropertyChangeHandler<Functor> onValueChanged(Functor f)
{
QPropertyChangeHandler<Functor> handler(f);
- iface->setObserver(data, &handler);
+ observe(&handler);
return handler;
}
@@ -691,17 +703,54 @@ public:
return onValueChanged(f);
}
- QPropertyBinding<T> binding() const
+ QUntypedPropertyBinding binding() const
{
if (!iface->getBinding)
- return QPropertyBinding<T>();
- return static_cast<QPropertyBinding<T> &&>(iface->getBinding(data));
+ return QUntypedPropertyBinding();
+ return iface->getBinding(data);
}
- QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
+ bool setBinding(const QUntypedPropertyBinding &binding)
{
if (!iface->setBinding)
- return QPropertyBinding<T>();
- return static_cast<QPropertyBinding<T> &&>(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<typename T>
+class QBindable : public QUntypedBindable
+{
+public:
+ using QUntypedBindable::QUntypedBindable;
+ explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b)
+ {
+ if (iface && iface->metaType() != QMetaType::fromType<T>()) {
+ data = nullptr;
+ iface = nullptr;
+ }
+ }
+
+ QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION)
+ {
+ return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
+ }
+ QPropertyBinding<T> binding() const
+ {
+ return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::binding());
+ }
+ using QUntypedBindable::setBinding;
+ QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
+ {
+ Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == iface->metaType());
+ return iface ? static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding)) : QPropertyBinding<T>();
}
#ifndef Q_CLANG_QDOC
template <typename Functor>
@@ -715,10 +764,6 @@ public:
template <typename Functor>
QPropertyBinding<T> 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<QPropertyObserver *>(_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<QUntypedBindable *>(_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<QPropertyBinding<%s> *>(_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<QPropertyBinding<%s> *>(_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<ClassDef> &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<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)