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 <david.faure@kdab.com>
(cherry picked from commit 1d799e91082092821a04885bd9d069febefc37da)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Christian Ehrlicher 2024-02-02 21:44:37 +01:00 committed by Qt Cherry-pick Bot
parent 96775c7c11
commit 8501df2c09
2 changed files with 41 additions and 5 deletions

View File

@ -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);

View File

@ -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()