Simplify storing of notification objects
QPropertyChangeHandler is a templated class and it's argument is a functor. That makes it inherently cumbersome to use the class in any context where the change handler needs to be stored. Introduce a QPropertyNotifier class that stores the functor in a std::function<void()>, and add a QProperty::addNotifier() method that can be used instead of onValueChanged(). Also make QPropertyNotifier default constructible. This significantly simplifies the code that needs to be written and makes it possible to store notifications as class members without major hassle. Fixes: QTBUG-92980 Change-Id: Id5b7baec093b9ac0467946cded943d92ad21030b Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
de15836dbf
commit
6c1a9f2b4d
@ -957,6 +957,18 @@ QString QPropertyBindingError::description() const
|
||||
\sa onValueChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template<typename Functor> QPropertyNotifier QUntypedBindable::addNotifier(Functor f)
|
||||
|
||||
Installs \a f as a change handler. Whenever the underlying property changes, \a f will be called, as
|
||||
long as the returned \c QPropertyNotifier and the property are kept alive.
|
||||
|
||||
This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
|
||||
It can therefore more easily be stored, e.g. as a member in a class.
|
||||
|
||||
\sa onValueChanged(), subscribe()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QUntypedPropertyBinding QUntypedBindable::binding() const
|
||||
|
||||
@ -1247,7 +1259,7 @@ QString QPropertyBindingError::description() const
|
||||
is either called immediately, or deferred, depending on the context.
|
||||
|
||||
The callback \a f is expected to be a type that has a plain call operator () without any
|
||||
parameters. This means that you can provide a C++ lambda expression, an std::function
|
||||
parameters. This means that you can provide a C++ lambda expression, a std::function
|
||||
or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the registration. When it
|
||||
@ -1261,12 +1273,31 @@ QString QPropertyBindingError::description() const
|
||||
the value of the property changes in the future. On each value change, the handler
|
||||
is either called immediately, or deferred, depending on the context.
|
||||
|
||||
The callback \a f is expected to be a type that can be copied and has a plain call
|
||||
operator() without any parameters. This means that you can provide a C++ lambda expression,
|
||||
a std::function or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the subscription. When it
|
||||
goes out of scope, the callback is unsubscribed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QPropertyNotifier QProperty<T>::addNotifier(Functor f)
|
||||
|
||||
Subscribes the given functor \a f as a callback that is called whenever
|
||||
the value of the property changes.
|
||||
|
||||
The callback \a f is expected to be a type that has a plain call operator () without any
|
||||
parameters. This means that you can provide a C++ lambda expression, an std::function
|
||||
parameters. This means that you can provide a C++ lambda expression, a std::function
|
||||
or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the subscription. When it
|
||||
goes out of scope, the callback is unsubscribed.
|
||||
|
||||
This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
|
||||
It can therefore more easily be stored, e.g. as a member in a class.
|
||||
|
||||
\sa onValueChanged(), subscribe()
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -1664,7 +1695,7 @@ QString QPropertyBindingError::description() const
|
||||
is either called immediately, or deferred, depending on the context.
|
||||
|
||||
The callback \a f is expected to be a type that has a plain call operator () without any
|
||||
parameters. This means that you can provide a C++ lambda expression, an std::function
|
||||
parameters. This means that you can provide a C++ lambda expression, a std::function
|
||||
or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the registration. When it
|
||||
@ -1679,13 +1710,32 @@ QString QPropertyBindingError::description() const
|
||||
is either called immediately, or deferred, depending on the context.
|
||||
|
||||
The callback \a f is expected to be a type that has a plain call operator () without any
|
||||
parameters. This means that you can provide a C++ lambda expression, an std::function
|
||||
parameters. This means that you can provide a C++ lambda expression, a std::function
|
||||
or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the subscription. When it
|
||||
goes out of scope, the callback is unsubscribed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyNotifier QObjectBindableProperty<Class, T, offset, Callback>::addNotifier(Functor f)
|
||||
|
||||
Subscribes the given functor \a f as a callback that is called whenever
|
||||
the value of the property changes.
|
||||
|
||||
The callback \a f is expected to be a type that has a plain call operator () without any
|
||||
parameters. This means that you can provide a C++ lambda expression, a std::function
|
||||
or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the subscription. When it
|
||||
goes out of scope, the callback is unsubscribed.
|
||||
|
||||
This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
|
||||
It can therefore more easily be stored, e.g. as a member in a class.
|
||||
|
||||
\sa onValueChanged(), subscribe()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T> QtPrivate::QPropertyBase &QObjectBindableProperty<Class, T, offset, Callback>::propertyBase() const
|
||||
\internal
|
||||
@ -1698,13 +1748,27 @@ QString QPropertyBindingError::description() const
|
||||
|
||||
\ingroup tools
|
||||
|
||||
QPropertyChangeHandler\<PropertyType, Functor\> is created when registering a
|
||||
QPropertyChangeHandler\<Functor\> is created when registering a
|
||||
callback on a QProperty to listen to changes to the property's value, using QProperty::onValueChanged
|
||||
and QProperty::subscribe. As long as the change handler is alive, the callback remains installed.
|
||||
|
||||
A handler instance can be transferred between C++ scopes using move semantics.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QPropertyNotifier
|
||||
\inmodule QtCore
|
||||
\brief The QPropertyNotifier class controls the lifecycle of change callback installed on a QProperty.
|
||||
|
||||
\ingroup tools
|
||||
|
||||
QPropertyNotifier is created when registering a
|
||||
callback on a QProperty to listen to changes to the property's value, using QProperty::addNotifier.
|
||||
As long as the change handler is alive, the callback remains installed.
|
||||
|
||||
A handler instance can be transferred between C++ scopes using move semantics.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QPropertyAlias
|
||||
\inmodule QtCore
|
||||
@ -1881,7 +1945,7 @@ QString QPropertyBindingError::description() const
|
||||
is either called immediately, or deferred, depending on the context.
|
||||
|
||||
The callback \a f is expected to be a type that has a plain call operator () without any
|
||||
parameters. This means that you can provide a C++ lambda expression, an std::function
|
||||
parameters. This means that you can provide a C++ lambda expression, a std::function
|
||||
or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the registration. When it
|
||||
@ -1896,13 +1960,32 @@ QString QPropertyBindingError::description() const
|
||||
is either called immediately, or deferred, depending on the context.
|
||||
|
||||
The callback \a f is expected to be a type that has a plain call operator () without any
|
||||
parameters. This means that you can provide a C++ lambda expression, an std::function
|
||||
parameters. This means that you can provide a C++ lambda expression, a std::function
|
||||
or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the subscription. When it
|
||||
goes out of scope, the callback is unsubscribed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T> QPropertyNotifier QPropertyAlias<T>::addNotifier(Functor f)
|
||||
|
||||
Subscribes the given functor \a f as a callback that is called whenever
|
||||
the value of the aliased property changes.
|
||||
|
||||
The callback \a f is expected to be a type that has a plain call operator () without any
|
||||
parameters. This means that you can provide a C++ lambda expression, a std::function
|
||||
or even a custom struct with a call operator.
|
||||
|
||||
The returned property change handler object keeps track of the subscription. When it
|
||||
goes out of scope, the callback is unsubscribed.
|
||||
|
||||
This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
|
||||
It can therefore more easily be stored, e.g. as a member in a class.
|
||||
|
||||
\sa onValueChanged(), subscribe()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T> bool QPropertyAlias<T>::isValid() const
|
||||
|
||||
|
@ -294,6 +294,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class [[nodiscard]] QPropertyNotifier : public QPropertyObserver
|
||||
{
|
||||
std::function<void()> m_handler;
|
||||
public:
|
||||
QPropertyNotifier() = default;
|
||||
template<typename Functor>
|
||||
QPropertyNotifier(Functor handler)
|
||||
: QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
|
||||
auto This = static_cast<QPropertyNotifier *>(self);
|
||||
This->m_handler();
|
||||
})
|
||||
, m_handler(handler)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Functor, typename Property, typename = typename Property::InheritsQUntypedPropertyData>
|
||||
QPropertyNotifier(const Property &property, Functor handler)
|
||||
: QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
|
||||
auto This = static_cast<QPropertyNotifier *>(self);
|
||||
This->m_handler();
|
||||
})
|
||||
, m_handler(handler)
|
||||
{
|
||||
setSource(property);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class QProperty : public QPropertyData<T>
|
||||
{
|
||||
@ -442,6 +469,13 @@ public:
|
||||
return onValueChanged(f);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyNotifier addNotifier(Functor f)
|
||||
{
|
||||
static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
|
||||
return QPropertyNotifier(*this, f);
|
||||
}
|
||||
|
||||
const QtPrivate::QPropertyBindingData &bindingData() const { return d; }
|
||||
private:
|
||||
void notify()
|
||||
@ -630,6 +664,14 @@ public:
|
||||
return onValueChanged(f);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyNotifier addNotifier(Functor f)
|
||||
{
|
||||
QPropertyNotifier handler(f);
|
||||
observe(&handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
QUntypedPropertyBinding binding() const
|
||||
{
|
||||
if (!isBindable()) {
|
||||
@ -871,6 +913,12 @@ public:
|
||||
return QBindable<T>(aliasedProperty(), iface).subscribe(f);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyNotifier addNotifier(Functor f)
|
||||
{
|
||||
return QBindable<T>(aliasedProperty(), iface).notify(f);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return aliasedProperty() != nullptr;
|
||||
@ -1109,6 +1157,13 @@ public:
|
||||
return onValueChanged(f);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyNotifier addNotifier(Functor f)
|
||||
{
|
||||
static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
|
||||
return QPropertyNotifier(*this, f);
|
||||
}
|
||||
|
||||
const QtPrivate::QPropertyBindingData &bindingData() const
|
||||
{
|
||||
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
||||
@ -1238,6 +1293,13 @@ public:
|
||||
return onValueChanged(f);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyNotifier addNotifier(Functor f)
|
||||
{
|
||||
static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
|
||||
return QPropertyNotifier(*this, f);
|
||||
}
|
||||
|
||||
QtPrivate::QPropertyBindingData &bindingData() const
|
||||
{
|
||||
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
||||
|
@ -570,6 +570,13 @@ public:
|
||||
return onValueChanged(f);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyNotifier addNotifier(Functor f)
|
||||
{
|
||||
static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
|
||||
return QPropertyNotifier(*this, f);
|
||||
}
|
||||
|
||||
QtPrivate::QPropertyBindingData &bindingData() const
|
||||
{
|
||||
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
||||
|
@ -108,6 +108,8 @@ private slots:
|
||||
void groupedNotifications();
|
||||
void groupedNotificationConsistency();
|
||||
void uninstalledBindingDoesNotEvaluate();
|
||||
|
||||
void notify();
|
||||
};
|
||||
|
||||
void tst_QProperty::functorBinding()
|
||||
@ -1774,6 +1776,36 @@ void tst_QProperty::uninstalledBindingDoesNotEvaluate()
|
||||
QCOMPARE(bindingEvaluationCounter, 2);
|
||||
}
|
||||
|
||||
void tst_QProperty::notify()
|
||||
{
|
||||
QProperty<int> testProperty(0);
|
||||
QList<int> recordedValues;
|
||||
int value = 0;
|
||||
QPropertyNotifier notifier;
|
||||
|
||||
{
|
||||
QPropertyNotifier handler = testProperty.addNotifier([&]() {
|
||||
recordedValues << testProperty;
|
||||
});
|
||||
notifier = testProperty.addNotifier([&]() {
|
||||
value = testProperty;
|
||||
});
|
||||
|
||||
testProperty = 1;
|
||||
testProperty = 2;
|
||||
}
|
||||
QCOMPARE(value, 2);
|
||||
testProperty = 3;
|
||||
QCOMPARE(value, 3);
|
||||
notifier = {};
|
||||
testProperty = 4;
|
||||
QCOMPARE(value, 3);
|
||||
|
||||
QCOMPARE(recordedValues.count(), 2);
|
||||
QCOMPARE(recordedValues.at(0), 1);
|
||||
QCOMPARE(recordedValues.at(1), 2);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QProperty);
|
||||
|
||||
#undef QT_SOURCE_LOCATION_NAMESPACE
|
||||
|
Loading…
x
Reference in New Issue
Block a user