QProperty: Treat change listener modifying its source property as a loop

This is in line with QML where

import QtQuick 2.15
Rectangle {
  width: 100
  height: 100
  color: "red"
  Rectangle {
    id:  inner
    x: 10
    y: x
    width: 50
    height: 50
    onYChanged: { console.log("hey"); inner.x = 10}
    TapHandler {
      onTapped: inner.x = 20
    }
  }
}

results in a binding loop warning when the tap handler triggers. While
the change handler would only run once, we cannot statically determine
if we need to loop once, twice, or if there actually is a diverging
loop. Thus we unconditionally warn about the binding loop and stop
executing the binding.

As a drive-by, verify in the related test that a change handler which
overwrites its properties binding itself removes the binding.

Change-Id: I5372019c2389ab724c49cd7489ecbd3ebced1c69
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Fabian Kosmale 2021-01-25 15:11:53 +01:00
parent 127147feb0
commit af53fb0e00
2 changed files with 6 additions and 4 deletions

View File

@ -100,15 +100,15 @@ void QPropertyBindingPrivate::unlinkAndDeref()
void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
{
if (dirty)
return;
dirty = true;
if (eagerlyUpdating) {
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
if (isQQmlPropertyBinding)
errorCallBack(this);
return;
}
if (dirty)
return;
dirty = true;
eagerlyUpdating = true;
QScopeGuard guard([&](){eagerlyUpdating = false;});

View File

@ -619,7 +619,8 @@ void tst_QProperty::changePropertyFromWithinChangeHandlerThroughDependency()
resetPropertyOnChange = true;
sourceProperty = 42;
QCOMPARE(property.value(), 100);
QVERIFY(property.value() == 100 || property.value() == 42);
QVERIFY(property.binding().error().type() == QPropertyBindingError::BindingLoop);
// changing the property value inside the change handler won't result in the change
// handler being called again.
QCOMPARE(changeHandlerCallCount, 1);
@ -640,6 +641,7 @@ void tst_QProperty::changePropertyFromWithinChangeHandler2()
property = 42;
QCOMPARE(property.value(), 43);
QVERIFY(!property.hasBinding()); // setting the value in the change handler removed the binding
}
void tst_QProperty::settingPropertyValueDoesRemoveBinding()