QHeaderView: fixing selection of reordered rows and columns

The old code incorrectly calculated visual region for a selection (specified by
QItemSelection object) in case of reordered / swapped rows or columns. It used
the leftmost and rightmost (upmost and downmost for vertical mode) logical
indexes directly. However some middle logical index (in case of reorder) may be
the leftmost or rightmost visual index. In such cases the repainting didn't
work properly. This fix first checks whether reordering / swapping is in use.
If it isn't (ie visual=logical) we use code similar to the old code. Otherwise
the new code scans all selected logical indexes, translates them into visual
ones and gets the correct leftmost and rightmost indexes.

[ChangeLog][QtWidgets][QHeaderView] Fixed a repainting issue when items had been reordered.

Task-number: QTBUG-50171
Change-Id: If6afabebf445cf32a8e0afe262b6ee149e7c4b2b
Reviewed-by: Thorbjørn Lund Martsum <tmartsum@gmail.com>
bb10
Vyacheslav Grigoryev 2016-04-04 01:03:20 +03:00
parent 9def501433
commit 072485048f
2 changed files with 90 additions and 44 deletions

View File

@ -2957,31 +2957,43 @@ QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) c
{
Q_D(const QHeaderView);
const int max = d->modelSectionCount();
if (d->orientation == Qt::Horizontal) {
int left = max;
int right = 0;
int rangeLeft, rangeRight;
int logicalLeft = max;
int logicalRight = 0;
for (int i = 0; i < selection.count(); ++i) {
QItemSelectionRange r = selection.at(i);
if (r.parent().isValid() || !r.isValid())
continue; // we only know about toplevel items and we don't want invalid ranges
// FIXME an item inside the range may be the leftmost or rightmost
rangeLeft = visualIndex(r.left());
if (rangeLeft == -1) // in some cases users may change the selections
continue; // before we have a chance to do the layout
rangeRight = visualIndex(r.right());
if (rangeRight == -1) // in some cases users may change the selections
continue; // before we have a chance to do the layout
if (rangeLeft < left)
left = rangeLeft;
if (rangeRight > right)
right = rangeRight;
if (d->visualIndices.empty()) {
// If no reordered sections, skip redundant visual-to-logical transformations
for (int i = 0; i < selection.count(); ++i) {
const QItemSelectionRange &r = selection.at(i);
if (r.parent().isValid() || !r.isValid())
continue; // we only know about toplevel items and we don't want invalid ranges
if (r.left() < logicalLeft)
logicalLeft = r.left();
if (r.right() > logicalRight)
logicalRight = r.right();
}
} else {
int left = max;
int right = 0;
for (int i = 0; i < selection.count(); ++i) {
const QItemSelectionRange &r = selection.at(i);
if (r.parent().isValid() || !r.isValid())
continue; // we only know about toplevel items and we don't want invalid ranges
for (int k = r.left(); k <= r.right(); ++k) {
int visual = visualIndex(k);
if (visual == -1) // in some cases users may change the selections
continue; // before we have a chance to do the layout
if (visual < left)
left = visual;
if (visual > right)
right = visual;
}
}
logicalLeft = logicalIndex(left);
logicalRight = logicalIndex(right);
}
int logicalLeft = logicalIndex(left);
int logicalRight = logicalIndex(right);
if (logicalLeft < 0 || logicalLeft >= count() ||
logicalRight < 0 || logicalRight >= count())
return QRegion();
@ -2992,32 +3004,46 @@ QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) c
return QRect(leftPos, 0, rightPos - leftPos, height());
}
// orientation() == Qt::Vertical
int top = max;
int bottom = 0;
int rangeTop, rangeBottom;
int logicalTop = max;
int logicalBottom = 0;
for (int i = 0; i < selection.count(); ++i) {
QItemSelectionRange r = selection.at(i);
if (r.parent().isValid() || !r.isValid())
continue; // we only know about toplevel items
// FIXME an item inside the range may be the leftmost or rightmost
rangeTop = visualIndex(r.top());
if (rangeTop == -1) // in some cases users may change the selections
continue; // before we have a chance to do the layout
rangeBottom = visualIndex(r.bottom());
if (rangeBottom == -1) // in some cases users may change the selections
continue; // before we have a chance to do the layout
if (rangeTop < top)
top = rangeTop;
if (rangeBottom > bottom)
bottom = rangeBottom;
if (d->visualIndices.empty()) {
// If no reordered sections, skip redundant visual-to-logical transformations
for (int i = 0; i < selection.count(); ++i) {
const QItemSelectionRange &r = selection.at(i);
if (r.parent().isValid() || !r.isValid())
continue; // we only know about toplevel items and we don't want invalid ranges
if (r.top() < logicalTop)
logicalTop = r.top();
if (r.bottom() > logicalBottom)
logicalBottom = r.bottom();
}
} else {
int top = max;
int bottom = 0;
for (int i = 0; i < selection.count(); ++i) {
const QItemSelectionRange &r = selection.at(i);
if (r.parent().isValid() || !r.isValid())
continue; // we only know about toplevel items and we don't want invalid ranges
for (int k = r.top(); k <= r.bottom(); ++k) {
int visual = visualIndex(k);
if (visual == -1) // in some cases users may change the selections
continue; // before we have a chance to do the layout
if (visual < top)
top = visual;
if (visual > bottom)
bottom = visual;
}
}
logicalTop = logicalIndex(top);
logicalBottom = logicalIndex(bottom);
}
int logicalTop = logicalIndex(top);
int logicalBottom = logicalIndex(bottom);
if (logicalTop == -1 || logicalBottom == -1)
return QRect();
if (logicalTop < 0 || logicalTop >= count() ||
logicalBottom < 0 || logicalBottom >= count())
return QRegion();
int topPos = sectionViewportPosition(logicalTop);
int bottomPos = sectionViewportPosition(logicalBottom) + sectionSize(logicalBottom);

View File

@ -77,6 +77,7 @@ public:
void testEvent();
void testhorizontalOffset();
void testverticalOffset();
void testVisualRegionForSelection();
friend class tst_QHeaderView;
};
@ -211,6 +212,7 @@ private slots:
void QTBUG8650_crashOnInsertSections();
void QTBUG12268_hiddenMovedSectionSorting();
void QTBUG14242_hideSectionAutoSize();
void QTBUG50171_visualRegionForSwappedItems();
void ensureNoIndexAtLength();
void offsetConsistent();
@ -2282,6 +2284,24 @@ void tst_QHeaderView::QTBUG14242_hideSectionAutoSize()
QCOMPARE(calced_length, afterlength);
}
void tst_QHeaderView::QTBUG50171_visualRegionForSwappedItems()
{
protected_QHeaderView headerView(Qt::Horizontal);
QtTestModel model;
model.rows = 2;
model.cols = 3;
headerView.setModel(&model);
headerView.swapSections(1, 2);
headerView.hideSection(0);
headerView.testVisualRegionForSelection();
}
void protected_QHeaderView::testVisualRegionForSelection()
{
QRegion r = visualRegionForSelection(QItemSelection(model()->index(1, 0), model()->index(1, 2)));
QCOMPARE(r.boundingRect().contains(QRect(1, 1, length() - 2, 1)), true);
}
void tst_QHeaderView::ensureNoIndexAtLength()
{
QTableView qtv;