summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/doc/src/objectmodel/bindableproperties.qdoc14
-rw-r--r--src/corelib/kernel/qobject.cpp25
-rw-r--r--src/corelib/kernel/qobject_p.h3
-rw-r--r--src/corelib/kernel/qobjectdefs_impl.h1
-rw-r--r--src/corelib/kernel/qproperty.cpp176
-rw-r--r--src/corelib/kernel/qproperty.h63
-rw-r--r--src/corelib/kernel/qproperty_p.h33
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp186
8 files changed, 501 insertions, 0 deletions
diff --git a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
index f9f129df97..d6f96f6579 100644
--- a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
+++ b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
@@ -247,4 +247,18 @@
be called for the current value of the property, register your callback using
subscribe() instead.
+ \section1 Interaction with Q_PROPERTYs
+
+ A \l {The Property System}{Q_PROPERTY} that defines \c BINDABLE can be bound and
+ used in binding expressions. You can implement such properties using \l {QProperty},
+ \l {QObjectBindableProperty}, or \l {QObjectComputedProperty}.
+
+ Q_PROPERTYs without \c BINDABLE can also be bound and be used in binding expressions,
+ as long as they define a \c NOTIFY signal. You must wrap the property in a \l QBindable
+ using the \c {QBindable(QObject* obj, const char* property)} constructor. Then, the
+ property can be bound using \c \l QBindable::setBinding() or used in a binding
+ expression via \c \l QBindable::value(). You must use \c QBindable::value() in binding
+ expressions instead of the normal property \c READ function (or \c MEMBER) to enable
+ dependency tracking if the property is not \c BINDABLE.
+
*/
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index fd7439f3e2..81bcf19114 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -5358,6 +5358,31 @@ inline bool QObjectPrivate::removeConnection(QObjectPrivate::Connection *c)
return true;
}
+/*!
+ \internal
+
+ Used by QPropertyAdaptorSlotObject to get an existing instance for a property, if available
+ */
+QtPrivate::QPropertyAdaptorSlotObject *
+QObjectPrivate::getPropertyAdaptorSlotObject(const QMetaProperty &property)
+{
+ if (auto conns = connections.loadRelaxed()) {
+ Q_Q(QObject);
+ const QMetaObject *metaObject = q->metaObject();
+ int signal_index = methodIndexToSignalIndex(&metaObject, property.notifySignalIndex());
+ auto connectionList = conns->connectionsForSignal(signal_index);
+ for (auto c = connectionList.first.loadRelaxed(); c;
+ c = c->nextConnectionList.loadRelaxed()) {
+ if (c->isSlotObject) {
+ if (auto p = QtPrivate::QPropertyAdaptorSlotObject::cast(c->slotObj,
+ property.propertyIndex()))
+ return p;
+ }
+ }
+ }
+ return nullptr;
+}
+
/*! \class QMetaObject::Connection
\inmodule QtCore
Represents a handle to a signal-slot (or signal-functor) connection.
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index f82dce51f3..dbb4eb2326 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -179,6 +179,9 @@ public:
virtual std::string flagsForDumping() const;
+ QtPrivate::QPropertyAdaptorSlotObject *
+ getPropertyAdaptorSlotObject(const QMetaProperty &property);
+
public:
mutable ExtraData *extraData; // extra data set by the user
// This atomic requires acquire/release semantics in a few places,
diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h
index e619ced4fe..10f06609ec 100644
--- a/src/corelib/kernel/qobjectdefs_impl.h
+++ b/src/corelib/kernel/qobjectdefs_impl.h
@@ -361,6 +361,7 @@ namespace QtPrivate {
inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, nullptr, a, &ret); return ret; }
inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, nullptr); }
+ bool isImpl(ImplFn f) const { return m_impl == f; }
protected:
~QSlotObjectBase() {}
private:
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 6b79e27e47..742d2c5f40 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -8,6 +8,9 @@
#include <QScopeGuard>
#include <QtCore/qloggingcategory.h>
#include <QThread>
+#include <QtCore/qmetaobject.h>
+
+#include "qobject_p.h"
QT_BEGIN_NAMESPACE
@@ -1136,6 +1139,31 @@ QString QPropertyBindingError::description() const
*/
/*!
+ \fn template<typename T> QBindable<T>::QBindable(QObject *obj, const char *property)
+
+ Constructs a QBindable for the \l Q_PROPERTY \a property on \a obj. The property must
+ have a notify signal but does not need to have \c BINDABLE in its \c Q_PROPERTY
+ definition, so even binding unaware \c {Q_PROPERTY}s can be bound or used in binding
+ expressions. You must use \c QBindable::value() in binding expressions instead of the
+ normal property \c READ function (or \c MEMBER) to enable dependency tracking if the
+ property is not \c BINDABLE. When binding using a lambda, you may prefer to capture the
+ QBindable by value to avoid the cost of calling this constructor in the binding
+ expression.
+ This constructor should not be used to implement \c BINDABLE for a Q_PROPERTY, as the
+ resulting Q_PROPERTY will not support dependency tracking. To make a property that is
+ usable directly without reading through a QBindable use \l QProperty or
+ \l QObjectBindableProperty.
+
+ \sa QProperty, QObjectBindableProperty, {Qt Bindable Properties}
+*/
+
+/*!
+ \fn template<typename T> QBindable<T>::QBindable(QObject *obj, const QMetaProperty &property)
+
+ See \c \l QBindable::QBindable(QObject *obj, const char *property)
+*/
+
+/*!
\fn template<typename T> QPropertyBinding<T> QBindable<T>::makeBinding(const QPropertyBindingSourceLocation &location) const
Constructs a binding evaluating to the underlying property's value, using a specified source
@@ -2386,6 +2414,154 @@ void printMetaTypeMismatch(QMetaType actual, QMetaType expected)
*/
QBindingStatus* getBindingStatus(QtPrivate::QBindingStatusAccessToken) { return &QT_PREPEND_NAMESPACE(bindingStatus); }
+namespace PropertyAdaptorSlotObjectHelpers {
+void getter(const QUntypedPropertyData *d, void *value)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ adaptor->bindingData().registerWithCurrentlyEvaluatingBinding();
+ auto mt = adaptor->metaProperty().metaType();
+ mt.destruct(value);
+ mt.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
+}
+
+void setter(QUntypedPropertyData *d, const void *value)
+{
+ auto adaptor = static_cast<QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ adaptor->bindingData().removeBinding();
+ adaptor->metaProperty().write(adaptor->object(),
+ QVariant(adaptor->metaProperty().metaType(), value));
+}
+
+QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ return QUntypedPropertyBinding(adaptor->bindingData().binding());
+}
+
+bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
+ QtPrivate::QPropertyBindingFunction binding, QUntypedPropertyData *temp,
+ void *value)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ type.destruct(value);
+ type.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
+ if (binding.vtable->call(type, temp, binding.functor)) {
+ adaptor->metaProperty().write(adaptor->object(), QVariant(type, value));
+ return true;
+ }
+ return false;
+}
+
+QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding,
+ QPropertyBindingWrapper wrapper)
+{
+ auto adaptor = static_cast<QPropertyAdaptorSlotObject *>(d);
+ return adaptor->bindingData().setBinding(binding, d, nullptr, wrapper);
+}
+
+void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer)
+{
+ observer->setSource(static_cast<const QPropertyAdaptorSlotObject *>(d)->bindingData());
+}
+}
+
+QPropertyAdaptorSlotObject::QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty &p)
+ : QSlotObjectBase(&impl), obj(o), metaProperty_(p)
+{
+}
+
+void QPropertyAdaptorSlotObject::impl(int which, QSlotObjectBase *this_, QObject *r, void **a,
+ bool *ret)
+{
+ auto self = static_cast<QPropertyAdaptorSlotObject *>(this_);
+ switch (which) {
+ case Destroy:
+ delete self;
+ break;
+ case Call:
+ if (!self->bindingData_.hasBinding())
+ self->bindingData_.notifyObservers(self);
+ break;
+ case Compare:
+ case NumOperations:
+ Q_UNUSED(r);
+ Q_UNUSED(a);
+ Q_UNUSED(ret);
+ break;
+ }
+}
+
} // namespace QtPrivate end
+QUntypedBindable::QUntypedBindable(QObject *obj, const QMetaProperty &metaProperty,
+ const QtPrivate::QBindableInterface *i)
+ : iface(i)
+{
+ if (!obj)
+ return;
+
+ if (!metaProperty.isValid()) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property is not valid";
+ return;
+ }
+
+ if (metaProperty.isBindable()) {
+ *this = metaProperty.bindable(obj);
+ return;
+ }
+
+ if (!metaProperty.hasNotifySignal()) {
+ qCWarning(lcQPropertyBinding)
+ << "QUntypedBindable: Property" << metaProperty.name() << "has no notify signal";
+ return;
+ }
+
+ auto metatype = iface->metaType();
+ if (metaProperty.metaType() != metatype) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
+ << "of type" << metaProperty.metaType().name()
+ << "does not match requested type" << metatype.name();
+ return;
+ }
+
+ // Test for name pointer equality proves it's exactly the same property
+ if (obj->metaObject()->property(metaProperty.propertyIndex()).name() != metaProperty.name()) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
+ << "does not belong to this object";
+ return;
+ }
+
+ // Get existing binding data if it exists
+ auto adaptor = QObjectPrivate::get(obj)->getPropertyAdaptorSlotObject(metaProperty);
+
+ if (!adaptor) {
+ adaptor = new QPropertyAdaptorSlotObject(obj, metaProperty);
+
+ auto c = QObjectPrivate::connect(obj, metaProperty.notifySignalIndex(), obj, adaptor,
+ Qt::DirectConnection);
+ Q_ASSERT(c);
+ }
+
+ data = adaptor;
+}
+
+QUntypedBindable::QUntypedBindable(QObject *obj, const char *property,
+ const QtPrivate::QBindableInterface *i)
+ : QUntypedBindable(
+ obj,
+ [=]() -> QMetaProperty {
+ if (!obj)
+ return {};
+ auto propertyIndex = obj->metaObject()->indexOfProperty(property);
+ if (propertyIndex < 0) {
+ qCWarning(lcQPropertyBinding)
+ << "QUntypedBindable: No property named" << property;
+ return {};
+ }
+ return obj->metaObject()->property(propertyIndex);
+ }(),
+ i)
+{
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index ec7ff2e0fd..cce9e9791f 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -597,6 +597,60 @@ enum Reason { InvalidInterface, NonBindableInterface, ReadOnlyInterface };
Q_CORE_EXPORT void printUnsuitableBindableWarning(QAnyStringView prefix, Reason reason);
Q_CORE_EXPORT void printMetaTypeMismatch(QMetaType actual, QMetaType expected);
}
+
+namespace PropertyAdaptorSlotObjectHelpers {
+Q_CORE_EXPORT void getter(const QUntypedPropertyData *d, void *value);
+Q_CORE_EXPORT void setter(QUntypedPropertyData *d, const void *value);
+Q_CORE_EXPORT QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d);
+Q_CORE_EXPORT bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
+ QtPrivate::QPropertyBindingFunction binding,
+ QUntypedPropertyData *temp, void *value);
+Q_CORE_EXPORT QUntypedPropertyBinding setBinding(QUntypedPropertyData *d,
+ const QUntypedPropertyBinding &binding,
+ QPropertyBindingWrapper wrapper);
+Q_CORE_EXPORT void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer);
+
+template<typename T>
+bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
+ QtPrivate::QPropertyBindingFunction binding)
+{
+ struct Data : QPropertyData<T>
+ {
+ void *data() { return &this->val; }
+ } temp;
+ return bindingWrapper(type, d, binding, &temp, temp.data());
+}
+
+template<typename T>
+QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding)
+{
+ return setBinding(d, binding, &bindingWrapper<T>);
+}
+
+template<typename T>
+QUntypedPropertyBinding makeBinding(const QUntypedPropertyData *d,
+ const QPropertyBindingSourceLocation &location)
+{
+ return Qt::makePropertyBinding(
+ [d]() -> T {
+ T r;
+ getter(d, &r);
+ return r;
+ },
+ location);
+}
+
+template<class T>
+inline constexpr QBindableInterface iface = {
+ &getter,
+ &setter,
+ &getBinding,
+ &setBinding<T>,
+ &makeBinding<T>,
+ &setObserver,
+ &QMetaType::fromType<T>,
+};
+}
}
class QUntypedBindable
@@ -609,6 +663,9 @@ protected:
: data(d), iface(i)
{}
+ Q_CORE_EXPORT QUntypedBindable(QObject* obj, const QMetaProperty &property, const QtPrivate::QBindableInterface *i);
+ Q_CORE_EXPORT QUntypedBindable(QObject* obj, const char* property, const QtPrivate::QBindableInterface *i);
+
public:
constexpr QUntypedBindable() = default;
template<typename Property>
@@ -745,6 +802,12 @@ public:
}
}
+ QBindable(QObject *obj, const QMetaProperty &property)
+ : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
+
+ QBindable(QObject *obj, const char *property)
+ : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
+
QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
{
return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index 38380f0207..a7fb9e9a1a 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -18,8 +18,10 @@
#include <private/qglobal_p.h>
#include <qproperty.h>
+#include <qmetaobject.h>
#include <qscopedpointer.h>
#include <qscopedvaluerollback.h>
+#include <qvariant.h>
#include <vector>
#include <QtCore/QVarLengthArray>
@@ -898,6 +900,37 @@ QPropertyBindingPrivate *QBindingObserverPtr::binding() const noexcept { return
QPropertyObserver *QBindingObserverPtr::operator->() { return d; }
+namespace QtPrivate {
+class QPropertyAdaptorSlotObject : public QUntypedPropertyData, public QSlotObjectBase
+{
+ QPropertyBindingData bindingData_;
+ QObject *obj;
+ QMetaProperty metaProperty_;
+
+ static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret);
+
+ QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty& p);
+
+public:
+ static QPropertyAdaptorSlotObject *cast(QSlotObjectBase *ptr, int propertyIndex)
+ {
+ if (ptr->isImpl(&QPropertyAdaptorSlotObject::impl)) {
+ auto p = static_cast<QPropertyAdaptorSlotObject *>(ptr);
+ if (p->metaProperty_.propertyIndex() == propertyIndex)
+ return p;
+ }
+ return nullptr;
+ }
+
+ inline const QPropertyBindingData &bindingData() const { return bindingData_; }
+ inline QPropertyBindingData &bindingData() { return bindingData_; }
+ inline QObject *object() const { return obj; }
+ inline const QMetaProperty &metaProperty() const { return metaProperty_; }
+
+ friend class QT_PREPEND_NAMESPACE(QUntypedBindable);
+};
+}
+
QT_END_NAMESPACE
#endif // QPROPERTY_P_H
diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
index ccbf5484c1..e9b2218497 100644
--- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
+++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
@@ -99,6 +99,8 @@ private slots:
void scheduleNotify();
void notifyAfterAllDepsGone();
+
+ void propertyAdaptorBinding();
};
void tst_QProperty::functorBinding()
@@ -1663,6 +1665,190 @@ void tst_QProperty::noFakeDependencies()
QCOMPARE(old, bindingFunctionCalled);
}
+class PropertyAdaptorTester : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged)
+ Q_PROPERTY(int foo1 READ foo WRITE setFoo)
+
+signals:
+ void fooChanged(int newFoo);
+
+public slots:
+ void fooHasChanged() { fooChangedCount++; }
+
+public:
+ int foo() const { return fooData; }
+ void setFoo(int i)
+ {
+ if (i != fooData) {
+ fooData = i;
+ fooChanged(fooData);
+ }
+ }
+
+public:
+ int fooData = 0;
+ int fooChangedCount = 0;
+};
+
+void tst_QProperty::propertyAdaptorBinding()
+{
+ QProperty<int> source { 5 };
+ QProperty<int> dest1 { 99 };
+ QProperty<int> dest2 { 98 };
+
+ // Check binding of non BINDABLE property
+ PropertyAdaptorTester object;
+ QObject::connect(&object, &PropertyAdaptorTester::fooChanged, &object,
+ &PropertyAdaptorTester::fooHasChanged);
+ QBindable<int> binding(&object, "foo");
+ binding.setBinding([&]() { return source + 1; });
+ QCOMPARE(object.foo(), 6);
+ QCOMPARE(object.fooChangedCount, 1);
+
+ struct MyBindable : QBindable<int> {
+ using QBindable<int>::QBindable;
+ QtPrivate::QPropertyAdaptorSlotObject* data() {
+ return static_cast<QtPrivate::QPropertyAdaptorSlotObject*>(QUntypedBindable::data);
+ }
+ } dataBinding(&object, "foo");
+ QPropertyBindingDataPointer data{&dataBinding.data()->bindingData()};
+
+ QCOMPARE(data.observerCount(), 0);
+ dest1.setBinding(binding.makeBinding());
+ QCOMPARE(data.observerCount(), 1);
+ dest2.setBinding([=]() { return binding.value() + 1; });
+ binding = {};
+ QCOMPARE(data.observerCount(), 2);
+
+ // Check addNotifer
+ {
+ int local_foo = 0;
+ auto notifier = QBindable<int>(&object, "foo").addNotifier([&]() { local_foo++; });
+ QCOMPARE(data.observerCount(), 3);
+ QCOMPARE(object.foo(), 6);
+ QCOMPARE(dest1.value(), 6);
+ QCOMPARE(dest2.value(), 7);
+ QCOMPARE(local_foo, 0);
+ QCOMPARE(object.fooChangedCount, 1);
+
+ source = 7;
+ QCOMPARE(object.foo(), 8);
+ QCOMPARE(dest1.value(), 8);
+ QCOMPARE(dest2.value(), 9);
+ QCOMPARE(local_foo, 1);
+ QCOMPARE(object.fooChangedCount, 2);
+ }
+
+ QCOMPARE(data.observerCount(), 2);
+
+ // Check a new QBindable object can override the existing binding
+ QBindable<int>(&object, "foo").setValue(10);
+ QCOMPARE(object.foo(), 10);
+ QCOMPARE(dest1.value(), 10);
+ QCOMPARE(dest2.value(), 11);
+ QCOMPARE(object.fooChangedCount, 3);
+ source.setValue(99);
+ QCOMPARE(object.foo(), 10);
+ QCOMPARE(dest1.value(), 10);
+ QCOMPARE(dest2.value(), 11);
+ QCOMPARE(object.fooChangedCount, 3);
+ object.setFoo(12);
+ QCOMPARE(object.foo(), 12);
+ QCOMPARE(dest1.value(), 12);
+ QCOMPARE(dest2.value(), 13);
+ QCOMPARE(object.fooChangedCount, 4);
+
+ // Check binding multiple notifiers
+ QProperty<int> source2 { 20 };
+ source.setValue(21);
+ binding = QBindable<int>(&object, "foo");
+ binding.setBinding([&]() { return source + source2; });
+ QCOMPARE(object.foo(), 41);
+ QCOMPARE(dest1.value(), 41);
+ QCOMPARE(object.fooChangedCount, 5);
+ source.setValue(22);
+ QCOMPARE(object.foo(), 42);
+ QCOMPARE(dest1.value(), 42);
+ QCOMPARE(object.fooChangedCount, 6);
+ source2.setValue(21);
+ QCOMPARE(object.foo(), 43);
+ QCOMPARE(dest1.value(), 43);
+ QCOMPARE(object.fooChangedCount, 7);
+
+ // Check update group
+ Qt::beginPropertyUpdateGroup();
+ source.setValue(23);
+ source2.setValue(22);
+ QCOMPARE(object.foo(), 43);
+ QCOMPARE(dest1.value(), 43);
+ QCOMPARE(object.fooChangedCount, 7);
+ Qt::endPropertyUpdateGroup();
+ QCOMPARE(object.foo(), 45);
+ QCOMPARE(dest1.value(), 45);
+ QCOMPARE(object.fooChangedCount, 8);
+
+ PropertyAdaptorTester object2;
+ PropertyAdaptorTester object3;
+
+ // Check multiple observers
+ QBindable<int> binding2(&object2, "foo");
+ QBindable<int> binding3(&object3, "foo");
+ binding.setBinding([=]() { return binding2.value(); });
+ binding3.setBinding([=]() { return binding.value(); });
+ QCOMPARE(object.foo(), 0);
+ QCOMPARE(object2.foo(), 0);
+ QCOMPARE(object3.foo(), 0);
+ QCOMPARE(dest1.value(), 0);
+ object2.setFoo(1);
+ QCOMPARE(object.foo(), 1);
+ QCOMPARE(object2.foo(), 1);
+ QCOMPARE(object3.foo(), 1);
+ QCOMPARE(dest1.value(), 1);
+
+ // Check interoperation with BINDABLE properties
+ MyQObject bindableObject;
+ bindableObject.fooData.setBinding([]() { return 5; });
+ QVERIFY(bindableObject.fooData.hasBinding());
+ QVERIFY(!bindableObject.barData.hasBinding());
+ QVERIFY(QBindable<int>(&bindableObject, "foo").hasBinding());
+ QBindable<int> bindableBar(&bindableObject, "bar");
+ QVERIFY(!bindableBar.hasBinding());
+ bindableBar.setBinding([]() { return 6; });
+ QVERIFY(bindableBar.hasBinding());
+ QVERIFY(bindableObject.barData.hasBinding());
+
+ // Check bad arguments
+#ifndef QT_NO_DEBUG
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QUntypedBindable: Property is not valid");
+#endif
+ QVERIFY(!QBindable<int>(&object, QMetaProperty{}).isValid());
+#ifndef QT_NO_DEBUG
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QUntypedBindable: Property foo1 has no notify signal");
+#endif
+ QVERIFY(!QBindable<int>(&object, "foo1").isValid());
+#ifndef QT_NO_DEBUG
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QUntypedBindable: Property foo of type int does not match requested type bool");
+#endif
+ QVERIFY(!QBindable<bool>(&object, "foo").isValid());
+#ifndef QT_NO_DEBUG
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg,
+ "QUntypedBindable: Property foo does not belong to this object");
+#endif
+ QObject qobj;
+ QVERIFY(!QBindable<int>(
+ &qobj,
+ object.metaObject()->property(object.metaObject()->indexOfProperty("foo")))
+ .isValid());
+#ifndef QT_NO_DEBUG
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QUntypedBindable: No property named fizz");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QUntypedBindable: Property is not valid");
+#endif
+ QVERIFY(!QBindable<int>(&object, "fizz").isValid());
+}
+
#if QT_CONFIG(thread)
struct ThreadSafetyTester : public QObject
{