From 8501df2c09195809c1625350e6ba2efbf0bfd0c7 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Fri, 2 Feb 2024 21:44:37 +0100 Subject: [PATCH] QAbstractItemView: close all child editors when parent is removed QAIV::rowsAboutToBeRemoved() closed all child editors when the child was a direct ancestor of the removed index but forgot to check if the index is an indirect ancestor. Some of those editors were removed later in updateEditorGeometries() but not all as the testcase in the bug report showed. Pick-to: 6.6 6.5 Fixes: QTBUG-103476 Change-Id: I90b3d3bff3857aa79f96eecf23d980928693b7bc Reviewed-by: David Faure (cherry picked from commit 1d799e91082092821a04885bd9d069febefc37da) Reviewed-by: Qt Cherry-pick Bot --- src/widgets/itemviews/qabstractitemview.cpp | 13 +++++++- .../tst_qabstractitemview.cpp | 33 ++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index b7c5cdaf7be..d395d242dae 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -3532,10 +3532,21 @@ void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int star } // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes + const auto findDirectChildOf = [](const QModelIndex &parent, QModelIndex child) + { + while (child.isValid()) { + const auto parentIndex = child.parent(); + if (parentIndex == parent) + return child; + child = parentIndex; + } + return QModelIndex(); + }; QEditorIndexHash::iterator i = d->editorIndexHash.begin(); while (i != d->editorIndexHash.end()) { const QModelIndex index = i.value(); - if (index.row() >= start && index.row() <= end && d->model->parent(index) == parent) { + const QModelIndex directChild = findDirectChildOf(parent, index); + if (directChild.isValid() && directChild.row() >= start && directChild.row() <= end) { QWidget *editor = i.key(); QEditorInfo info = d->indexEditorHash.take(index); i = d->editorIndexHash.erase(i); diff --git a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp index 30b9e9c4b28..b5b0d807768 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp +++ b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp @@ -109,6 +109,7 @@ private slots: void QTBUG6407_extendedSelection(); void QTBUG6753_selectOnSelection(); void testDelegateDestroyEditor(); + void testDelegateDestroyEditorChild(); void testClickedSignal(); void testChangeEditorState(); void deselectInSingleSelection(); @@ -176,17 +177,19 @@ public: QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override { openedEditor = new QWidget(parent); + virtualCtorCallCount++; return openedEditor; } void destroyEditor(QWidget *editor, const QModelIndex &) const override { - calledVirtualDtor = true; + virtualDtorCallCount++; editor->deleteLater(); } void changeSize() { size = QSize(50, 50); emit sizeHintChanged(QModelIndex()); } mutable QWidget *openedEditor = nullptr; QSize size; - mutable bool calledVirtualDtor = false; + mutable int virtualCtorCallCount = 0; + mutable int virtualDtorCallCount = 0; }; class DialogItemDelegate : public QStyledItemDelegate @@ -1616,9 +1619,31 @@ void tst_QAbstractItemView::testDelegateDestroyEditor() table.setItemDelegate(&delegate); table.edit(table.model()->index(1, 1)); QAbstractItemView *tv = &table; - QVERIFY(!delegate.calledVirtualDtor); + QCOMPARE(delegate.virtualDtorCallCount, 0); tv->closeEditor(delegate.openedEditor, QAbstractItemDelegate::NoHint); - QVERIFY(delegate.calledVirtualDtor); + QCOMPARE(delegate.virtualDtorCallCount, 1); +} + +void tst_QAbstractItemView::testDelegateDestroyEditorChild() +{ + QTreeWidget tree; + MyAbstractItemDelegate delegate; + tree.setItemDelegate(&delegate); + QTreeWidgetItem *topLevel = new QTreeWidgetItem; + QTreeWidgetItem *levelOne1 = new QTreeWidgetItem(topLevel); + QTreeWidgetItem *levelTwo1 = new QTreeWidgetItem(levelOne1); + QTreeWidgetItem *levelOne2 = new QTreeWidgetItem(topLevel); + QTreeWidgetItem *levelTwo2 = new QTreeWidgetItem(levelOne2); + tree.insertTopLevelItem(0, topLevel); + tree.openPersistentEditor(levelOne1); + tree.openPersistentEditor(levelTwo1); + tree.openPersistentEditor(levelOne2); + tree.openPersistentEditor(levelTwo2); + QCOMPARE(delegate.virtualCtorCallCount, 4); + levelOne1->removeChild(levelTwo1); + QCOMPARE(delegate.virtualDtorCallCount, 1); + topLevel->removeChild(levelOne2); + QCOMPARE(delegate.virtualDtorCallCount, 3); } void tst_QAbstractItemView::testClickedSignal()