From e5c031c1ac542be0d2ac7457e145da011fce0013 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 27 Feb 2024 15:57:42 +0100 Subject: QQuickTableView: remove syncView from destructor If a TableView has a syncView, we need to ensure that it detaches itself from the syncView when it's destroyed. Otherwise the syncView will have a dangling pointer to the deleted TableView afterwards, which can cause a crash. Fixes: QTBUG-120760 Pick-to: 6.7 6.6 6.5 Change-Id: I4c6acfaa0c623ea43ba8b938585fcd9c9247f66c Reviewed-by: Santhosh Kumar --- src/quick/items/qquicktableview.cpp | 8 ++++ .../quick/qquicktableview/data/unloadheader.qml | 43 ++++++++++++++++++++++ .../quick/qquicktableview/tst_qquicktableview.cpp | 16 ++++++++ 3 files changed, 67 insertions(+) create mode 100644 tests/auto/quick/qquicktableview/data/unloadheader.qml diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 92ebe817b6..13b6de40fc 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -5365,6 +5365,14 @@ QQuickTableView::QQuickTableView(QQuickTableViewPrivate &dd, QQuickItem *parent) QQuickTableView::~QQuickTableView() { + Q_D(QQuickTableView); + + if (d->syncView) { + // Remove this TableView as a sync child from the syncView + auto syncView_d = d->syncView->d_func(); + syncView_d->syncChildren.removeOne(this); + syncView_d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly); + } } void QQuickTableView::componentFinalized() diff --git a/tests/auto/quick/qquicktableview/data/unloadheader.qml b/tests/auto/quick/qquicktableview/data/unloadheader.qml new file mode 100644 index 0000000000..35e58d56c3 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/unloadheader.qml @@ -0,0 +1,43 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +import QtQuick +import Qt.labs.qmlmodels + +Item { + width: 640 + height: 480 + + property alias tableView: tableView + property alias loader: verticalHeaderLoader + + Loader { + id: verticalHeaderLoader + x: 0 + width: item ? item.contentWidth : 0 + height: parent.height + sourceComponent: TableView { + model: 5 + syncView: tableView + syncDirection: Qt.Vertical + delegate: Text { + text: index + } + } + } + + TableView { + id: tableView + anchors { + left: verticalHeaderLoader.right + right: parent.right + top: parent.top + bottom: parent.bottom + } + + model: 5 + delegate: Text { + text: index + } + } +} diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 090d546200..7fb5cc21e5 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -168,6 +168,7 @@ private slots: void checkSyncView_pageFlicking(); void checkSyncView_emptyModel(); void checkSyncView_topLeftChanged(); + void checkSyncView_unloadHeader(); void delegateWithRequiredProperties(); void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable(); void replaceModel(); @@ -3292,6 +3293,21 @@ void tst_QQuickTableView::checkSyncView_emptyModel() QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0); } +void tst_QQuickTableView::checkSyncView_unloadHeader() +{ + // Check that we don't get a crash in TableView if one + // of the sync children is suddenly deleted (from e.g a Loader). + LOAD_TABLEVIEW("unloadheader.qml"); + + const auto loader = view->rootObject()->property("loader").value(); + QVERIFY(loader); + QVERIFY(loader->item()); + loader->setActive(false); + QVERIFY(!loader->item()); + gc(*qmlEngine(tableView)); + tableView->forceLayout(); +} + void tst_QQuickTableView::checkSyncView_topLeftChanged() { LOAD_TABLEVIEW("syncviewsimple.qml"); -- cgit v1.2.3