aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2019-10-28 13:41:11 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2019-10-29 07:00:49 +0000
commit73ad6e87bbeceea5830ab3a6b3dc66fa99e30f45 (patch)
tree6eadb286cccad0ccd736a23b3836a27b3e839200
parent76b86342d47df953009457a554245aace49eecf6 (diff)
QQuickItem::setParentItem: add child earlier
Calling (de)refWindow can trigger QQuickItem::windowChanged, which in turn can call a user defined windowChanged handler. If that signal handler were to call setParentItem, we would encounter an inconsistent state: The item already has its parent set, but that parent would lack the item in its children list (as we would only call refWindow at a later point). Fixes: QTBUG-79573 Fixes: QTBUG-73439 Change-Id: I46adaa54a0521b5cd7f37810b3dd1a206e6a09c6 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/quick/items/qquickitem.cpp21
-rw-r--r--tests/auto/quick/qquickitem/data/setParentInWindowChange.qml12
-rw-r--r--tests/auto/quick/qquickitem/tst_qquickitem.cpp8
3 files changed, 37 insertions, 4 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 396012e1e6..26f02aeed7 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -2748,22 +2748,35 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
}
QQuickWindow *parentWindow = parentItem ? QQuickItemPrivate::get(parentItem)->window : nullptr;
+ bool alreadyAddedChild = false;
if (d->window == parentWindow) {
// Avoid freeing and reallocating resources if the window stays the same.
d->parentItem = parentItem;
} else {
- if (d->window)
- d->derefWindow();
+ auto oldParentItem = d->parentItem;
d->parentItem = parentItem;
+ if (d->parentItem) {
+ QQuickItemPrivate::get(d->parentItem)->addChild(this);
+ alreadyAddedChild = true;
+ }
+ if (d->window) {
+ d->derefWindow();
+ // as we potentially changed d->parentWindow above
+ // the check in derefWindow could not work
+ // thus, we redo it here with the old parent
+ if (!oldParentItem) {
+ QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this);
+ }
+ }
if (parentWindow)
d->refWindow(parentWindow);
}
d->dirty(QQuickItemPrivate::ParentChanged);
- if (d->parentItem)
+ if (d->parentItem && !alreadyAddedChild)
QQuickItemPrivate::get(d->parentItem)->addChild(this);
- else if (d->window)
+ else if (d->window && !alreadyAddedChild)
QQuickWindowPrivate::get(d->window)->parentlessItems.insert(this);
d->setEffectiveVisibleRecur(d->calcEffectiveVisible());
diff --git a/tests/auto/quick/qquickitem/data/setParentInWindowChange.qml b/tests/auto/quick/qquickitem/data/setParentInWindowChange.qml
new file mode 100644
index 0000000000..d68b7adb72
--- /dev/null
+++ b/tests/auto/quick/qquickitem/data/setParentInWindowChange.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.12
+
+Rectangle {
+ width: 800
+ height: 600
+ Item {
+ id: it
+ onWindowChanged: () => it.parent = newParent
+ }
+
+ Item { id: newParent }
+}
diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
index 7e132f97b6..9ce9766c92 100644
--- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
@@ -197,6 +197,8 @@ private slots:
void qtBug60123();
#endif
+ void setParentCalledInOnWindowChanged();
+
private:
enum PaintOrderOp {
@@ -2145,6 +2147,12 @@ void tst_qquickitem::qtBug60123()
activateWindowAndTestPress(&window);
}
#endif
+void tst_qquickitem::setParentCalledInOnWindowChanged()
+{
+ QQuickView view;
+ view.setSource(testFileUrl("setParentInWindowChange.qml"));
+ QVERIFY(ensureFocus(&view)); // should not crash
+}
QTEST_MAIN(tst_qquickitem)