Remove lazy binding evaluation
Too much of the existing code in Qt requires eager evaluation without large scale modifications. Combined with the fact that supporting both eager and lazy evaluation has a high maintenance burden, keeping lazy evaluation, at least in its current state, is not worth it. This does not diminish other benefits of the new property system, which include - a C++ API to setup and modify bindings and - faster execution compared to QML's existing bindings and the ability to use them without having a QML engine. We do no longer benefit from doing less work thanks to laziness. A later commit will introduce grouping support to recapture some of this benefit. [ChangeLog][Import Behavior Change][QProperty] QProperty uses always eager evaluation now when a dependency in a binding changes. Change-Id: I34694fd5c7bcb1d31a0052d2e3da8b68d016671b Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Andreas Buhr <andreas.buhr@qt.io> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
parent
d72067c938
commit
cf42a0fe5e
@ -50,8 +50,7 @@
|
|||||||
The binding expression computes the value by reading other QProperty values.
|
The binding expression computes the value by reading other QProperty values.
|
||||||
Behind the scenes this dependency is tracked. Whenever a change in any property's
|
Behind the scenes this dependency is tracked. Whenever a change in any property's
|
||||||
dependency is detected, the binding expression is re-evaluated and the new
|
dependency is detected, the binding expression is re-evaluated and the new
|
||||||
result is applied to the property. This happens lazily, by marking the binding
|
result is applied to the property. For example:
|
||||||
as dirty and evaluating it only when the property's value is requested. For example:
|
|
||||||
|
|
||||||
\code
|
\code
|
||||||
QProperty<QString> firstname("John");
|
QProperty<QString> firstname("John");
|
||||||
@ -63,20 +62,19 @@
|
|||||||
|
|
||||||
qDebug() << fullname.value(); // Prints "John Smith age: 41"
|
qDebug() << fullname.value(); // Prints "John Smith age: 41"
|
||||||
|
|
||||||
firstname = "Emma"; // Marks binding expression as dirty
|
firstname = "Emma"; // Triggers binding reevaluation
|
||||||
|
|
||||||
qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 41"
|
qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41"
|
||||||
|
|
||||||
// Birthday is coming up
|
// Birthday is coming up
|
||||||
age.setValue(age.value() + 1);
|
age.setValue(age.value() + 1); // Triggers re-evaluation
|
||||||
|
|
||||||
qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 42"
|
qDebug() << fullname.value(); // Prints "Emma Smith age: 42"
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
When a new value is assigned to the \c firstname property, the binding
|
When a new value is assigned to the \c firstname property, the binding
|
||||||
expression for \c fullname is marked as dirty. So when the last \c qDebug() statement
|
expression for \c fullname is reevaluated. So when the last \c qDebug() statement
|
||||||
tries to read the name value of the \c fullname property, the expression is
|
tries to read the name value of the \c fullname property, the new value is returned.
|
||||||
evaluated again, \c firstname() will be called again and return the new value.
|
|
||||||
|
|
||||||
Since bindings are C++ functions, they may do anything that's possible
|
Since bindings are C++ functions, they may do anything that's possible
|
||||||
in C++. This includes calling other functions. If those functions access values
|
in C++. This includes calling other functions. If those functions access values
|
||||||
|
@ -98,44 +98,17 @@ void QPropertyBindingPrivate::unlinkAndDeref()
|
|||||||
destroyAndFreeMemory(this);
|
destroyAndFreeMemory(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
|
void QPropertyBindingPrivate::evaluate()
|
||||||
{
|
{
|
||||||
if (eagerlyUpdating) {
|
|
||||||
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
|
|
||||||
if (isQQmlPropertyBinding)
|
|
||||||
errorCallBack(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (dirty)
|
|
||||||
return;
|
|
||||||
dirty = true;
|
|
||||||
|
|
||||||
eagerlyUpdating = true;
|
|
||||||
QScopeGuard guard([&](){eagerlyUpdating = false;});
|
|
||||||
bool knownToHaveChanged = false;
|
|
||||||
if (requiresEagerEvaluation()) {
|
|
||||||
// these are compat properties that we will need to evaluate eagerly
|
|
||||||
if (!evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr))
|
|
||||||
return;
|
|
||||||
knownToHaveChanged = true;
|
|
||||||
}
|
|
||||||
if (firstObserver)
|
|
||||||
firstObserver.notify(this, propertyDataPtr, knownToHaveChanged);
|
|
||||||
if (hasStaticObserver)
|
|
||||||
staticObserverCallback(propertyDataPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status)
|
|
||||||
{
|
|
||||||
Q_ASSERT(dirty);
|
|
||||||
|
|
||||||
if (updating) {
|
if (updating) {
|
||||||
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
|
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
|
||||||
if (isQQmlPropertyBinding)
|
if (isQQmlPropertyBinding)
|
||||||
errorCallBack(this);
|
errorCallBack(this);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QScopedValueRollback<bool> updateGuard(updating, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluating the binding might lead to the binding being broken. This can
|
* Evaluating the binding might lead to the binding being broken. This can
|
||||||
* cause ref to reach zero at the end of the function. However, the
|
* cause ref to reach zero at the end of the function. However, the
|
||||||
@ -145,23 +118,26 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper(
|
|||||||
* that the object is still alive when updateGuard's dtor runs.
|
* that the object is still alive when updateGuard's dtor runs.
|
||||||
*/
|
*/
|
||||||
QPropertyBindingPrivatePtr keepAlive {this};
|
QPropertyBindingPrivatePtr keepAlive {this};
|
||||||
QScopedValueRollback<bool> updateGuard(updating, true);
|
|
||||||
|
|
||||||
BindingEvaluationState evaluationFrame(this, status);
|
BindingEvaluationState evaluationFrame(this);
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
Q_ASSERT(propertyDataPtr == data);
|
|
||||||
QUntypedPropertyData *mutable_data = const_cast<QUntypedPropertyData *>(data);
|
|
||||||
|
|
||||||
|
bool changed;
|
||||||
|
auto bindingFunctor = reinterpret_cast<std::byte *>(this) +
|
||||||
|
QPropertyBindingPrivate::getSizeEnsuringAlignment();
|
||||||
if (hasBindingWrapper) {
|
if (hasBindingWrapper) {
|
||||||
changed = staticBindingWrapper(metaType, mutable_data, {vtable, reinterpret_cast<std::byte *>(this)+QPropertyBindingPrivate::getSizeEnsuringAlignment()});
|
changed = staticBindingWrapper(metaType, propertyDataPtr,
|
||||||
|
{vtable, bindingFunctor});
|
||||||
} else {
|
} else {
|
||||||
changed = vtable->call(metaType, mutable_data, reinterpret_cast<std::byte *>(this)+ QPropertyBindingPrivate::getSizeEnsuringAlignment());
|
changed = vtable->call(metaType, propertyDataPtr, bindingFunctor);
|
||||||
}
|
}
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
|
||||||
dirty = false;
|
// notify dependent bindings and emit changed signal
|
||||||
return changed;
|
if (firstObserver)
|
||||||
|
firstObserver.notify(propertyDataPtr);
|
||||||
|
if (hasStaticObserver)
|
||||||
|
staticObserverCallback(propertyDataPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
QUntypedPropertyBinding::QUntypedPropertyBinding() = default;
|
QUntypedPropertyBinding::QUntypedPropertyBinding() = default;
|
||||||
@ -250,7 +226,7 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
|
|||||||
if (auto *existingBinding = d.bindingPtr()) {
|
if (auto *existingBinding = d.bindingPtr()) {
|
||||||
if (existingBinding == newBinding.data())
|
if (existingBinding == newBinding.data())
|
||||||
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
||||||
if (existingBinding->isEagerlyUpdating()) {
|
if (existingBinding->isUpdating()) {
|
||||||
existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
|
existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
|
||||||
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
||||||
}
|
}
|
||||||
@ -267,18 +243,12 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
|
|||||||
d_ptr = reinterpret_cast<quintptr>(newBinding.data());
|
d_ptr = reinterpret_cast<quintptr>(newBinding.data());
|
||||||
d_ptr |= BindingBit;
|
d_ptr |= BindingBit;
|
||||||
auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
|
auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
|
||||||
newBindingRaw->setDirty(true);
|
|
||||||
newBindingRaw->setProperty(propertyDataPtr);
|
newBindingRaw->setProperty(propertyDataPtr);
|
||||||
if (observer)
|
if (observer)
|
||||||
newBindingRaw->prependObserver(observer);
|
newBindingRaw->prependObserver(observer);
|
||||||
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
|
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
|
||||||
if (newBindingRaw->requiresEagerEvaluation()) {
|
|
||||||
newBindingRaw->setEagerlyUpdating(true);
|
newBindingRaw->evaluate();
|
||||||
auto changed = newBindingRaw->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr);
|
|
||||||
if (changed)
|
|
||||||
observer.notify(newBindingRaw, propertyDataPtr, /*knownToHaveChanged=*/true);
|
|
||||||
newBindingRaw->setEagerlyUpdating(false);
|
|
||||||
}
|
|
||||||
} else if (observer) {
|
} else if (observer) {
|
||||||
d.setObservers(observer.ptr);
|
d.setObservers(observer.ptr);
|
||||||
} else {
|
} else {
|
||||||
@ -333,13 +303,9 @@ QPropertyBindingPrivate *QPropertyBindingPrivate::currentlyEvaluatingBinding()
|
|||||||
return currentState ? currentState->binding : nullptr;
|
return currentState ? currentState->binding : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *property) const
|
// ### Unused, kept for BC with 6.0
|
||||||
|
void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *) const
|
||||||
{
|
{
|
||||||
QPropertyBindingDataPointer d{this};
|
|
||||||
QPropertyBindingPrivate *binding = d.bindingPtr();
|
|
||||||
if (!binding)
|
|
||||||
return;
|
|
||||||
binding->evaluateIfDirtyAndReturnTrueIfValueChanged(property);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingData::removeBinding_helper()
|
void QPropertyBindingData::removeBinding_helper()
|
||||||
@ -370,7 +336,7 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding
|
|||||||
QPropertyBindingDataPointer d{this};
|
QPropertyBindingDataPointer d{this};
|
||||||
|
|
||||||
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
|
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
|
||||||
dependencyObserver.setBindingToMarkDirty(currentState->binding);
|
dependencyObserver.setBindingToNotify(currentState->binding);
|
||||||
dependencyObserver.observeProperty(d);
|
dependencyObserver.observeProperty(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,14 +344,7 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr
|
|||||||
{
|
{
|
||||||
QPropertyBindingDataPointer d{this};
|
QPropertyBindingDataPointer d{this};
|
||||||
if (QPropertyObserverPointer observer = d.firstObserver())
|
if (QPropertyObserverPointer observer = d.firstObserver())
|
||||||
observer.notify(d.bindingPtr(), propertyDataPtr);
|
observer.notify(propertyDataPtr);
|
||||||
}
|
|
||||||
|
|
||||||
void QPropertyBindingData::markDirty()
|
|
||||||
{
|
|
||||||
QPropertyBindingDataPointer d{this};
|
|
||||||
if (auto *binding = d.bindingPtr())
|
|
||||||
binding->setDirty(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int QPropertyBindingDataPointer::observerCount() const
|
int QPropertyBindingDataPointer::observerCount() const
|
||||||
@ -425,7 +384,7 @@ QPropertyObserver::~QPropertyObserver()
|
|||||||
|
|
||||||
QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept
|
QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept
|
||||||
{
|
{
|
||||||
bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {});
|
binding = std::exchange(other.binding, {});
|
||||||
next = std::exchange(other.next, {});
|
next = std::exchange(other.next, {});
|
||||||
prev = std::exchange(other.prev, {});
|
prev = std::exchange(other.prev, {});
|
||||||
if (next)
|
if (next)
|
||||||
@ -441,9 +400,9 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexc
|
|||||||
|
|
||||||
QPropertyObserverPointer d{this};
|
QPropertyObserverPointer d{this};
|
||||||
d.unlink();
|
d.unlink();
|
||||||
bindingToMarkDirty = nullptr;
|
binding = nullptr;
|
||||||
|
|
||||||
bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {});
|
binding = std::exchange(other.binding, {});
|
||||||
next = std::exchange(other.next, {});
|
next = std::exchange(other.next, {});
|
||||||
prev = std::exchange(other.prev, {});
|
prev = std::exchange(other.prev, {});
|
||||||
if (next)
|
if (next)
|
||||||
@ -480,10 +439,10 @@ void QPropertyObserverPointer::setAliasedProperty(QUntypedPropertyData *property
|
|||||||
ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias);
|
ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyObserverPointer::setBindingToMarkDirty(QPropertyBindingPrivate *binding)
|
void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
|
||||||
{
|
{
|
||||||
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
|
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
|
||||||
ptr->bindingToMarkDirty = binding;
|
ptr->binding = binding;
|
||||||
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
|
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,14 +475,8 @@ struct [[nodiscard]] QPropertyObserverNodeProtector {
|
|||||||
|
|
||||||
/*! \internal
|
/*! \internal
|
||||||
\a propertyDataPtr is a pointer to the observed property's property data
|
\a propertyDataPtr is a pointer to the observed property's property data
|
||||||
In case that property has a binding, \a triggeringBinding points to the binding's QPropertyBindingPrivate
|
|
||||||
\a alreadyKnownToHaveChanged is an optional parameter, which is needed in the case
|
|
||||||
of eager evaluation:
|
|
||||||
There, we have already evaluated the binding, and thus the change detection for the
|
|
||||||
ObserverNotifiesChangeHandler case would not work. Thus we instead pass the knowledge of
|
|
||||||
whether the value has changed we obtained when evaluating the binding eagerly along
|
|
||||||
*/
|
*/
|
||||||
void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged)
|
void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
|
||||||
{
|
{
|
||||||
auto observer = const_cast<QPropertyObserver*>(ptr);
|
auto observer = const_cast<QPropertyObserver*>(ptr);
|
||||||
/*
|
/*
|
||||||
@ -557,22 +510,17 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
|
|||||||
observer = next->next.data();
|
observer = next->next.data();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// both evaluateIfDirtyAndReturnTrueIfValueChanged and handlerToCall might modify the list
|
// handlerToCall might modify the list
|
||||||
QPropertyObserverNodeProtector protector(observer);
|
QPropertyObserverNodeProtector protector(observer);
|
||||||
if (!knownToHaveChanged && triggeringBinding) {
|
|
||||||
if (!triggeringBinding->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr))
|
|
||||||
return;
|
|
||||||
knownToHaveChanged = true;
|
|
||||||
}
|
|
||||||
handlerToCall(observer, propertyDataPtr);
|
handlerToCall(observer, propertyDataPtr);
|
||||||
next = protector.next();
|
next = protector.next();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QPropertyObserver::ObserverNotifiesBinding:
|
case QPropertyObserver::ObserverNotifiesBinding:
|
||||||
{
|
{
|
||||||
auto bindingToMarkDirty = observer->bindingToMarkDirty;
|
auto bindingToMarkDirty = observer->binding;
|
||||||
QPropertyObserverNodeProtector protector(observer);
|
QPropertyObserverNodeProtector protector(observer);
|
||||||
bindingToMarkDirty->markDirtyAndNotifyObservers();
|
bindingToMarkDirty->evaluate();
|
||||||
next = protector.next();
|
next = protector.next();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1873,18 +1821,23 @@ QBindingStorage::~QBindingStorage()
|
|||||||
QBindingStoragePrivate(d).destroy();
|
QBindingStoragePrivate(d).destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ### Unused, retained for BC with 6.0
|
||||||
void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const
|
void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const
|
||||||
|
{
|
||||||
|
registerDependency_helper(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(bindingStatus);
|
Q_ASSERT(bindingStatus);
|
||||||
QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
|
QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
|
||||||
auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ bindingStatus->currentlyEvaluatingBinding != nullptr);
|
auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ bindingStatus->currentlyEvaluatingBinding != nullptr);
|
||||||
if (!storage)
|
if (!storage)
|
||||||
return;
|
return;
|
||||||
if (auto *binding = storage->binding())
|
|
||||||
binding->evaluateIfDirtyAndReturnTrueIfValueChanged(const_cast<QUntypedPropertyData *>(data), bindingStatus);
|
|
||||||
storage->registerWithCurrentlyEvaluatingBinding(bindingStatus->currentlyEvaluatingBinding);
|
storage->registerWithCurrentlyEvaluatingBinding(bindingStatus->currentlyEvaluatingBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
|
QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
|
||||||
{
|
{
|
||||||
return QBindingStoragePrivate(d).get(data);
|
return QBindingStoragePrivate(d).get(data);
|
||||||
|
@ -229,7 +229,7 @@ private:
|
|||||||
QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
|
QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
QPropertyBindingPrivate *bindingToMarkDirty = nullptr;
|
QPropertyBindingPrivate *binding = nullptr;
|
||||||
ChangeHandler changeHandler;
|
ChangeHandler changeHandler;
|
||||||
QUntypedPropertyData *aliasedPropertyData;
|
QUntypedPropertyData *aliasedPropertyData;
|
||||||
};
|
};
|
||||||
@ -329,8 +329,6 @@ public:
|
|||||||
|
|
||||||
parameter_type value() const
|
parameter_type value() const
|
||||||
{
|
{
|
||||||
if (d.hasBinding())
|
|
||||||
d.evaluateIfDirty(this);
|
|
||||||
d.registerWithCurrentlyEvaluatingBinding();
|
d.registerWithCurrentlyEvaluatingBinding();
|
||||||
return this->val;
|
return this->val;
|
||||||
}
|
}
|
||||||
@ -389,9 +387,7 @@ public:
|
|||||||
|
|
||||||
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
|
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
|
||||||
{
|
{
|
||||||
QPropertyBinding<T> oldBinding(d.setBinding(newBinding, this));
|
return QPropertyBinding<T>(d.setBinding(newBinding, this));
|
||||||
notify();
|
|
||||||
return oldBinding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setBinding(const QUntypedPropertyBinding &newBinding)
|
bool setBinding(const QUntypedPropertyBinding &newBinding)
|
||||||
@ -402,11 +398,6 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDirty() {
|
|
||||||
d.markDirty();
|
|
||||||
notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef Q_CLANG_QDOC
|
#ifndef Q_CLANG_QDOC
|
||||||
template <typename Functor>
|
template <typename Functor>
|
||||||
QPropertyBinding<T> setBinding(Functor &&f,
|
QPropertyBinding<T> setBinding(Functor &&f,
|
||||||
@ -852,11 +843,11 @@ public:
|
|||||||
|
|
||||||
bool isEmpty() { return !d; }
|
bool isEmpty() { return !d; }
|
||||||
|
|
||||||
void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const
|
void registerDependency(const QUntypedPropertyData *data) const
|
||||||
{
|
{
|
||||||
if (!d && !bindingStatus->currentlyEvaluatingBinding)
|
if (!d && !bindingStatus->currentlyEvaluatingBinding)
|
||||||
return;
|
return;
|
||||||
maybeUpdateBindingAndRegister_helper(data);
|
registerDependency_helper(data);
|
||||||
}
|
}
|
||||||
QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const
|
QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const
|
||||||
{
|
{
|
||||||
@ -864,6 +855,9 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
return bindingData_helper(data);
|
return bindingData_helper(data);
|
||||||
}
|
}
|
||||||
|
// ### Qt 7: remove unused BIC shim
|
||||||
|
void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const { registerDependency(data); }
|
||||||
|
|
||||||
QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create)
|
QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create)
|
||||||
{
|
{
|
||||||
if (!d && !create)
|
if (!d && !create)
|
||||||
@ -871,6 +865,8 @@ public:
|
|||||||
return bindingData_helper(data, create);
|
return bindingData_helper(data, create);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
void registerDependency_helper(const QUntypedPropertyData *data) const;
|
||||||
|
// ### Unused, but keep for BC
|
||||||
void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const;
|
void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const;
|
||||||
QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const;
|
QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const;
|
||||||
QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create);
|
QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create);
|
||||||
@ -923,7 +919,7 @@ public:
|
|||||||
|
|
||||||
parameter_type value() const
|
parameter_type value() const
|
||||||
{
|
{
|
||||||
qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
|
qGetBindingStorage(owner())->registerDependency(this);
|
||||||
return this->val;
|
return this->val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -987,7 +983,6 @@ public:
|
|||||||
{
|
{
|
||||||
QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
|
QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
|
||||||
QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
|
QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
|
||||||
notify(bd);
|
|
||||||
return static_cast<QPropertyBinding<T> &>(oldBinding);
|
return static_cast<QPropertyBinding<T> &>(oldBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1018,15 +1013,6 @@ public:
|
|||||||
return bd && bd->binding() != nullptr;
|
return bd && bd->binding() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDirty() {
|
|
||||||
QBindingStorage *storage = qGetBindingStorage(owner());
|
|
||||||
auto bd = storage->bindingData(this, /*create=*/false);
|
|
||||||
if (bd) { // if we have no BindingData, nobody can listen anyway
|
|
||||||
bd->markDirty();
|
|
||||||
notify(bd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPropertyBinding<T> binding() const
|
QPropertyBinding<T> binding() const
|
||||||
{
|
{
|
||||||
auto *bd = qGetBindingStorage(owner())->bindingData(this);
|
auto *bd = qGetBindingStorage(owner())->bindingData(this);
|
||||||
@ -1130,7 +1116,7 @@ public:
|
|||||||
|
|
||||||
parameter_type value() const
|
parameter_type value() const
|
||||||
{
|
{
|
||||||
qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
|
qGetBindingStorage(owner())->registerDependency(this);
|
||||||
return (owner()->*Getter)();
|
return (owner()->*Getter)();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1176,15 +1162,13 @@ public:
|
|||||||
return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
|
return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDirty() {
|
void notify() {
|
||||||
// computed property can't store a binding, so there's nothing to mark
|
// computed property can't store a binding, so there's nothing to mark
|
||||||
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
||||||
auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
|
auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
|
||||||
if (bd)
|
if (bd)
|
||||||
bindingData().notifyObservers(this);
|
bd->notifyObservers(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
|
#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
|
||||||
|
@ -104,11 +104,11 @@ struct QPropertyObserverPointer
|
|||||||
|
|
||||||
void unlink();
|
void unlink();
|
||||||
|
|
||||||
void setBindingToMarkDirty(QPropertyBindingPrivate *binding);
|
void setBindingToNotify(QPropertyBindingPrivate *binding);
|
||||||
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
|
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
|
||||||
void setAliasedProperty(QUntypedPropertyData *propertyPtr);
|
void setAliasedProperty(QUntypedPropertyData *propertyPtr);
|
||||||
|
|
||||||
void notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged = false);
|
void notify(QUntypedPropertyData *propertyDataPtr);
|
||||||
void observeProperty(QPropertyBindingDataPointer property);
|
void observeProperty(QPropertyBindingDataPointer property);
|
||||||
|
|
||||||
explicit operator bool() const { return ptr != nullptr; }
|
explicit operator bool() const { return ptr != nullptr; }
|
||||||
@ -172,14 +172,11 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// a dependent property has changed, and the binding needs to be reevaluated on access
|
|
||||||
bool dirty = false;
|
|
||||||
// used to detect binding loops for lazy evaluated properties
|
// used to detect binding loops for lazy evaluated properties
|
||||||
bool updating = false;
|
bool updating = false;
|
||||||
bool hasStaticObserver = false;
|
bool hasStaticObserver = false;
|
||||||
bool hasBindingWrapper:1;
|
bool hasBindingWrapper:1;
|
||||||
// used to detect binding loops for eagerly evaluated properties
|
// used to detect binding loops for eagerly evaluated properties
|
||||||
bool eagerlyUpdating:1;
|
|
||||||
bool isQQmlPropertyBinding:1;
|
bool isQQmlPropertyBinding:1;
|
||||||
|
|
||||||
const QtPrivate::BindingFunctionVTable *vtable;
|
const QtPrivate::BindingFunctionVTable *vtable;
|
||||||
@ -230,13 +227,11 @@ public:
|
|||||||
// public because the auto-tests access it, too.
|
// public because the auto-tests access it, too.
|
||||||
size_t dependencyObserverCount = 0;
|
size_t dependencyObserverCount = 0;
|
||||||
|
|
||||||
bool isEagerlyUpdating() {return eagerlyUpdating;}
|
bool isUpdating() {return updating;}
|
||||||
void setEagerlyUpdating(bool b) {eagerlyUpdating = b;}
|
|
||||||
|
|
||||||
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
|
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
|
||||||
const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
|
const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
|
||||||
: hasBindingWrapper(false)
|
: hasBindingWrapper(false)
|
||||||
, eagerlyUpdating(false)
|
|
||||||
, isQQmlPropertyBinding(isQQmlPropertyBinding)
|
, isQQmlPropertyBinding(isQQmlPropertyBinding)
|
||||||
, vtable(vtable)
|
, vtable(vtable)
|
||||||
, location(location)
|
, location(location)
|
||||||
@ -245,7 +240,6 @@ public:
|
|||||||
~QPropertyBindingPrivate();
|
~QPropertyBindingPrivate();
|
||||||
|
|
||||||
|
|
||||||
void setDirty(bool d) { dirty = d; }
|
|
||||||
void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
|
void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
|
||||||
void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
|
void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
|
||||||
{
|
{
|
||||||
@ -312,13 +306,7 @@ public:
|
|||||||
|
|
||||||
void unlinkAndDeref();
|
void unlinkAndDeref();
|
||||||
|
|
||||||
void markDirtyAndNotifyObservers();
|
void evaluate();
|
||||||
bool evaluateIfDirtyAndReturnTrueIfValueChanged(const QUntypedPropertyData *data, QBindingStatus *status = nullptr)
|
|
||||||
{
|
|
||||||
if (!dirty)
|
|
||||||
return false;
|
|
||||||
return evaluateIfDirtyAndReturnTrueIfValueChanged_helper(data, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
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()); }
|
||||||
@ -334,8 +322,6 @@ public:
|
|||||||
clearDependencyObservers();
|
clearDependencyObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool requiresEagerEvaluation() const { return hasBindingWrapper; }
|
|
||||||
|
|
||||||
static QPropertyBindingPrivate *currentlyEvaluatingBinding();
|
static QPropertyBindingPrivate *currentlyEvaluatingBinding();
|
||||||
|
|
||||||
bool hasCustomVTable() const
|
bool hasCustomVTable() const
|
||||||
@ -353,9 +339,6 @@ public:
|
|||||||
delete[] reinterpret_cast<std::byte *>(priv);
|
delete[] reinterpret_cast<std::byte *>(priv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
bool evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status = nullptr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer)
|
inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer)
|
||||||
@ -434,7 +417,7 @@ public:
|
|||||||
const QBindingStorage *storage = qGetBindingStorage(owner());
|
const QBindingStorage *storage = qGetBindingStorage(owner());
|
||||||
// make sure we don't register this binding as a dependency to itself
|
// make sure we don't register this binding as a dependency to itself
|
||||||
if (!inBindingWrapper(storage))
|
if (!inBindingWrapper(storage))
|
||||||
storage->maybeUpdateBindingAndRegister(this);
|
storage->registerDependency(this);
|
||||||
return this->val;
|
return this->val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,15 +494,6 @@ public:
|
|||||||
return bd && bd->binding() != nullptr;
|
return bd && bd->binding() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDirty() {
|
|
||||||
QBindingStorage *storage = qGetBindingStorage(owner());
|
|
||||||
auto *bd = storage->bindingData(this, false);
|
|
||||||
if (bd) {
|
|
||||||
bd->markDirty();
|
|
||||||
notify(bd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeBindingUnlessInWrapper()
|
void removeBindingUnlessInWrapper()
|
||||||
{
|
{
|
||||||
QBindingStorage *storage = qGetBindingStorage(owner());
|
QBindingStorage *storage = qGetBindingStorage(owner());
|
||||||
@ -576,6 +550,7 @@ public:
|
|||||||
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
||||||
return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
|
return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void notify(const QtPrivate::QPropertyBindingData *binding)
|
void notify(const QtPrivate::QPropertyBindingData *binding)
|
||||||
{
|
{
|
||||||
|
@ -249,8 +249,7 @@ public:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void evaluateIfDirty(const QUntypedPropertyData *property) const;
|
void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused
|
||||||
void markDirty();
|
|
||||||
|
|
||||||
void removeBinding()
|
void removeBinding()
|
||||||
{
|
{
|
||||||
|
@ -241,7 +241,7 @@ void QTimer::start()
|
|||||||
stop();
|
stop();
|
||||||
d->nulltimer = (!d->inter && d->single);
|
d->nulltimer = (!d->inter && d->single);
|
||||||
d->id = QObject::startTimer(d->inter, d->type);
|
d->id = QObject::startTimer(d->inter, d->type);
|
||||||
d->isActiveData.markDirty();
|
d->isActiveData.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -278,7 +278,7 @@ void QTimer::stop()
|
|||||||
if (d->id != INV_TIMER) {
|
if (d->id != INV_TIMER) {
|
||||||
QObject::killTimer(d->id);
|
QObject::killTimer(d->id);
|
||||||
d->id = INV_TIMER;
|
d->id = INV_TIMER;
|
||||||
d->isActiveData.markDirty();
|
d->isActiveData.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,9 +763,8 @@ void QTimer::setInterval(int msec)
|
|||||||
// No need to call markDirty() for d->isActiveData here,
|
// No need to call markDirty() for d->isActiveData here,
|
||||||
// as timer state actually does not change
|
// as timer state actually does not change
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intervalChanged)
|
if (intervalChanged)
|
||||||
d->inter.markDirty();
|
d->inter.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
int QTimer::interval() const
|
int QTimer::interval() const
|
||||||
|
@ -95,7 +95,6 @@ private slots:
|
|||||||
void noFakeDependencies();
|
void noFakeDependencies();
|
||||||
|
|
||||||
void bindablePropertyWithInitialization();
|
void bindablePropertyWithInitialization();
|
||||||
void markDirty();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QProperty::functorBinding()
|
void tst_QProperty::functorBinding()
|
||||||
@ -129,8 +128,8 @@ void tst_QProperty::multipleDependencies()
|
|||||||
QProperty<int> sum;
|
QProperty<int> sum;
|
||||||
sum.setBinding([&]() { return firstDependency + secondDependency; });
|
sum.setBinding([&]() { return firstDependency + secondDependency; });
|
||||||
|
|
||||||
QCOMPARE(QPropertyBindingDataPointer::get(firstDependency).observerCount(), 0);
|
QCOMPARE(QPropertyBindingDataPointer::get(firstDependency).observerCount(), 1);
|
||||||
QCOMPARE(QPropertyBindingDataPointer::get(secondDependency).observerCount(), 0);
|
QCOMPARE(QPropertyBindingDataPointer::get(secondDependency).observerCount(), 1);
|
||||||
|
|
||||||
QCOMPARE(sum.value(), int(3));
|
QCOMPARE(sum.value(), int(3));
|
||||||
QCOMPARE(QPropertyBindingDataPointer::get(firstDependency).observerCount(), 1);
|
QCOMPARE(QPropertyBindingDataPointer::get(firstDependency).observerCount(), 1);
|
||||||
@ -484,23 +483,22 @@ class BindingLoopTester : public QObject
|
|||||||
|
|
||||||
void tst_QProperty::bindingLoop()
|
void tst_QProperty::bindingLoop()
|
||||||
{
|
{
|
||||||
QScopedPointer<QProperty<int>> firstProp;
|
QProperty<int> firstProp;
|
||||||
|
|
||||||
QProperty<int> secondProp([&]() -> int {
|
QProperty<int> secondProp([&]() -> int {
|
||||||
return firstProp ? firstProp->value() : 0;
|
return firstProp.value();
|
||||||
});
|
});
|
||||||
|
|
||||||
QProperty<int> thirdProp([&]() -> int {
|
QProperty<int> thirdProp([&]() -> int {
|
||||||
return secondProp.value();
|
return secondProp.value();
|
||||||
});
|
});
|
||||||
|
|
||||||
firstProp.reset(new QProperty<int>());
|
firstProp.setBinding([&]() -> int {
|
||||||
firstProp->setBinding([&]() -> int {
|
return secondProp.value() + thirdProp.value();
|
||||||
return secondProp.value();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
QCOMPARE(thirdProp.value(), 0);
|
thirdProp.setValue(10);
|
||||||
QCOMPARE(secondProp.binding().error().type(), QPropertyBindingError::BindingLoop);
|
QCOMPARE(firstProp.binding().error().type(), QPropertyBindingError::BindingLoop);
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1200,7 +1198,9 @@ void tst_QProperty::qobjectObservers()
|
|||||||
MyQObject object;
|
MyQObject object;
|
||||||
int onValueChangedCalled = 0;
|
int onValueChangedCalled = 0;
|
||||||
{
|
{
|
||||||
auto handler = object.bindableFoo().onValueChanged([&onValueChangedCalled]() { ++onValueChangedCalled;});
|
auto handler = object.bindableFoo().onValueChanged([&onValueChangedCalled]() {
|
||||||
|
++onValueChangedCalled;
|
||||||
|
});
|
||||||
QCOMPARE(onValueChangedCalled, 0);
|
QCOMPARE(onValueChangedCalled, 0);
|
||||||
|
|
||||||
object.setFoo(10);
|
object.setFoo(10);
|
||||||
@ -1606,82 +1606,6 @@ void tst_QProperty::bindablePropertyWithInitialization()
|
|||||||
QCOMPARE(tester.prop3().anotherValue, 20);
|
QCOMPARE(tester.prop3().anotherValue, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MarkDirtyTester : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Q_PROPERTY(int value1 READ value1 WRITE setValue1 BINDABLE bindableValue1)
|
|
||||||
Q_PROPERTY(int value2 READ value2 WRITE setValue1 BINDABLE bindableValue2)
|
|
||||||
Q_PROPERTY(int computed READ computed BINDABLE bindableComputed)
|
|
||||||
|
|
||||||
inline static int staticValue = 0;
|
|
||||||
|
|
||||||
int value1() const {return m_value1;}
|
|
||||||
void setValue1(int val) {m_value1 = val;}
|
|
||||||
QBindable<int> bindableValue1() {return { &m_value1 };}
|
|
||||||
|
|
||||||
int value2() const {return m_value2;}
|
|
||||||
void setValue2(int val) { m_value2.setValue(val); }
|
|
||||||
QBindable<int> bindableValue2() {return { &m_value2 };}
|
|
||||||
|
|
||||||
int computed() const { return staticValue + m_value1; }
|
|
||||||
QBindable<int> bindableComputed() {return {&m_computed};}
|
|
||||||
|
|
||||||
void incrementStaticValue() {
|
|
||||||
++staticValue;
|
|
||||||
m_computed.markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void markValue1Dirty() {
|
|
||||||
m_value1.markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void markValue2Dirty() {
|
|
||||||
m_value2.markDirty();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
Q_OBJECT_BINDABLE_PROPERTY(MarkDirtyTester, int, m_value1, nullptr)
|
|
||||||
Q_OBJECT_COMPAT_PROPERTY(MarkDirtyTester, int, m_value2, &MarkDirtyTester::setValue2)
|
|
||||||
Q_OBJECT_COMPUTED_PROPERTY(MarkDirtyTester, int, m_computed, &MarkDirtyTester::computed)
|
|
||||||
};
|
|
||||||
|
|
||||||
void tst_QProperty::markDirty()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
QProperty<int> testProperty;
|
|
||||||
int changeCounter = 0;
|
|
||||||
auto handler = testProperty.onValueChanged([&](){++changeCounter;});
|
|
||||||
testProperty.markDirty();
|
|
||||||
QCOMPARE(changeCounter, 1);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
MarkDirtyTester dirtyTester;
|
|
||||||
int computedChangeCounter = 0;
|
|
||||||
int value1ChangeCounter = 0;
|
|
||||||
auto handler = dirtyTester.bindableComputed().onValueChanged([&](){
|
|
||||||
computedChangeCounter++;
|
|
||||||
});
|
|
||||||
auto handler2 = dirtyTester.bindableValue1().onValueChanged([&](){
|
|
||||||
value1ChangeCounter++;
|
|
||||||
});
|
|
||||||
dirtyTester.incrementStaticValue();
|
|
||||||
QCOMPARE(computedChangeCounter, 1);
|
|
||||||
QCOMPARE(dirtyTester.computed(), 1);
|
|
||||||
dirtyTester.markValue1Dirty();
|
|
||||||
QCOMPARE(value1ChangeCounter, 1);
|
|
||||||
QCOMPARE(computedChangeCounter, 1);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
MarkDirtyTester dirtyTester;
|
|
||||||
int changeCounter = 0;
|
|
||||||
auto handler = dirtyTester.bindableValue2().onValueChanged([&](){
|
|
||||||
changeCounter++;
|
|
||||||
});
|
|
||||||
dirtyTester.markValue2Dirty();
|
|
||||||
QCOMPARE(changeCounter, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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