QProperty: Handle eager binding calling setBinding
When an eager binding triggers a setBinding call, we end up with a special kind of binding loop: setBinding() -> evaluate -> notifyObserver ^ | | / ---------------------------- We now catch set condition, and set the binding status to BindingLoop (with a distinct description). Task-number: QTBUG-87153 Task-number: QTBUG-87733 Pick-to: 6.0 Change-Id: I9f9915797d82eab820fc279baceaf89d7e5a3f4a Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
55245c769b
commit
ddc585b7c7
@ -250,6 +250,10 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
|
||||
if (auto *existingBinding = d.bindingPtr()) {
|
||||
if (existingBinding == newBinding.data())
|
||||
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
||||
if (existingBinding->isEagerlyUpdating()) {
|
||||
existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
|
||||
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
||||
}
|
||||
oldBinding = QPropertyBindingPrivatePtr(existingBinding);
|
||||
observer = static_cast<QPropertyBindingPrivate *>(oldBinding.data())->takeObservers();
|
||||
static_cast<QPropertyBindingPrivate *>(oldBinding.data())->unlinkAndDeref();
|
||||
@ -269,9 +273,11 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
|
||||
newBindingRaw->prependObserver(observer);
|
||||
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
|
||||
if (newBindingRaw->requiresEagerEvaluation()) {
|
||||
newBindingRaw->setEagerlyUpdating(true);
|
||||
auto changed = newBindingRaw->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr);
|
||||
if (changed)
|
||||
observer.notify(newBindingRaw, propertyDataPtr, /*alreadyKnownToHaveChanged=*/true);
|
||||
newBindingRaw->setEagerlyUpdating(false);
|
||||
}
|
||||
} else if (observer) {
|
||||
d.setObservers(observer.ptr);
|
||||
|
@ -226,6 +226,9 @@ public:
|
||||
// public because the auto-tests access it, too.
|
||||
size_t dependencyObserverCount = 0;
|
||||
|
||||
bool isEagerlyUpdating() {return eagerlyUpdating;}
|
||||
void setEagerlyUpdating(bool b) {eagerlyUpdating = b;}
|
||||
|
||||
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
|
||||
const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
|
||||
: hasBindingWrapper(false)
|
||||
|
@ -462,6 +462,7 @@ class BindingLoopTester : public QObject
|
||||
eagerData2.setBinding(Qt::makePropertyBinding([&](){ return eagerData.value() + 1; } ) );
|
||||
i->setValue(42);
|
||||
}
|
||||
BindingLoopTester() {}
|
||||
|
||||
int eagerProp() {return eagerData.value();}
|
||||
void setEagerProp(int i) { eagerData = i; }
|
||||
@ -495,11 +496,21 @@ void tst_QProperty::bindingLoop()
|
||||
QCOMPARE(secondProp.binding().error().type(), QPropertyBindingError::BindingLoop);
|
||||
|
||||
|
||||
QProperty<int> i;
|
||||
BindingLoopTester tester(&i);
|
||||
QCOMPARE(tester.bindableEagerProp().binding().error().type(), QPropertyBindingError::BindingLoop);
|
||||
QEXPECT_FAIL("", "Only the first property in a dependency cycle is set to the error state", Continue);
|
||||
QCOMPARE(tester.bindableEagerProp2().binding().error().type(), QPropertyBindingError::BindingLoop);
|
||||
{
|
||||
QProperty<int> i;
|
||||
BindingLoopTester tester(&i);
|
||||
QCOMPARE(tester.bindableEagerProp().binding().error().type(), QPropertyBindingError::BindingLoop);
|
||||
QCOMPARE(tester.bindableEagerProp2().binding().error().type(), QPropertyBindingError::BindingLoop);
|
||||
}
|
||||
{
|
||||
BindingLoopTester tester;
|
||||
auto handler = tester.bindableEagerProp().onValueChanged([&]() {
|
||||
tester.bindableEagerProp().setBinding([](){return 42;});
|
||||
});
|
||||
tester.bindableEagerProp().setBinding([]() {return 42;});
|
||||
QCOMPARE(tester.bindableEagerProp().binding().error().type(), QPropertyBindingError::BindingLoop);
|
||||
QCOMPARE(tester.bindableEagerProp().binding().error().description(), "Binding set during binding evaluation!");
|
||||
}
|
||||
}
|
||||
|
||||
class ReallocTester : public QObject
|
||||
|
Loading…
x
Reference in New Issue
Block a user