1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QDesktopWidget>
31#include <QHeaderView>
32#include <QProxyStyle>
33#include <QSignalSpy>
34#include <QSortFilterProxyModel>
35#include <QStandardItemModel>
36#include <QStringListModel>
37#include <QTableView>
38#include <QTest>
39#include <QTreeWidget>
40#include <QtWidgets/private/qheaderview_p.h>
41
42using BoolList = QVector<bool>;
43using IntList = QVector<int>;
44using ResizeVec = QVector<QHeaderView::ResizeMode>;
45
46class TestStyle : public QProxyStyle
47{
48 Q_OBJECT
49public:
50 void drawControl(ControlElement element, const QStyleOption *option,
51 QPainter *painter, const QWidget *widget) const override
52 {
53 if (element == CE_HeaderSection) {
54 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt: option))
55 lastPosition = header->position;
56 }
57 QProxyStyle::drawControl(element, option, painter, widget);
58 }
59 mutable QStyleOptionHeader::SectionPosition lastPosition = QStyleOptionHeader::Beginning;
60};
61
62class protected_QHeaderView : public QHeaderView
63{
64 Q_OBJECT
65public:
66 protected_QHeaderView(Qt::Orientation orientation) : QHeaderView(orientation)
67 {
68 resizeSections();
69 }
70
71 void testEvent();
72 void testhorizontalOffset();
73 void testverticalOffset();
74 void testVisualRegionForSelection();
75 friend class tst_QHeaderView;
76};
77
78class XResetModel : public QStandardItemModel
79{
80 Q_OBJECT
81public:
82 bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
83 {
84 blockSignals(b: true);
85 bool r = QStandardItemModel::removeRows(row, count, parent);
86 blockSignals(b: false);
87 beginResetModel();
88 endResetModel();
89 return r;
90 }
91 bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
92 {
93 blockSignals(b: true);
94 bool r = QStandardItemModel::insertRows(row, count, parent);
95 blockSignals(b: false);
96 beginResetModel();
97 endResetModel();
98 return r;
99 }
100};
101
102class tst_QHeaderView : public QObject
103{
104 Q_OBJECT
105
106public:
107 tst_QHeaderView();
108 static void initMain();
109
110private slots:
111 void initTestCase();
112 void cleanupTestCase();
113 void init();
114 void cleanup();
115 void getSetCheck();
116 void visualIndex();
117
118 void visualIndexAt_data();
119 void visualIndexAt();
120
121 void noModel();
122 void emptyModel();
123 void removeRows();
124 void removeCols();
125
126 void clickable();
127 void movable();
128 void hidden();
129 void stretch();
130
131 void sectionSize_data();
132 void sectionSize();
133
134 void length();
135 void offset();
136 void sectionSizeHint();
137 void logicalIndex();
138 void logicalIndexAt();
139 void swapSections();
140
141 void moveSection_data();
142 void moveSection();
143
144 void resizeMode();
145
146 void resizeSection_data();
147 void resizeSection();
148
149 void resizeAndMoveSection_data();
150 void resizeAndMoveSection();
151 void resizeHiddenSection_data();
152 void resizeHiddenSection();
153 void resizeAndInsertSection_data();
154 void resizeAndInsertSection();
155 void resizeWithResizeModes_data();
156 void resizeWithResizeModes();
157 void moveAndInsertSection_data();
158 void moveAndInsertSection();
159 void highlightSections();
160 void showSortIndicator();
161 void sortIndicatorTracking();
162 void removeAndInsertRow();
163 void unhideSection();
164 void testEvent();
165 void headerDataChanged();
166 void currentChanged();
167 void horizontalOffset();
168 void verticalOffset();
169 void stretchSectionCount();
170 void hiddenSectionCount();
171 void focusPolicy();
172 void moveSectionAndReset();
173 void moveSectionAndRemove();
174 void saveRestore();
175 void restoreQt4State();
176 void restoreToMoreColumns();
177 void restoreToMoreColumnsNoMovedColumns();
178 void restoreBeforeSetModel();
179 void defaultSectionSizeTest();
180 void defaultSectionSizeTestStyles();
181
182 void defaultAlignment_data();
183 void defaultAlignment();
184
185 void globalResizeMode_data();
186 void globalResizeMode();
187
188 void sectionPressedSignal_data();
189 void sectionPressedSignal();
190 void sectionClickedSignal_data() { sectionPressedSignal_data(); }
191 void sectionClickedSignal();
192
193 void defaultSectionSize_data();
194 void defaultSectionSize();
195
196 void oneSectionSize();
197
198 void hideAndInsert_data();
199 void hideAndInsert();
200
201 void removeSection();
202 void preserveHiddenSectionWidth();
203 void invisibleStretchLastSection();
204 void noSectionsWithNegativeSize();
205
206 void emptySectionSpan();
207 void task236450_hidden_data();
208 void task236450_hidden();
209 void task248050_hideRow();
210 void QTBUG6058_reset();
211 void QTBUG7833_sectionClicked();
212 void checkLayoutChangeEmptyModel();
213 void QTBUG8650_crashOnInsertSections();
214 void QTBUG12268_hiddenMovedSectionSorting();
215 void QTBUG14242_hideSectionAutoSize();
216 void QTBUG50171_visualRegionForSwappedItems();
217 void QTBUG53221_assertShiftHiddenRow();
218 void QTBUG75615_sizeHintWithStylesheet();
219 void ensureNoIndexAtLength();
220 void offsetConsistent();
221 void sectionsDontSortWhenNotClickingInThem();
222
223 void initialSortOrderRole();
224
225 void logicalIndexAtTest_data() { setupTestData(); }
226 void visualIndexAtTest_data() { setupTestData(); }
227 void hideShowTest_data() { setupTestData(); }
228 void swapSectionsTest_data() { setupTestData(); }
229 void moveSectionTest_data() { setupTestData(); }
230 void defaultSizeTest_data() { setupTestData(); }
231 void removeTest_data() { setupTestData(true); }
232 void insertTest_data() { setupTestData(true); }
233 void mixedTests_data() { setupTestData(true); }
234 void resizeToContentTest_data() { setupTestData(); }
235 void logicalIndexAtTest();
236 void visualIndexAtTest();
237 void hideShowTest();
238 void swapSectionsTest();
239 void moveSectionTest();
240 void defaultSizeTest();
241 void removeTest();
242 void insertTest();
243 void mixedTests();
244 void resizeToContentTest();
245 void testStreamWithHide();
246 void testStylePosition();
247 void stretchAndRestoreLastSection();
248 void testMinMaxSectionSize_data();
249 void testMinMaxSectionSize();
250 void sizeHintCrash();
251 void testResetCachedSizeHint();
252 void statusTips();
253 void testRemovingColumnsViaLayoutChanged();
254 void testModelMovingColumns();
255
256protected:
257 void setupTestData(bool use_reset_model = false);
258 void additionalInit();
259 void calculateAndCheck(int cppline, const int precalced_comparedata[]);
260 void testMinMaxSectionSize(bool stretchLastSection);
261
262 QWidget *topLevel = nullptr;
263 QHeaderView *view = nullptr;
264 QStandardItemModel *model = nullptr;
265 QTableView *m_tableview = nullptr;
266 bool m_using_reset_model = false;
267 bool m_special_prepare = false;
268 QElapsedTimer timer;
269};
270
271void tst_QHeaderView::initMain()
272{
273#ifdef Q_OS_WIN
274 // Ensure minimum size constraints of framed windows on High DPI screens
275 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
276#endif
277}
278
279class QtTestModel: public QAbstractTableModel
280{
281 Q_OBJECT
282public:
283 QtTestModel(int rc, int cc, QObject *parent = nullptr)
284 : QAbstractTableModel(parent), rows(rc), cols(cc) {}
285 int rowCount(const QModelIndex &) const override { return rows; }
286 int columnCount(const QModelIndex &) const override { return cols; }
287 bool isEditable(const QModelIndex &) const { return true; }
288 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
289 {
290 if (section < 0 || (role != Qt::DisplayRole && role != Qt::StatusTipRole))
291 return QVariant();
292 const int row = (orientation == Qt::Vertical ? section : 0);
293 const int col = (orientation == Qt::Horizontal ? section : 0);
294 if (orientation == Qt::Vertical && row >= rows)
295 return QVariant();
296 if (orientation == Qt::Horizontal && col >= cols)
297 return QVariant();
298 if (m_bMultiLine)
299 return QString("%1\n%1").arg(a: section);
300 return QLatin1Char('[') + QString::number(row) + QLatin1Char(',')
301 + QString::number(col) + QLatin1String(",0] -- Header");
302 }
303 QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override
304 {
305 if (role != Qt::DisplayRole)
306 return QVariant();
307 if (idx.row() < 0 || idx.column() < 0 || idx.column() >= cols || idx.row() >= rows) {
308 wrongIndex = true;
309 qWarning(msg: "Invalid modelIndex [%d,%d,%p]", idx.row(), idx.column(), idx.internalPointer());
310 }
311 return QLatin1Char('[') + QString::number(idx.row()) + QLatin1Char(',')
312 + QString::number(idx.column()) + QLatin1String(",0]");
313 }
314
315 void insertOneColumn(int col)
316 {
317 beginInsertColumns(parent: QModelIndex(), first: col, last: col);
318 --cols;
319 endInsertColumns();
320 }
321
322 void removeFirstRow()
323 {
324 beginRemoveRows(parent: QModelIndex(), first: 0, last: 0);
325 --rows;
326 endRemoveRows();
327 }
328
329 void removeLastRow()
330 {
331 beginRemoveRows(parent: QModelIndex(), first: rows - 1, last: rows - 1);
332 --rows;
333 endRemoveRows();
334 }
335
336 void removeAllRows()
337 {
338 beginRemoveRows(parent: QModelIndex(), first: 0, last: rows - 1);
339 rows = 0;
340 endRemoveRows();
341 }
342
343 void removeOneColumn(int col)
344 {
345 beginRemoveColumns(parent: QModelIndex(), first: col, last: col);
346 --cols;
347 endRemoveColumns();
348 }
349
350 void removeLastColumn()
351 {
352 beginRemoveColumns(parent: QModelIndex(), first: cols - 1, last: cols - 1);
353 --cols;
354 endRemoveColumns();
355 }
356
357 void removeAllColumns()
358 {
359 beginRemoveColumns(parent: QModelIndex(), first: 0, last: cols - 1);
360 cols = 0;
361 endRemoveColumns();
362 }
363
364 void moveColumn(int from, int to)
365 {
366 beginMoveColumns(sourceParent: QModelIndex(), sourceFirst: from, sourceLast: from, destinationParent: QModelIndex(), destinationColumn: to);
367 endMoveColumns();
368 }
369
370 void cleanup()
371 {
372 emit layoutAboutToBeChanged();
373 cols = 3;
374 rows = 3;
375 emit layoutChanged();
376 }
377
378 void emitLayoutChanged()
379 {
380 emit layoutAboutToBeChanged();
381 emit layoutChanged();
382 }
383
384 void emitLayoutChangedWithRemoveFirstRow()
385 {
386 emit layoutAboutToBeChanged();
387 QModelIndexList milNew;
388 const auto milOld = persistentIndexList();
389 milNew.reserve(alloc: milOld.size());
390 for (int i = 0; i < milOld.size(); ++i)
391 milNew += QModelIndex();
392 changePersistentIndexList(from: milOld, to: milNew);
393 emit layoutChanged();
394 }
395
396 void setMultiLineHeader(bool bEnable)
397 {
398 beginResetModel();
399 m_bMultiLine = bEnable;
400 endResetModel();
401 }
402
403 int rows = 0;
404 int cols = 0;
405 mutable bool wrongIndex = false;
406 bool m_bMultiLine = false;
407};
408
409// Testing get/set functions
410void tst_QHeaderView::getSetCheck()
411{
412 protected_QHeaderView obj1(Qt::Horizontal);
413 // bool QHeaderView::highlightSections()
414 // void QHeaderView::setHighlightSections(bool)
415 obj1.setHighlightSections(false);
416 QCOMPARE(false, obj1.highlightSections());
417 obj1.setHighlightSections(true);
418 QCOMPARE(true, obj1.highlightSections());
419
420 // bool QHeaderView::stretchLastSection()
421 // void QHeaderView::setStretchLastSection(bool)
422 obj1.setStretchLastSection(false);
423 QCOMPARE(false, obj1.stretchLastSection());
424 obj1.setStretchLastSection(true);
425 QCOMPARE(true, obj1.stretchLastSection());
426
427 // int QHeaderView::defaultSectionSize()
428 // void QHeaderView::setDefaultSectionSize(int)
429 obj1.setMinimumSectionSize(0);
430 obj1.setDefaultSectionSize(-1);
431 QVERIFY(obj1.defaultSectionSize() >= 0);
432 obj1.setDefaultSectionSize(0);
433 QCOMPARE(0, obj1.defaultSectionSize());
434 obj1.setDefaultSectionSize(99999);
435 QCOMPARE(99999, obj1.defaultSectionSize());
436
437 // int QHeaderView::minimumSectionSize()
438 // void QHeaderView::setMinimumSectionSize(int)
439 obj1.setMinimumSectionSize(-1);
440 QVERIFY(obj1.minimumSectionSize() >= 0);
441 obj1.setMinimumSectionSize(0);
442 QCOMPARE(0, obj1.minimumSectionSize());
443 obj1.setMinimumSectionSize(99999);
444 QCOMPARE(99999, obj1.minimumSectionSize());
445 obj1.setMinimumSectionSize(-1);
446 QVERIFY(obj1.minimumSectionSize() < 100);
447
448 // int QHeaderView::offset()
449 // void QHeaderView::setOffset(int)
450 obj1.setOffset(0);
451 QCOMPARE(0, obj1.offset());
452 obj1.setOffset(std::numeric_limits<int>::min());
453 QCOMPARE(std::numeric_limits<int>::min(), obj1.offset());
454 obj1.setOffset(std::numeric_limits<int>::max());
455 QCOMPARE(std::numeric_limits<int>::max(), obj1.offset());
456
457}
458
459tst_QHeaderView::tst_QHeaderView()
460{
461 qRegisterMetaType<Qt::SortOrder>(typeName: "Qt::SortOrder");
462}
463
464void tst_QHeaderView::initTestCase()
465{
466 m_tableview = new QTableView;
467 qDebug().noquote().nospace()
468 << "default min section size is "
469 << QString::number(m_tableview->verticalHeader()->minimumSectionSize())
470 << QLatin1Char('/')
471 << m_tableview->horizontalHeader()->minimumSectionSize()
472 << " (v/h)";
473}
474
475void tst_QHeaderView::cleanupTestCase()
476{
477 delete m_tableview;
478}
479
480void tst_QHeaderView::init()
481{
482 topLevel = new QWidget;
483 view = new QHeaderView(Qt::Vertical, topLevel);
484 // Some initial value tests before a model is added
485 QCOMPARE(view->length(), 0);
486 QCOMPARE(view->sizeHint(), QSize(0,0));
487 QCOMPARE(view->sectionSizeHint(0), -1);
488 view->setMinimumSectionSize(0); // system default min size can be to large
489
490 /*
491 model = new QStandardItemModel(1, 1);
492 view->setModel(model);
493 //qDebug() << view->count();
494 view->sizeHint();
495 */
496
497 int rows = 4;
498 int columns = 4;
499 model = new QStandardItemModel(rows, columns);
500 /*
501 for (int row = 0; row < rows; ++row) {
502 for (int column = 0; column < columns; ++column) {
503 QModelIndex index = model->index(row, column, QModelIndex());
504 model->setData(index, QVariant((row+1) * (column+1)));
505 }
506 }
507 */
508
509 QSignalSpy spy(view, &QHeaderView::sectionCountChanged);
510 view->setModel(model);
511 QCOMPARE(spy.count(), 1);
512 view->resize(w: 200,h: 200);
513}
514
515void tst_QHeaderView::cleanup()
516{
517 m_tableview->setUpdatesEnabled(true);
518 if (view && view->parent() != m_tableview)
519 delete view;
520 view = nullptr;
521 delete model;
522 model = nullptr;
523 delete topLevel;
524 topLevel = nullptr;
525}
526
527void tst_QHeaderView::noModel()
528{
529 QHeaderView emptyView(Qt::Vertical);
530 QCOMPARE(emptyView.count(), 0);
531}
532
533void tst_QHeaderView::emptyModel()
534{
535 QtTestModel testmodel(0, 0);
536 view->setModel(&testmodel);
537 QVERIFY(!testmodel.wrongIndex);
538 QCOMPARE(view->count(), testmodel.rows);
539 view->setModel(model);
540}
541
542void tst_QHeaderView::removeRows()
543{
544 QtTestModel model(10, 10);
545
546 QHeaderView vertical(Qt::Vertical);
547 QHeaderView horizontal(Qt::Horizontal);
548
549 vertical.setModel(&model);
550 horizontal.setModel(&model);
551 vertical.show();
552 horizontal.show();
553 QCOMPARE(vertical.count(), model.rows);
554 QCOMPARE(horizontal.count(), model.cols);
555
556 model.removeLastRow();
557 QVERIFY(!model.wrongIndex);
558 QCOMPARE(vertical.count(), model.rows);
559 QCOMPARE(horizontal.count(), model.cols);
560
561 model.removeAllRows();
562 QVERIFY(!model.wrongIndex);
563 QCOMPARE(vertical.count(), model.rows);
564 QCOMPARE(horizontal.count(), model.cols);
565}
566
567
568void tst_QHeaderView::removeCols()
569{
570 QtTestModel model(10, 10);
571
572 QHeaderView vertical(Qt::Vertical);
573 QHeaderView horizontal(Qt::Horizontal);
574 vertical.setModel(&model);
575 horizontal.setModel(&model);
576 vertical.show();
577 horizontal.show();
578 QCOMPARE(vertical.count(), model.rows);
579 QCOMPARE(horizontal.count(), model.cols);
580
581 model.removeLastColumn();
582 QVERIFY(!model.wrongIndex);
583 QCOMPARE(vertical.count(), model.rows);
584 QCOMPARE(horizontal.count(), model.cols);
585
586 model.removeAllColumns();
587 QVERIFY(!model.wrongIndex);
588 QCOMPARE(vertical.count(), model.rows);
589 QCOMPARE(horizontal.count(), model.cols);
590}
591
592void tst_QHeaderView::movable()
593{
594 QCOMPARE(view->sectionsMovable(), false);
595 view->setSectionsMovable(false);
596 QCOMPARE(view->sectionsMovable(), false);
597 view->setSectionsMovable(true);
598 QCOMPARE(view->sectionsMovable(), true);
599
600 QCOMPARE(view->isFirstSectionMovable(), true);
601 view->setFirstSectionMovable(false);
602 QCOMPARE(view->isFirstSectionMovable(), false);
603 view->setFirstSectionMovable(true);
604 QCOMPARE(view->isFirstSectionMovable(), true);
605}
606
607void tst_QHeaderView::clickable()
608{
609 QCOMPARE(view->sectionsClickable(), false);
610 view->setSectionsClickable(false);
611 QCOMPARE(view->sectionsClickable(), false);
612 view->setSectionsClickable(true);
613 QCOMPARE(view->sectionsClickable(), true);
614}
615
616void tst_QHeaderView::hidden()
617{
618 //hideSection() & showSection call setSectionHidden
619 // Test bad arguments
620 QCOMPARE(view->isSectionHidden(-1), false);
621 QCOMPARE(view->isSectionHidden(view->count()), false);
622 QCOMPARE(view->isSectionHidden(999999), false);
623
624 view->setSectionHidden(logicalIndex: -1, hide: true);
625 view->setSectionHidden(logicalIndex: view->count(), hide: true);
626 view->setSectionHidden(logicalIndex: 999999, hide: true);
627 view->setSectionHidden(logicalIndex: -1, hide: false);
628 view->setSectionHidden(logicalIndex: view->count(), hide: false);
629 view->setSectionHidden(logicalIndex: 999999, hide: false);
630
631 // Hidden sections shouldn't have visual properties (except position)
632 int pos = view->defaultSectionSize();
633 view->setSectionHidden(logicalIndex: 1, hide: true);
634 QCOMPARE(view->sectionSize(1), 0);
635 QCOMPARE(view->sectionPosition(1), pos);
636 view->resizeSection(logicalIndex: 1, size: 100);
637 QCOMPARE(view->sectionViewportPosition(1), pos);
638 QCOMPARE(view->sectionSize(1), 0);
639 view->setSectionHidden(logicalIndex: 1, hide: false);
640 QCOMPARE(view->isSectionHidden(0), false);
641 QCOMPARE(view->sectionSize(0), view->defaultSectionSize());
642
643 // d->hiddenSectionSize could go out of sync when a new model
644 // was set which has fewer sections than before and some of them
645 // were hidden
646 QStandardItemModel model2(model->rowCount() - 1, model->columnCount());
647
648 for (int i = 0; i < model->rowCount(); ++i)
649 view->setSectionHidden(logicalIndex: i, hide: true);
650 view->setModel(&model2);
651 QVERIFY(view->sectionsHidden());
652 for (int i = 0; i < model2.rowCount(); ++i)
653 QVERIFY(view->isSectionHidden(i));
654
655 view->setModel(model);
656 for (int i = 0; i < model2.rowCount(); ++i)
657 QVERIFY(view->isSectionHidden(i));
658 QCOMPARE(view->isSectionHidden(model->rowCount() - 1), false);
659 for (int i = 0; i < model->rowCount(); ++i)
660 view->setSectionHidden(logicalIndex: i, hide: false);
661}
662
663void tst_QHeaderView::stretch()
664{
665 // Show before resize and setStretchLastSection
666 QSize viewSize(500, 500);
667 view->resize(viewSize);
668 view->setStretchLastSection(true);
669 QCOMPARE(view->stretchLastSection(), true);
670 topLevel->show();
671 QVERIFY(QTest::qWaitForWindowExposed(topLevel));
672 QCOMPARE(view->width(), viewSize.width());
673 QCOMPARE(view->visualIndexAt(view->viewport()->height() - 5), 3);
674
675 view->setSectionHidden(logicalIndex: 3, hide: true);
676 QCOMPARE(view->visualIndexAt(view->viewport()->height() - 5), 2);
677
678 view->setStretchLastSection(false);
679 QCOMPARE(view->stretchLastSection(), false);
680}
681
682void tst_QHeaderView::oneSectionSize()
683{
684 //this ensures that if there is only one section, it gets a correct width (more than 0)
685 QHeaderView view (Qt::Vertical);
686 QtTestModel model(1, 1);
687
688 view.setSectionResizeMode(QHeaderView::Interactive);
689 view.setModel(&model);
690 view.show();
691 QVERIFY(QTest::qWaitForWindowExposed(&view));
692
693 QVERIFY(view.sectionSize(0) > 0);
694}
695
696
697void tst_QHeaderView::sectionSize_data()
698{
699 QTest::addColumn<IntList>(name: "boundsCheck");
700 QTest::addColumn<IntList>(name: "defaultSizes");
701 QTest::addColumn<int>(name: "initialDefaultSize");
702 QTest::addColumn<int>(name: "lastVisibleSectionSize");
703 QTest::addColumn<int>(name: "persistentSectionSize");
704
705 QTest::newRow(dataTag: "data set one")
706 << (IntList{ -1, 0, 4, 9999 })
707 << (IntList{ 10, 30, 30 })
708 << 30
709 << 300
710 << 20;
711}
712
713void tst_QHeaderView::sectionSize()
714{
715#if defined Q_OS_QNX
716 QSKIP("The section size is dpi dependent on QNX");
717#elif defined Q_OS_WINRT
718 QSKIP("Fails on WinRT - QTBUG-68297");
719#endif
720 QFETCH(const IntList, boundsCheck);
721 QFETCH(const IntList, defaultSizes);
722 QFETCH(int, initialDefaultSize);
723 QFETCH(int, lastVisibleSectionSize);
724 QFETCH(int, persistentSectionSize);
725
726 // bounds check
727 for (int val : boundsCheck)
728 view->sectionSize(logicalIndex: val);
729
730 // default size
731 QCOMPARE(view->defaultSectionSize(), initialDefaultSize);
732 for (int def : defaultSizes) {
733 view->setDefaultSectionSize(def);
734 QCOMPARE(view->defaultSectionSize(), def);
735 }
736
737 view->setDefaultSectionSize(initialDefaultSize);
738 for (int s = 0; s < view->count(); ++s)
739 QCOMPARE(view->sectionSize(s), initialDefaultSize);
740 view->doItemsLayout();
741
742 // stretch last section
743 view->setStretchLastSection(true);
744 int lastSection = view->count() - 1;
745
746 //test that when hiding the last column,
747 //resizing the new last visible columns still works
748 view->hideSection(alogicalIndex: lastSection);
749 view->resizeSection(logicalIndex: lastSection - 1, size: lastVisibleSectionSize);
750 QCOMPARE(view->sectionSize(lastSection - 1), lastVisibleSectionSize);
751 view->showSection(alogicalIndex: lastSection);
752
753 // turn off stretching
754 view->setStretchLastSection(false);
755 QCOMPARE(view->sectionSize(lastSection), initialDefaultSize);
756
757 // test persistence
758 int sectionCount = view->count();
759 for (int i = 0; i < sectionCount; ++i)
760 view->resizeSection(logicalIndex: i, size: persistentSectionSize);
761 QtTestModel model(sectionCount * 2, sectionCount * 2);
762 view->setModel(&model);
763 for (int j = 0; j < sectionCount; ++j)
764 QCOMPARE(view->sectionSize(j), persistentSectionSize);
765 for (int k = sectionCount; k < view->count(); ++k)
766 QCOMPARE(view->sectionSize(k), initialDefaultSize);
767}
768
769void tst_QHeaderView::visualIndex()
770{
771 // Test bad arguments
772 QCOMPARE(view->visualIndex(999999), -1);
773 QCOMPARE(view->visualIndex(-1), -1);
774 QCOMPARE(view->visualIndex(1), 1);
775 view->setSectionHidden(logicalIndex: 1, hide: true);
776 QCOMPARE(view->visualIndex(1), 1);
777 QCOMPARE(view->visualIndex(2), 2);
778
779 view->setSectionHidden(logicalIndex: 1, hide: false);
780 QCOMPARE(view->visualIndex(1), 1);
781 QCOMPARE(view->visualIndex(2), 2);
782}
783
784void tst_QHeaderView::visualIndexAt_data()
785{
786 QTest::addColumn<IntList>(name: "hidden");
787 QTest::addColumn<IntList>(name: "from");
788 QTest::addColumn<IntList>(name: "to");
789 QTest::addColumn<IntList>(name: "coordinate");
790 QTest::addColumn<IntList>(name: "visual");
791
792 const IntList coordinateList{ -1, 0, 31, 91, 99999 };
793
794 QTest::newRow(dataTag: "no hidden, no moved sections")
795 << IntList()
796 << IntList()
797 << IntList()
798 << coordinateList
799 << (IntList{ -1, 0, 1, 3, -1 });
800
801 QTest::newRow(dataTag: "no hidden, moved sections")
802 << IntList()
803 << (IntList{ 0 })
804 << (IntList{ 1 })
805 << coordinateList
806 << (IntList{ -1, 0, 1, 3, -1 });
807
808 QTest::newRow(dataTag: "hidden, no moved sections")
809 << (IntList{ 0 })
810 << IntList()
811 << IntList()
812 << coordinateList
813 << (IntList{ -1, 1, 2, 3, -1 });
814}
815
816void tst_QHeaderView::visualIndexAt()
817{
818#if defined Q_OS_QNX
819 QSKIP("The section size is dpi dependent on QNX");
820#elif defined Q_OS_WINRT
821 QSKIP("Fails on WinRT - QTBUG-68297");
822#endif
823 QFETCH(const IntList, hidden);
824 QFETCH(const IntList, from);
825 QFETCH(const IntList, to);
826 QFETCH(const IntList, coordinate);
827 QFETCH(const IntList, visual);
828
829 view->setStretchLastSection(true);
830 topLevel->show();
831 QVERIFY(QTest::qWaitForWindowExposed(topLevel));
832
833 for (int i : hidden)
834 view->setSectionHidden(logicalIndex: i, hide: true);
835
836 for (int j = 0; j < from.count(); ++j)
837 view->moveSection(from: from.at(i: j), to: to.at(i: j));
838
839 for (int k = 0; k < coordinate.count(); ++k)
840 QTRY_COMPARE(view->visualIndexAt(coordinate.at(k)), visual.at(k));
841}
842
843void tst_QHeaderView::length()
844{
845 view->setStretchLastSection(true);
846 topLevel->show();
847 QVERIFY(QTest::qWaitForWindowExposed(topLevel));
848
849 //minimumSectionSize should be the size of the last section of the widget is not tall enough
850 int length = view->minimumSectionSize();
851 for (int i = 0; i < view->count() - 1; i++)
852 length += view->sectionSize(logicalIndex: i);
853
854 length = qMax(a: length, b: view->viewport()->height());
855 QCOMPARE(length, view->length());
856
857 view->setStretchLastSection(false);
858 topLevel->show();
859 QVERIFY(QTest::qWaitForWindowExposed(topLevel));
860
861 QVERIFY(length != view->length());
862
863 // layoutChanged might mean rows have been removed
864 QtTestModel model(10, 10);
865 view->setModel(&model);
866 int oldLength = view->length();
867 model.cleanup();
868 QCOMPARE(model.rows, view->count());
869 QVERIFY(oldLength != view->length());
870}
871
872void tst_QHeaderView::offset()
873{
874 QCOMPARE(view->offset(), 0);
875 view->setOffset(10);
876 QCOMPARE(view->offset(), 10);
877 view->setOffset(0);
878 QCOMPARE(view->offset(), 0);
879
880 // Test odd arguments
881 view->setOffset(-1);
882}
883
884void tst_QHeaderView::sectionSizeHint()
885{
886 QCOMPARE(view->sectionSizeHint(-1), -1);
887 QCOMPARE(view->sectionSizeHint(99999), -1);
888 QVERIFY(view->sectionSizeHint(0) >= 0);
889}
890
891void tst_QHeaderView::logicalIndex()
892{
893 // Test bad arguments
894 QCOMPARE(view->logicalIndex(-1), -1);
895 QCOMPARE(view->logicalIndex(99999), -1);
896}
897
898void tst_QHeaderView::logicalIndexAt()
899{
900 // Test bad arguments
901 view->logicalIndexAt(position: -1);
902 view->logicalIndexAt(position: 99999);
903 QCOMPARE(view->logicalIndexAt(0), 0);
904 QCOMPARE(view->logicalIndexAt(1), 0);
905
906 topLevel->show();
907 QVERIFY(QTest::qWaitForWindowExposed(topLevel));
908 view->setStretchLastSection(true);
909 // First item
910 QCOMPARE(view->logicalIndexAt(0), 0);
911 QCOMPARE(view->logicalIndexAt(view->sectionSize(0)-1), 0);
912 QCOMPARE(view->logicalIndexAt(view->sectionSize(0)+1), 1);
913 // Last item
914 int last = view->length() - 1;//view->viewport()->height() - 10;
915 QCOMPARE(view->logicalIndexAt(last), 3);
916 // Not in widget
917 int outofbounds = view->length() + 1;//view->viewport()->height() + 1;
918 QCOMPARE(view->logicalIndexAt(outofbounds), -1);
919
920 view->moveSection(from: 0,to: 1);
921 // First item
922 QCOMPARE(view->logicalIndexAt(0), 1);
923 QCOMPARE(view->logicalIndexAt(view->sectionSize(0)-1), 1);
924 QCOMPARE(view->logicalIndexAt(view->sectionSize(0)+1), 0);
925 // Last item
926 QCOMPARE(view->logicalIndexAt(last), 3);
927 view->moveSection(from: 1,to: 0);
928
929}
930
931void tst_QHeaderView::swapSections()
932{
933 view->swapSections(first: -1, second: 1);
934 view->swapSections(first: 99999, second: 1);
935 view->swapSections(first: 1, second: -1);
936 view->swapSections(first: 1, second: 99999);
937
938 IntList logical{ 0, 1, 2, 3 };
939
940 QSignalSpy spy1(view, &QHeaderView::sectionMoved);
941
942 QCOMPARE(view->sectionsMoved(), false);
943 view->swapSections(first: 1, second: 1);
944 QCOMPARE(view->sectionsMoved(), false);
945 view->swapSections(first: 1, second: 2);
946 QCOMPARE(view->sectionsMoved(), true);
947 view->swapSections(first: 2, second: 1);
948 QCOMPARE(view->sectionsMoved(), true);
949 for (int i = 0; i < view->count(); ++i)
950 QCOMPARE(view->logicalIndex(i), logical.at(i));
951 QCOMPARE(spy1.count(), 4);
952
953 logical = { 3, 1, 2, 0 };
954 view->swapSections(first: 3, second: 0);
955 QCOMPARE(view->sectionsMoved(), true);
956 for (int j = 0; j < view->count(); ++j)
957 QCOMPARE(view->logicalIndex(j), logical.at(j));
958 QCOMPARE(spy1.count(), 6);
959}
960
961void tst_QHeaderView::moveSection_data()
962{
963 QTest::addColumn<IntList>(name: "hidden");
964 QTest::addColumn<IntList>(name: "from");
965 QTest::addColumn<IntList>(name: "to");
966 QTest::addColumn<BoolList>(name: "moved");
967 QTest::addColumn<IntList>(name: "logical");
968 QTest::addColumn<int>(name: "count");
969
970 QTest::newRow(dataTag: "bad args, no hidden")
971 << IntList()
972 << (IntList{ -1, 1, 99999, 1 })
973 << (IntList{ 1, -1, 1, 99999 })
974 << (BoolList{ false, false, false, false })
975 << (IntList{ 0, 1, 2, 3 })
976 << 0;
977
978 QTest::newRow(dataTag: "good args, no hidden")
979 << IntList()
980 << (IntList{ 1, 1, 2, 1 })
981 << (IntList{ 1, 2, 1, 2 })
982 << (BoolList{ false, true, true, true })
983 << (IntList{ 0, 2, 1, 3 })
984 << 3;
985
986 QTest::newRow(dataTag: "hidden sections")
987 << (IntList{ 0, 3 })
988 << (IntList{ 1, 1, 2, 1 })
989 << (IntList{ 1, 2, 1, 2 })
990 << (BoolList{ false, true, true, true })
991 << (IntList{ 0, 2, 1, 3 })
992 << 3;
993}
994
995void tst_QHeaderView::moveSection()
996{
997 QFETCH(const IntList, hidden);
998 QFETCH(const IntList, from);
999 QFETCH(const IntList, to);
1000 QFETCH(const BoolList, moved);
1001 QFETCH(const IntList, logical);
1002 QFETCH(int, count);
1003
1004 QCOMPARE(from.count(), to.count());
1005 QCOMPARE(from.count(), moved.count());
1006 QCOMPARE(view->count(), logical.count());
1007
1008 QSignalSpy spy1(view, &QHeaderView::sectionMoved);
1009 QCOMPARE(view->sectionsMoved(), false);
1010
1011 for (int h : hidden)
1012 view->setSectionHidden(logicalIndex: h, hide: true);
1013
1014 for (int i = 0; i < from.count(); ++i) {
1015 view->moveSection(from: from.at(i), to: to.at(i));
1016 QCOMPARE(view->sectionsMoved(), moved.at(i));
1017 }
1018
1019 for (int j = 0; j < view->count(); ++j)
1020 QCOMPARE(view->logicalIndex(j), logical.at(j));
1021
1022 QCOMPARE(spy1.count(), count);
1023}
1024
1025void tst_QHeaderView::resizeAndMoveSection_data()
1026{
1027 QTest::addColumn<IntList>(name: "logicalIndexes");
1028 QTest::addColumn<IntList>(name: "sizes");
1029 QTest::addColumn<int>(name: "logicalFrom");
1030 QTest::addColumn<int>(name: "logicalTo");
1031
1032 QTest::newRow(dataTag: "resizeAndMove-1")
1033 << (IntList{ 0, 1 })
1034 << (IntList{ 20, 40 })
1035 << 0 << 1;
1036
1037 QTest::newRow(dataTag: "resizeAndMove-2")
1038 << (IntList{ 0, 1, 2, 3 })
1039 << (IntList{ 20, 60, 10, 80 })
1040 << 0 << 2;
1041
1042 QTest::newRow(dataTag: "resizeAndMove-3")
1043 << (IntList{ 0, 1, 2, 3 })
1044 << (IntList{ 100, 60, 40, 10 })
1045 << 0 << 3;
1046
1047 QTest::newRow(dataTag: "resizeAndMove-4")
1048 << (IntList{ 0, 1, 2, 3 })
1049 << (IntList{ 10, 40, 80, 30 })
1050 << 1 << 2;
1051
1052 QTest::newRow(dataTag: "resizeAndMove-5")
1053 << (IntList{ 2, 3 })
1054 << (IntList{ 100, 200})
1055 << 3 << 2;
1056}
1057
1058void tst_QHeaderView::resizeAndMoveSection()
1059{
1060 QFETCH(const IntList, logicalIndexes);
1061 QFETCH(const IntList, sizes);
1062 QFETCH(int, logicalFrom);
1063 QFETCH(int, logicalTo);
1064
1065 // Save old visual indexes and sizes
1066 IntList oldVisualIndexes;
1067 IntList oldSizes;
1068 for (int logical : logicalIndexes) {
1069 oldVisualIndexes.append(t: view->visualIndex(logicalIndex: logical));
1070 oldSizes.append(t: view->sectionSize(logicalIndex: logical));
1071 }
1072
1073 // Resize sections
1074 for (int i = 0; i < logicalIndexes.size(); ++i) {
1075 int logical = logicalIndexes.at(i);
1076 view->resizeSection(logicalIndex: logical, size: sizes.at(i));
1077 }
1078
1079 // Move sections
1080 int visualFrom = view->visualIndex(logicalIndex: logicalFrom);
1081 int visualTo = view->visualIndex(logicalIndex: logicalTo);
1082 view->moveSection(from: visualFrom, to: visualTo);
1083 QCOMPARE(view->visualIndex(logicalFrom), visualTo);
1084
1085 // Check that sizes are still correct
1086 for (int i = 0; i < logicalIndexes.size(); ++i) {
1087 int logical = logicalIndexes.at(i);
1088 QCOMPARE(view->sectionSize(logical), sizes.at(i));
1089 }
1090
1091 // Move sections back
1092 view->moveSection(from: visualTo, to: visualFrom);
1093
1094 // Check that sizes are still correct
1095 for (int i = 0; i < logicalIndexes.size(); ++i) {
1096 int logical = logicalIndexes.at(i);
1097 QCOMPARE(view->sectionSize(logical), sizes.at(i));
1098 }
1099
1100 // Put everything back as it was
1101 for (int i = 0; i < logicalIndexes.size(); ++i) {
1102 int logical = logicalIndexes.at(i);
1103 view->resizeSection(logicalIndex: logical, size: oldSizes.at(i));
1104 QCOMPARE(view->visualIndex(logical), oldVisualIndexes.at(i));
1105 }
1106}
1107
1108void tst_QHeaderView::resizeHiddenSection_data()
1109{
1110 QTest::addColumn<int>(name: "section");
1111 QTest::addColumn<int>(name: "initialSize");
1112 QTest::addColumn<int>(name: "finalSize");
1113
1114 QTest::newRow(dataTag: "section 0 resize 50 to 20")
1115 << 0 << 50 << 20;
1116
1117 QTest::newRow(dataTag: "section 1 resize 50 to 20")
1118 << 1 << 50 << 20;
1119
1120 QTest::newRow(dataTag: "section 2 resize 50 to 20")
1121 << 2 << 50 << 20;
1122
1123 QTest::newRow(dataTag: "section 3 resize 50 to 20")
1124 << 3 << 50 << 20;
1125}
1126
1127void tst_QHeaderView::resizeHiddenSection()
1128{
1129 QFETCH(int, section);
1130 QFETCH(int, initialSize);
1131 QFETCH(int, finalSize);
1132
1133 view->resizeSection(logicalIndex: section, size: initialSize);
1134 view->setSectionHidden(logicalIndex: section, hide: true);
1135 QCOMPARE(view->sectionSize(section), 0);
1136
1137 view->resizeSection(logicalIndex: section, size: finalSize);
1138 QCOMPARE(view->sectionSize(section), 0);
1139
1140 view->setSectionHidden(logicalIndex: section, hide: false);
1141 QCOMPARE(view->sectionSize(section), finalSize);
1142}
1143
1144void tst_QHeaderView::resizeAndInsertSection_data()
1145{
1146 QTest::addColumn<int>(name: "section");
1147 QTest::addColumn<int>(name: "size");
1148 QTest::addColumn<int>(name: "insert");
1149 QTest::addColumn<int>(name: "compare");
1150 QTest::addColumn<int>(name: "expected");
1151
1152 QTest::newRow(dataTag: "section 0 size 50 insert 0")
1153 << 0 << 50 << 0 << 1 << 50;
1154
1155 QTest::newRow(dataTag: "section 1 size 50 insert 1")
1156 << 0 << 50 << 1 << 0 << 50;
1157
1158 QTest::newRow(dataTag: "section 1 size 50 insert 0")
1159 << 1 << 50 << 0 << 2 << 50;
1160
1161}
1162
1163void tst_QHeaderView::resizeAndInsertSection()
1164{
1165 QFETCH(int, section);
1166 QFETCH(int, size);
1167 QFETCH(int, insert);
1168 QFETCH(int, compare);
1169 QFETCH(int, expected);
1170
1171 view->setStretchLastSection(false);
1172
1173 view->resizeSection(logicalIndex: section, size);
1174 QCOMPARE(view->sectionSize(section), size);
1175
1176 model->insertRow(arow: insert);
1177
1178 QCOMPARE(view->sectionSize(compare), expected);
1179}
1180
1181void tst_QHeaderView::resizeWithResizeModes_data()
1182{
1183 QTest::addColumn<int>(name: "size");
1184 QTest::addColumn<IntList>(name: "sections");
1185 QTest::addColumn<ResizeVec>(name: "modes");
1186 QTest::addColumn<IntList>(name: "expected");
1187
1188 QTest::newRow(dataTag: "stretch first section")
1189 << 600
1190 << (IntList{ 100, 100, 100, 100 })
1191 << (ResizeVec
1192 { QHeaderView::Stretch,
1193 QHeaderView::Interactive,
1194 QHeaderView::Interactive,
1195 QHeaderView::Interactive })
1196 << (IntList{ 300, 100, 100, 100 });
1197}
1198
1199void tst_QHeaderView::resizeWithResizeModes()
1200{
1201 QFETCH(int, size);
1202 QFETCH(const IntList, sections);
1203 QFETCH(const ResizeVec, modes);
1204 QFETCH(const IntList, expected);
1205
1206 view->setStretchLastSection(false);
1207 for (int i = 0; i < sections.count(); ++i) {
1208 view->resizeSection(logicalIndex: i, size: sections.at(i));
1209 view->setSectionResizeMode(logicalIndex: i, mode: modes.at(i));
1210 }
1211 topLevel->show();
1212 QVERIFY(QTest::qWaitForWindowExposed(topLevel));
1213 view->resize(w: size, h: size);
1214 for (int j = 0; j < expected.count(); ++j)
1215 QCOMPARE(view->sectionSize(j), expected.at(j));
1216}
1217
1218void tst_QHeaderView::moveAndInsertSection_data()
1219{
1220 QTest::addColumn<int>(name: "from");
1221 QTest::addColumn<int>(name: "to");
1222 QTest::addColumn<int>(name: "insert");
1223 QTest::addColumn<IntList>(name: "mapping");
1224
1225 QTest::newRow(dataTag: "move from 1 to 3, insert 0")
1226 << 1 << 3 << 0 <<(IntList{ 0, 1, 3, 4, 2 });
1227
1228}
1229
1230void tst_QHeaderView::moveAndInsertSection()
1231{
1232 QFETCH(int, from);
1233 QFETCH(int, to);
1234 QFETCH(int, insert);
1235 QFETCH(IntList, mapping);
1236
1237 view->setStretchLastSection(false);
1238 view->moveSection(from, to);
1239 model->insertRow(arow: insert);
1240
1241 for (int i = 0; i < mapping.count(); ++i)
1242 QCOMPARE(view->logicalIndex(i), mapping.at(i));
1243}
1244
1245void tst_QHeaderView::resizeMode()
1246{
1247 // resizeMode must not be called with an invalid index
1248 int last = view->count() - 1;
1249 view->setSectionResizeMode(QHeaderView::Interactive);
1250 QCOMPARE(view->sectionResizeMode(last), QHeaderView::Interactive);
1251 QCOMPARE(view->sectionResizeMode(1), QHeaderView::Interactive);
1252 view->setSectionResizeMode(QHeaderView::Stretch);
1253 QCOMPARE(view->sectionResizeMode(last), QHeaderView::Stretch);
1254 QCOMPARE(view->sectionResizeMode(1), QHeaderView::Stretch);
1255 view->setSectionResizeMode(QHeaderView::Custom);
1256 QCOMPARE(view->sectionResizeMode(last), QHeaderView::Custom);
1257 QCOMPARE(view->sectionResizeMode(1), QHeaderView::Custom);
1258
1259 // test when sections have been moved
1260 view->setStretchLastSection(false);
1261 for (int i = 0; i < (view->count() - 1); ++i)
1262 view->setSectionResizeMode(logicalIndex: i, mode: QHeaderView::Interactive);
1263 int logicalIndex = view->count() / 2;
1264 view->setSectionResizeMode(logicalIndex, mode: QHeaderView::Stretch);
1265 view->moveSection(from: view->visualIndex(logicalIndex), to: 0);
1266 for (int i = 0; i < (view->count() - 1); ++i) {
1267 if (i == logicalIndex)
1268 QCOMPARE(view->sectionResizeMode(i), QHeaderView::Stretch);
1269 else
1270 QCOMPARE(view->sectionResizeMode(i), QHeaderView::Interactive);
1271 }
1272}
1273
1274void tst_QHeaderView::resizeSection_data()
1275{
1276 QTest::addColumn<int>(name: "initial");
1277 QTest::addColumn<IntList>(name: "logical");
1278 QTest::addColumn<IntList>(name: "size");
1279 QTest::addColumn<ResizeVec>(name: "mode");
1280 QTest::addColumn<int>(name: "resized");
1281 QTest::addColumn<IntList>(name: "expected");
1282
1283 QTest::newRow(dataTag: "bad args")
1284 << 100
1285 << (IntList{ -1, -1, 99999, 99999, 4 })
1286 << (IntList{ -1, 0, 99999, -1, -1 })
1287 << (ResizeVec{
1288 QHeaderView::Interactive,
1289 QHeaderView::Interactive,
1290 QHeaderView::Interactive,
1291 QHeaderView::Interactive })
1292 << 0
1293 << (IntList{ 0, 0, 0, 0, 0 });
1294}
1295
1296void tst_QHeaderView::resizeSection()
1297{
1298 QFETCH(int, initial);
1299 QFETCH(const IntList, logical);
1300 QFETCH(const IntList, size);
1301 QFETCH(const ResizeVec, mode);
1302 QFETCH(int, resized);
1303 QFETCH(const IntList, expected);
1304
1305 view->resize(w: 400, h: 400);
1306
1307 topLevel->show();
1308 QVERIFY(QTest::qWaitForWindowExposed(topLevel));
1309 view->setSectionsMovable(true);
1310 view->setStretchLastSection(false);
1311
1312 for (int i = 0; i < logical.count(); ++i)
1313 if (logical.at(i) > -1 && logical.at(i) < view->count()) // for now
1314 view->setSectionResizeMode(logicalIndex: logical.at(i), mode: mode.at(i));
1315
1316 for (int j = 0; j < logical.count(); ++j)
1317 view->resizeSection(logicalIndex: logical.at(i: j), size: initial);
1318
1319 QSignalSpy spy(view, &QHeaderView::sectionResized);
1320
1321 for (int k = 0; k < logical.count(); ++k)
1322 view->resizeSection(logicalIndex: logical.at(i: k), size: size.at(i: k));
1323
1324 QCOMPARE(spy.count(), resized);
1325
1326 for (int l = 0; l < logical.count(); ++l)
1327 QCOMPARE(view->sectionSize(logical.at(l)), expected.at(l));
1328}
1329
1330void tst_QHeaderView::highlightSections()
1331{
1332 view->setHighlightSections(true);
1333 QCOMPARE(view->highlightSections(), true);
1334 view->setHighlightSections(false);
1335 QCOMPARE(view->highlightSections(), false);
1336}
1337
1338void tst_QHeaderView::showSortIndicator()
1339{
1340 view->setSortIndicatorShown(true);
1341 QCOMPARE(view->isSortIndicatorShown(), true);
1342 QCOMPARE(view->sortIndicatorOrder(), Qt::DescendingOrder);
1343 view->setSortIndicator(logicalIndex: 1, order: Qt::AscendingOrder);
1344 QCOMPARE(view->sortIndicatorOrder(), Qt::AscendingOrder);
1345 view->setSortIndicator(logicalIndex: 1, order: Qt::DescendingOrder);
1346 QCOMPARE(view->sortIndicatorOrder(), Qt::DescendingOrder);
1347 view->setSortIndicatorShown(false);
1348 QCOMPARE(view->isSortIndicatorShown(), false);
1349
1350 view->setSortIndicator(logicalIndex: 999999, order: Qt::DescendingOrder);
1351 // Don't segfault baby :)
1352 view->setSortIndicatorShown(true);
1353
1354 view->setSortIndicator(logicalIndex: 0, order: Qt::DescendingOrder);
1355 // Don't assert baby :)
1356}
1357
1358void tst_QHeaderView::sortIndicatorTracking()
1359{
1360 QtTestModel model(10, 10);
1361 QHeaderView hv(Qt::Horizontal);
1362
1363 hv.setModel(&model);
1364 hv.show();
1365 hv.setSortIndicatorShown(true);
1366 hv.setSortIndicator(logicalIndex: 1, order: Qt::DescendingOrder);
1367
1368 model.removeOneColumn(col: 8);
1369 QCOMPARE(hv.sortIndicatorSection(), 1);
1370
1371 model.removeOneColumn(col: 2);
1372 QCOMPARE(hv.sortIndicatorSection(), 1);
1373
1374 model.insertOneColumn(col: 2);
1375 QCOMPARE(hv.sortIndicatorSection(), 1);
1376
1377 model.insertOneColumn(col: 1);
1378 QCOMPARE(hv.sortIndicatorSection(), 2);
1379
1380 model.removeOneColumn(col: 0);
1381 QCOMPARE(hv.sortIndicatorSection(), 1);
1382
1383 model.removeOneColumn(col: 1);
1384 QCOMPARE(hv.sortIndicatorSection(), -1);
1385}
1386
1387void tst_QHeaderView::removeAndInsertRow()
1388{
1389 // Check if logicalIndex returns the correct value after we have removed a row
1390 // we might as well te
1391 for (int i = 0; i < model->rowCount(); ++i)
1392 QCOMPARE(i, view->logicalIndex(i));
1393
1394 while (model->removeRow(arow: 0)) {
1395 for (int i = 0; i < model->rowCount(); ++i)
1396 QCOMPARE(i, view->logicalIndex(i));
1397 }
1398
1399 for (int pass = 0; pass < 5; pass++) {
1400 for (int i = 0; i < model->rowCount(); ++i)
1401 QCOMPARE(i, view->logicalIndex(i));
1402 model->insertRow(arow: 0);
1403 }
1404
1405 while (model->removeRows(row: 0, count: 2)) {
1406 for (int i = 0; i < model->rowCount(); ++i)
1407 QCOMPARE(i, view->logicalIndex(i));
1408 }
1409
1410 for (int pass = 0; pass < 3; pass++) {
1411 model->insertRows(row: 0, count: 2);
1412 for (int i = 0; i < model->rowCount(); ++i) {
1413 QCOMPARE(i, view->logicalIndex(i));
1414 }
1415 }
1416
1417 for (int pass = 0; pass < 3; pass++) {
1418 model->insertRows(row: 3, count: 2);
1419 for (int i = 0; i < model->rowCount(); ++i)
1420 QCOMPARE(i, view->logicalIndex(i));
1421 }
1422
1423 // Insert at end
1424 for (int pass = 0; pass < 3; pass++) {
1425 int rowCount = model->rowCount();
1426 model->insertRows(row: rowCount, count: 1);
1427 for (int i = 0; i < rowCount; ++i)
1428 QCOMPARE(i, view->logicalIndex(i));
1429 }
1430
1431}
1432void tst_QHeaderView::unhideSection()
1433{
1434 // You should not necessarily expect the same size back again, so the best test we can do is to test if it is larger than 0 after a unhide.
1435 QCOMPARE(view->sectionsHidden(), false);
1436 view->setSectionHidden(logicalIndex: 0, hide: true);
1437 QCOMPARE(view->sectionsHidden(), true);
1438 QCOMPARE(view->sectionSize(0), 0);
1439 view->setSectionResizeMode(QHeaderView::Interactive);
1440 view->setSectionHidden(logicalIndex: 0, hide: false);
1441 QVERIFY(view->sectionSize(0) > 0);
1442
1443 view->setSectionHidden(logicalIndex: 0, hide: true);
1444 QCOMPARE(view->sectionSize(0), 0);
1445 view->setSectionHidden(logicalIndex: 0, hide: true);
1446 QCOMPARE(view->sectionSize(0), 0);
1447 view->setSectionResizeMode(QHeaderView::Stretch);
1448 view->setSectionHidden(logicalIndex: 0, hide: false);
1449 QVERIFY(view->sectionSize(0) > 0);
1450
1451}
1452
1453void tst_QHeaderView::testEvent()
1454{
1455 protected_QHeaderView x(Qt::Vertical);
1456 x.testEvent();
1457 protected_QHeaderView y(Qt::Horizontal);
1458 y.testEvent();
1459}
1460
1461
1462void protected_QHeaderView::testEvent()
1463{
1464 // No crashy please
1465 QHoverEvent enterEvent(QEvent::HoverEnter, QPoint(), QPoint());
1466 event(e: &enterEvent);
1467 QHoverEvent eventLeave(QEvent::HoverLeave, QPoint(), QPoint());
1468 event(e: &eventLeave);
1469 QHoverEvent eventMove(QEvent::HoverMove, QPoint(), QPoint());
1470 event(e: &eventMove);
1471}
1472
1473void tst_QHeaderView::headerDataChanged()
1474{
1475 // This shouldn't assert because view is Vertical
1476 view->headerDataChanged(orientation: Qt::Horizontal, logicalFirst: -1, logicalLast: -1);
1477#if 0
1478 // This will assert
1479 view->headerDataChanged(Qt::Vertical, -1, -1);
1480#endif
1481
1482 // No crashing please
1483 view->headerDataChanged(orientation: Qt::Horizontal, logicalFirst: 0, logicalLast: 1);
1484 view->headerDataChanged(orientation: Qt::Vertical, logicalFirst: 0, logicalLast: 1);
1485}
1486
1487void tst_QHeaderView::currentChanged()
1488{
1489 view->setCurrentIndex(QModelIndex());
1490}
1491
1492void tst_QHeaderView::horizontalOffset()
1493{
1494 protected_QHeaderView x(Qt::Vertical);
1495 x.testhorizontalOffset();
1496 protected_QHeaderView y(Qt::Horizontal);
1497 y.testhorizontalOffset();
1498}
1499
1500void tst_QHeaderView::verticalOffset()
1501{
1502 protected_QHeaderView x(Qt::Vertical);
1503 x.testverticalOffset();
1504 protected_QHeaderView y(Qt::Horizontal);
1505 y.testverticalOffset();
1506}
1507
1508void protected_QHeaderView::testhorizontalOffset()
1509{
1510 if (orientation() == Qt::Horizontal) {
1511 QCOMPARE(horizontalOffset(), 0);
1512 setOffset(10);
1513 QCOMPARE(horizontalOffset(), 10);
1514 }
1515 else
1516 QCOMPARE(horizontalOffset(), 0);
1517}
1518
1519void protected_QHeaderView::testverticalOffset()
1520{
1521 if (orientation() == Qt::Vertical) {
1522 QCOMPARE(verticalOffset(), 0);
1523 setOffset(10);
1524 QCOMPARE(verticalOffset(), 10);
1525 }
1526 else
1527 QCOMPARE(verticalOffset(), 0);
1528}
1529
1530void tst_QHeaderView::stretchSectionCount()
1531{
1532 view->setStretchLastSection(false);
1533 QCOMPARE(view->stretchSectionCount(), 0);
1534 view->setStretchLastSection(true);
1535 QCOMPARE(view->stretchSectionCount(), 0);
1536
1537 view->setSectionResizeMode(logicalIndex: 0, mode: QHeaderView::Stretch);
1538 QCOMPARE(view->stretchSectionCount(), 1);
1539}
1540
1541void tst_QHeaderView::hiddenSectionCount()
1542{
1543 model->clear();
1544 model->insertRows(row: 0, count: 10);
1545 // Hide every other one
1546 for (int i = 0; i < 10; i++)
1547 view->setSectionHidden(logicalIndex: i, hide: (i & 1) == 0);
1548
1549 QCOMPARE(view->hiddenSectionCount(), 5);
1550
1551 view->setSectionResizeMode(QHeaderView::Stretch);
1552 QCOMPARE(view->hiddenSectionCount(), 5);
1553
1554 // Remove some rows and make sure they are now still counted
1555 model->removeRow(arow: 9);
1556 model->removeRow(arow: 8);
1557 model->removeRow(arow: 7);
1558 model->removeRow(arow: 6);
1559 QCOMPARE(view->count(), 6);
1560 QCOMPARE(view->hiddenSectionCount(), 3);
1561 model->removeRows(row: 0, count: 5);
1562 QCOMPARE(view->count(), 1);
1563 QCOMPARE(view->hiddenSectionCount(), 0);
1564 QVERIFY(view->count() >= view->hiddenSectionCount());
1565}
1566
1567void tst_QHeaderView::focusPolicy()
1568{
1569 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1570 QSKIP("Wayland: This fails. Figure out why.");
1571
1572 QHeaderView view(Qt::Horizontal);
1573 QCOMPARE(view.focusPolicy(), Qt::NoFocus);
1574
1575 QTreeWidget widget;
1576 QCOMPARE(widget.header()->focusPolicy(), Qt::NoFocus);
1577 QVERIFY(!widget.focusProxy());
1578 QVERIFY(!widget.hasFocus());
1579 QVERIFY(!widget.header()->focusProxy());
1580 QVERIFY(!widget.header()->hasFocus());
1581
1582 widget.show();
1583 widget.setFocus(Qt::OtherFocusReason);
1584 QApplication::setActiveWindow(&widget);
1585 widget.activateWindow();
1586 QVERIFY(QTest::qWaitForWindowActive(&widget));
1587 QVERIFY(widget.hasFocus());
1588 QVERIFY(!widget.header()->hasFocus());
1589
1590 widget.setFocusPolicy(Qt::NoFocus);
1591 widget.clearFocus();
1592 QTRY_VERIFY(!widget.hasFocus());
1593 QVERIFY(!widget.header()->hasFocus());
1594
1595 QTest::keyPress(widget: &widget, key: Qt::Key_Tab);
1596
1597 QCoreApplication::processEvents();
1598 QCoreApplication::processEvents();
1599
1600 QVERIFY(!widget.hasFocus());
1601 QVERIFY(!widget.header()->hasFocus());
1602}
1603
1604class SimpleModel : public QAbstractItemModel
1605{
1606 Q_OBJECT
1607public:
1608 using QAbstractItemModel::QAbstractItemModel;
1609 QModelIndex parent(const QModelIndex &/*child*/) const override
1610 {
1611 return QModelIndex();
1612 }
1613 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
1614 {
1615 return hasIndex(row, column, parent) ? createIndex(arow: row, acolumn: column) : QModelIndex();
1616 }
1617 int rowCount(const QModelIndex & /*parent*/ = QModelIndex()) const override
1618 {
1619 return 8;
1620 }
1621 int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const override
1622 {
1623 return m_col_count;
1624 }
1625 QVariant data(const QModelIndex &index, int role) const override
1626 {
1627 if (!index.isValid())
1628 return QVariant();
1629 if (role == Qt::DisplayRole)
1630 return QString::number(index.row()) + QLatin1Char(',') + QString::number(index.column());
1631 return QVariant();
1632 }
1633 void setColumnCount(int c)
1634 {
1635 m_col_count = c;
1636 }
1637private:
1638 int m_col_count = 3;
1639};
1640
1641void tst_QHeaderView::moveSectionAndReset()
1642{
1643 SimpleModel m;
1644 QHeaderView v(Qt::Horizontal);
1645 v.setModel(&m);
1646 int cc = 2;
1647 for (cc = 2; cc < 4; ++cc) {
1648 m.setColumnCount(cc);
1649 int movefrom = 0;
1650 int moveto;
1651 for (moveto = 1; moveto < cc; ++moveto) {
1652 v.moveSection(from: movefrom, to: moveto);
1653 m.setColumnCount(cc - 1);
1654 v.reset();
1655 for (int i = 0; i < cc - 1; ++i)
1656 QCOMPARE(v.logicalIndex(v.visualIndex(i)), i);
1657 }
1658 }
1659}
1660
1661void tst_QHeaderView::moveSectionAndRemove()
1662{
1663 QStandardItemModel m;
1664 QHeaderView v(Qt::Horizontal);
1665
1666 v.setModel(&m);
1667 v.model()->insertColumns(column: 0, count: 3);
1668 v.moveSection(from: 0, to: 1);
1669
1670 QCOMPARE(v.count(), 3);
1671 v.model()->removeColumns(column: 0, count: v.model()->columnCount());
1672 QCOMPARE(v.count(), 0);
1673}
1674
1675static QByteArray savedState()
1676{
1677 QStandardItemModel m(4, 4);
1678 QHeaderView h1(Qt::Horizontal);
1679 h1.setModel(&m);
1680 h1.setMinimumSectionSize(0); // system default min size can be to large
1681 h1.swapSections(first: 0, second: 2);
1682 h1.resizeSection(logicalIndex: 1, size: 10);
1683 h1.setSortIndicatorShown(true);
1684 h1.setSortIndicator(logicalIndex: 2, order: Qt::DescendingOrder);
1685 h1.setSectionHidden(logicalIndex: 3, hide: true);
1686 return h1.saveState();
1687}
1688
1689void tst_QHeaderView::saveRestore()
1690{
1691 QStandardItemModel m(4, 4);
1692 const QByteArray s1 = savedState();
1693
1694 QHeaderView h2(Qt::Vertical);
1695 QSignalSpy spy(&h2, &QHeaderView::sortIndicatorChanged);
1696
1697 h2.setModel(&m);
1698 QVERIFY(h2.restoreState(s1));
1699
1700 QCOMPARE(spy.count(), 1);
1701 QCOMPARE(spy.at(0).at(0).toInt(), 2);
1702
1703 QCOMPARE(h2.logicalIndex(0), 2);
1704 QCOMPARE(h2.logicalIndex(2), 0);
1705 QCOMPARE(h2.sectionSize(1), 10);
1706 QCOMPARE(h2.sortIndicatorSection(), 2);
1707 QCOMPARE(h2.sortIndicatorOrder(), Qt::DescendingOrder);
1708 QCOMPARE(h2.isSortIndicatorShown(), true);
1709 QVERIFY(!h2.isSectionHidden(2));
1710 QVERIFY(h2.isSectionHidden(3));
1711 QCOMPARE(h2.hiddenSectionCount(), 1);
1712
1713 QByteArray s2 = h2.saveState();
1714 QCOMPARE(s1, s2);
1715
1716 QVERIFY(!h2.restoreState(QByteArrayLiteral("Garbage")));
1717}
1718
1719void tst_QHeaderView::restoreQt4State()
1720{
1721#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1722 // QTBUG-40462
1723 // Setting from Qt4, where information about multiple sections were grouped together in one
1724 // sectionItem object
1725 QStandardItemModel m(4, 10);
1726 QHeaderView h2(Qt::Vertical);
1727 QByteArray settings_qt4 =
1728 QByteArray::fromHex(hexEncoded: "000000ff00000000000000010000000100000000010000000000000000000000000000"
1729 "0000000003e80000000a0101000100000000000000000000000064ffffffff00000081"
1730 "0000000000000001000003e80000000a00000000");
1731 QVERIFY(h2.restoreState(settings_qt4));
1732 int sectionItemsLengthTotal = 0;
1733 for (int i = 0; i < h2.count(); ++i)
1734 sectionItemsLengthTotal += h2.sectionSize(logicalIndex: i);
1735 QCOMPARE(sectionItemsLengthTotal, h2.length());
1736
1737 // Buggy setting where sum(sectionItems) != length. Check false is returned and this corrupted
1738 // state isn't restored
1739 QByteArray settings_buggy_length =
1740 QByteArray::fromHex(hexEncoded: "000000ff000000000000000100000000000000050100000000000000000000000a4000"
1741 "000000010000000600000258000000fb0000000a010100010000000000000000000000"
1742 "0064ffffffff00000081000000000000000a000000d30000000100000000000000c800"
1743 "000001000000000000008000000001000000000000005c00000001000000000000003c"
1744 "0000000100000000000002580000000100000000000000000000000100000000000002"
1745 "580000000100000000000002580000000100000000000003c000000001000000000000"
1746 "03e8");
1747 int old_length = h2.length();
1748 QByteArray old_state = h2.saveState();
1749 // Check setting is correctly recognized as corrupted
1750 QVERIFY(!h2.restoreState(settings_buggy_length));
1751 // Check nothing has been actually restored
1752 QCOMPARE(h2.length(), old_length);
1753 QCOMPARE(h2.saveState(), old_state);
1754#else
1755 QSKIP("Qt4 compatibility no longer needed in Qt6")
1756#endif
1757}
1758
1759void tst_QHeaderView::restoreToMoreColumns()
1760{
1761 // Restore state onto a model with more columns
1762 const QByteArray s1 = savedState();
1763 QHeaderView h4(Qt::Horizontal);
1764 QStandardItemModel fiveColumnsModel(1, 5);
1765 h4.setModel(&fiveColumnsModel);
1766 QCOMPARE(fiveColumnsModel.columnCount(), 5);
1767 QCOMPARE(h4.count(), 5);
1768 QVERIFY(h4.restoreState(s1));
1769 QCOMPARE(fiveColumnsModel.columnCount(), 5);
1770 QCOMPARE(h4.count(), 5);
1771 QCOMPARE(h4.sectionSize(1), 10);
1772 for (int i = 0; i < h4.count(); ++i)
1773 QVERIFY(h4.sectionSize(i) > 0 || h4.isSectionHidden(i));
1774 QVERIFY(!h4.isSectionHidden(2));
1775 QVERIFY(h4.isSectionHidden(3));
1776 QCOMPARE(h4.hiddenSectionCount(), 1);
1777 QCOMPARE(h4.sortIndicatorSection(), 2);
1778 QCOMPARE(h4.sortIndicatorOrder(), Qt::DescendingOrder);
1779 QCOMPARE(h4.logicalIndex(0), 2);
1780 QCOMPARE(h4.logicalIndex(1), 1);
1781 QCOMPARE(h4.logicalIndex(2), 0);
1782 QCOMPARE(h4.visualIndex(0), 2);
1783 QCOMPARE(h4.visualIndex(1), 1);
1784 QCOMPARE(h4.visualIndex(2), 0);
1785
1786 // Repainting shouldn't crash
1787 h4.show();
1788 QVERIFY(QTest::qWaitForWindowExposed(&h4));
1789}
1790
1791void tst_QHeaderView::restoreToMoreColumnsNoMovedColumns()
1792{
1793 // Given a model with 2 columns, for saving state
1794 QHeaderView h1(Qt::Horizontal);
1795 QStandardItemModel model1(1, 2);
1796 h1.setModel(&model1);
1797 QCOMPARE(h1.visualIndex(0), 0);
1798 QCOMPARE(h1.visualIndex(1), 1);
1799 QCOMPARE(h1.logicalIndex(0), 0);
1800 QCOMPARE(h1.logicalIndex(1), 1);
1801 const QByteArray savedState = h1.saveState();
1802
1803 // And a model with 3 columns, to apply that state upon
1804 QHeaderView h2(Qt::Horizontal);
1805 QStandardItemModel model2(1, 3);
1806 h2.setModel(&model2);
1807 QCOMPARE(h2.visualIndex(0), 0);
1808 QCOMPARE(h2.visualIndex(1), 1);
1809 QCOMPARE(h2.visualIndex(2), 2);
1810 QCOMPARE(h2.logicalIndex(0), 0);
1811 QCOMPARE(h2.logicalIndex(1), 1);
1812 QCOMPARE(h2.logicalIndex(2), 2);
1813
1814 // When calling restoreState()
1815 QVERIFY(h2.restoreState(savedState));
1816
1817 // Then the index mapping should still be as default
1818 QCOMPARE(h2.visualIndex(0), 0);
1819 QCOMPARE(h2.visualIndex(1), 1);
1820 QCOMPARE(h2.visualIndex(2), 2);
1821 QCOMPARE(h2.logicalIndex(0), 0);
1822 QCOMPARE(h2.logicalIndex(1), 1);
1823 QCOMPARE(h2.logicalIndex(2), 2);
1824
1825 // And repainting shouldn't crash
1826 h2.show();
1827 QVERIFY(QTest::qWaitForWindowExposed(&h2));
1828}
1829
1830void tst_QHeaderView::restoreBeforeSetModel()
1831{
1832 QHeaderView h2(Qt::Horizontal);
1833 const QByteArray s1 = savedState();
1834 // First restore
1835 QVERIFY(h2.restoreState(s1));
1836 // Then setModel
1837 QStandardItemModel model(4, 4);
1838 h2.setModel(&model);
1839
1840 // Check the result
1841 QCOMPARE(h2.logicalIndex(0), 2);
1842 QCOMPARE(h2.logicalIndex(2), 0);
1843 QCOMPARE(h2.sectionSize(1), 10);
1844 QCOMPARE(h2.sortIndicatorSection(), 2);
1845 QCOMPARE(h2.sortIndicatorOrder(), Qt::DescendingOrder);
1846 QCOMPARE(h2.isSortIndicatorShown(), true);
1847 QVERIFY(!h2.isSectionHidden(2));
1848 QVERIFY(h2.isSectionHidden(3));
1849 QCOMPARE(h2.hiddenSectionCount(), 1);
1850}
1851
1852void tst_QHeaderView::defaultSectionSizeTest()
1853{
1854#if defined Q_OS_WINRT
1855 QSKIP("Fails on WinRT - QTBUG-73309");
1856#endif
1857
1858 // Setup
1859 QTableView qtv;
1860 QHeaderView *hv = qtv.verticalHeader();
1861 hv->setMinimumSectionSize(10);
1862 hv->setDefaultSectionSize(99); // Set it to a value different from defaultSize.
1863 QStandardItemModel amodel(4, 4);
1864 qtv.setModel(&amodel);
1865 QCOMPARE(hv->sectionSize(0), 99);
1866 QCOMPARE(hv->visualIndexAt(50), 0); // <= also make sure that indexes are calculated
1867 hv->setDefaultSectionSize(40); // Set it to a value different from defaultSize.
1868 QCOMPARE(hv->visualIndexAt(50), 1);
1869
1870 const int defaultSize = 26;
1871 hv->setDefaultSectionSize(defaultSize + 1); // Set it to a value different from defaultSize.
1872
1873 // no hidden Sections
1874 hv->resizeSection(logicalIndex: 1, size: 0);
1875 hv->setDefaultSectionSize(defaultSize);
1876 QCOMPARE(hv->sectionSize(1), defaultSize);
1877
1878 // with hidden sections
1879 hv->resizeSection(logicalIndex: 1, size: 0);
1880 hv->hideSection(alogicalIndex: 2);
1881 hv->setDefaultSectionSize(defaultSize);
1882
1883 QVERIFY(hv->sectionSize(0) == defaultSize); // trivial case.
1884 QVERIFY(hv->sectionSize(1) == defaultSize); // just sized 0. Now it should be 10
1885 QVERIFY(hv->sectionSize(2) == 0); // section is hidden. It should not be resized.
1886}
1887
1888class TestHeaderViewStyle : public QProxyStyle
1889{
1890 Q_OBJECT
1891public:
1892 using QProxyStyle::QProxyStyle;
1893 int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr,
1894 const QWidget *widget = nullptr) const override
1895 {
1896 if (metric == QStyle::PM_HeaderDefaultSectionSizeHorizontal)
1897 return horizontalSectionSize;
1898 else
1899 return QProxyStyle::pixelMetric(metric, option, widget);
1900 }
1901 int horizontalSectionSize = 100;
1902};
1903
1904void tst_QHeaderView::defaultSectionSizeTestStyles()
1905{
1906 TestHeaderViewStyle style1;
1907 TestHeaderViewStyle style2;
1908 style1.horizontalSectionSize = 100;
1909 style2.horizontalSectionSize = 200;
1910
1911 QHeaderView hv(Qt::Horizontal);
1912 hv.setStyle(&style1);
1913 QCOMPARE(hv.defaultSectionSize(), style1.horizontalSectionSize);
1914 hv.setStyle(&style2);
1915 QCOMPARE(hv.defaultSectionSize(), style2.horizontalSectionSize);
1916 hv.setDefaultSectionSize(70);
1917 QCOMPARE(hv.defaultSectionSize(), 70);
1918 hv.setStyle(&style1);
1919 QCOMPARE(hv.defaultSectionSize(), 70);
1920 hv.resetDefaultSectionSize();
1921 QCOMPARE(hv.defaultSectionSize(), style1.horizontalSectionSize);
1922 hv.setStyle(&style2);
1923 QCOMPARE(hv.defaultSectionSize(), style2.horizontalSectionSize);
1924}
1925
1926void tst_QHeaderView::defaultAlignment_data()
1927{
1928 QTest::addColumn<Qt::Orientation>(name: "direction");
1929 QTest::addColumn<Qt::Alignment>(name: "initial");
1930 QTest::addColumn<Qt::Alignment>(name: "alignment");
1931
1932 QTest::newRow(dataTag: "horizontal right aligned")
1933 << Qt::Horizontal
1934 << Qt::Alignment(Qt::AlignCenter)
1935 << Qt::Alignment(Qt::AlignRight);
1936
1937 QTest::newRow(dataTag: "horizontal left aligned")
1938 << Qt::Horizontal
1939 << Qt::Alignment(Qt::AlignCenter)
1940 << Qt::Alignment(Qt::AlignLeft);
1941
1942 QTest::newRow(dataTag: "vertical right aligned")
1943 << Qt::Vertical
1944 << Qt::Alignment(Qt::AlignLeft|Qt::AlignVCenter)
1945 << Qt::Alignment(Qt::AlignRight);
1946
1947 QTest::newRow(dataTag: "vertical left aligned")
1948 << Qt::Vertical
1949 << Qt::Alignment(Qt::AlignLeft|Qt::AlignVCenter)
1950 << Qt::Alignment(Qt::AlignLeft);
1951}
1952
1953void tst_QHeaderView::defaultAlignment()
1954{
1955 QFETCH(Qt::Orientation, direction);
1956 QFETCH(Qt::Alignment, initial);
1957 QFETCH(Qt::Alignment, alignment);
1958
1959 SimpleModel m;
1960
1961 QHeaderView header(direction);
1962 header.setModel(&m);
1963
1964 QCOMPARE(header.defaultAlignment(), initial);
1965 header.setDefaultAlignment(alignment);
1966 QCOMPARE(header.defaultAlignment(), alignment);
1967}
1968
1969void tst_QHeaderView::globalResizeMode_data()
1970{
1971 QTest::addColumn<Qt::Orientation>(name: "direction");
1972 QTest::addColumn<QHeaderView::ResizeMode>(name: "mode");
1973 QTest::addColumn<int>(name: "insert");
1974
1975 QTest::newRow(dataTag: "horizontal ResizeToContents 0")
1976 << Qt::Horizontal
1977 << QHeaderView::ResizeToContents
1978 << 0;
1979}
1980
1981void tst_QHeaderView::globalResizeMode()
1982{
1983 QFETCH(Qt::Orientation, direction);
1984 QFETCH(QHeaderView::ResizeMode, mode);
1985 QFETCH(int, insert);
1986
1987 QStandardItemModel m(4, 4);
1988 QHeaderView h(direction);
1989 h.setModel(&m);
1990
1991 h.setSectionResizeMode(mode);
1992 m.insertRow(arow: insert);
1993 for (int i = 0; i < h.count(); ++i)
1994 QCOMPARE(h.sectionResizeMode(i), mode);
1995}
1996
1997
1998void tst_QHeaderView::sectionPressedSignal_data()
1999{
2000 QTest::addColumn<Qt::Orientation>(name: "direction");
2001 QTest::addColumn<bool>(name: "clickable");
2002 QTest::addColumn<int>(name: "count");
2003
2004 QTest::newRow(dataTag: "horizontal unclickable 0")
2005 << Qt::Horizontal
2006 << false
2007 << 0;
2008
2009 QTest::newRow(dataTag: "horizontal clickable 1")
2010 << Qt::Horizontal
2011 << true
2012 << 1;
2013}
2014
2015void tst_QHeaderView::sectionPressedSignal()
2016{
2017 QFETCH(Qt::Orientation, direction);
2018 QFETCH(bool, clickable);
2019 QFETCH(int, count);
2020
2021 QStandardItemModel m(4, 4);
2022 QHeaderView h(direction);
2023
2024 h.setModel(&m);
2025 h.show();
2026 h.setSectionsClickable(clickable);
2027
2028 QSignalSpy spy(&h, &QHeaderView::sectionPressed);
2029
2030 QCOMPARE(spy.count(), 0);
2031 QTest::mousePress(widget: h.viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(5, 5));
2032 QCOMPARE(spy.count(), count);
2033}
2034
2035void tst_QHeaderView::sectionClickedSignal()
2036{
2037 QFETCH(Qt::Orientation, direction);
2038 QFETCH(bool, clickable);
2039 QFETCH(int, count);
2040
2041 QStandardItemModel m(4, 4);
2042 QHeaderView h(direction);
2043
2044 h.setModel(&m);
2045 h.show();
2046 h.setSectionsClickable(clickable);
2047 h.setSortIndicatorShown(true);
2048
2049 QSignalSpy spy(&h, &QHeaderView::sectionClicked);
2050 QSignalSpy spy2(&h, &QHeaderView::sortIndicatorChanged);
2051
2052 QCOMPARE(spy.count(), 0);
2053 QCOMPARE(spy2.count(), 0);
2054 QTest::mouseClick(widget: h.viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(5, 5));
2055 QCOMPARE(spy.count(), count);
2056 QCOMPARE(spy2.count(), count);
2057
2058 //now let's try with the sort indicator hidden (the result should be the same
2059 spy.clear();
2060 spy2.clear();
2061 h.setSortIndicatorShown(false);
2062 QTest::mouseClick(widget: h.viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(5, 5));
2063 QCOMPARE(spy.count(), count);
2064 QCOMPARE(spy2.count(), count);
2065}
2066
2067void tst_QHeaderView::defaultSectionSize_data()
2068{
2069 QTest::addColumn<Qt::Orientation>(name: "direction");
2070 QTest::addColumn<int>(name: "oldDefaultSize");
2071 QTest::addColumn<int>(name: "newDefaultSize");
2072
2073 //QTest::newRow("horizontal,-5") << int(Qt::Horizontal) << 100 << -5;
2074 QTest::newRow(dataTag: "horizontal, 0") << Qt::Horizontal << 100 << 0;
2075 QTest::newRow(dataTag: "horizontal, 5") << Qt::Horizontal << 100 << 5;
2076 QTest::newRow(dataTag: "horizontal,25") << Qt::Horizontal << 100 << 5;
2077}
2078
2079void tst_QHeaderView::defaultSectionSize()
2080{
2081 QFETCH(Qt::Orientation, direction);
2082 QFETCH(int, oldDefaultSize);
2083 QFETCH(int, newDefaultSize);
2084
2085 QStandardItemModel m(4, 4);
2086 QHeaderView h(direction);
2087
2088 h.setModel(&m);
2089 h.setMinimumSectionSize(0);
2090
2091 QCOMPARE(h.defaultSectionSize(), oldDefaultSize);
2092 h.setDefaultSectionSize(newDefaultSize);
2093 QCOMPARE(h.defaultSectionSize(), newDefaultSize);
2094 h.reset();
2095 for (int i = 0; i < h.count(); ++i)
2096 QCOMPARE(h.sectionSize(i), newDefaultSize);
2097}
2098
2099void tst_QHeaderView::hideAndInsert_data()
2100{
2101 QTest::addColumn<Qt::Orientation>(name: "direction");
2102 QTest::addColumn<int>(name: "hide");
2103 QTest::addColumn<int>(name: "insert");
2104 QTest::addColumn<int>(name: "hidden");
2105
2106 QTest::newRow(dataTag: "horizontal, 0, 0") << Qt::Horizontal << 0 << 0 << 1;
2107}
2108
2109void tst_QHeaderView::hideAndInsert()
2110{
2111 QFETCH(Qt::Orientation, direction);
2112 QFETCH(int, hide);
2113 QFETCH(int, insert);
2114 QFETCH(int, hidden);
2115
2116 QStandardItemModel m(4, 4);
2117 QHeaderView h(direction);
2118 h.setModel(&m);
2119 h.setSectionHidden(logicalIndex: hide, hide: true);
2120
2121 if (direction == Qt::Vertical)
2122 m.insertRow(arow: insert);
2123 else
2124 m.insertColumn(acolumn: insert);
2125
2126 for (int i = 0; i < h.count(); ++i)
2127 QCOMPARE(h.isSectionHidden(i), i == hidden);
2128}
2129
2130void tst_QHeaderView::removeSection()
2131{
2132 const int hidden = 3; //section that will be hidden
2133
2134 QStringListModel model({ "0", "1", "2", "3", "4", "5", "6" });
2135 QHeaderView view(Qt::Vertical);
2136 view.setModel(&model);
2137 view.hideSection(alogicalIndex: hidden);
2138 view.hideSection(alogicalIndex: 1);
2139 model.removeRow(arow: 1);
2140 view.show();
2141
2142 for(int i = 0; i < view.count(); i++) {
2143 if (i == (hidden - 1)) { //-1 because we removed a row in the meantime
2144 QCOMPARE(view.sectionSize(i), 0);
2145 QVERIFY(view.isSectionHidden(i));
2146 } else {
2147 QCOMPARE(view.sectionSize(i), view.defaultSectionSize() );
2148 QVERIFY(!view.isSectionHidden(i));
2149 }
2150 }
2151}
2152
2153void tst_QHeaderView::preserveHiddenSectionWidth()
2154{
2155 QStringListModel model({ "0", "1", "2", "3" });
2156 QHeaderView view(Qt::Vertical);
2157 view.setModel(&model);
2158 view.resizeSection(logicalIndex: 0, size: 100);
2159 view.resizeSection(logicalIndex: 1, size: 10);
2160 view.resizeSection(logicalIndex: 2, size: 50);
2161 view.setSectionResizeMode(logicalIndex: 3, mode: QHeaderView::Stretch);
2162 view.show();
2163
2164 view.hideSection(alogicalIndex: 2);
2165 model.removeRow(arow: 1);
2166 view.showSection(alogicalIndex: 1);
2167 QCOMPARE(view.sectionSize(0), 100);
2168 QCOMPARE(view.sectionSize(1), 50);
2169
2170 view.hideSection(alogicalIndex: 1);
2171 model.insertRow(arow: 1);
2172 view.showSection(alogicalIndex: 2);
2173 QCOMPARE(view.sectionSize(0), 100);
2174 QCOMPARE(view.sectionSize(1), view.defaultSectionSize());
2175 QCOMPARE(view.sectionSize(2), 50);
2176}
2177
2178void tst_QHeaderView::invisibleStretchLastSection()
2179{
2180#ifdef Q_OS_WINRT
2181 QSKIP("Fails on WinRT - QTBUG-68297");
2182#endif
2183 int count = 6;
2184 QStandardItemModel model(1, count);
2185 QHeaderView view(Qt::Horizontal);
2186 view.setModel(&model);
2187 int height = view.height();
2188
2189 view.resize(w: view.defaultSectionSize() * (count / 2), h: height); // don't show all sections
2190 view.show();
2191 view.setStretchLastSection(true);
2192 // stretch section is not visible; it should not be stretched
2193 for (int i = 0; i < count; ++i)
2194 QCOMPARE(view.sectionSize(i), view.defaultSectionSize());
2195
2196 view.resize(w: view.defaultSectionSize() * (count + 1), h: height); // give room to stretch
2197
2198 // stretch section is visible; it should be stretched
2199 for (int i = 0; i < count - 1; ++i)
2200 QCOMPARE(view.sectionSize(i), view.defaultSectionSize());
2201 QCOMPARE(view.sectionSize(count - 1), view.defaultSectionSize() * 2);
2202}
2203
2204void tst_QHeaderView::noSectionsWithNegativeSize()
2205{
2206 QStandardItemModel m(4, 4);
2207 QHeaderView h(Qt::Horizontal);
2208 h.setModel(&m);
2209 h.resizeSection(logicalIndex: 1, size: -5);
2210 QVERIFY(h.sectionSize(1) >= 0); // Sections with negative sizes not well defined.
2211}
2212
2213void tst_QHeaderView::emptySectionSpan()
2214{
2215 QHeaderViewPrivate::SectionItem section;
2216 QCOMPARE(section.sectionSize(), 0);
2217}
2218
2219void tst_QHeaderView::task236450_hidden_data()
2220{
2221 QTest::addColumn<IntList>(name: "hide1");
2222 QTest::addColumn<IntList>(name: "hide2");
2223
2224 QTest::newRow(dataTag: "set 1") << (IntList{ 1, 3 })
2225 << (IntList{ 1, 5 });
2226
2227 QTest::newRow(dataTag: "set 2") << (IntList{ 2, 3 })
2228 << (IntList{ 1, 5 });
2229
2230 QTest::newRow(dataTag: "set 3") << (IntList{ 0, 2, 4 })
2231 << (IntList{ 2, 3, 5 });
2232
2233}
2234
2235void tst_QHeaderView::task236450_hidden()
2236{
2237 QFETCH(const IntList, hide1);
2238 QFETCH(const IntList, hide2);
2239
2240 QStringListModel model({ "0", "1", "2", "3", "4", "5" });
2241 protected_QHeaderView view(Qt::Vertical);
2242 view.setModel(&model);
2243 view.show();
2244
2245 for (int i : hide1)
2246 view.hideSection(alogicalIndex: i);
2247
2248 QCOMPARE(view.hiddenSectionCount(), hide1.count());
2249 for (int i = 0; i < 6; i++)
2250 QCOMPARE(!view.isSectionHidden(i), !hide1.contains(i));
2251
2252 view.setDefaultSectionSize(2);
2253 view.scheduleDelayedItemsLayout();
2254 view.executeDelayedItemsLayout(); //force to do a relayout
2255
2256 QCOMPARE(view.hiddenSectionCount(), hide1.count());
2257 for (int i = 0; i < 6; i++) {
2258 QCOMPARE(!view.isSectionHidden(i), !hide1.contains(i));
2259 view.setSectionHidden(logicalIndex: i, hide: hide2.contains(t: i));
2260 }
2261
2262 QCOMPARE(view.hiddenSectionCount(), hide2.count());
2263 for (int i = 0; i < 6; i++)
2264 QCOMPARE(!view.isSectionHidden(i), !hide2.contains(i));
2265}
2266
2267void tst_QHeaderView::task248050_hideRow()
2268{
2269 //this is the sequence of events that make the task fail
2270 protected_QHeaderView header(Qt::Vertical);
2271 QStandardItemModel model(0, 1);
2272 header.setMinimumSectionSize(0); // system default min size can be to large
2273 header.setStretchLastSection(false);
2274 header.setDefaultSectionSize(17);
2275 header.setModel(&model);
2276 header.doItemsLayout();
2277
2278 model.setRowCount(3);
2279
2280 QCOMPARE(header.sectionPosition(2), 17*2);
2281
2282 header.hideSection(alogicalIndex: 1);
2283 QCOMPARE(header.sectionPosition(2), 17);
2284
2285 QTest::qWait(ms: 100);
2286 //the size of the section shouldn't have changed
2287 QCOMPARE(header.sectionPosition(2), 17);
2288}
2289
2290
2291//returns 0 if everything is fine.
2292static int checkHeaderViewOrder(const QHeaderView *view, const IntList &expected)
2293{
2294 if (view->count() != expected.count())
2295 return 1;
2296
2297 for (int i = 0; i < expected.count(); i++) {
2298 if (view->logicalIndex(visualIndex: i) != expected.at(i))
2299 return i + 10;
2300 if (view->visualIndex(logicalIndex: expected.at(i)) != i)
2301 return i + 100;
2302 }
2303 return 0;
2304}
2305
2306
2307void tst_QHeaderView::QTBUG6058_reset()
2308{
2309 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2310 QSKIP("Wayland: This fails. Figure out why.");
2311
2312 QStringListModel model1({ "0", "1", "2", "3", "4", "5" });
2313 QStringListModel model2({ "a", "b", "c" });
2314 QSortFilterProxyModel proxy;
2315
2316 QHeaderView view(Qt::Vertical);
2317 view.setModel(&proxy);
2318 view.show();
2319 QVERIFY(QTest::qWaitForWindowExposed(&view));
2320 QVERIFY(QTest::qWaitForWindowActive(&view));
2321
2322 proxy.setSourceModel(&model1);
2323 view.swapSections(first: 0, second: 2);
2324 view.swapSections(first: 1, second: 4);
2325 IntList expectedOrder{2, 4, 0, 3, 1, 5};
2326 QTRY_COMPARE(checkHeaderViewOrder(&view, expectedOrder) , 0);
2327
2328 proxy.setSourceModel(&model2);
2329 expectedOrder = {2, 0, 1};
2330 QTRY_COMPARE(checkHeaderViewOrder(&view, expectedOrder) , 0);
2331
2332 proxy.setSourceModel(&model1);
2333 expectedOrder = {2, 0, 1, 3, 4, 5};
2334 QTRY_COMPARE(checkHeaderViewOrder(&view, expectedOrder) , 0);
2335}
2336
2337void tst_QHeaderView::QTBUG7833_sectionClicked()
2338{
2339 QTableView tv;
2340 QStandardItemModel *sim = new QStandardItemModel(&tv);
2341 QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(&tv);
2342 proxyModel->setSourceModel(sim);
2343 proxyModel->setDynamicSortFilter(true);
2344 proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
2345
2346 QList<QStandardItem *> row;
2347 for (char i = 0; i < 12; i++)
2348 row.append(t: new QStandardItem(QString(QLatin1Char('A' + i))));
2349 sim->appendRow(items: row);
2350 row.clear();
2351 for (char i = 12; i > 0; i--)
2352 row.append(t: new QStandardItem(QString(QLatin1Char('A' + i))));
2353 sim->appendRow(items: row);
2354
2355 tv.setSortingEnabled(true);
2356 tv.horizontalHeader()->setSortIndicatorShown(true);
2357 tv.horizontalHeader()->setSectionsClickable(true);
2358 tv.horizontalHeader()->setStretchLastSection(true);
2359 tv.horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
2360
2361 tv.setModel(proxyModel);
2362 const int section4Size = tv.horizontalHeader()->sectionSize(logicalIndex: 4) + 1;
2363 const int section5Size = section4Size + 1;
2364 tv.horizontalHeader()->resizeSection(logicalIndex: 4, size: section4Size);
2365 tv.horizontalHeader()->resizeSection(logicalIndex: 5, size: section5Size);
2366 tv.setColumnHidden(column: 5, hide: true);
2367 tv.setColumnHidden(column: 6, hide: true);
2368 tv.horizontalHeader()->swapSections(first: 8, second: 10);
2369 tv.sortByColumn(column: 1, order: Qt::AscendingOrder);
2370
2371 QCOMPARE(tv.isColumnHidden(5), true);
2372 QCOMPARE(tv.isColumnHidden(6), true);
2373 QCOMPARE(tv.horizontalHeader()->sectionsMoved(), true);
2374 QCOMPARE(tv.horizontalHeader()->logicalIndex(8), 10);
2375 QCOMPARE(tv.horizontalHeader()->logicalIndex(10), 8);
2376 QCOMPARE(tv.horizontalHeader()->sectionSize(4), section4Size);
2377 tv.setColumnHidden(column: 5, hide: false); // unhide, section size must be properly restored
2378 QCOMPARE(tv.horizontalHeader()->sectionSize(5), section5Size);
2379 tv.setColumnHidden(column: 5, hide: true);
2380
2381 QSignalSpy clickedSpy(tv.horizontalHeader(), &QHeaderView::sectionClicked);
2382 QSignalSpy pressedSpy(tv.horizontalHeader(), &QHeaderView::sectionPressed);
2383
2384
2385 QTest::mouseClick(widget: tv.horizontalHeader()->viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier,
2386 pos: QPoint(tv.horizontalHeader()->sectionViewportPosition(logicalIndex: 11) +
2387 tv.horizontalHeader()->sectionSize(logicalIndex: 11) / 2, 5));
2388 QCOMPARE(clickedSpy.count(), 1);
2389 QCOMPARE(pressedSpy.count(), 1);
2390 QCOMPARE(clickedSpy.at(0).at(0).toInt(), 11);
2391 QCOMPARE(pressedSpy.at(0).at(0).toInt(), 11);
2392
2393 QTest::mouseClick(widget: tv.horizontalHeader()->viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier,
2394 pos: QPoint(tv.horizontalHeader()->sectionViewportPosition(logicalIndex: 8) +
2395 tv.horizontalHeader()->sectionSize(logicalIndex: 0) / 2, 5));
2396
2397 QCOMPARE(clickedSpy.count(), 2);
2398 QCOMPARE(pressedSpy.count(), 2);
2399 QCOMPARE(clickedSpy.at(1).at(0).toInt(), 8);
2400 QCOMPARE(pressedSpy.at(1).at(0).toInt(), 8);
2401
2402 QTest::mouseClick(widget: tv.horizontalHeader()->viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier,
2403 pos: QPoint(tv.horizontalHeader()->sectionViewportPosition(logicalIndex: 0) +
2404 tv.horizontalHeader()->sectionSize(logicalIndex: 0) / 2, 5));
2405
2406 QCOMPARE(clickedSpy.count(), 3);
2407 QCOMPARE(pressedSpy.count(), 3);
2408 QCOMPARE(clickedSpy.at(2).at(0).toInt(), 0);
2409 QCOMPARE(pressedSpy.at(2).at(0).toInt(), 0);
2410}
2411
2412void tst_QHeaderView::checkLayoutChangeEmptyModel()
2413{
2414 QtTestModel tm(0, 11);
2415 QTableView tv;
2416 tv.verticalHeader()->setStretchLastSection(true);
2417 tv.setModel(&tm);
2418
2419 const int section4Size = tv.horizontalHeader()->sectionSize(logicalIndex: 4) + 1;
2420 const int section5Size = section4Size + 1;
2421 tv.horizontalHeader()->resizeSection(logicalIndex: 4, size: section4Size);
2422 tv.horizontalHeader()->resizeSection(logicalIndex: 5, size: section5Size);
2423 tv.setColumnHidden(column: 5, hide: true);
2424 tv.setColumnHidden(column: 6, hide: true);
2425 tv.horizontalHeader()->swapSections(first: 8, second: 10);
2426
2427 tv.sortByColumn(column: 1, order: Qt::AscendingOrder);
2428 tm.emitLayoutChanged();
2429
2430 QCOMPARE(tv.isColumnHidden(5), true);
2431 QCOMPARE(tv.isColumnHidden(6), true);
2432 QCOMPARE(tv.horizontalHeader()->sectionsMoved(), true);
2433 QCOMPARE(tv.horizontalHeader()->logicalIndex(8), 10);
2434 QCOMPARE(tv.horizontalHeader()->logicalIndex(10), 8);
2435 QCOMPARE(tv.horizontalHeader()->sectionSize(4), section4Size);
2436 tv.setColumnHidden(column: 5, hide: false); // unhide, section size must be properly restored
2437 QCOMPARE(tv.horizontalHeader()->sectionSize(5), section5Size);
2438 tv.setColumnHidden(column: 5, hide: true);
2439
2440 // adjust
2441 tm.rows = 3;
2442 tm.emitLayoutChanged();
2443
2444 // remove the row used for QPersistenModelIndexes
2445 tm.emitLayoutChangedWithRemoveFirstRow();
2446 QCOMPARE(tv.isColumnHidden(5), true);
2447 QCOMPARE(tv.isColumnHidden(6), true);
2448 QCOMPARE(tv.horizontalHeader()->sectionsMoved(), true);
2449 QCOMPARE(tv.horizontalHeader()->logicalIndex(8), 10);
2450 QCOMPARE(tv.horizontalHeader()->logicalIndex(10), 8);
2451 QCOMPARE(tv.horizontalHeader()->sectionSize(4), section4Size);
2452 tv.setColumnHidden(column: 5, hide: false); // unhide, section size must be properly restored
2453 QCOMPARE(tv.horizontalHeader()->sectionSize(5), section5Size);
2454 tv.setColumnHidden(column: 5, hide: true);
2455}
2456
2457void tst_QHeaderView::QTBUG8650_crashOnInsertSections()
2458{
2459 QStringList headerLabels;
2460 QHeaderView view(Qt::Horizontal);
2461 QStandardItemModel model(2, 2);
2462 view.setModel(&model);
2463 view.moveSection(from: 1, to: 0);
2464 view.hideSection(alogicalIndex: 0);
2465
2466 model.insertColumn(column: 0, items: { new QStandardItem("c") });
2467}
2468
2469static void setModelTexts(QStandardItemModel *model)
2470{
2471 const int columnCount = model->columnCount();
2472 for (int i = 0, rowCount = model->rowCount(); i < rowCount; ++i) {
2473 const QString prefix = QLatin1String("item [") + QString::number(i) + QLatin1Char(',');
2474 for (int j = 0; j < columnCount; ++j)
2475 model->setData(index: model->index(row: i, column: j), value: prefix + QString::number(j) + QLatin1Char(']'));
2476 }
2477}
2478
2479void tst_QHeaderView::QTBUG12268_hiddenMovedSectionSorting()
2480{
2481 QTableView view; // ### this test fails on QTableView &view = *m_tableview; !? + shadowing view member
2482 QStandardItemModel model(4, 3);
2483 setModelTexts(&model);
2484 view.setModel(&model);
2485 view.horizontalHeader()->setSectionsMovable(true);
2486 view.setSortingEnabled(true);
2487 view.sortByColumn(column: 1, order: Qt::AscendingOrder);
2488 view.horizontalHeader()->moveSection(from: 0,to: 2);
2489 view.setColumnHidden(column: 1, hide: true);
2490 view.show();
2491 QVERIFY(QTest::qWaitForWindowExposed(&view));
2492 QCOMPARE(view.horizontalHeader()->hiddenSectionCount(), 1);
2493 QTest::mouseClick(widget: view.horizontalHeader()->viewport(), button: Qt::LeftButton);
2494 QCOMPARE(view.horizontalHeader()->hiddenSectionCount(), 1);
2495}
2496
2497void tst_QHeaderView::QTBUG14242_hideSectionAutoSize()
2498{
2499 QTableView qtv;
2500 QStandardItemModel amodel(4, 4);
2501 qtv.setModel(&amodel);
2502 QHeaderView *hv = qtv.verticalHeader();
2503 hv->setDefaultSectionSize(25);
2504 hv->setSectionResizeMode(QHeaderView::ResizeToContents);
2505 qtv.show();
2506
2507 hv->hideSection(alogicalIndex: 0);
2508 int afterlength = hv->length();
2509
2510 int calced_length = 0;
2511 for (int u = 0; u < hv->count(); ++u)
2512 calced_length += hv->sectionSize(logicalIndex: u);
2513
2514 QCOMPARE(calced_length, afterlength);
2515}
2516
2517void tst_QHeaderView::QTBUG50171_visualRegionForSwappedItems()
2518{
2519 protected_QHeaderView headerView(Qt::Horizontal);
2520 QtTestModel model(2, 3);
2521 headerView.setModel(&model);
2522 headerView.swapSections(first: 1, second: 2);
2523 headerView.hideSection(alogicalIndex: 0);
2524 headerView.testVisualRegionForSelection();
2525}
2526
2527class QTBUG53221_Model : public QAbstractItemModel
2528{
2529 Q_OBJECT
2530public:
2531 void insertRowAtBeginning()
2532 {
2533 Q_EMIT layoutAboutToBeChanged();
2534 m_displayNames.insert(i: 0, QStringLiteral("Item %1").arg(a: m_displayNames.count()));
2535 // Rows are always inserted at the beginning, so move all others.
2536 const auto pl = persistentIndexList();
2537 // The vertical header view will have a persistent index stored here on the second call to insertRowAtBeginning.
2538 for (const QModelIndex &persIndex : pl)
2539 changePersistentIndex(from: persIndex, to: index(row: persIndex.row() + 1, column: persIndex.column(), persIndex.parent()));
2540 Q_EMIT layoutChanged();
2541 }
2542
2543 QVariant data(const QModelIndex &index, int role) const override
2544 {
2545 return (role == Qt::DisplayRole) ? m_displayNames.at(i: index.row()) : QVariant();
2546 }
2547
2548 QModelIndex index(int row, int column, const QModelIndex &) const override { return createIndex(arow: row, acolumn: column); }
2549 QModelIndex parent(const QModelIndex &) const override { return QModelIndex(); }
2550 int rowCount(const QModelIndex &) const override { return m_displayNames.count(); }
2551 int columnCount(const QModelIndex &) const override { return 1; }
2552
2553private:
2554 QStringList m_displayNames;
2555};
2556
2557void tst_QHeaderView::QTBUG53221_assertShiftHiddenRow()
2558{
2559 QTableView tableView;
2560 QTBUG53221_Model modelTableView;
2561 tableView.setModel(&modelTableView);
2562
2563 modelTableView.insertRowAtBeginning();
2564 tableView.setRowHidden(row: 0, hide: true);
2565 QCOMPARE(tableView.verticalHeader()->isSectionHidden(0), true);
2566 modelTableView.insertRowAtBeginning();
2567 QCOMPARE(tableView.verticalHeader()->isSectionHidden(0), false);
2568 QCOMPARE(tableView.verticalHeader()->isSectionHidden(1), true);
2569 modelTableView.insertRowAtBeginning();
2570 QCOMPARE(tableView.verticalHeader()->isSectionHidden(0), false);
2571 QCOMPARE(tableView.verticalHeader()->isSectionHidden(1), false);
2572 QCOMPARE(tableView.verticalHeader()->isSectionHidden(2), true);
2573}
2574
2575void tst_QHeaderView::QTBUG75615_sizeHintWithStylesheet()
2576{
2577 QTableView tableView;
2578 QStandardItemModel model(1, 1);
2579 tableView.setModel(&model);
2580 tableView.show();
2581
2582 const auto headerView = tableView.horizontalHeader();
2583 const auto oldSizeHint = headerView->sizeHint();
2584 QVERIFY(oldSizeHint.isValid());
2585
2586 tableView.setStyleSheet("QTableView QHeaderView::section { height: 100px;}");
2587 QCOMPARE(headerView->sizeHint().width(), oldSizeHint.width());
2588 QCOMPARE(headerView->sizeHint().height(), 100);
2589
2590 tableView.setStyleSheet("QTableView QHeaderView::section { width: 100px;}");
2591 QCOMPARE(headerView->sizeHint().height(), oldSizeHint.height());
2592 QCOMPARE(headerView->sizeHint().width(), 100);
2593}
2594
2595void protected_QHeaderView::testVisualRegionForSelection()
2596{
2597 QRegion r = visualRegionForSelection(selection: QItemSelection(model()->index(row: 1, column: 0), model()->index(row: 1, column: 2)));
2598 QCOMPARE(r.boundingRect().contains(QRect(1, 1, length() - 2, 1)), true);
2599}
2600
2601void tst_QHeaderView::ensureNoIndexAtLength()
2602{
2603 QTableView qtv;
2604 QStandardItemModel amodel(4, 4);
2605 qtv.setModel(&amodel);
2606 QHeaderView *hv = qtv.verticalHeader();
2607 QCOMPARE(hv->visualIndexAt(hv->length()), -1);
2608 hv->resizeSection(logicalIndex: hv->count() - 1, size: 0);
2609 QCOMPARE(hv->visualIndexAt(hv->length()), -1);
2610}
2611
2612void tst_QHeaderView::offsetConsistent()
2613{
2614 // Ensure that a hidden section 'far away'
2615 // does not affect setOffsetToSectionPosition ..
2616 const int sectionToHide = 513;
2617 QTableView qtv;
2618 QStandardItemModel amodel(1000, 4);
2619 qtv.setModel(&amodel);
2620 QHeaderView *hv = qtv.verticalHeader();
2621 for (int u = 0; u < 100; u += 2)
2622 hv->resizeSection(logicalIndex: u, size: 0);
2623 hv->setOffsetToSectionPosition(150);
2624 int offset1 = hv->offset();
2625 hv->hideSection(alogicalIndex: sectionToHide);
2626 hv->setOffsetToSectionPosition(150);
2627 int offset2 = hv->offset();
2628 QCOMPARE(offset1, offset2);
2629 // Ensure that hidden indexes (still) is considered.
2630 hv->resizeSection(logicalIndex: sectionToHide, size: hv->sectionSize(logicalIndex: 200) * 2);
2631 hv->setOffsetToSectionPosition(800);
2632 offset1 = hv->offset();
2633 hv->showSection(alogicalIndex: sectionToHide);
2634 hv->setOffsetToSectionPosition(800);
2635 offset2 = hv->offset();
2636 QVERIFY(offset2 > offset1);
2637}
2638
2639void tst_QHeaderView::sectionsDontSortWhenNotClickingInThem()
2640{
2641 QTableView qtv;
2642 QStandardItemModel amodel(1000, 4);
2643 qtv.setModel(&amodel);
2644 QHeaderView *hv = qtv.horizontalHeader();
2645 hv->setSectionsClickable(true);
2646 hv->setFirstSectionMovable(true);
2647 hv->setSectionsMovable(false);
2648
2649 enum { DefaultYOffset = 5, OutOfRangeYOffset = 10000 };
2650
2651 const auto pressOnSection = [&](int section, int yOffset = DefaultYOffset)
2652 {
2653 QTest::mousePress(widget: hv->viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier,
2654 pos: QPoint(hv->sectionViewportPosition(logicalIndex: section) + hv->sectionSize(logicalIndex: section) / 2, yOffset));
2655 };
2656 const auto moveOntoSection = [&](int section, int yOffset = DefaultYOffset)
2657 {
2658 QTest::mouseMove(widget: hv->viewport(),
2659 pos: QPoint(hv->sectionViewportPosition(logicalIndex: section) + hv->sectionSize(logicalIndex: section) / 2, yOffset));
2660 };
2661 const auto releaseOnSection = [&](int section, int yOffset = DefaultYOffset)
2662 {
2663 QTest::mouseRelease(widget: hv->viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier,
2664 pos: QPoint(hv->sectionViewportPosition(logicalIndex: section) + hv->sectionSize(logicalIndex: section) / 2, yOffset));
2665 };
2666
2667 hv->setSortIndicator(logicalIndex: -1, order: Qt::AscendingOrder);
2668 QCOMPARE(hv->sortIndicatorSection(), -1);
2669
2670 pressOnSection(0);
2671 releaseOnSection(0);
2672 // RESULT: sorting
2673 QCOMPARE(hv->sortIndicatorSection(), 0);
2674
2675 hv->setSortIndicator(logicalIndex: -1, order: Qt::AscendingOrder);
2676 QCOMPARE(hv->sortIndicatorSection(), -1);
2677
2678 pressOnSection(0);
2679 moveOntoSection(1);
2680 releaseOnSection(1);
2681 // RESULT: no sorting
2682 QCOMPARE(hv->sortIndicatorSection(), -1);
2683
2684 pressOnSection(0);
2685 moveOntoSection(1);
2686 moveOntoSection(2);
2687 releaseOnSection(2);
2688 // RESULT: no sorting
2689 QCOMPARE(hv->sortIndicatorSection(), -1);
2690
2691 pressOnSection(0);
2692 moveOntoSection(1);
2693 moveOntoSection(0);
2694 releaseOnSection(0);
2695 // RESULT: sorting by 0
2696 QCOMPARE(hv->sortIndicatorSection(), 0);
2697
2698 pressOnSection(0);
2699 moveOntoSection(1);
2700 releaseOnSection(1);
2701 // RESULT: no change, still sorting by 0
2702 QCOMPARE(hv->sortIndicatorSection(), 0);
2703
2704 auto sortOrder = hv->sortIndicatorOrder();
2705 pressOnSection(1);
2706 moveOntoSection(0);
2707 releaseOnSection(0);
2708 // RESULT: no change, still sorting by 0
2709 QCOMPARE(hv->sortIndicatorSection(), 0);
2710 QCOMPARE(hv->sortIndicatorOrder(), sortOrder);
2711
2712 pressOnSection(1);
2713 moveOntoSection(0);
2714 moveOntoSection(1);
2715 releaseOnSection(1);
2716 // RESULT: sorting by 1
2717 QCOMPARE(hv->sortIndicatorSection(), 1);
2718
2719 pressOnSection(1);
2720 moveOntoSection(0);
2721 releaseOnSection(0);
2722 // RESULT: no change, still sorting by 1
2723 QCOMPARE(hv->sortIndicatorSection(), 1);
2724
2725 hv->setSortIndicator(logicalIndex: -1, order: Qt::AscendingOrder);
2726 QCOMPARE(hv->sortIndicatorSection(), -1);
2727
2728 pressOnSection(0);
2729 releaseOnSection(0, OutOfRangeYOffset);
2730 // RESULT: no sorting
2731 QCOMPARE(hv->sortIndicatorSection(), -1);
2732
2733 pressOnSection(0);
2734 moveOntoSection(0, OutOfRangeYOffset);
2735 releaseOnSection(0, OutOfRangeYOffset);
2736 // RESULT: no sorting
2737 QCOMPARE(hv->sortIndicatorSection(), -1);
2738
2739 pressOnSection(0);
2740 moveOntoSection(0, OutOfRangeYOffset);
2741 moveOntoSection(0);
2742 releaseOnSection(0);
2743 // RESULT: sorting by 0
2744 QCOMPARE(hv->sortIndicatorSection(), 0);
2745
2746 pressOnSection(1);
2747 releaseOnSection(1, OutOfRangeYOffset);
2748 // RESULT: no change, still sorting by 0
2749 QCOMPARE(hv->sortIndicatorSection(), 0);
2750
2751 pressOnSection(1);
2752 moveOntoSection(1, OutOfRangeYOffset);
2753 releaseOnSection(1, OutOfRangeYOffset);
2754 // RESULT: no change, still sorting by 0
2755 QCOMPARE(hv->sortIndicatorSection(), 0);
2756
2757 pressOnSection(1);
2758 moveOntoSection(1, OutOfRangeYOffset);
2759 moveOntoSection(1);
2760 releaseOnSection(1);
2761 // RESULT: sorting by 1
2762 QCOMPARE(hv->sortIndicatorSection(), 1);
2763
2764 pressOnSection(2);
2765 moveOntoSection(1);
2766 moveOntoSection(2);
2767 moveOntoSection(2, OutOfRangeYOffset);
2768 releaseOnSection(2, OutOfRangeYOffset);
2769 // RESULT: no change, still sorting by 1
2770 QCOMPARE(hv->sortIndicatorSection(), 1);
2771}
2772
2773void tst_QHeaderView::initialSortOrderRole()
2774{
2775 QTableView view; // ### Shadowing member view (of type QHeaderView)
2776 QStandardItemModel model(4, 3);
2777 setModelTexts(&model);
2778 QStandardItem *ascendingItem = new QStandardItem();
2779 QStandardItem *descendingItem = new QStandardItem();
2780 ascendingItem->setData(value: Qt::AscendingOrder, role: Qt::InitialSortOrderRole);
2781 descendingItem->setData(value: Qt::DescendingOrder, role: Qt::InitialSortOrderRole);
2782 model.setHorizontalHeaderItem(column: 1, item: ascendingItem);
2783 model.setHorizontalHeaderItem(column: 2, item: descendingItem);
2784 view.setModel(&model);
2785 view.setSortingEnabled(true);
2786 view.sortByColumn(column: 0, order: Qt::AscendingOrder);
2787 view.show();
2788 QVERIFY(QTest::qWaitForWindowExposed(&view));
2789
2790 const int column1Pos = view.horizontalHeader()->sectionViewportPosition(logicalIndex: 1) + 5; // +5 not to be on the handle
2791 QTest::mouseClick(widget: view.horizontalHeader()->viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(column1Pos, 0));
2792 QCOMPARE(view.horizontalHeader()->sortIndicatorSection(), 1);
2793 QCOMPARE(view.horizontalHeader()->sortIndicatorOrder(), Qt::AscendingOrder);
2794
2795 const int column2Pos = view.horizontalHeader()->sectionViewportPosition(logicalIndex: 2) + 5; // +5 not to be on the handle
2796 QTest::mouseClick(widget: view.horizontalHeader()->viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(column2Pos, 0));
2797 QCOMPARE(view.horizontalHeader()->sortIndicatorSection(), 2);
2798 QCOMPARE(view.horizontalHeader()->sortIndicatorOrder(), Qt::DescendingOrder);
2799
2800 const int column0Pos = view.horizontalHeader()->sectionViewportPosition(logicalIndex: 0) + 5; // +5 not to be on the handle
2801 QTest::mouseClick(widget: view.horizontalHeader()->viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(column0Pos, 0));
2802 QCOMPARE(view.horizontalHeader()->sortIndicatorSection(), 0);
2803 QCOMPARE(view.horizontalHeader()->sortIndicatorOrder(), Qt::AscendingOrder);
2804}
2805
2806const bool block_some_signals = true; // The test should also work with this set to false
2807const int rowcount = 500;
2808const int colcount = 10;
2809
2810static inline QString istr(int n, bool comma = true)
2811{
2812 QString s;
2813 s.setNum(n);
2814 if (comma)
2815 s += ", ";
2816 return s;
2817}
2818
2819void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_comparedata[])
2820{
2821 qint64 endtimer = timer.elapsed();
2822 const bool silentmode = true;
2823 if (!silentmode)
2824 qDebug().nospace() << "(Time:" << endtimer << ")";
2825
2826 QString sline;
2827 sline.setNum(n: cppline - 1);
2828
2829 const int p1 = 3133777; // just a prime (maybe not that random ;) )
2830 const int p2 = 135928393; // just a random large prime - a bit less than signed 32-bit
2831
2832 int sum_visual = 0;
2833 int sum_logical = 0;
2834 int sum_size = 0;
2835 int sum_hidden_size = 0;
2836 int sum_lookup_visual = 0;
2837 int sum_lookup_logical = 0;
2838
2839 int chk_visual = 1;
2840 int chk_logical = 1;
2841 int chk_sizes = 1;
2842 int chk_hidden_size = 1;
2843 int chk_lookup_visual = 1;
2844 int chk_lookup_logical = 1;
2845
2846 int header_lenght = 0;
2847 int lastindex = view->count() - 1;
2848
2849 // calculate information based on index
2850 for (int i = 0; i <= lastindex; ++i) {
2851 int visual = view->visualIndex(logicalIndex: i);
2852 int logical = view->logicalIndex(visualIndex: i);
2853 int ssize = view->sectionSize(logicalIndex: i);
2854
2855 sum_visual += visual;
2856 sum_logical += logical;
2857 sum_size += ssize;
2858
2859 if (visual >= 0) {
2860 chk_visual %= p2;
2861 chk_visual *= (visual + 1) * (i + 1) * p1;
2862 }
2863
2864 if (logical >= 0) {
2865 chk_logical %= p2;
2866 chk_logical *= (logical + 1) * (i + 1 + (logical != i) ) * p1;
2867 }
2868
2869 if (ssize >= 0) {
2870 chk_sizes %= p2;
2871 chk_sizes *= ( (ssize + 2) * (i + 1) * p1);
2872 }
2873
2874 if (view->isSectionHidden(logicalIndex: i)) {
2875 view->showSection(alogicalIndex: i);
2876 int hiddensize = view->sectionSize(logicalIndex: i);
2877 sum_hidden_size += hiddensize;
2878 chk_hidden_size %= p2;
2879 chk_hidden_size += ( (hiddensize + 1) * (i + 1) * p1);
2880 // (hiddensize + 1) in the above to differ between hidden and size 0
2881 // Though it can be changed (why isn't sections with size 0 hidden?)
2882
2883 view->hideSection(alogicalIndex: i);
2884 }
2885 }
2886
2887 // lookup indexes by pixel position
2888 const int max_lookup_count = 500;
2889 int lookup_to = view->height() + 1;
2890 if (lookup_to > max_lookup_count)
2891 lookup_to = max_lookup_count; // We limit this lookup - not to spend years when testing.
2892 // Notice that lookupTest also has its own extra test
2893 for (int u = 0; u < max_lookup_count; ++u) {
2894 int visu = view->visualIndexAt(position: u);
2895 int logi = view->logicalIndexAt(position: u);
2896 sum_lookup_visual += logi;
2897 sum_lookup_logical += visu;
2898 chk_lookup_visual %= p2;
2899 chk_lookup_visual *= ( (u + 1) * p1 * (visu + 2));
2900 chk_lookup_logical %= p2;
2901 chk_lookup_logical *= ( (u + 1) * p1 * (logi + 2));
2902 }
2903 header_lenght = view->length();
2904
2905 // visual and logical indexes.
2906 int sum_to_last_index = (lastindex * (lastindex + 1)) / 2; // == 0 + 1 + 2 + 3 + ... + lastindex
2907
2908 const bool write_calced_data = false; // Do not write calculated output (unless the test fails)
2909 if (write_calced_data) {
2910 qDebug().nospace() << "(" << cppline - 1 << ")" // << " const int precalced[] = "
2911 << " {" << chk_visual << ", " << chk_logical << ", " << chk_sizes << ", " << chk_hidden_size
2912 << ", " << chk_lookup_visual << ", " << chk_lookup_logical << ", " << header_lenght << "};";
2913 }
2914
2915 const bool sanity_checks = true;
2916 if (sanity_checks) {
2917 QString msg = QString("sanity problem at ") + sline;
2918 const QScopedArrayPointer<char> holder(QTest::toString(str: msg));
2919 const auto verifytext = holder.data();
2920
2921 QVERIFY2(m_tableview->model()->rowCount() == view->count() , verifytext);
2922 QVERIFY2(view->visualIndex(lastindex + 1) <= 0, verifytext); // there is no such index in model
2923 QVERIFY2(view->logicalIndex(lastindex + 1) <= 0, verifytext); // there is no such index in model.
2924 QVERIFY2(view->logicalIndex(lastindex + 1) <= 0, verifytext); // there is no such index in model.
2925 QVERIFY2(lastindex < 0 || view->visualIndex(0) >= 0, verifytext); // no rows or legal index
2926 QVERIFY2(lastindex < 0 || view->logicalIndex(0) >= 0, verifytext); // no rows or legal index
2927 QVERIFY2(lastindex < 0 || view->visualIndex(lastindex) >= 0, verifytext); // no rows or legal index
2928 QVERIFY2(lastindex < 0 || view->logicalIndex(lastindex) >= 0, verifytext); // no rows or legal index
2929 QVERIFY2(view->visualIndexAt(-1) == -1, verifytext);
2930 QVERIFY2(view->logicalIndexAt(-1) == -1, verifytext);
2931 QVERIFY2(view->visualIndexAt(view->length()) == -1, verifytext);
2932 QVERIFY2(view->logicalIndexAt(view->length()) == -1, verifytext);
2933 QVERIFY2(sum_visual == sum_logical, verifytext);
2934 QVERIFY2(sum_to_last_index == sum_logical, verifytext);
2935 }
2936
2937 // Semantic test
2938 const bool check_semantics = true; // Otherwise there is no 'real' test
2939 if (!check_semantics)
2940 return;
2941
2942 const int *x = precalced_comparedata;
2943
2944 QString msg = "semantic problem at " + QString(__FILE__) + " (" + sline + ")";
2945 msg += "\nThe *expected* result was : {" + istr(n: x[0]) + istr(n: x[1]) + istr(n: x[2]) + istr(n: x[3])
2946 + istr(n: x[4]) + istr(n: x[5]) + istr(n: x[6], comma: false) + QLatin1Char('}');
2947 msg += "\nThe calculated result was : {";
2948 msg += istr(n: chk_visual) + istr(n: chk_logical) + istr(n: chk_sizes) + istr(n: chk_hidden_size)
2949 + istr(n: chk_lookup_visual) + istr(n: chk_lookup_logical) + istr(n: header_lenght, comma: false) + "};";
2950
2951 const QScopedArrayPointer<char> holder(QTest::toString(str: msg));
2952 const auto verifytext = holder.data();
2953
2954 QVERIFY2(chk_visual == x[0], verifytext);
2955 QVERIFY2(chk_logical == x[1], verifytext);
2956 QVERIFY2(chk_sizes == x[2], verifytext);
2957 QVERIFY2(chk_hidden_size == x[3], verifytext);
2958 QVERIFY2(chk_lookup_visual == x[4], verifytext);
2959 QVERIFY2(chk_lookup_logical == x[5], verifytext);
2960 QVERIFY2(header_lenght == x[6], verifytext);
2961}
2962
2963void tst_QHeaderView::setupTestData(bool also_use_reset_model)
2964{
2965 QTest::addColumn<bool>(name: "updates_enabled");
2966 QTest::addColumn<bool>(name: "special_prepare");
2967 QTest::addColumn<bool>(name: "reset_model");
2968
2969 if (also_use_reset_model) {
2970 QTest::newRow(dataTag: "no_updates+normal+reset") << false << false << true;
2971 QTest::newRow(dataTag: "hasupdates+normal+reset") << true << false << true;
2972 QTest::newRow(dataTag: "no_updates+special+reset") << false << true << true;
2973 QTest::newRow(dataTag: "hasupdates+special+reset") << true << true << true;
2974 }
2975
2976 QTest::newRow(dataTag: "no_updates+normal") << false << false << false;
2977 QTest::newRow(dataTag: "hasupdates+normal") << true << false << false;
2978 QTest::newRow(dataTag: "no_updates+special") << false << true << false;
2979 QTest::newRow(dataTag: "hasupdates+special") << true << true << false;
2980}
2981
2982void tst_QHeaderView::additionalInit()
2983{
2984 m_tableview->setVerticalHeader(view);
2985
2986 QFETCH(bool, updates_enabled);
2987 QFETCH(bool, special_prepare);
2988 QFETCH(bool, reset_model);
2989
2990 m_using_reset_model = reset_model;
2991 m_special_prepare = special_prepare;
2992
2993 if (m_using_reset_model) {
2994 XResetModel *m = new XResetModel();
2995 m_tableview->setModel(m);
2996 delete model;
2997 model = m;
2998 } else {
2999 m_tableview->setModel(model);
3000 }
3001
3002 const int default_section_size = 25;
3003 view->setDefaultSectionSize(default_section_size); // Important - otherwise there will be semantic changes
3004
3005 model->clear();
3006
3007 if (special_prepare) {
3008
3009 for (int u = 0; u <= rowcount; ++u) // ensures fragmented spans in e.g Qt 4.7
3010 model->setRowCount(u);
3011
3012 model->setColumnCount(colcount);
3013 view->swapSections(first: 0, second: rowcount - 1);
3014 view->swapSections(first: 0, second: rowcount - 1); // No real change - setup visual and log-indexes
3015 view->hideSection(alogicalIndex: rowcount - 1);
3016 view->showSection(alogicalIndex: rowcount - 1); // No real change - (maybe init hidden vector)
3017 } else {
3018 model->setColumnCount(colcount);
3019 model->setRowCount(rowcount);
3020 }
3021
3022 QString s;
3023 for (int i = 0; i < model->rowCount(); ++i) {
3024 model->setData(index: model->index(row: i, column: 0), value: QVariant(i));
3025 s.setNum(n: i);
3026 s += QLatin1Char('.');
3027 s += 'a' + (i % 25);
3028 model->setData(index: model->index(row: i, column: 1), value: QVariant(s));
3029 }
3030 m_tableview->setUpdatesEnabled(updates_enabled);
3031 view->blockSignals(b: block_some_signals);
3032 timer.start();
3033}
3034
3035void tst_QHeaderView::logicalIndexAtTest()
3036{
3037 additionalInit();
3038
3039 view->swapSections(first: 4, second: 9); // Make sure that visual and logical Indexes are not just the same.
3040
3041 int check1 = 0;
3042 int check2 = 0;
3043 for (int u = 0; u < model->rowCount(); ++u) {
3044 view->resizeSection(logicalIndex: u, size: 10 + u % 30);
3045 int v = view->visualIndexAt(position: u * 29);
3046 view->visualIndexAt(position: u * 29);
3047 check1 += v;
3048 check2 += u * v;
3049 }
3050 view->resizeSection(logicalIndex: 0, size: 0); // Make sure that we have a 0 size section - before the result set
3051 view->setSectionHidden(logicalIndex: 6, hide: true); // Make sure we have a real hidden section before result set
3052
3053 //qDebug() << "logicalIndexAtTest" << check1 << check2;
3054 const int precalced_check1 = 106327;
3055 const int precalced_check2 = 29856418;
3056 QCOMPARE(precalced_check1, check1);
3057 QCOMPARE(precalced_check2, check2);
3058
3059 const int precalced_results[] = { 1145298384, -1710423344, -650981936, 372919464, -1544372176, -426463328, 12124 };
3060 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3061}
3062
3063void tst_QHeaderView::visualIndexAtTest()
3064{
3065 additionalInit();
3066
3067 view->swapSections(first: 4, second: 9); // Make sure that visual and logical Indexes are not just the same.
3068 int check1 = 0;
3069 int check2 = 0;
3070
3071 for (int u = 0; u < model->rowCount(); ++u) {
3072 view->resizeSection(logicalIndex: u, size: 3 + u % 17);
3073 int v = view->visualIndexAt(position: u * 29);
3074 check1 += v;
3075 check2 += u * v;
3076 }
3077
3078 view->resizeSection(logicalIndex: 1, size: 0); // Make sure that we have a 0 size section - before the result set
3079 view->setSectionHidden(logicalIndex: 5, hide: true); // Make sure we have a real hidden section before result set
3080
3081 //qDebug() << "visualIndexAtTest" << check1 << check2;
3082 const int precalced_check1 = 72665;
3083 const int precalced_check2 = 14015890;
3084 QCOMPARE(precalced_check1, check1);
3085 QCOMPARE(precalced_check2, check2);
3086
3087 const int precalced_results[] = { 1145298384, -1710423344, -1457520212, 169223959, 557466160, -324939600, 5453 };
3088 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3089}
3090
3091void tst_QHeaderView::hideShowTest()
3092{
3093 additionalInit();
3094
3095 for (int u = model->rowCount(); u >= 0; --u)
3096 if (u % 8 != 0)
3097 view->hideSection(alogicalIndex: u);
3098
3099 for (int u = model->rowCount(); u >= 0; --u)
3100 if (u % 3 == 0)
3101 view->showSection(alogicalIndex: u);
3102
3103 view->setSectionHidden(logicalIndex: model->rowCount(), hide: true); // invalid hide (should be ignored)
3104 view->setSectionHidden(logicalIndex: -1, hide: true); // invalid hide (should be ignored)
3105
3106 const int precalced_results[] = { -1523279360, -1523279360, -1321506816, 2105322423, 1879611280, 1879611280, 5225 };
3107 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3108}
3109
3110void tst_QHeaderView::swapSectionsTest()
3111{
3112 additionalInit();
3113
3114 for (int u = 0; u < rowcount / 2; ++u)
3115 view->swapSections(first: u, second: rowcount - u - 1);
3116
3117 for (int u = 0; u < rowcount; u += 2)
3118 view->swapSections(first: u, second: u + 1);
3119
3120 view->swapSections(first: 0, second: model->rowCount()); // invalid swapsection (should be ignored)
3121
3122 const int precalced_results[] = { -1536450048, -1774641430, -1347156568, 1, 1719705216, -240077576, 12500 };
3123 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3124}
3125
3126void tst_QHeaderView::moveSectionTest()
3127{
3128 additionalInit();
3129
3130 for (int u = 1; u < 5; ++u)
3131 view->moveSection(from: u, to: model->rowCount() - u);
3132
3133 view->moveSection(from: 2, to: model->rowCount() / 2);
3134 view->moveSection(from: 0, to: 10);
3135 view->moveSection(from: 0, to: model->rowCount() - 10);
3136
3137 view->moveSection(from: 0, to: model->rowCount()); // invalid move (should be ignored)
3138
3139 const int precalced_results[] = { 645125952, 577086896, -1347156568, 1, 1719705216, 709383416, 12500 };
3140 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3141}
3142
3143void tst_QHeaderView::defaultSizeTest()
3144{
3145 additionalInit();
3146
3147 view->hideSection(alogicalIndex: rowcount / 2);
3148 int restore_to = view->defaultSectionSize();
3149 view->setDefaultSectionSize(restore_to + 5);
3150
3151 const int precalced_results[] = { -1523279360, -1523279360, -1739688320, -1023807777, 997629696, 997629696, 14970 };
3152 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3153
3154 view->setDefaultSectionSize(restore_to);
3155}
3156
3157void tst_QHeaderView::removeTest()
3158{
3159 additionalInit();
3160
3161 view->swapSections(first: 0, second: 5);
3162 model->removeRows(row: 0, count: 1); // remove one row
3163 model->removeRows(row: 4, count: 10);
3164 model->setRowCount(model->rowCount() / 2 - 1);
3165
3166 if (m_using_reset_model) {
3167 const int precalced_results[] = { 1741224292, -135269187, -569519837, 1, 1719705216, -1184395000, 6075 };
3168 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3169 } else {
3170 const int precalced_results[] = { 289162397, 289162397, -569519837, 1, 1719705216, 1719705216, 6075 };
3171 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3172 }
3173}
3174
3175void tst_QHeaderView::insertTest()
3176{
3177 additionalInit();
3178
3179 view->swapSections(first: 0, second: model->rowCount() - 1);
3180 model->insertRows(row: 0, count: 1); // insert one row
3181 model->insertRows(row: 4, count: 10);
3182 model->setRowCount(model->rowCount() * 2 - 1);
3183
3184 if (m_using_reset_model) {
3185 const int precalced_results[] = { 2040508069, -1280266538, -150350734, 1, 1719705216, 1331312784, 25525 };
3186 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3187 } else {
3188 const int precalced_results[] = { -1909447021, 339092083, -150350734, 1, 1719705216, -969712728, 25525 };
3189 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3190 }
3191}
3192
3193void tst_QHeaderView::mixedTests()
3194{
3195 additionalInit();
3196
3197 model->setRowCount(model->rowCount() + 10);
3198
3199 for (int u = 0; u < model->rowCount(); u += 2)
3200 view->swapSections(first: u, second: u + 1);
3201
3202 view->moveSection(from: 0, to: 5);
3203
3204 for (int u = model->rowCount(); u >= 0; --u) {
3205 if (u % 5 != 0) {
3206 view->hideSection(alogicalIndex: u);
3207 QVERIFY(view->isSectionHidden(u));
3208 }
3209 if (u % 3 != 0) {
3210 view->showSection(alogicalIndex: u);
3211 QVERIFY(!view->isSectionHidden(u));
3212 }
3213 }
3214
3215 model->insertRows(row: 3, count: 7);
3216 model->removeRows(row: 8, count: 3);
3217 model->setRowCount(model->rowCount() - 10);
3218
3219 // the upper is not visible (when m_using_reset_model is true)
3220 // the lower 11 are modified due to insert/removeRows
3221 for (int u = model->rowCount() - 1; u >= 11; --u) {
3222 // when using reset, the hidden rows will *not* move
3223 const int calcMod = m_using_reset_model ? u : u - 4; // 7 added, 3 removed
3224 if (calcMod % 5 != 0 && calcMod % 3 == 0) {
3225 QVERIFY(view->isSectionHidden(u));
3226 }
3227 if (calcMod % 3 != 0) {
3228 QVERIFY(!view->isSectionHidden(u));
3229 }
3230 }
3231 if (m_using_reset_model) {
3232 const int precalced_results[] = { 898296472, 337096378, -543340640, -1964432121, -1251526424, -568618976, 9250 };
3233 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3234 } else {
3235 const int precalced_results[] = { 1911338224, 1693514365, -613398968, -1912534953, 1582159424, -1851079000, 9300 };
3236 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3237 }
3238}
3239
3240void tst_QHeaderView::resizeToContentTest()
3241{
3242 additionalInit();
3243
3244 QModelIndex idx = model->index(row: 2, column: 2);
3245 model->setData(index: idx, value: QVariant("A normal string"));
3246
3247 idx = model->index(row: 1, column: 3);
3248 model->setData(index: idx, value: QVariant("A normal longer string to test resize"));
3249
3250 QHeaderView *hh = m_tableview->horizontalHeader();
3251 hh->resizeSections(mode: QHeaderView::ResizeToContents);
3252 QVERIFY(hh->sectionSize(3) > hh->sectionSize(2));
3253
3254 for (int u = 0; u < 10; ++u)
3255 view->resizeSection(logicalIndex: u, size: 1);
3256
3257 view->resizeSections(mode: QHeaderView::ResizeToContents);
3258 QVERIFY(view->sectionSize(1) > 1);
3259 QVERIFY(view->sectionSize(2) > 1);
3260
3261 // Check minimum section size
3262 hh->setMinimumSectionSize(150);
3263 model->setData(index: idx, value: QVariant("i"));
3264 hh->resizeSections(mode: QHeaderView::ResizeToContents);
3265 QCOMPARE(hh->sectionSize(3), 150);
3266 hh->setMinimumSectionSize(-1);
3267
3268 // Check maximumSection size
3269 hh->setMaximumSectionSize(200);
3270 model->setData(index: idx, value: QVariant("This is a even longer string that is expected to be more than 200 pixels"));
3271 hh->resizeSections(mode: QHeaderView::ResizeToContents);
3272 QCOMPARE(hh->sectionSize(3), 200);
3273 hh->setMaximumSectionSize(-1);
3274
3275 view->setDefaultSectionSize(25); // To make sure our precalced data are correct. We do not know font height etc.
3276
3277 const int precalced_results[] = { -1523279360, -1523279360, -1347156568, 1, 1719705216, 1719705216, 12500 };
3278 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3279}
3280
3281void tst_QHeaderView::testStreamWithHide()
3282{
3283#ifndef QT_NO_DATASTREAM
3284 m_tableview->setVerticalHeader(view);
3285 m_tableview->setModel(model);
3286 view->setDefaultSectionSize(25);
3287 view->hideSection(alogicalIndex: 2);
3288 view->swapSections(first: 1, second: 2);
3289
3290 QByteArray s = view->saveState();
3291 view->swapSections(first: 1, second: 2);
3292 view->setDefaultSectionSize(30); // To make sure our precalced data are correct.
3293 view->restoreState(state: s);
3294
3295 const int precalced_results[] = { -1116614432, -1528653200, -1914165644, 244434607, -1111214068, 750357900, 75};
3296 calculateAndCheck(__LINE__, precalced_comparedata: precalced_results);
3297#else
3298 QSKIP("Datastream required for testStreamWithHide. Skipping this test.");
3299#endif
3300}
3301
3302void tst_QHeaderView::testStylePosition()
3303{
3304 topLevel->show();
3305 QVERIFY(QTest::qWaitForWindowExposed(topLevel));
3306
3307 protected_QHeaderView *header = static_cast<protected_QHeaderView *>(view);
3308
3309 TestStyle proxy;
3310 header->setStyle(&proxy);
3311
3312 QImage image(1, 1, QImage::Format_ARGB32);
3313 QPainter p(&image);
3314
3315 // 0, 1, 2, 3
3316 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 0);
3317 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::Beginning);
3318 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 1);
3319 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::Middle);
3320 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 2);
3321 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::Middle);
3322 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 3);
3323 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::End);
3324
3325 // (0),2,1,3
3326 view->setSectionHidden(logicalIndex: 0, hide: true);
3327 view->swapSections(first: 1, second: 2);
3328 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 1);
3329 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::Middle);
3330 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 2);
3331 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::Beginning);
3332 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 3);
3333 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::End);
3334
3335 // (1),2,0,(3)
3336 view->setSectionHidden(logicalIndex: 3, hide: true);
3337 view->setSectionHidden(logicalIndex: 0, hide: false);
3338 view->setSectionHidden(logicalIndex: 1, hide: true);
3339 view->swapSections(first: 0, second: 1);
3340 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 0);
3341 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::End);
3342 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 2);
3343 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::Beginning);
3344
3345 // (1),2,(0),(3)
3346 view->setSectionHidden(logicalIndex: 0, hide: true);
3347 header->paintSection(painter: &p, rect: view->rect(), logicalIndex: 2);
3348 QCOMPARE(proxy.lastPosition, QStyleOptionHeader::OnlyOneSection);
3349}
3350
3351void tst_QHeaderView::sizeHintCrash()
3352{
3353 QTreeView treeView;
3354 QStandardItemModel *model = new QStandardItemModel(&treeView);
3355 model->appendRow(aitem: new QStandardItem("QTBUG-48543"));
3356 treeView.setModel(model);
3357 treeView.header()->sizeHintForColumn(column: 0);
3358 treeView.header()->sizeHintForRow(row: 0);
3359}
3360
3361void tst_QHeaderView::stretchAndRestoreLastSection()
3362{
3363 QStandardItemModel m(10, 10);
3364 QTableView tv;
3365 tv.setModel(&m);
3366 tv.showMaximized();
3367
3368 const int minimumSectionSize = 20;
3369 const int defaultSectionSize = 30;
3370 const int someOtherSectionSize = 40;
3371 const int biggerSizeThanAnySection = 50;
3372
3373 QVERIFY(QTest::qWaitForWindowExposed(&tv));
3374
3375 QHeaderView &header = *tv.horizontalHeader();
3376 // set minimum size before resizeSections() is called
3377 // which is done inside setStretchLastSection
3378 header.setMinimumSectionSize(minimumSectionSize);
3379 header.setDefaultSectionSize(defaultSectionSize);
3380 header.resizeSection(logicalIndex: 9, size: someOtherSectionSize);
3381 header.setStretchLastSection(true);
3382
3383 // Default last section is larger
3384 QCOMPARE(header.sectionSize(8), defaultSectionSize);
3385 QVERIFY(header.sectionSize(9) >= biggerSizeThanAnySection);
3386
3387 // Moving last section away (restore old last section 9 - and make 8 larger)
3388 header.swapSections(first: 9, second: 8);
3389 QCOMPARE(header.sectionSize(9), someOtherSectionSize);
3390 QVERIFY(header.sectionSize(8) >= biggerSizeThanAnySection);
3391
3392 // Make section 9 the large one again
3393 header.hideSection(alogicalIndex: 8);
3394 QVERIFY(header.sectionSize(9) >= biggerSizeThanAnySection);
3395
3396 // Show section 8 again - and make that one the last one.
3397 header.showSection(alogicalIndex: 8);
3398 QVERIFY(header.sectionSize(8) > biggerSizeThanAnySection);
3399 QCOMPARE(header.sectionSize(9), someOtherSectionSize);
3400
3401 // Swap the sections so the logical indexes are equal to visible indexes again.
3402 header.moveSection(from: 9, to: 8);
3403 QCOMPARE(header.sectionSize(8), defaultSectionSize);
3404 QVERIFY(header.sectionSize(9) >= biggerSizeThanAnySection);
3405
3406 // Append sections
3407 m.setColumnCount(15);
3408 QCOMPARE(header.sectionSize(9), someOtherSectionSize);
3409 QVERIFY(header.sectionSize(14) >= biggerSizeThanAnySection);
3410
3411 // Truncate sections (remove sections with the last section)
3412 m.setColumnCount(10);
3413 QVERIFY(header.sectionSize(9) >= biggerSizeThanAnySection);
3414 for (int u = 0; u < 9; ++u)
3415 QCOMPARE(header.sectionSize(u), defaultSectionSize);
3416
3417 // Insert sections
3418 m.insertColumns(column: 2, count: 2);
3419 QCOMPARE(header.sectionSize(9), defaultSectionSize);
3420 QCOMPARE(header.sectionSize(10), defaultSectionSize);
3421 QVERIFY(header.sectionSize(11) >= biggerSizeThanAnySection);
3422
3423 // Append an extra section and check restore
3424 m.setColumnCount(m.columnCount() + 1);
3425 QCOMPARE(header.sectionSize(11), someOtherSectionSize);
3426 QVERIFY(header.sectionSize(12) >= biggerSizeThanAnySection);
3427
3428 // Remove some sections but not the last one.
3429 m.removeColumns(column: 2, count: 2);
3430 QCOMPARE(header.sectionSize(9), someOtherSectionSize);
3431 QVERIFY(header.sectionSize(10) >= biggerSizeThanAnySection);
3432 for (int u = 0; u < 9; ++u)
3433 QCOMPARE(header.sectionSize(u), defaultSectionSize);
3434
3435 // Empty the header and start over with some more tests
3436 m.setColumnCount(0);
3437 m.setColumnCount(10);
3438 QVERIFY(header.sectionSize(9) >= biggerSizeThanAnySection);
3439
3440 // Check resize of the last section
3441 header.resizeSection(logicalIndex: 9, size: someOtherSectionSize);
3442 QVERIFY(header.sectionSize(9) >= biggerSizeThanAnySection); // It should still be stretched
3443 header.swapSections(first: 9, second: 8);
3444 QCOMPARE(header.sectionSize(9), someOtherSectionSize);
3445
3446 // Restore the order
3447 header.swapSections(first: 9, second: 8);
3448 QVERIFY(header.sectionSize(9) >= biggerSizeThanAnySection);
3449
3450 // Hide the last 3 sections and test stretch last section on swap/move
3451 // when hidden sections with a larger visual index exists.
3452 header.hideSection(alogicalIndex: 7);
3453 header.hideSection(alogicalIndex: 8);
3454 header.hideSection(alogicalIndex: 9);
3455 QVERIFY(header.sectionSize(6) >= biggerSizeThanAnySection);
3456 header.moveSection(from: 2, to: 7);
3457 QVERIFY(header.sectionSize(2) >= biggerSizeThanAnySection);
3458 header.swapSections(first: 1, second: 8);
3459 QCOMPARE(header.sectionSize(2), defaultSectionSize);
3460 QVERIFY(header.sectionSize(1) >= biggerSizeThanAnySection);
3461
3462 // Inserting sections 2
3463 m.setColumnCount(0);
3464 m.setColumnCount(10);
3465 header.resizeSection(logicalIndex: 1, size: someOtherSectionSize);
3466 header.swapSections(first: 1, second: 9);
3467 m.insertColumns(column: 9, count: 2);
3468 header.swapSections(first: 1, second: 11);
3469 QCOMPARE(header.sectionSize(1), someOtherSectionSize);
3470
3471 // Clear and re-add. This triggers a different code path than seColumnCount(0)
3472 m.clear();
3473 m.setColumnCount(3);
3474 QVERIFY(header.sectionSize(2) >= biggerSizeThanAnySection);
3475
3476 // Test import/export of the original (not stretched) sectionSize.
3477 m.setColumnCount(0);
3478 m.setColumnCount(10);
3479 header.resizeSection(logicalIndex: 9, size: someOtherSectionSize);
3480 QVERIFY(header.sectionSize(9) >= biggerSizeThanAnySection);
3481 QByteArray b = header.saveState();
3482 m.setColumnCount(0);
3483 m.setColumnCount(10);
3484 header.restoreState(state: b);
3485 header.setStretchLastSection(false);
3486 QCOMPARE(header.sectionSize(9), someOtherSectionSize);
3487}
3488
3489void tst_QHeaderView::testMinMaxSectionSize_data()
3490{
3491 QTest::addColumn<bool>(name: "stretchLastSection");
3492 QTest::addRow(format: "stretched") << true;
3493 QTest::addRow(format: "not stretched") << false;
3494}
3495
3496void tst_QHeaderView::testMinMaxSectionSize()
3497{
3498 QFETCH(bool, stretchLastSection);
3499
3500 QStandardItemModel m(5, 5);
3501 QTableView tv;
3502 tv.setModel(&m);
3503 tv.show();
3504
3505 const int sectionSizeMin = 20;
3506 const int sectionSizeMax = 40;
3507 const int defaultSectionSize = 30;
3508
3509 QVERIFY(QTest::qWaitForWindowExposed(&tv));
3510
3511 QHeaderView &header = *tv.horizontalHeader();
3512 header.setMinimumSectionSize(sectionSizeMin);
3513 header.setMaximumSectionSize(sectionSizeMax);
3514 // check bounds for default section size
3515 header.setDefaultSectionSize(sectionSizeMin - 1);
3516 QCOMPARE(header.defaultSectionSize(), sectionSizeMin);
3517 header.setDefaultSectionSize(sectionSizeMax + 1);
3518 QCOMPARE(header.defaultSectionSize(), sectionSizeMax);
3519
3520 header.setDefaultSectionSize(defaultSectionSize);
3521 QCOMPARE(header.defaultSectionSize(), defaultSectionSize);
3522 header.setStretchLastSection(stretchLastSection);
3523 QCOMPARE(header.stretchLastSection(), stretchLastSection);
3524
3525 // check defaults
3526 QCOMPARE(header.sectionSize(0), defaultSectionSize);
3527 QCOMPARE(header.sectionSize(3), defaultSectionSize);
3528
3529 // do not go above maxSectionSize
3530 header.resizeSection(logicalIndex: 0, size: sectionSizeMax + 1);
3531 QCOMPARE(header.sectionSize(0), sectionSizeMax);
3532
3533 // do not go below minSectionSize
3534 header.resizeSection(logicalIndex: 0, size: sectionSizeMin - 1);
3535 QCOMPARE(header.sectionSize(0), sectionSizeMin);
3536
3537 // change section size on max change
3538 header.setMinimumSectionSize(sectionSizeMin);
3539 header.setMaximumSectionSize(sectionSizeMax);
3540 header.resizeSection(logicalIndex: 0, size: sectionSizeMax);
3541 QCOMPARE(header.sectionSize(0), sectionSizeMax);
3542 header.setMaximumSectionSize(defaultSectionSize);
3543 QTRY_COMPARE(header.sectionSize(0), defaultSectionSize);
3544
3545 // change section size on min change
3546 header.setMinimumSectionSize(sectionSizeMin);
3547 header.setMaximumSectionSize(sectionSizeMax);
3548 header.resizeSection(logicalIndex: 0, size: sectionSizeMin);
3549 QCOMPARE(header.sectionSize(0), sectionSizeMin);
3550 header.setMinimumSectionSize(defaultSectionSize);
3551 QTRY_COMPARE(header.sectionSize(0), defaultSectionSize);
3552}
3553
3554void tst_QHeaderView::testResetCachedSizeHint()
3555{
3556 QtTestModel model(10, 10);
3557 QTableView tv;
3558 tv.setModel(&model);
3559 tv.show();
3560 QVERIFY(QTest::qWaitForWindowExposed(&tv));
3561
3562 QSize s1 = tv.horizontalHeader()->sizeHint();
3563 model.setMultiLineHeader(true);
3564 QSize s2 = tv.horizontalHeader()->sizeHint();
3565 model.setMultiLineHeader(false);
3566 QSize s3 = tv.horizontalHeader()->sizeHint();
3567 QCOMPARE(s1, s3);
3568 QVERIFY(s1 != s2);
3569}
3570
3571
3572class StatusTipHeaderView : public QHeaderView
3573{
3574 Q_OBJECT
3575public:
3576 using QHeaderView::QHeaderView;
3577 QString statusTipText;
3578 bool gotStatusTipEvent = false;
3579protected:
3580 bool event(QEvent *e) override
3581 {
3582 if (e->type() == QEvent::StatusTip) {
3583 gotStatusTipEvent = true;
3584 statusTipText = static_cast<QStatusTipEvent *>(e)->tip();
3585 }
3586 return QHeaderView::event(e);
3587 }
3588};
3589
3590void tst_QHeaderView::statusTips()
3591{
3592 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3593 QSKIP("Wayland: This fails. Figure out why.");
3594
3595 StatusTipHeaderView headerView(Qt::Horizontal);
3596 QtTestModel model(5, 5);
3597 headerView.setModel(&model);
3598 headerView.viewport()->setMouseTracking(true);
3599 headerView.setGeometry(QRect(QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)),
3600 QSize(500, 500)));
3601 headerView.show();
3602 QApplication::setActiveWindow(&headerView);
3603 QVERIFY(QTest::qWaitForWindowActive(&headerView));
3604
3605 // Ensure it is moved away first and then moved to the relevant section
3606 QTest::mouseMove(widget: QApplication::desktop(),
3607 pos: headerView.rect().bottomLeft() + QPoint(20, 20));
3608 QPoint centerPoint = QRect(headerView.sectionPosition(logicalIndex: 0), 0,
3609 headerView.sectionSize(logicalIndex: 0), headerView.height()).center();
3610 QTest::mouseMove(window: headerView.windowHandle(), pos: centerPoint);
3611 QTRY_VERIFY(headerView.gotStatusTipEvent);
3612 QCOMPARE(headerView.statusTipText, QLatin1String("[0,0,0] -- Header"));
3613
3614 headerView.gotStatusTipEvent = false;
3615 headerView.statusTipText.clear();
3616 centerPoint = QRect(headerView.sectionPosition(logicalIndex: 1), 0,
3617 headerView.sectionSize(logicalIndex: 1), headerView.height()).center();
3618 QTest::mouseMove(window: headerView.windowHandle(), pos: centerPoint);
3619 QTRY_VERIFY(headerView.gotStatusTipEvent);
3620 QCOMPARE(headerView.statusTipText, QLatin1String("[0,1,0] -- Header"));
3621}
3622
3623void tst_QHeaderView::testRemovingColumnsViaLayoutChanged()
3624{
3625 const int persistentSectionSize = 101;
3626
3627 QtTestModel model(5, 5);
3628 view->setModel(&model);
3629 for (int i = 0; i < model.cols; ++i)
3630 view->resizeSection(logicalIndex: i, size: persistentSectionSize + i);
3631 model.cleanup(); // down to 3 via layoutChanged (not columnsRemoved)
3632 for (int j = 0; j < model.cols; ++j)
3633 QCOMPARE(view->sectionSize(j), persistentSectionSize + j);
3634 // The main point of this test is that the section-size restoring code didn't go out of bounds.
3635}
3636
3637void tst_QHeaderView::testModelMovingColumns()
3638{
3639 QtTestModel model(10, 10);
3640 QHeaderView hv(Qt::Horizontal);
3641 hv.setModel(&model);
3642 hv.resizeSections(mode: QHeaderView::ResizeToContents);
3643 hv.show();
3644
3645 QPersistentModelIndex index3 = model.index(row: 0, column: 3);
3646 model.moveColumn(from: 3, to: 1);
3647 QCOMPARE(index3.column(), 1);
3648}
3649
3650QTEST_MAIN(tst_QHeaderView)
3651#include "tst_qheaderview.moc"
3652

source code of qtbase/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp