QProperty: Notify observers even when dependency is gone
Problem description: -------------------- Assume we have two properties, P1 and P2. Assume further that we assign a binding to P2, so that it depends on P1. Let the binding additionally capture some (non-QProperty) boolean, and only create the dependency to P1 if the boolean is true. The state afterwards is P1:[p1vaue|firstObserver] | | v ---[p2binding] / P2:[p2value|binding] If the boolean is set to false, and P1 changes its value, we still correctly re-evaluate the binding and update P2's value. However, during binding evaluation we will notice that there is no further dependency from P2 on P1, and remove its observer. The state afterwards is P1:[p1vaue|firstObserver=nullptr] ---[p2binding] / P2:[p2value|binding] Then, during the notify phase, we traverse the observer's again, starting from P1's firstObserver. Given that it is nullptr now, we never reach P2's binding, and thus won't send a notification from it. Fix: ---- We store a list of all visited binding-observers (in a QVarLengthArray, to avoid allocations as long as possible). After the binding evaluation phase, we then use that list to send notifications from every binding that we visited. As we already have a list of all bindings, we no longer need to recurse on binding-observes during the notification process; instead, we only need to deal with static callbacks and ChangeHandlers. The pre-existing notification logic is still kept for the grouped update case, where we already have a list of all delayed properties, and should therefore not encounter the same issue. Unifying its codepath with the existing logic is left as an exercise for a later patch. Fixes: QTBUG-105204 Task-number: QTBUG-104982 Change-Id: I2951f7d9597f4da0b8560a64dfb834f7ad86e757 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> (cherry picked from commit f1b1773d0ae636fa9afa36224ba17566484af3cc) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
3e8530d701
commit
2ebdf562b9
@ -118,11 +118,12 @@ struct QPropertyDelayedNotifications
|
|||||||
Change notifications are sent later with notify (following the logic of separating
|
Change notifications are sent later with notify (following the logic of separating
|
||||||
binding updates and notifications used in non-deferred updates).
|
binding updates and notifications used in non-deferred updates).
|
||||||
*/
|
*/
|
||||||
void evaluateBindings(int index, QBindingStatus *status) {
|
[[nodiscard]] PendingBindingObserverList evaluateBindings(int index, QBindingStatus *status) {
|
||||||
|
PendingBindingObserverList bindingObservers;
|
||||||
auto *delayed = delayedProperties + index;
|
auto *delayed = delayedProperties + index;
|
||||||
auto *bindingData = delayed->originalBindingData;
|
auto *bindingData = delayed->originalBindingData;
|
||||||
if (!bindingData)
|
if (!bindingData)
|
||||||
return;
|
return bindingObservers;
|
||||||
|
|
||||||
bindingData->d_ptr = delayed->d_ptr;
|
bindingData->d_ptr = delayed->d_ptr;
|
||||||
Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit));
|
Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit));
|
||||||
@ -134,7 +135,8 @@ struct QPropertyDelayedNotifications
|
|||||||
QPropertyBindingDataPointer bindingDataPointer{bindingData};
|
QPropertyBindingDataPointer bindingDataPointer{bindingData};
|
||||||
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
|
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
|
||||||
if (observer)
|
if (observer)
|
||||||
observer.evaluateBindings(status);
|
observer.evaluateBindings(bindingObservers, status);
|
||||||
|
return bindingObservers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -216,8 +218,11 @@ void Qt::endPropertyUpdateGroup()
|
|||||||
// update all delayed properties
|
// update all delayed properties
|
||||||
auto start = data;
|
auto start = data;
|
||||||
while (data) {
|
while (data) {
|
||||||
for (int i = 0; i < data->used; ++i)
|
for (int i = 0; i < data->used; ++i) {
|
||||||
data->evaluateBindings(i, status);
|
PendingBindingObserverList bindingObserves = data->evaluateBindings(i, status);
|
||||||
|
Q_UNUSED(bindingObserves);
|
||||||
|
// ### TODO: Use bindingObservers for notify
|
||||||
|
}
|
||||||
data = data->next;
|
data = data->next;
|
||||||
}
|
}
|
||||||
// notify all delayed properties
|
// notify all delayed properties
|
||||||
@ -271,11 +276,11 @@ void QPropertyBindingPrivate::unlinkAndDeref()
|
|||||||
destroyAndFreeMemory(this);
|
destroyAndFreeMemory(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingPrivate::evaluateRecursive(QBindingStatus *status)
|
void QPropertyBindingPrivate::evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
|
||||||
{
|
{
|
||||||
if (!status)
|
if (!status)
|
||||||
status = &bindingStatus;
|
status = &bindingStatus;
|
||||||
return evaluateRecursive_inline(status);
|
return evaluateRecursive_inline(bindingObservers, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingPrivate::notifyRecursive()
|
void QPropertyBindingPrivate::notifyRecursive()
|
||||||
@ -294,6 +299,31 @@ void QPropertyBindingPrivate::notifyRecursive()
|
|||||||
updating = false;
|
updating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QPropertyBindingPrivate::notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
|
||||||
|
{
|
||||||
|
notifyNonRecursive();
|
||||||
|
for (auto &&bindingObserver: bindingObservers) {
|
||||||
|
bindingObserver.binding()->notifyNonRecursive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRecursive()
|
||||||
|
{
|
||||||
|
if (!pendingNotify)
|
||||||
|
return Delayed;
|
||||||
|
pendingNotify = false;
|
||||||
|
Q_ASSERT(!updating);
|
||||||
|
updating = true;
|
||||||
|
if (firstObserver) {
|
||||||
|
firstObserver.noSelfDependencies(this);
|
||||||
|
firstObserver.notifyOnlyChangeHandler(propertyDataPtr);
|
||||||
|
}
|
||||||
|
if (hasStaticObserver)
|
||||||
|
staticObserverCallback(propertyDataPtr);
|
||||||
|
updating = false;
|
||||||
|
return Sent;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Constructs a null QUntypedPropertyBinding.
|
Constructs a null QUntypedPropertyBinding.
|
||||||
|
|
||||||
@ -461,8 +491,9 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
|
|||||||
newBindingRaw->prependObserver(observer);
|
newBindingRaw->prependObserver(observer);
|
||||||
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
|
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
|
||||||
|
|
||||||
newBindingRaw->evaluateRecursive();
|
PendingBindingObserverList bindingObservers;
|
||||||
newBindingRaw->notifyRecursive();
|
newBindingRaw->evaluateRecursive(bindingObservers);
|
||||||
|
newBindingRaw->notifyNonRecursive(bindingObservers);
|
||||||
} else if (observer) {
|
} else if (observer) {
|
||||||
d.setObservers(observer.ptr);
|
d.setObservers(observer.ptr);
|
||||||
} else {
|
} else {
|
||||||
@ -565,18 +596,23 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr
|
|||||||
return;
|
return;
|
||||||
QPropertyBindingDataPointer d{this};
|
QPropertyBindingDataPointer d{this};
|
||||||
|
|
||||||
|
PendingBindingObserverList bindingObservers;
|
||||||
if (QPropertyObserverPointer observer = d.firstObserver()) {
|
if (QPropertyObserverPointer observer = d.firstObserver()) {
|
||||||
if (notifyObserver_helper(propertyDataPtr, observer, storage) == Evaluated) {
|
if (notifyObserver_helper(propertyDataPtr, storage, observer, bindingObservers) == Evaluated) {
|
||||||
// evaluateBindings() can trash the observers. We need to re-fetch here.
|
// evaluateBindings() can trash the observers. We need to re-fetch here.
|
||||||
if (QPropertyObserverPointer observer = d.firstObserver())
|
if (QPropertyObserverPointer observer = d.firstObserver())
|
||||||
observer.notify(propertyDataPtr);
|
observer.notifyOnlyChangeHandler(propertyDataPtr);
|
||||||
|
for (auto &&bindingObserver: bindingObservers)
|
||||||
|
bindingObserver.binding()->notifyNonRecursive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper(
|
QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper
|
||||||
QUntypedPropertyData *propertyDataPtr, QPropertyObserverPointer observer,
|
(
|
||||||
QBindingStorage *storage) const
|
QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
|
||||||
|
QPropertyObserverPointer observer,
|
||||||
|
PendingBindingObserverList &bindingObservers) const
|
||||||
{
|
{
|
||||||
#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
|
#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
|
||||||
QBindingStatus *status = storage ? storage->bindingStatus : nullptr;
|
QBindingStatus *status = storage ? storage->bindingStatus : nullptr;
|
||||||
@ -591,7 +627,7 @@ QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_he
|
|||||||
return Delayed;
|
return Delayed;
|
||||||
}
|
}
|
||||||
|
|
||||||
observer.evaluateBindings(status);
|
observer.evaluateBindings(bindingObservers, status);
|
||||||
return Evaluated;
|
return Evaluated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,7 +760,7 @@ void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *bindi
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status)
|
void QPropertyObserverPointer::evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
|
||||||
{
|
{
|
||||||
Q_ASSERT(status);
|
Q_ASSERT(status);
|
||||||
auto observer = const_cast<QPropertyObserver*>(ptr);
|
auto observer = const_cast<QPropertyObserver*>(ptr);
|
||||||
@ -733,9 +769,10 @@ void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status)
|
|||||||
QPropertyObserver *next = observer->next.data();
|
QPropertyObserver *next = observer->next.data();
|
||||||
|
|
||||||
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
|
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
|
||||||
|
bindingObservers.push_back(observer);
|
||||||
auto bindingToEvaluate = observer->binding;
|
auto bindingToEvaluate = observer->binding;
|
||||||
QPropertyObserverNodeProtector protector(observer);
|
QPropertyObserverNodeProtector protector(observer);
|
||||||
bindingToEvaluate->evaluateRecursive_inline(status);
|
bindingToEvaluate->evaluateRecursive_inline(bindingObservers, status);
|
||||||
next = protector.next();
|
next = protector.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <qscopedpointer.h>
|
#include <qscopedpointer.h>
|
||||||
#include <qscopedvaluerollback.h>
|
#include <qscopedvaluerollback.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <QtCore/QVarLengthArray>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@ -29,6 +30,34 @@ namespace QtPrivate {
|
|||||||
struct QBindingStatusAccessToken {};
|
struct QBindingStatusAccessToken {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
Similar to \c QPropertyBindingPrivatePtr, but stores a
|
||||||
|
\c QPropertyObserver * linking to the QPropertyBindingPrivate*
|
||||||
|
instead of the QPropertyBindingPrivate* itself
|
||||||
|
*/
|
||||||
|
struct QBindingObserverPtr
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
QPropertyObserver *d = nullptr;
|
||||||
|
public:
|
||||||
|
QBindingObserverPtr() = default;
|
||||||
|
Q_DISABLE_COPY(QBindingObserverPtr);
|
||||||
|
void swap(QBindingObserverPtr &other) noexcept
|
||||||
|
{ qt_ptr_swap(d, other.d); }
|
||||||
|
QBindingObserverPtr(QBindingObserverPtr &&other) : d(std::exchange(other.d, nullptr)) {}
|
||||||
|
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QBindingObserverPtr);
|
||||||
|
|
||||||
|
|
||||||
|
inline QBindingObserverPtr(QPropertyObserver *observer);
|
||||||
|
inline ~QBindingObserverPtr();
|
||||||
|
inline QPropertyBindingPrivate *binding() const;
|
||||||
|
inline QPropertyObserver *operator ->();
|
||||||
|
};
|
||||||
|
|
||||||
|
using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>;
|
||||||
|
|
||||||
// Keep all classes related to QProperty in one compilation unit. Performance of this code is crucial and
|
// Keep all classes related to QProperty in one compilation unit. Performance of this code is crucial and
|
||||||
// we need to allow the compiler to inline where it makes sense.
|
// we need to allow the compiler to inline where it makes sense.
|
||||||
|
|
||||||
@ -106,19 +135,29 @@ struct QPropertyObserverPointer
|
|||||||
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding);
|
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding);
|
||||||
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
|
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
|
||||||
|
|
||||||
|
enum class Notify {Everything, OnlyChangeHandlers};
|
||||||
|
|
||||||
|
template<Notify notifyPolicy = Notify::Everything>
|
||||||
void notify(QUntypedPropertyData *propertyDataPtr);
|
void notify(QUntypedPropertyData *propertyDataPtr);
|
||||||
|
void notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr);
|
||||||
#ifndef QT_NO_DEBUG
|
#ifndef QT_NO_DEBUG
|
||||||
void noSelfDependencies(QPropertyBindingPrivate *binding);
|
void noSelfDependencies(QPropertyBindingPrivate *binding);
|
||||||
#else
|
#else
|
||||||
void noSelfDependencies(QPropertyBindingPrivate *) {}
|
void noSelfDependencies(QPropertyBindingPrivate *) {}
|
||||||
#endif
|
#endif
|
||||||
void evaluateBindings(QBindingStatus *status);
|
void evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status);
|
||||||
void observeProperty(QPropertyBindingDataPointer property);
|
void observeProperty(QPropertyBindingDataPointer property);
|
||||||
|
|
||||||
explicit operator bool() const { return ptr != nullptr; }
|
explicit operator bool() const { return ptr != nullptr; }
|
||||||
|
|
||||||
QPropertyObserverPointer nextObserver() const { return {ptr->next.data()}; }
|
QPropertyObserverPointer nextObserver() const { return {ptr->next.data()}; }
|
||||||
|
|
||||||
|
QPropertyBindingPrivate *binding() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding);
|
||||||
|
return ptr->binding;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void unlink_common()
|
void unlink_common()
|
||||||
{
|
{
|
||||||
@ -321,10 +360,21 @@ public:
|
|||||||
|
|
||||||
void unlinkAndDeref();
|
void unlinkAndDeref();
|
||||||
|
|
||||||
void evaluateRecursive(QBindingStatus *status = nullptr);
|
void evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status = nullptr);
|
||||||
void Q_ALWAYS_INLINE evaluateRecursive_inline(QBindingStatus *status);
|
|
||||||
|
// ### TODO: remove as soon as declarative no longer needs this overload
|
||||||
|
void evaluateRecursive()
|
||||||
|
{
|
||||||
|
PendingBindingObserverList bindingObservers;
|
||||||
|
evaluateRecursive(bindingObservers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Q_ALWAYS_INLINE evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status);
|
||||||
|
|
||||||
void notifyRecursive();
|
void notifyRecursive();
|
||||||
|
void notifyNonRecursive(const PendingBindingObserverList &bindingObservers);
|
||||||
|
enum NotificationState : bool { Delayed, Sent };
|
||||||
|
NotificationState notifyNonRecursive();
|
||||||
|
|
||||||
static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
|
static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
|
||||||
{ return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
|
{ return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
|
||||||
@ -566,11 +616,14 @@ public:
|
|||||||
QPropertyBindingDataPointer d{bd};
|
QPropertyBindingDataPointer d{bd};
|
||||||
if (QPropertyObserverPointer observer = d.firstObserver()) {
|
if (QPropertyObserverPointer observer = d.firstObserver()) {
|
||||||
if (!inBindingWrapper(storage)) {
|
if (!inBindingWrapper(storage)) {
|
||||||
if (bd->notifyObserver_helper(this, observer, storage)
|
PendingBindingObserverList bindingObservers;
|
||||||
|
if (bd->notifyObserver_helper(this, storage, observer, bindingObservers)
|
||||||
== QtPrivate::QPropertyBindingData::Evaluated) {
|
== QtPrivate::QPropertyBindingData::Evaluated) {
|
||||||
// evaluateBindings() can trash the observers. We need to re-fetch here.
|
// evaluateBindings() can trash the observers. We need to re-fetch here.
|
||||||
if (QPropertyObserverPointer observer = d.firstObserver())
|
if (QPropertyObserverPointer observer = d.firstObserver())
|
||||||
observer.notify(this);
|
observer.notifyOnlyChangeHandler(this);
|
||||||
|
for (auto&& bindingObserver: bindingObservers)
|
||||||
|
bindingObserver.binding()->notifyNonRecursive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -727,7 +780,7 @@ struct QUntypedBindablePrivate
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *status)
|
inline void QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
|
||||||
{
|
{
|
||||||
if (updating) {
|
if (updating) {
|
||||||
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
|
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
|
||||||
@ -766,9 +819,10 @@ inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *st
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
firstObserver.noSelfDependencies(this);
|
firstObserver.noSelfDependencies(this);
|
||||||
firstObserver.evaluateBindings(status);
|
firstObserver.evaluateBindings(bindingObservers, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<QPropertyObserverPointer::Notify notifyPolicy>
|
||||||
inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
|
inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
|
||||||
{
|
{
|
||||||
auto observer = const_cast<QPropertyObserver*>(ptr);
|
auto observer = const_cast<QPropertyObserver*>(ptr);
|
||||||
@ -808,10 +862,12 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP
|
|||||||
}
|
}
|
||||||
case QPropertyObserver::ObserverNotifiesBinding:
|
case QPropertyObserver::ObserverNotifiesBinding:
|
||||||
{
|
{
|
||||||
|
if constexpr (notifyPolicy == Notify::Everything) {
|
||||||
auto bindingToNotify = observer->binding;
|
auto bindingToNotify = observer->binding;
|
||||||
QPropertyObserverNodeProtector protector(observer);
|
QPropertyObserverNodeProtector protector(observer);
|
||||||
bindingToNotify->notifyRecursive();
|
bindingToNotify->notifyRecursive();
|
||||||
next = protector.next();
|
next = protector.next();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QPropertyObserver::ObserverIsPlaceholder:
|
case QPropertyObserver::ObserverIsPlaceholder:
|
||||||
@ -825,12 +881,29 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void QPropertyObserverPointer::notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr)
|
||||||
|
{
|
||||||
|
notify<Notify::OnlyChangeHandlers>(propertyDataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
inline QPropertyObserverNodeProtector::~QPropertyObserverNodeProtector()
|
inline QPropertyObserverNodeProtector::~QPropertyObserverNodeProtector()
|
||||||
{
|
{
|
||||||
QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)};
|
QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)};
|
||||||
d.unlink_fast();
|
d.unlink_fast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) : d(observer)
|
||||||
|
{
|
||||||
|
Q_ASSERT(d);
|
||||||
|
QPropertyObserverPointer{d}.binding()->addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
QBindingObserverPtr::~QBindingObserverPtr() { if (d) QPropertyObserverPointer{d}.binding()->deref(); }
|
||||||
|
|
||||||
|
QPropertyBindingPrivate *QBindingObserverPtr::binding() const { return QPropertyObserverPointer{d}.binding(); }
|
||||||
|
|
||||||
|
QPropertyObserver *QBindingObserverPtr::operator->() { return d; }
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // QPROPERTY_P_H
|
#endif // QPROPERTY_P_H
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qglobal.h>
|
||||||
#include <QtCore/qtaggedpointer.h>
|
#include <QtCore/qtaggedpointer.h>
|
||||||
#include <QtCore/qmetatype.h>
|
#include <QtCore/qmetatype.h>
|
||||||
|
#include <QtCore/qcontainerfwd.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@ -28,6 +29,9 @@ class QBindingStorage;
|
|||||||
template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
|
template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
|
||||||
class QObjectCompatProperty;
|
class QObjectCompatProperty;
|
||||||
|
|
||||||
|
struct QBindingObserverPtr;
|
||||||
|
using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>;
|
||||||
|
|
||||||
namespace QtPrivate {
|
namespace QtPrivate {
|
||||||
// QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline
|
// QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline
|
||||||
// the constructor and copy constructor
|
// the constructor and copy constructor
|
||||||
@ -115,6 +119,7 @@ private:
|
|||||||
class QUntypedPropertyBinding;
|
class QUntypedPropertyBinding;
|
||||||
class QPropertyBindingPrivate;
|
class QPropertyBindingPrivate;
|
||||||
struct QPropertyBindingDataPointer;
|
struct QPropertyBindingDataPointer;
|
||||||
|
class QPropertyObserver;
|
||||||
struct QPropertyObserverPointer;
|
struct QPropertyObserverPointer;
|
||||||
|
|
||||||
class QUntypedPropertyData
|
class QUntypedPropertyData
|
||||||
@ -308,8 +313,9 @@ private:
|
|||||||
|
|
||||||
enum NotificationResult { Delayed, Evaluated };
|
enum NotificationResult { Delayed, Evaluated };
|
||||||
NotificationResult notifyObserver_helper(
|
NotificationResult notifyObserver_helper(
|
||||||
QUntypedPropertyData *propertyDataPtr, QPropertyObserverPointer observer,
|
QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
|
||||||
QBindingStorage *storage) const;
|
QPropertyObserverPointer observer,
|
||||||
|
PendingBindingObserverList &bindingObservers) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Tag>
|
template <typename T, typename Tag>
|
||||||
|
@ -95,6 +95,8 @@ private slots:
|
|||||||
|
|
||||||
void qpropertyAlias();
|
void qpropertyAlias();
|
||||||
void scheduleNotify();
|
void scheduleNotify();
|
||||||
|
|
||||||
|
void notifyAfterAllDepsGone();
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QProperty::functorBinding()
|
void tst_QProperty::functorBinding()
|
||||||
@ -2042,6 +2044,28 @@ void tst_QProperty::scheduleNotify()
|
|||||||
QCOMPARE(p.value(), 0);
|
QCOMPARE(p.value(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QProperty::notifyAfterAllDepsGone()
|
||||||
|
{
|
||||||
|
bool b = true;
|
||||||
|
QProperty<int> iprop;
|
||||||
|
QProperty<int> jprop(42);
|
||||||
|
iprop.setBinding([&](){
|
||||||
|
if (b)
|
||||||
|
return jprop.value();
|
||||||
|
return 13;
|
||||||
|
});
|
||||||
|
int changeCounter = 0;
|
||||||
|
auto keepAlive = iprop.onValueChanged([&](){ changeCounter++; });
|
||||||
|
QCOMPARE(iprop.value(), 42);
|
||||||
|
jprop = 44;
|
||||||
|
QCOMPARE(iprop.value(), 44);
|
||||||
|
QCOMPARE(changeCounter, 1);
|
||||||
|
b = false;
|
||||||
|
jprop = 43;
|
||||||
|
QCOMPARE(iprop.value(), 13);
|
||||||
|
QCOMPARE(changeCounter, 2);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QProperty);
|
QTEST_MAIN(tst_QProperty);
|
||||||
|
|
||||||
#undef QT_SOURCE_LOCATION_NAMESPACE
|
#undef QT_SOURCE_LOCATION_NAMESPACE
|
||||||
|
Loading…
x
Reference in New Issue
Block a user