QHeaderView: respect min/maximumSectionSize property

QHeaderView::resizeSection() did not check if the given section size is
inside the min/max property bounds.
Also on calling setMin/MaximumSectionSize() the current section sizes
were not checked if they are inside the new given bounds.
This is a small behavior change when a user is setting the section size
via resizeSection() without respecting the min/maxSectionSizes.

Task-number: QTBUG-64173
Change-Id: Ia9c9eebf058d60c776ab5f8f8336642013ec553f
Reviewed-by: Thorbjørn Lund Martsum <tmartsum@gmail.com>
This commit is contained in:
Christian Ehrlicher 2017-10-31 16:05:58 +01:00
parent 1c0ac8c4b8
commit 20604ea554
3 changed files with 136 additions and 9 deletions

View File

@ -886,6 +886,10 @@ void QHeaderView::resizeSection(int logical, int size)
if (logical < 0 || logical >= count() || size < 0 || size > maxSizeSection)
return;
// make sure to not exceed bounds when setting size programmatically
if (size > 0)
size = qBound(minimumSectionSize(), size, maximumSectionSize());
if (isSectionHidden(logical)) {
d->hiddenSectionSize.insert(logical, size);
return;
@ -1661,9 +1665,25 @@ void QHeaderView::setMinimumSectionSize(int size)
Q_D(QHeaderView);
if (size < -1 || size > maxSizeSection)
return;
// larger new min size - check current section sizes
const bool needSizeCheck = size > d->minimumSectionSize;
d->minimumSectionSize = size;
if (d->minimumSectionSize > maximumSectionSize())
d->maximumSectionSize = size;
setMaximumSectionSize(size);
if (needSizeCheck) {
if (d->hasAutoResizeSections()) {
d->doDelayedResizeSections();
} else {
for (int visual = 0; visual < d->sectionCount(); ++visual) {
if (d->isVisualIndexHidden(visual))
continue;
if (d->headerSectionSize(visual) < d->minimumSectionSize)
resizeSection(logicalIndex(visual), size);
}
}
}
}
/*!
@ -1700,7 +1720,22 @@ void QHeaderView::setMaximumSectionSize(int size)
if (minimumSectionSize() > size)
d->minimumSectionSize = size;
// smaller new max size - check current section sizes
const bool needSizeCheck = size < d->maximumSectionSize;
d->maximumSectionSize = size;
if (needSizeCheck) {
if (d->hasAutoResizeSections()) {
d->doDelayedResizeSections();
} else {
for (int visual = 0; visual < d->sectionCount(); ++visual) {
if (d->isVisualIndexHidden(visual))
continue;
if (d->headerSectionSize(visual) > d->maximumSectionSize)
resizeSection(logicalIndex(visual), size);
}
}
}
}
@ -3431,9 +3466,11 @@ void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool
int logicalIndex = q->logicalIndex(i);
sectionSize = qMax(viewSectionSizeHint(logicalIndex),
q->sectionSizeHint(logicalIndex));
if (sectionSize > q->maximumSectionSize())
sectionSize = q->maximumSectionSize();
}
sectionSize = qBound(q->minimumSectionSize(),
sectionSize,
q->maximumSectionSize());
section_sizes.append(sectionSize);
lengthToStretch -= sectionSize;
}

View File

@ -239,13 +239,15 @@ private slots:
void testStreamWithHide();
void testStylePosition();
void stretchAndRestoreLastSection();
void testMinMaxSectionSizeStretched();
void testMinMaxSectionSizeNotStretched();
void sizeHintCrash();
protected:
void setupTestData(bool use_reset_model = false);
void additionalInit();
void calculateAndCheck(int cppline, const int precalced_comparedata[]);
void testMinMaxSectionSize(bool stretchLastSection);
QWidget *topLevel;
QHeaderView *view;
@ -402,6 +404,7 @@ void tst_QHeaderView::init()
QCOMPARE(view->length(), 0);
QCOMPARE(view->sizeHint(), QSize(0,0));
QCOMPARE(view->sectionSizeHint(0), -1);
view->setMinimumSectionSize(0); // system default min size can be to large
/*
model = new QStandardItemModel(1, 1);
@ -1604,6 +1607,7 @@ static QByteArray savedState()
QStandardItemModel m(4, 4);
QHeaderView h1(Qt::Horizontal);
h1.setModel(&m);
h1.setMinimumSectionSize(0); // system default min size can be to large
h1.swapSections(0, 2);
h1.resizeSection(1, 10);
h1.setSortIndicatorShown(true);
@ -2194,6 +2198,7 @@ void tst_QHeaderView::task248050_hideRow()
//this is the sequence of events that make the task fail
protected_QHeaderView header(Qt::Vertical);
QStandardItemModel model(0, 1);
header.setMinimumSectionSize(0); // system default min size can be to large
header.setStretchLastSection(false);
header.setDefaultSectionSize(17);
header.setModel(&model);
@ -3182,5 +3187,76 @@ void tst_QHeaderView::stretchAndRestoreLastSection()
QCOMPARE(header.sectionSize(9), someOtherSectionSize);
}
void tst_QHeaderView::testMinMaxSectionSizeStretched()
{
testMinMaxSectionSize(true);
}
void tst_QHeaderView::testMinMaxSectionSizeNotStretched()
{
testMinMaxSectionSize(false);
}
static void waitFor(const std::function<bool()> &func)
{
for (int i = 0; i < 100; i++)
{
if (func())
return;
QTest::qWait(10);
}
}
void tst_QHeaderView::testMinMaxSectionSize(bool stretchLastSection)
{
QStandardItemModel m(5, 5);
QTableView tv;
tv.setModel(&m);
tv.show();
const int sectionSizeMin = 20;
const int sectionSizeMax = 40;
const int defaultSectionSize = 30;
QVERIFY(QTest::qWaitForWindowExposed(&tv));
QHeaderView &header = *tv.horizontalHeader();
header.setMinimumSectionSize(sectionSizeMin);
header.setMaximumSectionSize(sectionSizeMax);
header.setDefaultSectionSize(defaultSectionSize);
header.setStretchLastSection(stretchLastSection);
// check defaults
QCOMPARE(header.sectionSize(0), defaultSectionSize);
QCOMPARE(header.sectionSize(3), defaultSectionSize);
// do not go above maxSectionSize
header.resizeSection(0, sectionSizeMax + 1);
QCOMPARE(header.sectionSize(0), sectionSizeMax);
// do not go below minSectionSize
header.resizeSection(0, sectionSizeMin - 1);
QCOMPARE(header.sectionSize(0), sectionSizeMin);
// change section size on max change
header.setMinimumSectionSize(sectionSizeMin);
header.setMaximumSectionSize(sectionSizeMax);
header.resizeSection(0, sectionSizeMax);
QCOMPARE(header.sectionSize(0), sectionSizeMax);
header.setMaximumSectionSize(defaultSectionSize);
waitFor([this, &header, defaultSectionSize]() { return header.sectionSize(0) == defaultSectionSize; });
QCOMPARE(header.sectionSize(0), defaultSectionSize);
// change section size on min change
header.setMinimumSectionSize(sectionSizeMin);
header.setMaximumSectionSize(sectionSizeMax);
header.resizeSection(0, sectionSizeMin);
QCOMPARE(header.sectionSize(0), sectionSizeMin);
header.setMinimumSectionSize(defaultSectionSize);
waitFor([this, &header, defaultSectionSize]() { return header.sectionSize(0) == defaultSectionSize; });
QCOMPARE(header.sectionSize(0), defaultSectionSize);
}
QTEST_MAIN(tst_QHeaderView)
#include "tst_qheaderview.moc"

View File

@ -426,6 +426,9 @@ public:
this, SLOT(slotCurrentChanged(QModelIndex,QModelIndex)));
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(itemSelectionChanged(QItemSelection,QItemSelection)));
// Allow small sections in this test, since this test was made before we correctly enforced minimum sizes.
horizontalHeader()->setMinimumSectionSize(0);
verticalHeader()->setMinimumSectionSize(0);
}
// enum CursorAction and moveCursor() are protected in QTableView.
@ -738,6 +741,8 @@ void tst_QTableView::headerSections()
QHeaderView *vheader = view.verticalHeader();
view.setModel(&model);
hheader->setMinimumSectionSize(columnWidth);
vheader->setMinimumSectionSize(rowHeight);
view.show();
hheader->doItemsLayout();
@ -1147,6 +1152,9 @@ void tst_QTableView::moveCursor()
QtTestTableView view;
view.setModel(&model);
// we have to make sure that PgUp/PgDown can scroll to the bottom/top
view.resize(view.horizontalHeader()->length() + 50,
view.verticalHeader()->length() + 50);
view.hideRow(hideRow);
view.hideColumn(hideColumn);
if (moveColumn.first != moveColumn.second)
@ -2085,6 +2093,8 @@ void tst_QTableView::visualRect()
QTableView view;
view.setModel(&model);
view.horizontalHeader()->setMinimumSectionSize(0);
view.verticalHeader()->setMinimumSectionSize(0);
// Make sure that it has 1 pixel between each cell.
view.setGridStyle(Qt::SolidLine);
for (int i = 0; i < view.verticalHeader()->count(); ++i)
@ -3533,6 +3543,9 @@ void tst_QTableView::editSpanFromDirections()
TableViewWithCursorExposed view;
view.setModel(model.data());
// we have to make sure that PgUp/PgDown can scroll to the bottom/top
view.resize(view.horizontalHeader()->length() + 50,
view.verticalHeader()->length() + 50);
view.setSpan(row, column, rowSpan, columnSpan);
view.show();
QVERIFY(QTest::qWaitForWindowActive(&view));
@ -3978,7 +3991,7 @@ void tst_QTableView::mouseWheel_data()
<< 10 + qApp->wheelScrollLines() << 10 + qApp->wheelScrollLines();
QTest::newRow("scroll down per pixel")
<< int(QAbstractItemView::ScrollPerPixel) << -120
<< 10 + qApp->wheelScrollLines() * 89 << 10 + qApp->wheelScrollLines() * 28;
<< 10 + qApp->wheelScrollLines() * 91 << 10 + qApp->wheelScrollLines() * 46;
}
void tst_QTableView::mouseWheel()
@ -3992,16 +4005,17 @@ void tst_QTableView::mouseWheel()
QWidget topLevel;
QtTestTableView view(&topLevel);
view.resize(500, 500);
for (int r = 0; r < 100; ++r)
view.setRowHeight(r, 50);
for (int c = 0; c < 100; ++c)
view.setColumnWidth(c, 100);
topLevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
view.setModel(&model);
for (int r = 0; r < 100; ++r)
view.setRowHeight(r, 50);
for (int c = 0; c < 100; ++c)
view.setColumnWidth(c, 100);
view.setHorizontalScrollMode((QAbstractItemView::ScrollMode)scrollMode);
view.setVerticalScrollMode((QAbstractItemView::ScrollMode)scrollMode);
view.horizontalScrollBar()->setValue(10);