QNotifiedProperty: pass old value to callback if requested
Check at compile time whether the static callback takes an argument (which has to be of the same time as the type of the property). If so, retrieve the old value and pass it to the callback. Change-Id: Ib1c4c9e05b826b6be492b03f66fa72ad015963ee Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
c2fb27f054
commit
6a24ac7c4e
@ -95,7 +95,7 @@ QPropertyBase::~QPropertyBase()
|
|||||||
QUntypedPropertyBinding QPropertyBase::setBinding(const QUntypedPropertyBinding &binding,
|
QUntypedPropertyBinding QPropertyBase::setBinding(const QUntypedPropertyBinding &binding,
|
||||||
void *propertyDataPtr,
|
void *propertyDataPtr,
|
||||||
void *staticObserver,
|
void *staticObserver,
|
||||||
void (*staticObserverCallback)(void*))
|
void (*staticObserverCallback)(void*, void*))
|
||||||
{
|
{
|
||||||
QPropertyBindingPrivatePtr oldBinding;
|
QPropertyBindingPrivatePtr oldBinding;
|
||||||
QPropertyBindingPrivatePtr newBinding = binding.d;
|
QPropertyBindingPrivatePtr newBinding = binding.d;
|
||||||
|
@ -374,11 +374,21 @@ namespace Qt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Class, void(Class::*Callback)()>
|
namespace detail {
|
||||||
class QNotifiedProperty<T, Callback>
|
template <typename F>
|
||||||
|
struct ExtractClassFromFunctionPointer;
|
||||||
|
|
||||||
|
template<typename T, typename C>
|
||||||
|
struct ExtractClassFromFunctionPointer<T C::*> { using Class = C; };
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, auto Callback>
|
||||||
|
class QNotifiedProperty
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
using Class = typename detail::ExtractClassFromFunctionPointer<decltype(Callback)>::Class;
|
||||||
|
static_assert(std::is_invocable_v<decltype(Callback), Class, T> || std::is_invocable_v<decltype(Callback), Class>);
|
||||||
|
|
||||||
QNotifiedProperty() = default;
|
QNotifiedProperty() = default;
|
||||||
|
|
||||||
@ -420,45 +430,83 @@ public:
|
|||||||
|
|
||||||
void setValue(Class *owner, T &&newValue)
|
void setValue(Class *owner, T &&newValue)
|
||||||
{
|
{
|
||||||
|
if constexpr(std::is_invocable_v<decltype(Callback), Class>) {
|
||||||
if (d.setValueAndReturnTrueIfChanged(std::move(newValue)))
|
if (d.setValueAndReturnTrueIfChanged(std::move(newValue)))
|
||||||
notify(owner);
|
notify(owner);
|
||||||
|
} else {
|
||||||
|
T oldValue = value(); // TODO: kind of pointless if there was no change
|
||||||
|
if (d.setValueAndReturnTrueIfChanged(std::move(newValue)))
|
||||||
|
notify(owner, &oldValue);
|
||||||
|
}
|
||||||
d.priv.removeBinding();
|
d.priv.removeBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setValue(Class *owner, const T &newValue)
|
void setValue(Class *owner, const T &newValue)
|
||||||
{
|
{
|
||||||
|
if constexpr(std::is_invocable_v<decltype(Callback), Class>) {
|
||||||
if (d.setValueAndReturnTrueIfChanged(newValue))
|
if (d.setValueAndReturnTrueIfChanged(newValue))
|
||||||
notify(owner);
|
notify(owner);
|
||||||
|
} else {
|
||||||
|
T oldValue = value();
|
||||||
|
if (d.setValueAndReturnTrueIfChanged(newValue))
|
||||||
|
notify(owner, &oldValue);
|
||||||
|
}
|
||||||
d.priv.removeBinding();
|
d.priv.removeBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
QPropertyBinding<T> setBinding(Class *owner, const QPropertyBinding<T> &newBinding)
|
QPropertyBinding<T> setBinding(Class *owner, const QPropertyBinding<T> &newBinding)
|
||||||
{
|
{
|
||||||
|
if constexpr(std::is_invocable_v<decltype(Callback), Class>) {
|
||||||
QPropertyBinding<T> oldBinding(d.priv.setBinding(newBinding, &d, owner, [](void *o) {
|
QPropertyBinding<T> oldBinding(d.priv.setBinding(newBinding, &d, owner, [](void *o) {
|
||||||
(reinterpret_cast<Class *>(o)->*Callback)();
|
(reinterpret_cast<Class *>(o)->*Callback)();
|
||||||
}));
|
}));
|
||||||
notify(owner);
|
notify(owner);
|
||||||
return oldBinding;
|
return oldBinding;
|
||||||
|
} else {
|
||||||
|
T oldValue = value();
|
||||||
|
QPropertyBinding<T> oldBinding(d.priv.setBinding(newBinding, &d, owner, [](void *o, void *oldValue) {
|
||||||
|
(reinterpret_cast<Class *>(o)->*Callback)(*reinterpret_cast<T *>(oldValue));
|
||||||
|
}));
|
||||||
|
notify(owner, &oldValue);
|
||||||
|
return oldBinding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QPropertyBinding<T> setBinding(Class *owner, QPropertyBinding<T> &&newBinding)
|
QPropertyBinding<T> setBinding(Class *owner, QPropertyBinding<T> &&newBinding)
|
||||||
{
|
{
|
||||||
QPropertyBinding<T> b(std::move(newBinding));
|
QPropertyBinding<T> b(std::move(newBinding));
|
||||||
QPropertyBinding<T> oldBinding(d.priv.setBinding(b, &d, owner, [](void *o) {
|
if constexpr(std::is_invocable_v<decltype(Callback), Class>) {
|
||||||
|
QPropertyBinding<T> oldBinding(d.priv.setBinding(b, &d, owner, [](void *o, void *) {
|
||||||
(reinterpret_cast<Class *>(o)->*Callback)();
|
(reinterpret_cast<Class *>(o)->*Callback)();
|
||||||
}));
|
}));
|
||||||
notify(owner);
|
notify(owner);
|
||||||
return oldBinding;
|
return oldBinding;
|
||||||
|
} else {
|
||||||
|
T oldValue = value();
|
||||||
|
QPropertyBinding<T> oldBinding(d.priv.setBinding(b, &d, owner, [](void *o, void *oldValue) {
|
||||||
|
(reinterpret_cast<Class *>(o)->*Callback)(*reinterpret_cast<T *>(oldValue));
|
||||||
|
}));
|
||||||
|
notify(owner, &oldValue);
|
||||||
|
return oldBinding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setBinding(Class *owner, const QUntypedPropertyBinding &newBinding)
|
bool setBinding(Class *owner, const QUntypedPropertyBinding &newBinding)
|
||||||
{
|
{
|
||||||
if (newBinding.valueMetaType().id() != qMetaTypeId<T>())
|
if (newBinding.valueMetaType().id() != qMetaTypeId<T>())
|
||||||
return false;
|
return false;
|
||||||
d.priv.setBinding(newBinding, &d, owner, [](void *o) {
|
if constexpr(std::is_invocable_v<decltype(Callback), Class>) {
|
||||||
|
d.priv.setBinding(newBinding, &d, owner, [](void *o, void *) {
|
||||||
(reinterpret_cast<Class *>(o)->*Callback)();
|
(reinterpret_cast<Class *>(o)->*Callback)();
|
||||||
});
|
});
|
||||||
notify(owner);
|
notify(owner);
|
||||||
|
} else {
|
||||||
|
T oldValue = value();
|
||||||
|
d.priv.setBinding(newBinding, &d, owner, [](void *o, void *oldValue) {
|
||||||
|
(reinterpret_cast<Class *>(o)->*Callback)(*reinterpret_cast<T *>(oldValue));
|
||||||
|
});
|
||||||
|
notify(owner, &oldValue);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,10 +541,13 @@ public:
|
|||||||
QPropertyChangeHandler<Functor> subscribe(Functor f);
|
QPropertyChangeHandler<Functor> subscribe(Functor f);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void notify(Class *owner)
|
void notify(Class *owner, T *oldValue=nullptr)
|
||||||
{
|
{
|
||||||
d.priv.notifyObservers(&d);
|
d.priv.notifyObservers(&d);
|
||||||
|
if constexpr(std::is_invocable_v<decltype(Callback), Class>)
|
||||||
(owner->*Callback)();
|
(owner->*Callback)();
|
||||||
|
else
|
||||||
|
(owner->*Callback)(*oldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DISABLE_COPY_MOVE(QNotifiedProperty)
|
Q_DISABLE_COPY_MOVE(QNotifiedProperty)
|
||||||
@ -624,7 +675,7 @@ QPropertyChangeHandler<Functor> QProperty<T>::subscribe(Functor f)
|
|||||||
return onValueChanged(f);
|
return onValueChanged(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Class, void(Class::*Callback)()>
|
template <typename T, auto Callback>
|
||||||
template<typename Functor>
|
template<typename Functor>
|
||||||
QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback>::onValueChanged(Functor f)
|
QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback>::onValueChanged(Functor f)
|
||||||
{
|
{
|
||||||
@ -634,7 +685,7 @@ QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback>::onValueChanged(F
|
|||||||
return QPropertyChangeHandler<Functor>(*this, f);
|
return QPropertyChangeHandler<Functor>(*this, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Class, void(Class::*Callback)()>
|
template <typename T, auto Callback>
|
||||||
template<typename Functor>
|
template<typename Functor>
|
||||||
QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback>::subscribe(Functor f)
|
QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback>::subscribe(Functor f)
|
||||||
{
|
{
|
||||||
|
@ -69,8 +69,15 @@ void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
|
|||||||
dirty = true;
|
dirty = true;
|
||||||
if (firstObserver)
|
if (firstObserver)
|
||||||
firstObserver.notify(this, propertyDataPtr);
|
firstObserver.notify(this, propertyDataPtr);
|
||||||
if (hasStaticObserver)
|
if (hasStaticObserver) {
|
||||||
staticObserverCallback(staticObserver);
|
if (metaType == QMetaType::fromType<bool>()) {
|
||||||
|
auto propertyPtr = reinterpret_cast<QPropertyBase *>(propertyDataPtr);
|
||||||
|
bool oldValue = propertyPtr->extraBit();
|
||||||
|
staticObserverCallback(staticObserver, &oldValue);
|
||||||
|
} else {
|
||||||
|
staticObserverCallback(staticObserver, propertyDataPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged()
|
bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged()
|
||||||
|
@ -80,7 +80,7 @@ private:
|
|||||||
ObserverArray inlineDependencyObservers;
|
ObserverArray inlineDependencyObservers;
|
||||||
struct {
|
struct {
|
||||||
void *staticObserver;
|
void *staticObserver;
|
||||||
void (*staticObserverCallback)(void*);
|
void (*staticObserverCallback)(void*, void*);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
QScopedPointer<std::vector<QPropertyObserver>> heapObservers;
|
QScopedPointer<std::vector<QPropertyObserver>> heapObservers;
|
||||||
@ -107,7 +107,7 @@ public:
|
|||||||
|
|
||||||
void setDirty(bool d) { dirty = d; }
|
void setDirty(bool d) { dirty = d; }
|
||||||
void setProperty(void *propertyPtr) { propertyDataPtr = propertyPtr; }
|
void setProperty(void *propertyPtr) { propertyDataPtr = propertyPtr; }
|
||||||
void setStaticObserver(void *observer, void (*callback)(void*))
|
void setStaticObserver(void *observer, void (*callback)(void*, void*))
|
||||||
{
|
{
|
||||||
if (observer) {
|
if (observer) {
|
||||||
if (!hasStaticObserver) {
|
if (!hasStaticObserver) {
|
||||||
|
@ -84,7 +84,7 @@ public:
|
|||||||
|
|
||||||
QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding,
|
QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding,
|
||||||
void *propertyDataPtr, void *staticObserver = nullptr,
|
void *propertyDataPtr, void *staticObserver = nullptr,
|
||||||
void (*staticObserverCallback)(void*) = nullptr);
|
void (*staticObserverCallback)(void *, void *) = nullptr);
|
||||||
QPropertyBindingPrivate *binding();
|
QPropertyBindingPrivate *binding();
|
||||||
|
|
||||||
void evaluateIfDirty();
|
void evaluateIfDirty();
|
||||||
|
@ -72,6 +72,7 @@ private slots:
|
|||||||
void multipleObservers();
|
void multipleObservers();
|
||||||
void propertyAlias();
|
void propertyAlias();
|
||||||
void notifiedProperty();
|
void notifiedProperty();
|
||||||
|
void notifiedPropertyWithOldValueCallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QProperty::functorBinding()
|
void tst_QProperty::functorBinding()
|
||||||
@ -779,6 +780,15 @@ struct ClassWithNotifiedProperty
|
|||||||
QNotifiedProperty<int, &ClassWithNotifiedProperty::callback> property;
|
QNotifiedProperty<int, &ClassWithNotifiedProperty::callback> property;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ClassWithNotifiedProperty2
|
||||||
|
{
|
||||||
|
QVector<int> recordedValues;
|
||||||
|
|
||||||
|
void callback(int oldValue) { recordedValues << oldValue; }
|
||||||
|
|
||||||
|
QNotifiedProperty<int, &ClassWithNotifiedProperty2::callback> property;
|
||||||
|
};
|
||||||
|
|
||||||
void tst_QProperty::notifiedProperty()
|
void tst_QProperty::notifiedProperty()
|
||||||
{
|
{
|
||||||
ClassWithNotifiedProperty instance;
|
ClassWithNotifiedProperty instance;
|
||||||
@ -854,6 +864,18 @@ void tst_QProperty::notifiedProperty()
|
|||||||
QCOMPARE(subscribedCount, 10);
|
QCOMPARE(subscribedCount, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QProperty::notifiedPropertyWithOldValueCallback()
|
||||||
|
{
|
||||||
|
ClassWithNotifiedProperty2 instance;
|
||||||
|
instance.property.setValue(&instance, 1);
|
||||||
|
instance.property.setBinding(&instance, [](){return 2;});
|
||||||
|
instance.property.setBinding(&instance, [](){return 3;});
|
||||||
|
instance.property.setValue(&instance, 4);
|
||||||
|
QVector<int> expected {0, 1, 2, 3};
|
||||||
|
QCOMPARE(instance.recordedValues, expected);
|
||||||
|
QCOMPARE(instance.property.value(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QProperty);
|
QTEST_MAIN(tst_QProperty);
|
||||||
|
|
||||||
#include "tst_qproperty.moc"
|
#include "tst_qproperty.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user