From 3adfcbf1ed9b63c3ce41d7417658db3836fd3530 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sun, 21 Jan 2018 19:55:40 +0100 Subject: QHeaderView: properly restore section data after layoutChanged() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Richard Moe Gustavsen --- src/widgets/itemviews/qheaderview.cpp | 88 ++++++++++++++++------ src/widgets/itemviews/qheaderview_p.h | 11 +-- .../itemviews/qheaderview/tst_qheaderview.cpp | 13 +++- 3 files changed, 82 insertions(+), 30 deletions(-) diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 0905a20812..e1aec3f4bd 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -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(); - - clear(); - q->initializeSections(); - invalidateCachedSizeHint(); + const auto oldPersistentSections = layoutChangePersistentSections; + layoutChangePersistentSections.clear(); - 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(); + } } /*! diff --git a/src/widgets/itemviews/qheaderview_p.h b/src/widgets/itemviews/qheaderview_p.h index 8fc8b88aa5..c9c2cf8493 100644 --- a/src/widgets/itemviews/qheaderview_p.h +++ b/src/widgets/itemviews/qheaderview_p.h @@ -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 persistentHiddenSections; mutable bool sectionStartposRecalc; int resizeContentsPrecision; // header sections @@ -335,6 +330,11 @@ public: }; QVector sectionItems; + struct LayoutChangeItem { + QPersistentModelIndex index; + SectionItem section; + }; + QVector 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 diff --git a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp index 90019a1798..6dae2cf8e4 100644 --- a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp +++ b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp @@ -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))); -- cgit v1.2.3