diff options
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/doc/src/objectmodel/bindableproperties.qdoc | 16 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty.cpp | 127 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty.h | 42 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty_p.h | 37 | ||||
-rw-r--r-- | src/corelib/kernel/qpropertyprivate.h | 3 | ||||
-rw-r--r-- | src/corelib/kernel/qtimer.cpp | 7 |
6 files changed, 70 insertions, 162 deletions
diff --git a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc index 8b50c65d7c..1bb20935d0 100644 --- a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc +++ b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc @@ -50,8 +50,7 @@ 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 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 - as dirty and evaluating it only when the property's value is requested. For example: + result is applied to the property. For example: \code QProperty<QString> firstname("John"); @@ -63,20 +62,19 @@ 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 - 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 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 - tries to read the name value of the \c fullname property, the expression is - evaluated again, \c firstname() will be called again and return the new value. + 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 new value is returned. Since bindings are C++ functions, they may do anything that's possible in C++. This includes calling other functions. If those functions access values diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index d03991eac1..ad25492283 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -98,43 +98,16 @@ void QPropertyBindingPrivate::unlinkAndDeref() destroyAndFreeMemory(this); } -void QPropertyBindingPrivate::markDirtyAndNotifyObservers() +void QPropertyBindingPrivate::evaluate() { - if (eagerlyUpdating) { + if (updating) { 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) { - error = QPropertyBindingError(QPropertyBindingError::BindingLoop); - if (isQQmlPropertyBinding) - errorCallBack(this); - return false; - } + QScopedValueRollback<bool> updateGuard(updating, true); /* * Evaluating the binding might lead to the binding being broken. This can @@ -145,23 +118,26 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper( * that the object is still alive when updateGuard's dtor runs. */ QPropertyBindingPrivatePtr keepAlive {this}; - QScopedValueRollback<bool> updateGuard(updating, true); - - BindingEvaluationState evaluationFrame(this, status); - bool changed = false; - - Q_ASSERT(propertyDataPtr == data); - QUntypedPropertyData *mutable_data = const_cast<QUntypedPropertyData *>(data); + BindingEvaluationState evaluationFrame(this); + bool changed; + auto bindingFunctor = reinterpret_cast<std::byte *>(this) + + QPropertyBindingPrivate::getSizeEnsuringAlignment(); if (hasBindingWrapper) { - changed = staticBindingWrapper(metaType, mutable_data, {vtable, reinterpret_cast<std::byte *>(this)+QPropertyBindingPrivate::getSizeEnsuringAlignment()}); + changed = staticBindingWrapper(metaType, propertyDataPtr, + {vtable, bindingFunctor}); } 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; - return changed; + // notify dependent bindings and emit changed signal + if (firstObserver) + firstObserver.notify(propertyDataPtr); + if (hasStaticObserver) + staticObserverCallback(propertyDataPtr); } QUntypedPropertyBinding::QUntypedPropertyBinding() = default; @@ -250,7 +226,7 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB if (auto *existingBinding = d.bindingPtr()) { if (existingBinding == newBinding.data()) return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data())); - if (existingBinding->isEagerlyUpdating()) { + if (existingBinding->isUpdating()) { existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")}); 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 |= BindingBit; auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data()); - newBindingRaw->setDirty(true); newBindingRaw->setProperty(propertyDataPtr); if (observer) newBindingRaw->prependObserver(observer); newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback); - if (newBindingRaw->requiresEagerEvaluation()) { - newBindingRaw->setEagerlyUpdating(true); - auto changed = newBindingRaw->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr); - if (changed) - observer.notify(newBindingRaw, propertyDataPtr, /*knownToHaveChanged=*/true); - newBindingRaw->setEagerlyUpdating(false); - } + + newBindingRaw->evaluate(); } else if (observer) { d.setObservers(observer.ptr); } else { @@ -333,13 +303,9 @@ QPropertyBindingPrivate *QPropertyBindingPrivate::currentlyEvaluatingBinding() 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() @@ -370,7 +336,7 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding QPropertyBindingDataPointer d{this}; QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver(); - dependencyObserver.setBindingToMarkDirty(currentState->binding); + dependencyObserver.setBindingToNotify(currentState->binding); dependencyObserver.observeProperty(d); } @@ -378,14 +344,7 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr { QPropertyBindingDataPointer d{this}; if (QPropertyObserverPointer observer = d.firstObserver()) - observer.notify(d.bindingPtr(), propertyDataPtr); -} - -void QPropertyBindingData::markDirty() -{ - QPropertyBindingDataPointer d{this}; - if (auto *binding = d.bindingPtr()) - binding->setDirty(true); + observer.notify(propertyDataPtr); } int QPropertyBindingDataPointer::observerCount() const @@ -425,7 +384,7 @@ QPropertyObserver::~QPropertyObserver() QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept { - bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {}); + binding = std::exchange(other.binding, {}); next = std::exchange(other.next, {}); prev = std::exchange(other.prev, {}); if (next) @@ -441,9 +400,9 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexc QPropertyObserverPointer d{this}; d.unlink(); - bindingToMarkDirty = nullptr; + binding = nullptr; - bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {}); + binding = std::exchange(other.binding, {}); next = std::exchange(other.next, {}); prev = std::exchange(other.prev, {}); if (next) @@ -480,10 +439,10 @@ void QPropertyObserverPointer::setAliasedProperty(QUntypedPropertyData *property ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias); } -void QPropertyObserverPointer::setBindingToMarkDirty(QPropertyBindingPrivate *binding) +void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding) { Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder); - ptr->bindingToMarkDirty = binding; + ptr->binding = binding; ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding); } @@ -516,14 +475,8 @@ struct [[nodiscard]] QPropertyObserverNodeProtector { /*! \internal \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); /* @@ -557,22 +510,17 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding observer = next->next.data(); continue; } - // both evaluateIfDirtyAndReturnTrueIfValueChanged and handlerToCall might modify the list + // handlerToCall might modify the list QPropertyObserverNodeProtector protector(observer); - if (!knownToHaveChanged && triggeringBinding) { - if (!triggeringBinding->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr)) - return; - knownToHaveChanged = true; - } handlerToCall(observer, propertyDataPtr); next = protector.next(); break; } case QPropertyObserver::ObserverNotifiesBinding: { - auto bindingToMarkDirty = observer->bindingToMarkDirty; + auto bindingToMarkDirty = observer->binding; QPropertyObserverNodeProtector protector(observer); - bindingToMarkDirty->markDirtyAndNotifyObservers(); + bindingToMarkDirty->evaluate(); next = protector.next(); break; } @@ -1873,18 +1821,23 @@ QBindingStorage::~QBindingStorage() QBindingStoragePrivate(d).destroy(); } +// ### Unused, retained for BC with 6.0 void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const { + registerDependency_helper(data); +} + +void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const +{ Q_ASSERT(bindingStatus); QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data); auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ bindingStatus->currentlyEvaluatingBinding != nullptr); if (!storage) return; - if (auto *binding = storage->binding()) - binding->evaluateIfDirtyAndReturnTrueIfValueChanged(const_cast<QUntypedPropertyData *>(data), bindingStatus); storage->registerWithCurrentlyEvaluatingBinding(bindingStatus->currentlyEvaluatingBinding); } + QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const { return QBindingStoragePrivate(d).get(data); diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 717c6713d4..40b7a9452d 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -229,7 +229,7 @@ private: QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev; union { - QPropertyBindingPrivate *bindingToMarkDirty = nullptr; + QPropertyBindingPrivate *binding = nullptr; ChangeHandler changeHandler; QUntypedPropertyData *aliasedPropertyData; }; @@ -329,8 +329,6 @@ public: parameter_type value() const { - if (d.hasBinding()) - d.evaluateIfDirty(this); d.registerWithCurrentlyEvaluatingBinding(); return this->val; } @@ -389,9 +387,7 @@ public: QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding) { - QPropertyBinding<T> oldBinding(d.setBinding(newBinding, this)); - notify(); - return oldBinding; + return QPropertyBinding<T>(d.setBinding(newBinding, this)); } bool setBinding(const QUntypedPropertyBinding &newBinding) @@ -402,11 +398,6 @@ public: return true; } - void markDirty() { - d.markDirty(); - notify(); - } - #ifndef Q_CLANG_QDOC template <typename Functor> QPropertyBinding<T> setBinding(Functor &&f, @@ -852,11 +843,11 @@ public: bool isEmpty() { return !d; } - void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const + void registerDependency(const QUntypedPropertyData *data) const { if (!d && !bindingStatus->currentlyEvaluatingBinding) return; - maybeUpdateBindingAndRegister_helper(data); + registerDependency_helper(data); } QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const { @@ -864,6 +855,9 @@ public: return nullptr; 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) { if (!d && !create) @@ -871,6 +865,8 @@ public: return bindingData_helper(data, create); } private: + void registerDependency_helper(const QUntypedPropertyData *data) const; + // ### Unused, but keep for BC void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const; QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const; QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create); @@ -923,7 +919,7 @@ public: parameter_type value() const { - qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this); + qGetBindingStorage(owner())->registerDependency(this); return this->val; } @@ -987,7 +983,6 @@ public: { QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true); QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr)); - notify(bd); return static_cast<QPropertyBinding<T> &>(oldBinding); } @@ -1018,15 +1013,6 @@ public: 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 { auto *bd = qGetBindingStorage(owner())->bindingData(this); @@ -1130,7 +1116,7 @@ public: parameter_type value() const { - qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this); + qGetBindingStorage(owner())->registerDependency(this); return (owner()->*Getter)(); } @@ -1176,15 +1162,13 @@ public: 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 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner())); auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false); if (bd) - bindingData().notifyObservers(this); + bd->notifyObservers(this); } - -private: }; #define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \ diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index d459433528..00978e8ea6 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -104,11 +104,11 @@ struct QPropertyObserverPointer void unlink(); - void setBindingToMarkDirty(QPropertyBindingPrivate *binding); + void setBindingToNotify(QPropertyBindingPrivate *binding); void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler); void setAliasedProperty(QUntypedPropertyData *propertyPtr); - void notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged = false); + void notify(QUntypedPropertyData *propertyDataPtr); void observeProperty(QPropertyBindingDataPointer property); explicit operator bool() const { return ptr != nullptr; } @@ -172,14 +172,11 @@ 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 bool updating = false; bool hasStaticObserver = false; bool hasBindingWrapper:1; // used to detect binding loops for eagerly evaluated properties - bool eagerlyUpdating:1; bool isQQmlPropertyBinding:1; const QtPrivate::BindingFunctionVTable *vtable; @@ -230,13 +227,11 @@ public: // public because the auto-tests access it, too. size_t dependencyObserverCount = 0; - bool isEagerlyUpdating() {return eagerlyUpdating;} - void setEagerlyUpdating(bool b) {eagerlyUpdating = b;} + bool isUpdating() {return updating;} QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable, const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false) : hasBindingWrapper(false) - , eagerlyUpdating(false) , isQQmlPropertyBinding(isQQmlPropertyBinding) , vtable(vtable) , location(location) @@ -245,7 +240,6 @@ public: ~QPropertyBindingPrivate(); - void setDirty(bool d) { dirty = d; } void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; } void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper) { @@ -312,13 +306,7 @@ public: void unlinkAndDeref(); - void markDirtyAndNotifyObservers(); - bool evaluateIfDirtyAndReturnTrueIfValueChanged(const QUntypedPropertyData *data, QBindingStatus *status = nullptr) - { - if (!dirty) - return false; - return evaluateIfDirtyAndReturnTrueIfValueChanged_helper(data, status); - } + void evaluate(); static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding) { return static_cast<QPropertyBindingPrivate *>(binding.d.data()); } @@ -334,8 +322,6 @@ public: clearDependencyObservers(); } - bool requiresEagerEvaluation() const { return hasBindingWrapper; } - static QPropertyBindingPrivate *currentlyEvaluatingBinding(); bool hasCustomVTable() const @@ -353,9 +339,6 @@ public: delete[] reinterpret_cast<std::byte *>(priv); } } - -private: - bool evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status = nullptr); }; inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer) @@ -434,7 +417,7 @@ public: const QBindingStorage *storage = qGetBindingStorage(owner()); // make sure we don't register this binding as a dependency to itself if (!inBindingWrapper(storage)) - storage->maybeUpdateBindingAndRegister(this); + storage->registerDependency(this); return this->val; } @@ -511,15 +494,6 @@ public: 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() { QBindingStorage *storage = qGetBindingStorage(owner()); @@ -576,6 +550,7 @@ public: auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner())); return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true); } + private: void notify(const QtPrivate::QPropertyBindingData *binding) { diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h index 896ad17395..b6d93529d1 100644 --- a/src/corelib/kernel/qpropertyprivate.h +++ b/src/corelib/kernel/qpropertyprivate.h @@ -249,8 +249,7 @@ public: } - void evaluateIfDirty(const QUntypedPropertyData *property) const; - void markDirty(); + void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused void removeBinding() { diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp index 333e6c24ba..10e26fcdef 100644 --- a/src/corelib/kernel/qtimer.cpp +++ b/src/corelib/kernel/qtimer.cpp @@ -241,7 +241,7 @@ void QTimer::start() stop(); d->nulltimer = (!d->inter && d->single); 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) { QObject::killTimer(d->id); 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, // as timer state actually does not change } - if (intervalChanged) - d->inter.markDirty(); + d->inter.notify(); } int QTimer::interval() const |