QHeaderView: properly restore section data after layoutChanged()

QHeaderView is doing a complete rebuild of the sections when the layout
changed because everything could have happened. But since layoutChanged
is also called during e.g. sorting, the old data must be restored when
possible.

Task-number: QTBUG-65478
Change-Id: I088d4d843cad362b97df6dc5e0dcb9819b13547f
Reviewed-by: Thorbjørn Lund Martsum <tmartsum@gmail.com>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Christian Ehrlicher 2018-01-21 19:55:40 +01:00
parent 5a05348fb6
commit 3adfcbf1ed
3 changed files with 82 additions and 30 deletions

View File

@ -351,7 +351,7 @@ void QHeaderView::setModel(QAbstractItemModel *model)
if (model == this->model())
return;
Q_D(QHeaderView);
d->persistentHiddenSections.clear();
d->layoutChangePersistentSections.clear();
if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
if (d->orientation == Qt::Horizontal) {
QObject::disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
@ -2072,14 +2072,28 @@ void QHeaderViewPrivate::_q_layoutAboutToBeChanged()
|| model->columnCount(root) == 0)
return;
if (hiddenSectionSize.count() == 0)
return;
layoutChangePersistentSections.clear();
layoutChangePersistentSections.reserve(std::min(10, sectionItems.count()));
// after layoutChanged another section can be last stretched section
if (stretchLastSection) {
const int visual = visualIndex(lastSectionLogicalIdx);
sectionItems[visual].size = lastSectionSize;
}
for (int i = 0; i < sectionItems.size(); ++i) {
const auto &s = sectionItems.at(i);
// only add if the section is not default and not visually moved
if (s.size == defaultSectionSize && !s.isHidden && s.resizeMode == globalResizeMode)
continue;
for (int i = 0; i < sectionItems.count(); ++i)
if (isVisualIndexHidden(i)) // ### note that we are using column or row 0
persistentHiddenSections.append(orientation == Qt::Horizontal
? model->index(0, logicalIndex(i), root)
: model->index(logicalIndex(i), 0, root));
// ### note that we are using column or row 0
layoutChangePersistentSections.append({orientation == Qt::Horizontal
? model->index(0, logicalIndex(i), root)
: model->index(logicalIndex(i), 0, root),
s});
if (layoutChangePersistentSections.size() > 1000)
break;
}
}
void QHeaderViewPrivate::_q_layoutChanged()
@ -2087,25 +2101,57 @@ void QHeaderViewPrivate::_q_layoutChanged()
Q_Q(QHeaderView);
viewport->update();
const auto hiddenSections = persistentHiddenSections;
persistentHiddenSections.clear();
const auto oldPersistentSections = layoutChangePersistentSections;
layoutChangePersistentSections.clear();
clear();
q->initializeSections();
invalidateCachedSizeHint();
if (modelIsEmpty()) {
const int newCount = modelSectionCount();
const int oldCount = sectionItems.size();
if (newCount == 0) {
clear();
if (oldCount != 0)
emit q->sectionCountChanged(oldCount, 0);
return;
}
for (const auto &index : hiddenSections) {
if (index.isValid()) {
const int logical = (orientation == Qt::Horizontal
? index.column()
: index.row());
q->setSectionHidden(logical, true);
// adjust section size
if (newCount != oldCount) {
const int min = qBound(0, oldCount, newCount - 1);
q->initializeSections(min, newCount - 1);
}
// reset sections
sectionItems.fill(SectionItem(defaultSectionSize, globalResizeMode), newCount);
// all hidden sections are in oldPersistentSections
hiddenSectionSize.clear();
for (const auto &item : oldPersistentSections) {
const auto &index = item.index;
if (!index.isValid())
continue;
const int newLogicalIndex = (orientation == Qt::Horizontal
? index.column()
: index.row());
// the new visualIndices are already adjusted / reset by initializeSections()
const int newVisualIndex = visualIndex(newLogicalIndex);
auto &newSection = sectionItems[newVisualIndex];
newSection = item.section;
if (newSection.isHidden) {
// otherwise setSectionHidden will return without doing anything
newSection.isHidden = false;
q->setSectionHidden(newLogicalIndex, true);
}
}
recalcSectionStartPos();
length = headerLength();
if (stretchLastSection) {
// force rebuild of stretched section later on
lastSectionLogicalIdx = -1;
maybeRestorePrevLastSectionAndStretchLast();
}
}
/*!

View File

@ -231,10 +231,6 @@ public:
: model->rowCount(root));
}
inline bool modelIsEmpty() const {
return (model->rowCount(root) == 0 || model->columnCount(root) == 0);
}
inline void doDelayedResizeSections() {
if (!delayedResize.isActive())
delayedResize.start(0, q_func());
@ -304,7 +300,6 @@ public:
QLabel *sectionIndicator;
#endif
QHeaderView::ResizeMode globalResizeMode;
QList<QPersistentModelIndex> persistentHiddenSections;
mutable bool sectionStartposRecalc;
int resizeContentsPrecision;
// header sections
@ -335,6 +330,11 @@ public:
};
QVector<SectionItem> sectionItems;
struct LayoutChangeItem {
QPersistentModelIndex index;
SectionItem section;
};
QVector<LayoutChangeItem> layoutChangePersistentSections;
void createSectionItems(int start, int end, int size, QHeaderView::ResizeMode mode);
void removeSectionsFromSectionItems(int start, int end);
@ -388,6 +388,7 @@ public:
};
Q_DECLARE_TYPEINFO(QHeaderViewPrivate::SectionItem, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(QHeaderViewPrivate::LayoutChangeItem, Q_MOVABLE_TYPE);
QT_END_NAMESPACE

View File

@ -2251,10 +2251,6 @@ void tst_QHeaderView::QTBUG6058_reset()
void tst_QHeaderView::QTBUG7833_sectionClicked()
{
QTableView tv;
QStandardItemModel *sim = new QStandardItemModel(&tv);
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(&tv);
@ -2278,11 +2274,20 @@ void tst_QHeaderView::QTBUG7833_sectionClicked()
tv.horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
tv.setModel(proxyModel);
const int section4Size = tv.horizontalHeader()->sectionSize(4) + 1;
tv.horizontalHeader()->resizeSection(4, section4Size);
tv.setColumnHidden(5, true);
tv.setColumnHidden(6, true);
tv.horizontalHeader()->swapSections(8, 10);
tv.sortByColumn(1, Qt::AscendingOrder);
QCOMPARE(tv.isColumnHidden(5), true);
QCOMPARE(tv.isColumnHidden(6), true);
QCOMPARE(tv.horizontalHeader()->sectionsMoved(), true);
QCOMPARE(tv.horizontalHeader()->logicalIndex(8), 10);
QCOMPARE(tv.horizontalHeader()->logicalIndex(10), 8);
QCOMPARE(tv.horizontalHeader()->sectionSize(4), section4Size);
QSignalSpy clickedSpy(tv.horizontalHeader(), SIGNAL(sectionClicked(int)));
QSignalSpy pressedSpy(tv.horizontalHeader(), SIGNAL(sectionPressed(int)));