summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
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 /src/corelib/kernel
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>
Diffstat (limited to 'src/corelib/kernel')
-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
5 files changed, 97 insertions, 30 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;