Fix the crashes when animated QTreeWidgetItems are hidden

QTreeView's drawTree implementation performs lazy layouting
when calling itemDecorationAt. If animations are enabled,
this can change the list of items, and invalidate the copy
made earlier.

Don't copy the list of items, use a reference instead so that
code iterating over the items later operates on valid data.

Add an assert in the private itemHeight method, it must not
be called with an index that is out of bounds.

Fixes: QTBUG-42469
Change-Id: Ifdb782881447912e00baffd1c407de10a1d8d0d4
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit f140ef04a0c54c2c8a699db33433b8d7235d137c)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Qiang Li 2021-03-03 19:26:27 +08:00 committed by Qt Cherry-pick Bot
parent 46b0f3e170
commit 3a2fe41c9f
2 changed files with 27 additions and 1 deletions

View File

@ -1486,7 +1486,8 @@ void QTreeViewPrivate::adjustViewOptionsForIndex(QStyleOptionViewItem *option, c
void QTreeView::drawTree(QPainter *painter, const QRegion &region) const
{
Q_D(const QTreeView);
const QList<QTreeViewItem> viewItems = d->viewItems;
// d->viewItems changes when posted layouts are executed in itemDecorationAt, so don't copy
const QList<QTreeViewItem> &viewItems = d->viewItems;
QStyleOptionViewItem option;
initViewItemOption(&option);
@ -3484,6 +3485,7 @@ int QTreeViewPrivate::indentationForItem(int item) const
int QTreeViewPrivate::itemHeight(int item) const
{
Q_ASSERT(item < viewItems.count());
if (uniformRowHeights)
return defaultItemHeight;
if (viewItems.isEmpty())

View File

@ -250,6 +250,7 @@ private slots:
void taskQTBUG_7232_AllowUserToControlSingleStep();
void taskQTBUG_8376();
void taskQTBUG_61476();
void taskQTBUG_42469_crash();
void testInitialFocus();
void fetchUntilScreenFull();
void expandAfterTake();
@ -5070,6 +5071,29 @@ void tst_QTreeView::taskQTBUG_61476()
QCOMPARE(lastTopLevel->checkState(), Qt::Checked);
}
void tst_QTreeView::taskQTBUG_42469_crash()
{
QTreeWidget treeWidget;
QTreeWidgetItem *itemOne = new QTreeWidgetItem(QStringList("item1"));
QTreeWidgetItem *itemTwo = new QTreeWidgetItem(QStringList("item2"));
treeWidget.addTopLevelItem(itemOne);
treeWidget.addTopLevelItem(itemTwo);
treeWidget.topLevelItem(1)->addChild(new QTreeWidgetItem(QStringList("child1")));
treeWidget.setAnimated(true);
QObject::connect(&treeWidget, &QTreeWidget::itemExpanded, [&](QTreeWidgetItem* p_item) {
auto tempCount = treeWidget.topLevelItemCount();
for (int j = 0; j < tempCount; ++j)
if (treeWidget.topLevelItem(j) != p_item) {
auto temp = treeWidget.topLevelItem(j);
temp->setHidden(true);
}
});
treeWidget.show();
itemTwo->setExpanded(true);
}
void tst_QTreeView::fetchUntilScreenFull()
{
class TreeModel : public QAbstractItemModel