QTableView: optimize selection when spans are present
If a span is selected, we used to assume that rows and columns might be moved, and made a selection with a range for each cell in the span. This resulted in very large selection models. We already had optimized the case that we didn't have any moved rows or columns, skipping the mapping for the respective (or, usually, both) directions and just making a single range. Apply that same optimization for the case where a span exists and intersects with the selection area. Avoid code duplication by only updating the top/left/bottom/right values depending on the configuration of the table, and then create the selection based on those. Adapt the test case; we now get a single range, even when a span is present, and the range includes all cells included in the span. Add a debug streaming operator in the test case, as there is none implemented in QTableWidgetSelectionRange, to ease debugging. That operator can become a hidden friend of QTableWidgetSelectionRange in a follow-up commit. Pick-to: 6.7 6.5 Fixes: QTBUG-119076 Change-Id: If699463944ca2abaed8f93a2cd3ea30f33b79145 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io> (cherry picked from commit 57d209c4fdea4766f24479a1f20c2975d34a1a0f) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
208ad426b4
commit
38f08eec14
@ -2009,28 +2009,32 @@ void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF
|
||||
if (!d->selectionModel || !tl.isValid() || !br.isValid() || !d->isIndexEnabled(tl) || !d->isIndexEnabled(br))
|
||||
return;
|
||||
|
||||
bool verticalMoved = verticalHeader()->sectionsMoved();
|
||||
bool horizontalMoved = horizontalHeader()->sectionsMoved();
|
||||
const bool verticalMoved = verticalHeader()->sectionsMoved();
|
||||
const bool horizontalMoved = horizontalHeader()->sectionsMoved();
|
||||
|
||||
QItemSelection selection;
|
||||
int top = tl.row();
|
||||
int bottom = br.row();
|
||||
int left = tl.column();
|
||||
int right = br.column();
|
||||
|
||||
if (d->hasSpans()) {
|
||||
bool expanded;
|
||||
// when the current selection does not intersect with any spans of merged cells,
|
||||
// the range of selected cells must be the same as if there were no merged cells
|
||||
bool intersectsSpan = false;
|
||||
int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row()));
|
||||
int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column()));
|
||||
int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row()));
|
||||
int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column()));
|
||||
top = qMin(d->visualRow(tl.row()), d->visualRow(br.row()));
|
||||
left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column()));
|
||||
bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row()));
|
||||
right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column()));
|
||||
do {
|
||||
expanded = false;
|
||||
for (QSpanCollection::Span *it : d->spans.spans) {
|
||||
const QSpanCollection::Span &span = *it;
|
||||
int t = d->visualRow(span.top());
|
||||
int l = d->visualColumn(span.left());
|
||||
int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
|
||||
int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
|
||||
const int t = d->visualRow(span.top());
|
||||
const int l = d->visualColumn(span.left());
|
||||
const int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
|
||||
const int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
|
||||
if ((t > bottom) || (l > right) || (top > b) || (left > r))
|
||||
continue; // no intersect
|
||||
intersectsSpan = true;
|
||||
@ -2054,26 +2058,34 @@ void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF
|
||||
break;
|
||||
}
|
||||
} while (expanded);
|
||||
if (intersectsSpan) {
|
||||
selection.reserve((right - left + 1) * (bottom - top + 1));
|
||||
for (int horizontal = left; horizontal <= right; ++horizontal) {
|
||||
int column = d->logicalColumn(horizontal);
|
||||
for (int vertical = top; vertical <= bottom; ++vertical) {
|
||||
int row = d->logicalRow(vertical);
|
||||
QModelIndex index = d->model->index(row, column, d->root);
|
||||
selection.append(QItemSelectionRange(index));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QItemSelectionRange range(tl, br);
|
||||
if (!range.isEmpty())
|
||||
selection.append(range);
|
||||
if (!intersectsSpan) {
|
||||
top = tl.row();
|
||||
bottom = br.row();
|
||||
left = tl.column();
|
||||
right = br.column();
|
||||
} else if (!verticalMoved && !horizontalMoved) {
|
||||
// top/left/bottom/right are visual, update indexes
|
||||
tl = d->model->index(top, left, d->root);
|
||||
br = d->model->index(bottom, right, d->root);
|
||||
}
|
||||
} else if (verticalMoved && horizontalMoved) {
|
||||
int top = d->visualRow(tl.row());
|
||||
int left = d->visualColumn(tl.column());
|
||||
int bottom = d->visualRow(br.row());
|
||||
int right = d->visualColumn(br.column());
|
||||
top = d->visualRow(tl.row());
|
||||
bottom = d->visualRow(br.row());
|
||||
left = d->visualColumn(tl.column());
|
||||
right = d->visualColumn(br.column());
|
||||
} else if (horizontalMoved) {
|
||||
top = tl.row();
|
||||
bottom = br.row();
|
||||
left = d->visualColumn(tl.column());
|
||||
right = d->visualColumn(br.column());
|
||||
} else if (verticalMoved) {
|
||||
top = d->visualRow(tl.row());
|
||||
bottom = d->visualRow(br.row());
|
||||
left = tl.column();
|
||||
right = br.column();
|
||||
}
|
||||
|
||||
if (horizontalMoved && verticalMoved) {
|
||||
selection.reserve((right - left + 1) * (bottom - top + 1));
|
||||
for (int horizontal = left; horizontal <= right; ++horizontal) {
|
||||
int column = d->logicalColumn(horizontal);
|
||||
@ -2084,23 +2096,19 @@ void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF
|
||||
}
|
||||
}
|
||||
} else if (horizontalMoved) {
|
||||
int left = d->visualColumn(tl.column());
|
||||
int right = d->visualColumn(br.column());
|
||||
selection.reserve(right - left + 1);
|
||||
for (int visual = left; visual <= right; ++visual) {
|
||||
int column = d->logicalColumn(visual);
|
||||
QModelIndex topLeft = d->model->index(tl.row(), column, d->root);
|
||||
QModelIndex bottomRight = d->model->index(br.row(), column, d->root);
|
||||
QModelIndex topLeft = d->model->index(top, column, d->root);
|
||||
QModelIndex bottomRight = d->model->index(bottom, column, d->root);
|
||||
selection.append(QItemSelectionRange(topLeft, bottomRight));
|
||||
}
|
||||
} else if (verticalMoved) {
|
||||
int top = d->visualRow(tl.row());
|
||||
int bottom = d->visualRow(br.row());
|
||||
selection.reserve(bottom - top + 1);
|
||||
for (int visual = top; visual <= bottom; ++visual) {
|
||||
int row = d->logicalRow(visual);
|
||||
QModelIndex topLeft = d->model->index(row, tl.column(), d->root);
|
||||
QModelIndex bottomRight = d->model->index(row, br.column(), d->root);
|
||||
QModelIndex topLeft = d->model->index(row, left, d->root);
|
||||
QModelIndex bottomRight = d->model->index(row, right, d->root);
|
||||
selection.append(QItemSelectionRange(topLeft, bottomRight));
|
||||
}
|
||||
} else { // nothing moved
|
||||
|
@ -9,6 +9,16 @@
|
||||
#include <QTableWidget>
|
||||
#include <QTest>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
QDebug operator<<(QDebug dbg, const QTableWidgetSelectionRange &range)
|
||||
{
|
||||
QDebugStateSaver saver(dbg);
|
||||
dbg.nospace() << "Range(" << range.topRow() << "," << range.leftColumn() << "->"
|
||||
<< range.bottomRow() << "," << range.rightColumn() << ")";
|
||||
return dbg;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class QObjectTableItem : public QObject, public QTableWidgetItem
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -578,7 +588,7 @@ void tst_QTableWidget::selectedSpannedCells_data()
|
||||
|
||||
QTest::newRow("merge 2 cells in column, select those and one more")
|
||||
<< QRect(1, 2, 1, 2) << QPoint(1, 1) << QPoint(1, 3)
|
||||
<< 3 << QTableWidgetSelectionRange(1, 1, 1, 1);
|
||||
<< 1 << QTableWidgetSelectionRange(1, 1, 3, 1);
|
||||
|
||||
QTest::newRow("merge 2 cells in column, select rows above")
|
||||
<< QRect(1, 2, 1, 2) << QPoint(0, 0) << QPoint(3, 1)
|
||||
@ -590,7 +600,7 @@ void tst_QTableWidget::selectedSpannedCells_data()
|
||||
|
||||
QTest::newRow("merge 3 cells in row, select those and one more")
|
||||
<< QRect(0, 1, 3, 1) << QPoint(0, 1) << QPoint(3, 1)
|
||||
<< 4 << QTableWidgetSelectionRange(1, 0, 1, 0);
|
||||
<< 1 << QTableWidgetSelectionRange(1, 0, 1, 3);
|
||||
|
||||
QTest::newRow("merge 3 cells in row, select adjacent to right")
|
||||
<< QRect(0, 1, 3, 1) << QPoint(3, 0) << QPoint(3, 2)
|
||||
|
Loading…
x
Reference in New Issue
Block a user