diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2020-04-29 12:41:36 +0200 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2020-05-28 21:08:51 +0000 |
commit | 36f69229253480116de72a7ef46c3bdde1e805e5 (patch) | |
tree | ea94df7ec60092acabcf749cab64d009fd3a5fcc /src/corelib/kernel/qproperty.h | |
parent | 66d6943478c3ae76f996c2cd2fdf04fee4fcb767 (diff) |
Implement support for QProperty<T> with a static observer
A common pattern in Qt Quick will be QProperty members that are
connected to a callback that needs to perform something when the value
changes, for example emitting a compatibility signal or marking scene
graph node data dirty.
To make such a pattern more efficient, a new QNotifiedProperty type is
introduced that offers the same API as QProperty<T>, with two changes:
(1) The template instantiation not only takes the property type as
parameter but also a callback pointer-to-member.
(2) Since that member itself cannot be called without an instance
and to avoid storing an instance pointer permanently, the API for
setBinding and setValue are adjusted to also take the instance
pointer. For the former it gets stored in the binding, for the
latter it is used to invoke the callback after setting the new
value.
Change-Id: I85cc1d1d1c0472164c4ae87808cfdc0d0b1475e1
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/corelib/kernel/qproperty.h')
-rw-r--r-- | src/corelib/kernel/qproperty.h | 146 |
1 files changed, 144 insertions, 2 deletions
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 76c41f356c..e2a6172d84 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -85,6 +85,7 @@ struct Q_CORE_EXPORT QPropertyBindingSourceLocation template <typename Functor> class QPropertyChangeHandler; template <typename T> class QProperty; +template <typename T, auto callbackMember> class QNotifiedProperty; class QPropertyBindingErrorPrivate; @@ -178,12 +179,15 @@ public: : QUntypedPropertyBinding(property.d.priv.binding()) {} -private: + template<auto notifier> + QPropertyBinding(const QNotifiedProperty<PropertyType, notifier> &property) + : QUntypedPropertyBinding(property.d.priv.binding()) + {} + // Internal explicit QPropertyBinding(const QUntypedPropertyBinding &binding) : QUntypedPropertyBinding(binding) {} - friend class QProperty<PropertyType>; }; namespace QtPrivate { @@ -370,6 +374,140 @@ namespace Qt { } } +template <typename T, typename Class, void(Class::*Callback)()> +class QNotifiedProperty<T, Callback> +{ +public: + using value_type = T; + + QNotifiedProperty() = default; + + explicit QNotifiedProperty(const T &initialValue) : d(initialValue) {} + explicit QNotifiedProperty(T &&initialValue) : d(std::move(initialValue)) {} + + QNotifiedProperty(Class *owner, const QPropertyBinding<T> &binding) + : QNotifiedProperty() + { setBinding(owner, binding); } + QNotifiedProperty(Class *owner, QPropertyBinding<T> &&binding) + : QNotifiedProperty() + { setBinding(owner, std::move(binding)); } + +#ifndef Q_CLANG_QDOC + template <typename Functor> + explicit QNotifiedProperty(Class *owner, Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, + typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = 0) + : QNotifiedProperty(QPropertyBinding<T>(owner, std::forward<Functor>(f), location)) + {} +#else + template <typename Functor> + explicit QProperty(Class *owner, Functor &&f); +#endif + + ~QNotifiedProperty() = default; + + T value() const + { + if (d.priv.hasBinding()) + d.priv.evaluateIfDirty(); + d.priv.registerWithCurrentlyEvaluatingBinding(); + return d.getValue(); + } + + operator T() const + { + return value(); + } + + void setValue(Class *owner, T &&newValue) + { + if (d.setValueAndReturnTrueIfChanged(std::move(newValue))) + notify(owner); + d.priv.removeBinding(); + } + + void setValue(Class *owner, const T &newValue) + { + if (d.setValueAndReturnTrueIfChanged(newValue)) + notify(owner); + d.priv.removeBinding(); + } + + QPropertyBinding<T> setBinding(Class *owner, const QPropertyBinding<T> &newBinding) + { + QPropertyBinding<T> oldBinding(d.priv.setBinding(newBinding, &d, owner, [](void *o) { + (reinterpret_cast<Class *>(o)->*Callback)(); + })); + notify(owner); + return oldBinding; + } + + QPropertyBinding<T> setBinding(Class *owner, QPropertyBinding<T> &&newBinding) + { + QPropertyBinding<T> b(std::move(newBinding)); + QPropertyBinding<T> oldBinding(d.priv.setBinding(b, &d, owner, [](void *o) { + (reinterpret_cast<Class *>(o)->*Callback)(); + })); + notify(owner); + return oldBinding; + } + + bool setBinding(Class *owner, const QUntypedPropertyBinding &newBinding) + { + if (newBinding.valueMetaType().id() != qMetaTypeId<T>()) + return false; + d.priv.setBinding(newBinding, &d, owner, [](void *o) { + (reinterpret_cast<Class *>(o)->*Callback)(); + }); + notify(owner); + return true; + } + +#ifndef Q_CLANG_QDOC + template <typename Functor> + QPropertyBinding<T> setBinding(Class *owner, Functor &&f, + const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, + std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr) + { + return setBinding(owner, Qt::makePropertyBinding(std::forward<Functor>(f), location)); + } +#else + template <typename Functor> + QPropertyBinding<T> setBinding(Class *owner, Functor f); +#endif + + bool hasBinding() const { return d.priv.hasBinding(); } + + QPropertyBinding<T> binding() const + { + return QPropertyBinding<T>(*this); + } + + QPropertyBinding<T> takeBinding() + { + return QPropertyBinding<T>(d.priv.setBinding(QUntypedPropertyBinding(), &d)); + } + + template<typename Functor> + QPropertyChangeHandler<Functor> onValueChanged(Functor f); + template<typename Functor> + QPropertyChangeHandler<Functor> subscribe(Functor f); + +private: + void notify(Class *owner) + { + d.priv.notifyObservers(&d); + (owner->*Callback)(); + } + + Q_DISABLE_COPY_MOVE(QNotifiedProperty) + + friend class QPropertyBinding<T>; + friend class QPropertyObserver; + // Mutable because querying for the value may require evalating the binding expression, calling + // non-const functions on QPropertyBase. + mutable QtPrivate::QPropertyValueStorage<T> d; +}; + struct QPropertyObserverPrivate; struct QPropertyObserverPointer; @@ -392,6 +530,10 @@ public: void setSource(const QProperty<PropertyType> &property) { setSource(property.d.priv); } + template <typename PropertyType, auto notifier> + void setSource(const QNotifiedProperty<PropertyType, notifier> &property) + { setSource(property.d.priv); } + protected: QPropertyObserver(void (*callback)(QPropertyObserver*, void *)); QPropertyObserver(void *aliasedPropertyPtr); |