summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qproperty.h
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2020-04-29 12:41:36 +0200
committerFabian Kosmale <fabian.kosmale@qt.io>2020-05-28 21:08:51 +0000
commit36f69229253480116de72a7ef46c3bdde1e805e5 (patch)
treeea94df7ec60092acabcf749cab64d009fd3a5fcc /src/corelib/kernel/qproperty.h
parent66d6943478c3ae76f996c2cd2fdf04fee4fcb767 (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.h146
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);