summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qproperty.h
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2020-08-25 12:27:56 +0200
committerLars Knoll <lars.knoll@qt.io>2020-09-02 22:44:28 +0200
commit3b3b190eef036ffefb5275e6f03e9f933d3609ff (patch)
tree6c1f58ff6e0f2dab418d5b9bb11423d78ad32789 /src/corelib/kernel/qproperty.h
parentaf149ada60b7fd8a51ebae97b1324e06c919f2e9 (diff)
Add support for bindable properties to QObject
Add Q_OBJECT_BINDABLE_PROPERTY() macro that can be used to define a bindable property inside QObject. The macro and the class behind it creates storage for a property that is bindable inside a QObject or QObjectPrivate. The property only uses as much space as the data contained, ie. it has no storage overhead, as long as no bindings are being used. Bindings are being stored and looked up in the QBindingStorage associated with the owning object. Change-Id: I1dadd7bddbad6fbf10cfa791d6461574b9db82dd Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/corelib/kernel/qproperty.h')
-rw-r--r--src/corelib/kernel/qproperty.h187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index 4013b8c92b..14319f0382 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -742,6 +742,193 @@ public:
QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create);
};
+
+template<typename Class, typename T, auto Offset, auto Signal = nullptr>
+class QObjectBindableProperty : public QPropertyData<T>
+{
+ using ThisType = QObjectBindableProperty<Class, T, Offset, Signal>;
+ static bool constexpr HasSignal = !std::is_same_v<decltype(Signal), std::nullptr_t>;
+ Class *owner()
+ {
+ char *that = reinterpret_cast<char *>(this);
+ return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
+ }
+ const Class *owner() const
+ {
+ char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
+ return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
+ }
+ static void signalCallBack(QUntypedPropertyData *o)
+ {
+ QObjectBindableProperty *that = static_cast<QObjectBindableProperty *>(o);
+ if constexpr (HasSignal)
+ (that->owner()->*Signal)();
+ }
+public:
+ using value_type = typename QPropertyData<T>::value_type;
+ using parameter_type = typename QPropertyData<T>::parameter_type;
+ using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
+ using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
+
+ QObjectBindableProperty() = default;
+ explicit QObjectBindableProperty(const T &initialValue) : QPropertyData<T>(initialValue) {}
+ explicit QObjectBindableProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue)) {}
+ explicit QObjectBindableProperty(const QPropertyBinding<T> &binding)
+ : QObjectBindableProperty()
+ { setBinding(binding); }
+#ifndef Q_CLANG_QDOC
+ template <typename Functor>
+ explicit QObjectBindableProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
+ typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = 0)
+ : QObjectBindableProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
+ {}
+#else
+ template <typename Functor>
+ explicit QProperty(Functor &&f);
+#endif
+
+ parameter_type value() const {
+ qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
+ return this->val;
+ }
+
+ arrow_operator_result operator->() const
+ {
+ if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
+ return value();
+ } else if constexpr (std::is_pointer_v<T>) {
+ value();
+ return this->val;
+ } else {
+ return;
+ }
+ }
+
+ parameter_type operator*() const
+ {
+ return value();
+ }
+
+ operator parameter_type() const
+ {
+ return value();
+ }
+
+ void setValue(parameter_type t) {
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ if (bd)
+ bd->removeBinding();
+ if (this->val == t)
+ return;
+ this->val = t;
+ notify(bd);
+ }
+
+ void setValue(rvalue_ref t) {
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ if (bd)
+ bd->removeBinding();
+ if (this->val == t)
+ return;
+ this->val = std::move(t);
+ notify(bd);
+ }
+
+ QObjectBindableProperty &operator=(rvalue_ref newValue)
+ {
+ setValue(std::move(newValue));
+ return *this;
+ }
+
+ QObjectBindableProperty &operator=(parameter_type newValue)
+ {
+ setValue(newValue);
+ return *this;
+ }
+
+ QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
+ {
+ QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
+ QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
+ notify(bd);
+ return static_cast<QPropertyBinding<T> &>(oldBinding);
+ }
+
+ bool setBinding(const QUntypedPropertyBinding &newBinding)
+ {
+ if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
+ return false;
+ setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
+ return true;
+ }
+
+#ifndef Q_CLANG_QDOC
+ template <typename Functor>
+ QPropertyBinding<T> setBinding(Functor &&f,
+ const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
+ std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
+ {
+ return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
+ }
+#else
+ template <typename Functor>
+ QPropertyBinding<T> setBinding(Functor f);
+#endif
+
+ bool hasBinding() const {
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ return bd && bd->binding() != nullptr;
+ }
+
+ QPropertyBinding<T> binding() const
+ {
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ return static_cast<QPropertyBinding<T> &&>(QUntypedPropertyBinding(bd ? bd->binding() : nullptr));
+ }
+
+ QPropertyBinding<T> takeBinding()
+ {
+ return setBinding(QPropertyBinding<T>());
+ }
+
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> onValueChanged(Functor f)
+ {
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+ return QPropertyChangeHandler<Functor>(*this, f);
+ }
+
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> subscribe(Functor f)
+ {
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+ f();
+ return onValueChanged(f);
+ }
+
+ const QtPrivate::QPropertyBindingData &bindingData() const
+ {
+ auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
+ return *storage->bindingData(const_cast<ThisType *>(this), true);
+ }
+private:
+ void notify(const QtPrivate::QPropertyBindingData *binding)
+ {
+ if (binding)
+ binding->notifyObservers(this);
+ if constexpr (HasSignal)
+ (owner()->*Signal)();
+ }
+};
+
+#define Q_OBJECT_BINDABLE_PROPERTY(Class, Type, name, ...) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ return offsetof(Class, name); \
+ QT_WARNING_POP \
+ } \
+ QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
+
QT_END_NAMESPACE
#endif // QPROPERTY_H