summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChristian Ehrlicher <ch.ehrlicher@gmx.de>2019-12-23 15:44:48 +0100
committerChristian Ehrlicher <ch.ehrlicher@gmx.de>2020-03-28 09:03:18 +0100
commit8de62d34321cd827e60b0a7b6e7434070de301ae (patch)
tree048f18f000b6d9235057d800e5f3d79be35a5570 /src
parentb8be5b4002bd6163851bbae397171ebbf632f02f (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.cpp28
-rw-r--r--src/widgets/itemviews/qabstractitemview_p.h1
-rw-r--r--src/widgets/itemviews/qtableview.cpp46
-rw-r--r--src/widgets/itemviews/qtableview_p.h1
-rw-r--r--src/widgets/itemviews/qtreeview.cpp18
-rw-r--r--src/widgets/itemviews/qtreeview_p.h1
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);