From 2f9c72028d2481f587f378a256654d0a362e3d44 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Fri, 12 Apr 2024 20:28:46 +0200 Subject: [PATCH] QTreeView: optimize Private::intersectedRect() The implementation for QTreeViewPrivate::intersectedRect() calculated the view rect for every single index which is not needed: - when the row is spanned, the first item spans the complete viewport - the x-position of an index in the same column but different row is the same so no need to re-calculate the boundaries again (similar to the implementation in QTableView) This reduces the visualRect() call count from n*m to n+m Task-number: QTBUG-124173 Change-Id: I651b2ff9b6d4e68a82f32b3f5c3c0e746f88ce5e Reviewed-by: Axel Spoerl --- src/widgets/itemviews/qtreeview.cpp | 45 +++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 744f29ca17f..1ae81023c53 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -1412,13 +1412,48 @@ QRect QTreeViewPrivate::intersectedRect(const QRect rect, const QModelIndex &top const auto parentIdx = topLeft.parent(); executePostedLayout(); QRect updateRect; - for (int r = topLeft.row(); r <= bottomRight.row(); ++r) { - if (isRowHidden(model->index(r, 0, parentIdx))) + int left = std::numeric_limits::max(); + int right = std::numeric_limits::min(); + for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { + const auto idxCol0 = model->index(row, 0, parentIdx); + if (isRowHidden(idxCol0)) continue; - for (int c = topLeft.column(); c <= bottomRight.column(); ++c) { - const QModelIndex idx(model->index(r, c, parentIdx)); - updateRect |= visualRect(idx, SingleSection); + QRect rowRect; + if (left != std::numeric_limits::max()) { + // we already know left and right boundary of the rect to update + rowRect = visualRect(idxCol0, FullRow); + if (!rowRect.intersects(rect)) + continue; + rowRect = QRect(left, rowRect.top(), right, rowRect.bottom()); + } else if (!spanningIndexes.isEmpty() && spanningIndexes.contains(idxCol0)) { + // isFirstColumnSpanned re-creates the child index so take a shortcut here + // spans the whole row, therefore ask for FullRow instead for every cell + rowRect = visualRect(idxCol0, FullRow); + if (!rowRect.intersects(rect)) + continue; + } else { + for (int col = topLeft.column(); col <= bottomRight.column(); ++col) { + if (header->isSectionHidden(col)) + continue; + const QModelIndex idx(model->index(row, col, parentIdx)); + const QRect idxRect = visualRect(idx, SingleSection); + if (idxRect.isNull()) + continue; + // early exit when complete row is out of viewport + if (idxRect.top() > rect.bottom() && idxRect.bottom() < rect.top()) + break; + if (!idxRect.intersects(rect)) + continue; + rowRect = rowRect.united(idxRect); + if (rowRect.left() < rect.left() && rowRect.right() > rect.right()) + break; + } + left = std::min(left, rowRect.left()); + right = std::max(right, rowRect.right()); } + updateRect = updateRect.united(rowRect); + if (updateRect.contains(rect)) // already full rect covered? + break; } return rect.intersected(updateRect); }