summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThorbjørn Lund Martsum <[email protected]>2025-05-09 16:23:12 +0200
committerThorbjørn Lund Martsum <[email protected]>2025-05-14 04:46:20 +0000
commitadda61e87b749f0baffd1861f34d201036b97be6 (patch)
treec934acadfabddb7b18fc640b7ac60900b6368359
parent63093b041050cf443d92062fa3bd630b5a67e3b5 (diff)
Fix QHeaderView auto section resize corner case
In some situations where the model is set sufficient late an auto resize would never be applied. See bug report for further details. This is solved by moving to the normal memory model when the global resize mode is changed to resizeToContents or stretch. Fixes: QTBUG-136453 Pick-to: 6.9 Change-Id: Ie0448042a7d23b059eaf7464a662f0f551d89fd9 Reviewed-by: Richard Moe Gustavsen <[email protected]> (cherry picked from commit 0d61e8ec5341b1642b63c34d5b2ce96ada54bd73) Reviewed-by: Thorbjørn Lindeijer <[email protected]>
-rw-r--r--src/widgets/itemviews/qheaderview.cpp8
-rw-r--r--tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp106
2 files changed, 114 insertions, 0 deletions
diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp
index 73c448362ac..3b4f8911e07 100644
--- a/src/widgets/itemviews/qheaderview.cpp
+++ b/src/widgets/itemviews/qheaderview.cpp
@@ -1234,6 +1234,14 @@ void QHeaderView::setSectionResizeMode(ResizeMode mode)
initializeSections();
d->stretchSections = (mode == Stretch ? count() : 0);
d->contentsSections = (mode == ResizeToContents ? count() : 0);
+
+ if (d->noSectionMemoryUsage() && (mode == Stretch || mode == ResizeToContents)) {
+ // Stretch can/could *_maybe_* in the future be used to switch back to low memory mode
+ // (if no sections are moved or swapped), but for now we simply instantly switch
+ // to normal memory usage on auto resize.
+ d->switchToFlexibleModeWithSectionMemoryUsage();
+ }
+
d->setGlobalHeaderResizeMode(mode);
if (d->hasAutoResizeSections())
d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
diff --git a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp
index 230ed47b39f..f1a54a27f8a 100644
--- a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp
+++ b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp
@@ -11,6 +11,7 @@
#include <QTableView>
#include <QTest>
#include <QTreeWidget>
+#include <QStyledItemDelegate>
#include <QtWidgets/private/qheaderview_p.h>
#include <QtWidgets/private/qapplication_p.h>
@@ -237,6 +238,7 @@ private slots:
void normalMemoryUsageOnHide();
void storeRestoreLowMemoryMode();
void setSectionResizeModeWithSectionWillTakeMemory();
+ void setModelWithAutoSizeWillSwitchToMemoryMode();
void setDefaultSectionSizeRespectsColumnWidth();
@@ -3854,6 +3856,110 @@ void tst_QHeaderView::setSectionResizeModeWithSectionWillTakeMemory()
QVERIFY(!tv2.hasLowMemoryUsage());
}
+class SpecialResizeModeTestDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ SpecialResizeModeTestDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent)
+ {}
+ QSize sizeHint(const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) const override
+ {
+ return QSize(m_cellWidth, m_cellWidth);
+ }
+ int cellWidth() const { return m_cellWidth; }
+ void setCellWidth(int width) { m_cellWidth = width; }
+
+private:
+ int m_cellWidth{100};
+};
+
+class SpecialResizeModeTestModel : public QAbstractTableModel
+{
+public:
+ SpecialResizeModeTestModel(QObject *parent = nullptr) : QAbstractTableModel(parent) {}
+ int rowCount(const QModelIndex & = {}) const override { return 105; }
+ int columnCount(const QModelIndex & = {}) const override { return 15; }
+
+ QVariant data(const QModelIndex &i, int role) const override
+ {
+ return (role == Qt::DisplayRole) ? QString("R: %1, C: %2").arg(i.row()).arg(i.column()) : QVariant();
+ }
+
+ QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, int role = Qt::DisplayRole) const override
+ {
+ return (role == Qt::SizeHintRole) ? QSize(1, 1) : QVariant();
+ }
+};
+
+// Custom table view that sets the cell sizes based on a property
+class SpecialResizeModeTableView : public QTableView
+{
+ Q_OBJECT
+public:
+ SpecialResizeModeTableView(QWidget *parent = nullptr) : QTableView(parent)
+ {
+ QHeaderView *hHeader = horizontalHeader();
+ QHeaderView *vHeader = verticalHeader();
+ // Hide the headers, otherwise it appears their size will be used
+ hHeader->hide();
+ vHeader->hide();
+ hHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
+ vHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
+ hHeader->setMinimumSectionSize(1);
+ vHeader->setMinimumSectionSize(1);
+
+ setItemDelegate(&delegate_);
+ }
+
+ int cellWidth() const { return delegate_.cellWidth(); }
+ void setCellWidth(int width)
+ {
+ delegate_.setCellWidth(width);
+ scheduleDelayedItemsLayout();
+ }
+
+ // The following are overridden for optimization purposes but not relevant to the example
+ int sizeHintForRow(int) const override { return cellWidth(); }
+ int sizeHintForColumn(int) const override { return cellWidth(); }
+
+ QSize sizeHint() const override
+ {
+ return QSize(720, 480); // Fixed size for the example
+ }
+
+private:
+ SpecialResizeModeTestDelegate delegate_;
+};
+
+void tst_QHeaderView::setModelWithAutoSizeWillSwitchToMemoryMode()
+{
+ SpecialResizeModeTableView v;
+ const int defaultWidth = 20;
+
+ QByteArray emptyState = v.horizontalHeader()->saveState();
+ v.horizontalHeader()->setDefaultSectionSize(defaultWidth);
+ v.horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+
+ v.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&v));
+
+ auto *model = new SpecialResizeModeTestModel(&v);
+ v.setModel(model);
+ const int headerLength = v.horizontalHeader()->length();
+ const int unexpectedLength = defaultWidth * model->columnCount();
+ // The length of the header is not the default section size times sections.
+ // If it fails we like to see this.
+ QVERIFY(headerLength != unexpectedLength);
+ // A secondary test is that obviously the header length is the bigger one.
+ QCOMPARE_GT(headerLength, unexpectedLength);
+ // and finally we should have switched memory model.
+ QByteArray nonEmptyState = v.horizontalHeader()->saveState();
+ const int delta = model->columnCount() * 8;
+ // even with delta help the nonEmptyState should now be bigger.
+ QCOMPARE_GT(nonEmptyState.size(), emptyState.size() + delta);
+}
+
void tst_QHeaderView::setDefaultSectionSizeRespectsColumnWidth()
{
QTreeWidget tree;