diff options
author | Christian Ehrlicher <ch.ehrlicher@gmx.de> | 2019-12-23 15:44:48 +0100 |
---|---|---|
committer | Christian Ehrlicher <ch.ehrlicher@gmx.de> | 2020-03-28 09:03:18 +0100 |
commit | 8de62d34321cd827e60b0a7b6e7434070de301ae (patch) | |
tree | 048f18f000b6d9235057d800e5f3d79be35a5570 /src | |
parent | b8be5b4002bd6163851bbae397171ebbf632f02f (diff) |
QAbstractItemView::dataChanged(): optimize call to QWidget::update()
When topLeft and bottomRight are different in QAIV::dataChanged(), the
current implementation simply calls QWidget::update() without checking
if the affected cells are visible. This results in a big performance hit
when cells are updated frequently.
Now try to compute the exact update rect by iterating through the
modified indexes.
Fixes: QTBUG-58580
Change-Id: I97de567d494e40ed8cdb1ea1f5b3cf3a2f60455e
Reviewed-by: Samuel Gaist <samuel.gaist@idiap.ch>
Diffstat (limited to 'src')
-rw-r--r-- | src/widgets/itemviews/qabstractitemview.cpp | 28 | ||||
-rw-r--r-- | src/widgets/itemviews/qabstractitemview_p.h | 1 | ||||
-rw-r--r-- | src/widgets/itemviews/qtableview.cpp | 46 | ||||
-rw-r--r-- | src/widgets/itemviews/qtableview_p.h | 1 | ||||
-rw-r--r-- | src/widgets/itemviews/qtreeview.cpp | 18 | ||||
-rw-r--r-- | src/widgets/itemviews/qtreeview_p.h | 1 |
6 files changed, 93 insertions, 2 deletions
diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index ebef36d033..b8c30321ff 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -3335,8 +3335,19 @@ void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelInde } } else { d->updateEditorData(topLeft, bottomRight); - if (isVisible() && !d->delayedPendingLayout) - d->viewport->update(); + if (isVisible() && !d->delayedPendingLayout) { + if (!topLeft.isValid() || + topLeft.parent() != bottomRight.parent() || + topLeft.row() > bottomRight.row() || + topLeft.column() > bottomRight.column()) { + // invalid parameter - call update() to redraw all + d->viewport->update(); + } else { + const QRect updateRect = d->intersectedRect(d->viewport->rect(), topLeft, bottomRight); + if (!updateRect.isEmpty()) + d->viewport->update(updateRect); + } + } } #ifndef QT_NO_ACCESSIBILITY @@ -3620,6 +3631,19 @@ void QAbstractItemViewPrivate::_q_columnsMoved(const QModelIndex &, int, int, co _q_layoutChanged(); } +QRect QAbstractItemViewPrivate::intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const +{ + Q_Q(const QAbstractItemView); + + const auto parentIdx = topLeft.parent(); + QRect updateRect; + for (int r = topLeft.row(); r <= bottomRight.row(); ++r) { + for (int c = topLeft.column(); c <= bottomRight.column(); ++c) + updateRect |= q->visualRect(model->index(r, c, parentIdx)); + } + return rect.intersected(updateRect); +} + /*! This slot is called when the selection is changed. The previous selection (which may be empty), is specified by \a deselected, and the diff --git a/src/widgets/itemviews/qabstractitemview_p.h b/src/widgets/itemviews/qabstractitemview_p.h index fe1c00248f..4b29b68b66 100644 --- a/src/widgets/itemviews/qabstractitemview_p.h +++ b/src/widgets/itemviews/qabstractitemview_p.h @@ -108,6 +108,7 @@ public: virtual void _q_layoutChanged(); virtual void _q_rowsMoved(const QModelIndex &source, int sourceStart, int sourceEnd, const QModelIndex &destination, int destinationStart); virtual void _q_columnsMoved(const QModelIndex &source, int sourceStart, int sourceEnd, const QModelIndex &destination, int destinationStart); + virtual QRect intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const; void _q_headerDataChanged() { doDelayedItemsLayout(); } void _q_scrollerStateChanged(); diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index 81fceba8dc..c3ccfac5be 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -669,6 +669,52 @@ void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const *range = QItemSelectionRange(topLeft, bottomRight); } +QRect QTableViewPrivate::intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const +{ + Q_Q(const QTableView); + + using minMaxPair = std::pair<int, int>; + const auto calcMinMax = [q, rect](QHeaderView *hdr, int startIdx, int endIdx, minMaxPair bounds) -> minMaxPair + { + minMaxPair ret(std::numeric_limits<int>::max(), std::numeric_limits<int>::min()); + if (hdr->sectionsMoved()) { + for (int i = startIdx; i <= endIdx; ++i) { + const int start = hdr->sectionViewportPosition(i); + const int end = start + hdr->sectionSize(i); + ret.first = std::min(start, ret.first); + ret.second = std::max(end, ret.second); + if (ret.first <= bounds.first && ret.second >= bounds.second) + break; + } + } else { + if (q->isRightToLeft() && q->horizontalHeader() == hdr) + std::swap(startIdx, endIdx); + ret.first = hdr->sectionViewportPosition(startIdx); + ret.second = hdr->sectionViewportPosition(endIdx) + + hdr->sectionSize(endIdx); + } + return ret; + }; + + const auto yVals = calcMinMax(verticalHeader, topLeft.row(), bottomRight.row(), + minMaxPair(rect.top(), rect.bottom())); + if (yVals.first == yVals.second) // all affected rows are hidden + return QRect(); + + // short circuit: check if no row is inside rect + const QRect colRect(QPoint(rect.left(), yVals.first), + QPoint(rect.right(), yVals.second)); + const QRect intersected = rect.intersected(colRect); + if (intersected.isNull()) + return QRect(); + + const auto xVals = calcMinMax(horizontalHeader, topLeft.column(), bottomRight.column(), + minMaxPair(rect.left(), rect.right())); + const QRect updateRect(QPoint(xVals.first, yVals.first), + QPoint(xVals.second, yVals.second)); + return rect.intersected(updateRect); +} + /*! \internal Sets the span for the cell at (\a row, \a column). diff --git a/src/widgets/itemviews/qtableview_p.h b/src/widgets/itemviews/qtableview_p.h index 8f174351d2..e17773c4bd 100644 --- a/src/widgets/itemviews/qtableview_p.h +++ b/src/widgets/itemviews/qtableview_p.h @@ -149,6 +149,7 @@ public: } void init(); void trimHiddenSelections(QItemSelectionRange *range) const; + QRect intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const override; inline bool isHidden(int row, int col) const { return verticalHeader->isSectionHidden(row) diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 9aba17be70..a3bebb8f3c 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -1396,6 +1396,24 @@ void QTreeViewPrivate::_q_modelDestroyed() QAbstractItemViewPrivate::_q_modelDestroyed(); } +QRect QTreeViewPrivate::intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const +{ + Q_Q(const QTreeView); + + const auto parentIdx = topLeft.parent(); + executePostedLayout(); + QRect updateRect; + for (int r = topLeft.row(); r <= bottomRight.row(); ++r) { + if (isRowHidden(model->index(r, 0, parentIdx))) + continue; + for (int c = topLeft.column(); c <= bottomRight.column(); ++c) { + const QModelIndex idx(model->index(r, c, parentIdx)); + updateRect |= q->visualRect(idx); + } + } + return rect.intersected(updateRect); +} + /*! \reimp diff --git a/src/widgets/itemviews/qtreeview_p.h b/src/widgets/itemviews/qtreeview_p.h index 836d8f0c2d..1206850b71 100644 --- a/src/widgets/itemviews/qtreeview_p.h +++ b/src/widgets/itemviews/qtreeview_p.h @@ -135,6 +135,7 @@ public: void _q_modelAboutToBeReset(); void _q_sortIndicatorChanged(int column, Qt::SortOrder order); void _q_modelDestroyed() override; + QRect intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const override; void layout(int item, bool recusiveExpanding = false, bool afterIsUninitialized = false); |