// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include #include #include #include //#include #include #include #include #include #include #include using namespace QTestPrivate; class tst_QGridLayout : public QObject { Q_OBJECT private slots: void cleanup(); void getItemPosition(); void itemAtPosition(); void badDistributionBug(); void setMinAndMaxSize(); void spacingAndSpacers(); void spacingsAndMargins(); void spacingsAndMargins_data(); void minMaxSize_data(); void minMaxSize(); void styleDependentSpacingsAndMargins_data(); void styleDependentSpacingsAndMargins(); void layoutSpacing_data(); void layoutSpacing(); void spacing(); void spacerWithSpacing(); void contentsRect(); void distributeMultiCell(); void taskQTBUG_27420_takeAtShouldUnparentLayout(); void taskQTBUG_40609_addingWidgetToItsOwnLayout(); void taskQTBUG_40609_addingLayoutToItself(); void taskQTBUG_52357_spacingWhenItemIsHidden(); void taskQTBUG_91261_itemIndexRange(); void replaceWidget(); void dontCrashWhenExtendsToEnd(); }; static inline int visibleTopLevelWidgetCount() { int result= 0; foreach (const QWidget *topLevel, QApplication::topLevelWidgets()) { if (topLevel->isVisible()) ++result; } return result; } void tst_QGridLayout::cleanup() { // Verify that no visible top levels are leaked. Cannot check for // topLevelWidgets().isEmpty() here since the data driven test layoutSpacing() // will appear to "leak" top levels due to it creating widgets in the test data. QCOMPARE(visibleTopLevelWidgetCount(), 0); } class ItemTestWidget : public QWidget { public: ItemTestWidget(); QGridLayout *testLayout; QWidget *w1; QWidget *w2; QWidget *w3; QSpacerItem *sp; }; ItemTestWidget::ItemTestWidget() : testLayout(new QGridLayout(this)) , w1(new QWidget(this)) , w2(new QWidget(this)) , w3(new QWidget(this)) , sp(new QSpacerItem(4, 4)) { setObjectName("testWidget"); setWindowTitle(QTest::currentTestFunction()); w1->setPalette(QPalette(Qt::red)); testLayout->addWidget(w1, 0, 0); testLayout->addWidget(w2, 1, 1, 2, 2); w2->setPalette(QPalette(Qt::green)); testLayout->addWidget(w3, 0, 1, 1, 2); w3->setPalette(QPalette(Qt::blue)); testLayout->addItem(sp, 1, 3, 2, 1); } void tst_QGridLayout::getItemPosition() { ItemTestWidget testWidget; testWidget.resize(200, 200); testWidget.show(); QLayoutItem *item; int counter = 0; bool seenW1 = false; bool seenW2 = false; bool seenW3 = false; bool seenSpacer = false; while ((item = testWidget.testLayout->itemAt(counter))) { QWidget *w = item->widget(); int r,c,rs,cs; testWidget.testLayout->getItemPosition(counter, &r, &c, &rs, &cs); // qDebug() << "item" << counter << "has" <itemAtPosition(row, col); QVERIFY(item == table[row][col] || (item && item->widget() == table[row][col])); } } } #include "ui_sortdialog.h" void tst_QGridLayout::badDistributionBug() { QDialog dialog; Ui::SortDialog ui; ui.setupUi(&dialog); ui.gridLayout->setContentsMargins(0, 0, 0, 0); ui.gridLayout->setSpacing(0); ui.vboxLayout->setContentsMargins(0, 0, 0, 0); ui.vboxLayout->setSpacing(0); ui.okButton->setFixedHeight(20); ui.moreButton->setFixedHeight(20); ui.primaryGroupBox->setAttribute(Qt::WA_LayoutUsesWidgetRect); ui.primaryGroupBox->setFixedHeight(200); QSize minSize = dialog.layout()->minimumSize(); QCOMPARE(minSize.height(), 200); } void tst_QGridLayout::setMinAndMaxSize() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("This test crashes on Wayland, see also QTBUG-107184"); QWidget widget; setFrameless(&widget); QGridLayout layout(&widget); layout.setContentsMargins(0, 0, 0, 0); layout.setSpacing(0); layout.setSizeConstraint(QLayout::SetMinAndMaxSize); widget.show(); QWidget leftChild; leftChild.setPalette(QPalette(Qt::red)); leftChild.setMinimumSize(100, 100); leftChild.setMaximumSize(200, 200); layout.addWidget(&leftChild, 0, 0); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumSize(), leftChild.minimumSize()); QCOMPARE(widget.maximumSize(), leftChild.maximumSize()); QWidget rightChild; rightChild.setPalette(QPalette(Qt::green)); rightChild.setMinimumSize(100, 100); rightChild.setMaximumSize(200, 200); layout.addWidget(&rightChild, 0, 2); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumWidth(), leftChild.minimumWidth() + rightChild.minimumWidth()); QCOMPARE(widget.minimumHeight(), qMax(leftChild.minimumHeight(), rightChild.minimumHeight())); QCOMPARE(widget.maximumWidth(), leftChild.maximumWidth() + rightChild.maximumWidth()); QCOMPARE(widget.maximumHeight(), qMax(leftChild.maximumHeight(), rightChild.maximumHeight())); static const int colMin = 100; layout.setColumnMinimumWidth(1, colMin); QCOMPARE(layout.columnMinimumWidth(1), colMin); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumWidth(), leftChild.minimumWidth() + rightChild.minimumWidth() + colMin); QCOMPARE(widget.maximumWidth(), leftChild.maximumWidth() + rightChild.maximumWidth() + colMin); QCOMPARE(widget.minimumHeight(), qMax(leftChild.minimumHeight(), rightChild.minimumHeight())); QCOMPARE(widget.maximumHeight(), qMax(leftChild.maximumHeight(), rightChild.maximumHeight())); layout.setColumnStretch(1,1); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumWidth(), leftChild.minimumWidth() + rightChild.minimumWidth() + colMin); QCOMPARE(widget.maximumWidth(), QLAYOUTSIZE_MAX); QCOMPARE(widget.minimumHeight(), qMax(leftChild.minimumHeight(), rightChild.minimumHeight())); QCOMPARE(widget.maximumHeight(), qMax(leftChild.maximumHeight(), rightChild.maximumHeight())); layout.setColumnStretch(1,0); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumWidth(), leftChild.minimumWidth() + rightChild.minimumWidth() + colMin); QCOMPARE(widget.maximumWidth(), leftChild.maximumWidth() + rightChild.maximumWidth() + colMin); QCOMPARE(widget.minimumHeight(), qMax(leftChild.minimumHeight(), rightChild.minimumHeight())); QCOMPARE(widget.maximumHeight(), qMax(leftChild.maximumHeight(), rightChild.maximumHeight())); layout.setColumnMinimumWidth(1, 0); static const int spacerS = 250; QSpacerItem *spacer = new QSpacerItem(spacerS, spacerS); layout.addItem(spacer, 0, 1); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumWidth(), leftChild.minimumWidth() + rightChild.minimumWidth() + spacerS); QCOMPARE(widget.maximumWidth(), QLAYOUTSIZE_MAX); QCOMPARE(widget.minimumHeight(), qMax(qMax(leftChild.minimumHeight(), rightChild.minimumHeight()), spacerS)); QCOMPARE(widget.maximumHeight(), qMax(leftChild.maximumHeight(), rightChild.maximumHeight())); spacer->changeSize(spacerS, spacerS, QSizePolicy::Fixed, QSizePolicy::Minimum); layout.invalidate(); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumWidth(), leftChild.minimumWidth() + rightChild.minimumWidth() + spacerS); QCOMPARE(widget.maximumWidth(), leftChild.maximumWidth() + rightChild.maximumWidth() + spacerS); layout.removeItem(spacer); delete spacer; spacer = nullptr; rightChild.hide(); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumSize(), leftChild.minimumSize()); QCOMPARE(widget.maximumSize(), leftChild.maximumSize()); rightChild.show(); layout.removeWidget(&rightChild); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumSize(), leftChild.minimumSize()); QCOMPARE(widget.maximumSize(), leftChild.maximumSize()); QWidget bottomChild(&widget); bottomChild.setPalette(QPalette(Qt::green)); bottomChild.setMinimumSize(100, 100); bottomChild.setMaximumSize(200, 200); layout.addWidget(&bottomChild, 1, 0); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumHeight(), leftChild.minimumHeight() + bottomChild.minimumHeight()); QCOMPARE(widget.minimumWidth(), qMax(leftChild.minimumWidth(), bottomChild.minimumWidth())); QCOMPARE(widget.maximumHeight(), leftChild.maximumHeight() + bottomChild.maximumHeight()); QCOMPARE(widget.maximumWidth(), qMax(leftChild.maximumWidth(), bottomChild.maximumWidth())); bottomChild.hide(); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumSize(), leftChild.minimumSize()); QCOMPARE(widget.maximumSize(), leftChild.maximumSize()); bottomChild.show(); layout.removeWidget(&bottomChild); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.minimumSize(), leftChild.minimumSize()); QCOMPARE(widget.maximumSize(), leftChild.maximumSize()); } class SizeHinter : public QWidget { public: SizeHinter(const QSize &s, QWidget *parent = nullptr) : QWidget(parent), sh(s) { } SizeHinter(int w, int h, QWidget *parent = nullptr) : QWidget(parent), sh(QSize(w,h)) {} void setSizeHint(QSize s) { sh = s; } QSize sizeHint() const override { return sh; } private: QSize sh; }; void tst_QGridLayout::spacingAndSpacers() { QWidget widget; setFrameless(&widget); QGridLayout layout(&widget); layout.setContentsMargins(0, 0, 0, 0); layout.setSpacing(0); widget.show(); QSize expectedSizeHint; SizeHinter leftChild(100,100); leftChild.setPalette(QPalette(Qt::red)); layout.addWidget(&leftChild, 0, 0); QApplication::sendPostedEvents(0, 0); expectedSizeHint = leftChild.sizeHint(); QCOMPARE(widget.sizeHint(), expectedSizeHint); SizeHinter rightChild(200,100); rightChild.setPalette(QPalette(Qt::green)); layout.addWidget(&rightChild, 0, 2); QApplication::sendPostedEvents(0, 0); QCOMPARE(rightChild.sizeHint(), QSize(200,100)); expectedSizeHint += QSize(rightChild.sizeHint().width(), 0); QCOMPARE(widget.sizeHint(), expectedSizeHint); layout.setColumnMinimumWidth(1, 100); widget.adjustSize(); expectedSizeHint += QSize(100,0); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.sizeHint(), expectedSizeHint); rightChild.hide(); QApplication::sendPostedEvents(0, 0); expectedSizeHint -= QSize(rightChild.sizeHint().width(), 0); QCOMPARE(widget.sizeHint(), expectedSizeHint); layout.setColumnMinimumWidth(1, 0); expectedSizeHint -= QSize(100, 0); QCOMPARE(widget.sizeHint(), expectedSizeHint); rightChild.show(); layout.removeWidget(&rightChild); QApplication::sendPostedEvents(0, 0); QCOMPARE(widget.sizeHint(), expectedSizeHint); } class Qt42Style : public QProxyStyle { Q_OBJECT public: Qt42Style() : QProxyStyle(QStyleFactory::create("windows")) { } virtual int pixelMetric(PixelMetric metric, const QStyleOption * option = nullptr, const QWidget * widget = nullptr ) const override; int spacing = 6; int margin = 9; int margin_toplevel = 11; }; int Qt42Style::pixelMetric(PixelMetric metric, const QStyleOption * option, const QWidget * widget) const { switch (metric) { case PM_LayoutLeftMargin: case PM_LayoutRightMargin: case PM_LayoutTopMargin: case PM_LayoutBottomMargin: if (option && option->state & State_Window) return margin_toplevel; if (widget && widget->isWindow()) return margin_toplevel; return margin; case PM_LayoutHorizontalSpacing: case PM_LayoutVerticalSpacing: return spacing; default: break; } return QProxyStyle::pixelMetric(metric, option, widget); } typedef QList PointList; class SizeHinterFrame : public QLabel { public: SizeHinterFrame(QWidget *parent = nullptr) : QLabel(parent) { init(-1); } SizeHinterFrame(const QSize &s, int numPixels = -1) : QLabel(0), sh(s) { init(numPixels); } SizeHinterFrame(int w, int h) : QLabel(0), sh(QSize(w,h)) { init(-1); } void setSizeHint(const QSize &s) { sh = s; } QSize sizeHint() const override { return sh; } void setMinimumSizeHint(const QSize &s) { msh = s; } QSize minimumSizeHint() const override { return msh; } virtual int heightForWidth(int width) const override; void setNumberOfPixels(int numPixels) { m_numPixels = numPixels; QSizePolicy sp = sizePolicy(); sp.setHeightForWidth(m_numPixels != -1); setSizePolicy(sp); } private: void init(int numPixels = -1){ setText(QLatin1Char('(') + QString::number(sh.width()) + QLatin1Char(',') + QString::number(sh.height()) + QLatin1Char(')')); setFrameStyle(QFrame::Box | QFrame::Plain); setNumberOfPixels(numPixels); } private: QSize sh; QSize msh; int m_numPixels; }; int SizeHinterFrame::heightForWidth(int width) const { // Special hack if m_numPixels == -2 then we report that we are heightForWidth aware, but we // return sizeHint().width() so that it should be laid out as if we had't any hfw. // This enables us to run the tests twice and to see if we get the same results with hfw as without hfw. if (m_numPixels == -2) { return sizeHint().height(); } if (m_numPixels == -1 || width == 0) return -1; // this widget should always cover the same amount of pixels (provided that we don't get any rounding errors) return (m_numPixels)/width; } void tst_QGridLayout::spacingsAndMargins_data() { // input QTest::addColumn("columns"); QTest::addColumn("rows"); QTest::addColumn("sizehint"); // expected QTest::addColumn("expectedpositions"); int child_offset_y = 11 + 100 + 6 + 9 ; QTest::newRow("1x1 grid") << 1 << 1 << QSize(100, 100) << (PointList() // toplevel << QPoint( 11, 11) // children << QPoint( 20, child_offset_y) ); QTest::newRow("2x1 grid") << 2 << 1 << QSize(100, 100) << (PointList() // toplevel << QPoint( 11, 11) << QPoint( 11 + 100 + 6, 11) // children << QPoint( 20, child_offset_y) << QPoint( 20 + 100 + 6, child_offset_y) ); QTest::newRow("3x1 grid") << 3 << 1 << QSize(100, 100) << (PointList() // toplevel << QPoint( 11, 11) << QPoint( 11 + 100 + 6, 11) << QPoint( 11 + 100 + 6 + 100 + 6, 11) // children << QPoint( 20, child_offset_y) << QPoint( 20 + 100 + 6, child_offset_y) << QPoint( 20 + 100 + 6 + 100 + 6, child_offset_y) ); child_offset_y = 11 + 9 + 100 + 6 + 100 + 6; QTest::newRow("1x2 grid") << 1 << 2 << QSize(100, 100) << (PointList() // toplevel << QPoint( 11, 11) << QPoint( 11, 11 + 100 + 6) // children << QPoint( 20, child_offset_y) << QPoint( 20, child_offset_y + 100 + 6) ); child_offset_y = 11 + 9 + 100 + 6 + 100 + 6 + 100 + 6; QTest::newRow("1x3 grid") << 1 << 3 << QSize(100, 100) << (PointList() // toplevel << QPoint( 11, 11) << QPoint( 11, 11 + 100 + 6) << QPoint( 11, 11 + 100 + 6 + 100 + 6) // children << QPoint( 20, child_offset_y) << QPoint( 20, child_offset_y + 100 + 6) << QPoint( 20, child_offset_y + 100 + 6 + 100 + 6) ); child_offset_y = 11 + 9 + 100 + 6 + 100 + 6; QTest::newRow("2x2 grid") << 2 << 2 << QSize(100, 100) << (PointList() // toplevel << QPoint( 11, 11) << QPoint( 11 + 100 + 6, 11) << QPoint( 11, 11 + 100 + 6) << QPoint( 11 + 100 + 6, 11 + 100 + 6) // children << QPoint( 20, child_offset_y) << QPoint( 20 + 100 + 6, child_offset_y) << QPoint( 20, child_offset_y + 100 + 6) << QPoint( 20 + 100 + 6, child_offset_y + 100 + 6) ); } void tst_QGridLayout::spacingsAndMargins() { /* The test tests a gridlayout as a child of a top-level widget, and then a gridlayout as a child of a non-toplevel widget. The expectedpositions should then contain the list of widget positions in the first gridlayout, then followed by a list of widget positions in the second gridlayout. */ QFETCH(int, columns); QFETCH(int, rows); QFETCH(QSize, sizehint); QFETCH(PointList, expectedpositions); QApplication::setStyle(new Qt42Style); QWidget toplevel; setFrameless(&toplevel); QVBoxLayout vbox(&toplevel); QGridLayout grid1; vbox.addLayout(&grid1); // a layout with a top-level parent widget QList > sizehinters; for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { SizeHinterFrame *sh = new SizeHinterFrame(sizehint); sh->setMinimumSizeHint(sizehint); sizehinters.append(sh); grid1.addWidget(sh, i, j); } } // Add the child widget QWidget widget; vbox.addWidget(&widget); QGridLayout grid2; widget.setLayout(&grid2); // add a layout to the child widget for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { SizeHinterFrame *sh = new SizeHinterFrame(sizehint); sh->setMinimumSizeHint(sizehint); sizehinters.append(sh); grid2.addWidget(sh, i, j); } } grid1.setColumnStretch(columns-1, 1); grid1.setRowStretch(rows-1, 1); toplevel.showNormal(); toplevel.adjustSize(); QApplication::processEvents(); QVERIFY(QTest::qWaitForWindowExposed(&toplevel)); QSize topsize = toplevel.size(); QSize minimumsize = vbox.totalMinimumSize(); if (topsize.width() < minimumsize.width() || topsize.height() < minimumsize.height()) QSKIP("The screen is too small to run this test case"); // We are relying on the order here... for (int pi = 0; pi < sizehinters.size(); ++pi) { QPoint pt = sizehinters.at(pi)->mapTo(&toplevel, QPoint(0, 0)); QCOMPARE(pt, expectedpositions.at(pi)); } } struct SizeInfo { SizeInfo(const QPoint &expected, const QSize &sh, const QSize &minimumSize = QSize(), const QSize &maximumSize = QSize(), int numPixelsToCover = -1) { expectedPos = expected; sizeHint = sh; minSize = minimumSize; maxSize = maximumSize; hfwNumPixels = numPixelsToCover; } SizeInfo(const QRect &expected, const QSize &sh, const QSize &minimumSize = QSize(), const QSize &maximumSize = QSize(), int numPixelsToCover = -1) { expectedPos = expected.topLeft(); expectedSize = expected.size(); sizeHint = sh; minSize = minimumSize; maxSize = maximumSize; hfwNumPixels = numPixelsToCover; } SizeInfo(const SizeInfo& other) { (*this)=other; } SizeInfo &operator=(const SizeInfo& other) { expectedPos = other.expectedPos; expectedSize = other.expectedSize; sizeHint = other.sizeHint; minSize = other.minSize; maxSize = other.maxSize; hfwNumPixels = other.hfwNumPixels; return (*this); } QPoint expectedPos; QSize expectedSize; QSize sizeHint; QSize minSize; QSize maxSize; int hfwNumPixels; }; typedef QList SizeInfoList; Q_DECLARE_METATYPE(SizeInfoList) void tst_QGridLayout::minMaxSize_data() { // input QTest::addColumn("stylename"); QTest::addColumn("columns"); QTest::addColumn("rows"); QTest::addColumn("sizePolicy"); QTest::addColumn("fixedSize"); //input and expected output QTest::addColumn("sizeinfos"); QTest::newRow("3x1 grid, extend to minimumSize") << QString() << 3 << 1 << int(QSizePolicy::Minimum) << QSize(152, 50) << (SizeInfoList() << SizeInfo(QRect(10, 10, 43, 30), QSize( 75, 75), QSize(0,0)) << SizeInfo(QRect(10 + 45, 10, 43, 30), QSize(75, 75), QSize( 0, 0)) << SizeInfo(QRect(10 + 45 + 44, 10, 42, 30), QSize(75, 75), QSize( 0, 0)) ); QTest::newRow("1x1 grid, extend to minimumSize") << QString() << 1 << 1 << int(QSizePolicy::Preferred) << QSize() << (SizeInfoList() << SizeInfo(QPoint(10, 10), QSize( 90, 90), QSize(100,100)) ); QTest::newRow("2x1 grid, extend to minimumSize") << QString() << 2 << 1 << int(QSizePolicy::Preferred) << QSize() << (SizeInfoList() << SizeInfo(QPoint(10, 10), QSize( 90, 90), QSize(100,100)) << SizeInfo(QPoint(10 + 100 + 1, 10), QSize( 90, 90)) ); QTest::newRow("2x1 grid, extend to minimumSize, windows") << QString::fromLatin1("windows") << 2 << 1 << int(QSizePolicy::Preferred) << QSize() << (SizeInfoList() << SizeInfo(QPoint(11, 11), QSize( 90, 90), QSize(100,100)) << SizeInfo(QPoint(11 + 100 + 6, 11), QSize( 90, 90)) ); QTest::newRow("1x2 grid, extend to minimumSize") << QString() << 1 << 2 << int(QSizePolicy::Preferred) << QSize() << (SizeInfoList() << SizeInfo(QPoint(10, 10), QSize( 90, 90), QSize(100,100)) << SizeInfo(QPoint(10, 10 + 100 + 1), QSize( 90, 90)) ); QTest::newRow("2x1 grid, crop to maximumSize") << QString() << 2 << 1 << int(QSizePolicy::Preferred) << QSize() << (SizeInfoList() << SizeInfo(QPoint(10, 10), QSize(110,110), QSize(), QSize(100, 100)) << SizeInfo(QPoint(10 + 100 + 1, 10), QSize( 90, 90)) ); QTest::newRow("1x2 grid, crop to maximumSize") << QString() << 1 << 2 << int(QSizePolicy::Preferred) << QSize() << (SizeInfoList() << SizeInfo(QPoint(10, 10), QSize(110,110), QSize(), QSize(100, 100)) << SizeInfo(QPoint(10, 10 + 100 + 1), QSize( 90, 90)) ); QTest::newRow("1x3 grid, heightForWidth") << QString() << 1 << 3 << int(QSizePolicy::Preferred) << QSize() << (SizeInfoList() << SizeInfo(QPoint(10, 10), QSize(), QSize(200,100), QSize()) << SizeInfo(QPoint(10, 10 + 100 + 1), QSize(100,100), QSize(), QSize(), 100*100) << SizeInfo(QPoint(10, 10 + 100 + 1 + 50 + 1), QSize(100,100), QSize(), QSize(100, 100)) ); } void tst_QGridLayout::minMaxSize() { /* The test tests a gridlayout as a child of a top-level widget */ // input QFETCH(QString, stylename); QFETCH(int, columns); QFETCH(int, rows); QFETCH(int, sizePolicy); QFETCH(QSize, fixedSize); //input and expected output QFETCH(SizeInfoList, sizeinfos); QStyle *style = 0; if (stylename.isEmpty()) { Qt42Style *s = new Qt42Style; s->margin_toplevel = 10; s->margin = 5; s->spacing = 1; style = static_cast(s); }else{ style = QStyleFactory::create(stylename); if (!style) { QSKIP( qPrintable(QString::fromLatin1("Qt has been compiled without style: %1").arg(stylename))); } } QApplication::setStyle(style); QWidget toplevel; toplevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char(' ') + QLatin1String(QTest::currentDataTag())); setFrameless(&toplevel); QGridLayout *grid = new QGridLayout; if (fixedSize.isValid()) { toplevel.setFixedSize(fixedSize); } else { toplevel.setMinimumSize(QSize(0,0)); toplevel.setMaximumSize(QSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX)); } // Do a two-pass one using the real testdata, the other pass enables heightForWidth // on the widget, but the heightForWidth() function just return sizeHint().width() for (int pass = 0; pass < 2; ++pass) { toplevel.hide(); QApplication::processEvents(); QTest::qWait(20); // Test if removeItem uninitializes data properly while (grid->count()) { QLayoutItem *item = grid->itemAt(0); grid->removeItem(item); delete item->widget(); delete item; } toplevel.setLayout(grid); // a layout with a top-level parent widget QList > sizehinters; for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { SizeInfo si = sizeinfos.at(sizehinters.size()); int numpixels = si.hfwNumPixels; if (pass == 1 && numpixels == -1) numpixels = -2; //### yuk, (and don't fake it if it already tests sizehint) SizeHinterFrame *sh = new SizeHinterFrame(si.sizeHint, numpixels); QSizePolicy sp = sh->sizePolicy(); sp.setHorizontalPolicy((QSizePolicy::Policy)sizePolicy); sh->setSizePolicy(sp); sh->setParent(&toplevel); if (si.minSize.isValid()) sh->setMinimumSize(si.minSize); if (si.maxSize.isValid()) sh->setMaximumSize(si.maxSize); sizehinters.append(sh); grid->addWidget(sh, i, j); } } toplevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&toplevel)); toplevel.adjustSize(); QTest::qWait(240); // wait for the implicit adjustSize // If the following fails we might have to wait longer. // If that does not help there is likely a problem with the implicit adjustSize in show() if (!fixedSize.isValid()) { // Note that this can fail if the desktop has large fonts on windows. QTRY_COMPARE(toplevel.size(), toplevel.sizeHint()); } // We are relying on the order here... for (int pi = 0; pi < sizehinters.size(); ++pi) { QPoint pt = sizehinters.at(pi)->mapTo(&toplevel, QPoint(0, 0)); QCOMPARE(pt, sizeinfos.at(pi).expectedPos); } } } class CustomLayoutStyle : public QProxyStyle { Q_OBJECT public: CustomLayoutStyle() : QProxyStyle(QStyleFactory::create("windows")) { hspacing = 5; vspacing = 10; reimplementSubelementRect = false; } virtual int pixelMetric(PixelMetric metric, const QStyleOption * option = nullptr, const QWidget * widget = nullptr ) const override; virtual QRect subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *widget) const override; int hspacing; int vspacing; bool reimplementSubelementRect; int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const override; }; QRect CustomLayoutStyle::subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *widget) const { QRect rect; if (reimplementSubelementRect) { switch (sr) { case SE_FrameLayoutItem: rect = opt->rect; rect.adjust(+4, +9, -4, 0); // The hspacing=5 and vspacing=10, so we keep it safe. break; case SE_GroupBoxLayoutItem: rect = opt->rect.adjusted(0, +10, 0, 0); break; default: break; } } if (rect.isNull()) rect = QProxyStyle::subElementRect(sr, opt, widget); return rect; } #define CT1(c) CT2(c, c) #define CT2(c1, c2) ((uint)c1 << 16) | (uint)c2 int CustomLayoutStyle::layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption * /*option = nullptr*/, const QWidget * /*widget = nullptr*/) const { if (orientation == Qt::Horizontal) { switch (CT2(control1, control2)) { case CT1(QSizePolicy::PushButton): return 2; break; } return 5; } else { switch (CT2(control1, control2)) { case CT1(QSizePolicy::RadioButton): return 2; break; } return 10; } } int CustomLayoutStyle::pixelMetric(PixelMetric metric, const QStyleOption * option /*= nullptr*/, const QWidget * widget /*= nullptr*/ ) const { switch (metric) { case PM_LayoutLeftMargin: return 0; break; case PM_LayoutTopMargin: return 3; break; case PM_LayoutRightMargin: return 6; break; case PM_LayoutBottomMargin: return 9; break; case PM_LayoutHorizontalSpacing: return hspacing; case PM_LayoutVerticalSpacing: return vspacing; break; default: break; } return QProxyStyle::pixelMetric(metric, option, widget); } void tst_QGridLayout::styleDependentSpacingsAndMargins_data() { // input QTest::addColumn("columns"); QTest::addColumn("rows"); QTest::addColumn("sizehint"); // expected QTest::addColumn("expectedpositions"); QTest::newRow("1x1 grid") << 1 << 1 << QSize(100, 100) << (PointList() << QPoint(0, 3) ); QTest::newRow("2x1 grid") << 2 << 1 << QSize(100, 100) << (PointList() << QPoint(0, 3) << QPoint(0+100+5, 3)); QTest::newRow("3x1 grid") << 3 << 1 << QSize(100, 100) << (PointList() << QPoint(0, 3) << QPoint(0+100+5, 3) << QPoint(0 + 2*105, 3)); QTest::newRow("1x2 grid") << 1 << 2 << QSize(100, 100) << (PointList() << QPoint(0, 3) << QPoint(0, 3+100+10)); QTest::newRow("1x3 grid") << 1 << 3 << QSize(100, 100) << (PointList() << QPoint(0, 3) << QPoint(0, 3+100+10) << QPoint(0, 3+2*110)); QTest::newRow("2x2 grid") << 2 << 2 << QSize(100, 100) << (PointList() << QPoint(0, 3) << QPoint(0+100+5, 3) << QPoint(0, 3+100+10) << QPoint(0+100+5, 3+100+10)); } void tst_QGridLayout::styleDependentSpacingsAndMargins() { QFETCH(int, columns); QFETCH(int, rows); QFETCH(QSize, sizehint); QFETCH(PointList, expectedpositions); QApplication::setStyle(new CustomLayoutStyle()); QWidget widget; setFrameless(&widget); QGridLayout layout(&widget); QList > sizehinters; for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { SizeHinterFrame *sh = new SizeHinterFrame(sizehint); sh->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); sh->setParent(&widget); sizehinters.append(sh); layout.addWidget(sh, i, j); } } layout.setColumnStretch(columns, 1); layout.setRowStretch(rows, 1); widget.show(); widget.adjustSize(); QApplication::processEvents(); for (int pi = 0; pi < expectedpositions.size(); ++pi) { QCOMPARE(sizehinters.at(pi)->pos(), expectedpositions.at(pi)); } } void tst_QGridLayout::layoutSpacing_data() { QTest::addColumn("widget"); // expected QTest::addColumn("expectedpositions"); QTest::addColumn("hSpacing"); QTest::addColumn("vSpacing"); QTest::addColumn("customSubElementRect"); CustomLayoutStyle *style = new CustomLayoutStyle(); { // If the layoutSpacing is negative, the layouting code will call // layoutSpacing() style->hspacing = -1; style->vspacing = -1; style->reimplementSubelementRect = false; QApplication::setStyle(style); QWidget *w = new QWidget(); setFrameless(w); QVBoxLayout *layout = new QVBoxLayout(); QRadioButton *rb1 = new QRadioButton(QLatin1String("Radio 1"), w); QRadioButton *rb2 = new QRadioButton(QLatin1String("Radio 2"), w); QRadioButton *rb3 = new QRadioButton(QLatin1String("Radio 3"), w); layout->addWidget(rb1, 0, Qt::AlignTop | Qt::AlignLeft); layout->addWidget(rb2, 0, Qt::AlignTop | Qt::AlignLeft); layout->addWidget(rb3, 0, Qt::AlignTop | Qt::AlignLeft); QPushButton *b1 = new QPushButton(QLatin1String("Push 1"), w); QPushButton *b2 = new QPushButton(QLatin1String("Push 2"), w); QPushButton *b3 = new QPushButton(QLatin1String("Push 3"), w); layout->addWidget(b1, 0, Qt::AlignTop | Qt::AlignLeft); layout->addWidget(b2, 0, Qt::AlignTop | Qt::AlignLeft); layout->addWidget(b3, 0, Qt::AlignTop | Qt::AlignLeft); layout->addStretch(1); w->setLayout(layout); int rh = rb1->sizeHint().height(); int ph = b1->sizeHint().height(); QTest::newRow("1x6, radio + push buttons") << w << (PointList() << QPoint(0, 3) << QPoint(0, 3 + rh + 2) << QPoint(0, 3 + 2*(rh + 2)) << QPoint(0, 3 + 2*(rh + 2) + (rh + 10)) << QPoint(0, 3 + 2*(rh + 2) + (rh + 10 + ph + 10)) << QPoint(0, 3 + 2*(rh + 2) + rh + 10 + 2*(ph + 10))) << style->hspacing << style->vspacing << style->reimplementSubelementRect; } { style->hspacing = -1; style->vspacing = -1; style->reimplementSubelementRect = false; QApplication::setStyle(style); QWidget *w = new QWidget(); setFrameless(w); QHBoxLayout *layout = new QHBoxLayout(); QLineEdit *le1 = new QLineEdit(w); QLineEdit *le2 = new QLineEdit(w); QLineEdit *le3 = new QLineEdit(w); layout->addWidget(le1, 0, Qt::AlignTop | Qt::AlignLeft); layout->addWidget(le2, 0, Qt::AlignTop | Qt::AlignLeft); layout->addWidget(le3, 0, Qt::AlignTop | Qt::AlignLeft); QPushButton *b1 = new QPushButton(QLatin1String("Push 1"), w); QPushButton *b2 = new QPushButton(QLatin1String("Push 2"), w); QPushButton *b3 = new QPushButton(QLatin1String("Push 3"), w); layout->addWidget(b1, 0, Qt::AlignTop | Qt::AlignLeft); layout->addWidget(b2, 0, Qt::AlignTop | Qt::AlignLeft); layout->addWidget(b3, 0, Qt::AlignTop | Qt::AlignLeft); layout->addStretch(1); w->setLayout(layout); int lw = le1->sizeHint().width(); int pw = b1->sizeHint().width(); QTest::newRow("6x1, line edit + push buttons") << w << (PointList() << QPoint(0, 3) << QPoint(0 + lw + 5, 3) << QPoint(0 + 2*(lw + 5), 3) << QPoint(0 + 3*(lw + 5), 3) << QPoint(0 + 3*(lw + 5) + 1*(pw + 2), 3) << QPoint(0 + 3*(lw + 5) + 2*(pw + 2), 3)) << style->hspacing << style->vspacing << style->reimplementSubelementRect; } { style->hspacing = 5; style->vspacing = 10; style->reimplementSubelementRect = true; QApplication::setStyle(style); QWidget *w = new QWidget(); setFrameless(w); QVBoxLayout *layout = new QVBoxLayout(); QPushButton *pb1 = new QPushButton(QLatin1String("Push 1"), w); QGroupBox *g1 = new QGroupBox(QLatin1String("GroupBox 1"), w); QRadioButton *rb = new QRadioButton(QLatin1String("Radio 1"), g1); QVBoxLayout *g1layout = new QVBoxLayout(); g1layout->addWidget(rb); g1->setLayout(g1layout); QPushButton *pb3 = new QPushButton(QLatin1String("Push 3"), w); layout->addWidget(pb1); layout->addWidget(g1 ); layout->addWidget(pb3); w->setLayout(layout); QSize psh = pb1->sizeHint(); QSize gsh = g1->sizeHint(); QTest::newRow("subElementRect1") << w << (PointList() << QPoint(0, 3) << QPoint(0, 3 + psh.height() + 10 - 10) << QPoint(0, 3 + psh.height() + 10 - 10 + gsh.height() + 10) ) << style->hspacing << style->vspacing << style->reimplementSubelementRect; } { style->hspacing = 5; style->vspacing = 10; style->reimplementSubelementRect = true; QApplication::setStyle(style); QWidget *w = new QWidget(); QFont font; font.setPixelSize(10); w->setFont(font); setFrameless(w); QGridLayout *layout = new QGridLayout(); QPushButton *pb1 = new QPushButton(QLatin1String("Push 1"), w); QPushButton *pb2 = new QPushButton(QLatin1String("Push 2"), w); QPushButton *pb3 = new QPushButton(QLatin1String("Push 3"), w); QPushButton *pb4 = new QPushButton(QLatin1String("Push 4"), w); layout->addWidget(pb1, 0, 0); layout->addWidget(pb2, 0, 1); layout->addWidget(pb3, 0, 2); layout->addWidget(pb4, 1, 0, Qt::AlignTop); QFrame *f1 = new QFrame(w); f1->setFrameStyle(QFrame::Box | QFrame::Plain); f1->setMinimumSize(100, 20); f1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); layout->addWidget(f1, 1, 1, Qt::AlignTop); QPushButton *pb6 = new QPushButton(QLatin1String("Push 6"), w); QPushButton *pb7 = new QPushButton(QLatin1String("Push 7"), w); QPushButton *pb8 = new QPushButton(QLatin1String("Push 8"), w); QPushButton *pb9 = new QPushButton(QLatin1String("Push 9"), w); layout->addWidget(pb6, 1, 2, Qt::AlignTop); layout->addWidget(pb7, 2, 0, Qt::AlignTop); layout->addWidget(pb8, 2, 1, Qt::AlignTop); layout->addWidget(pb9, 2, 2, Qt::AlignTop); layout->setColumnStretch(2, 1); layout->setRowStretch(2, 1); w->setLayout(layout); int c[3]; c[0] = pb1->sizeHint().width(); c[1] = f1->minimumSize().width() - 2*4; c[2] = pb3->sizeHint().width(); int r[3]; r[0] = pb1->sizeHint().height(); r[1] = pb4->sizeHint().height(); r[2] = pb7->sizeHint().height(); QTest::newRow("subElementRect2") << w << (PointList() << QPoint(0, 3) << QPoint(0 + c[0] + 5, 3) << QPoint(0 + c[0] + 5 + c[1] + 5, 3) << QPoint(0, 3 + r[0] + 10) << QPoint(0 + c[0] + 5 - 4, 3 + r[0] + 10 - 9) << QPoint(0 + c[0] + 5 + c[1] + 5, 3 + r[0] + 10) << QPoint(0, 3 + r[0] + 10 + r[1] + 10) << QPoint(0 + c[0] + 5, 3 + r[0] + 10 + r[1] + 10) << QPoint(0 + c[0] + 5 + c[1] + 5, 3 + r[0] + 10 + r[1] + 10) ) << style->hspacing << style->vspacing << style->reimplementSubelementRect; } { style->hspacing = 5; style->vspacing = 10; style->reimplementSubelementRect = true; QApplication::setStyle(style); QWidget *w = new QWidget(); setFrameless(w); QVBoxLayout *layout = new QVBoxLayout(); QPushButton *pb1 = new QPushButton(QLatin1String("Push 1"), w); QGroupBox *g1 = new QGroupBox(QLatin1String("GroupBox 1"), w); QRadioButton *rb = new QRadioButton(QLatin1String("Radio 1"), g1); QVBoxLayout *g1layout = new QVBoxLayout(); g1layout->addWidget(rb); g1->setLayout(g1layout); QPushButton *pb3 = new QPushButton(QLatin1String("Push 3"), w); pb1->setAttribute(Qt::WA_LayoutUsesWidgetRect, true); g1->setAttribute(Qt::WA_LayoutUsesWidgetRect, true); pb3->setAttribute(Qt::WA_LayoutUsesWidgetRect, true); layout->addWidget(pb1); layout->addWidget(g1 ); layout->addWidget(pb3); w->setLayout(layout); QSize psh = pb1->sizeHint(); QSize gsh = g1->sizeHint(); QTest::newRow("subElementRect1, use widgetRect") << w << (PointList() << QPoint(0, 3) << QPoint(0, 3 + psh.height() + 10) << QPoint(0, 3 + psh.height() + 10 + gsh.height() + 10) ) << style->hspacing << style->vspacing << style->reimplementSubelementRect; } { style->hspacing = 5; style->vspacing = 10; style->reimplementSubelementRect = true; QApplication::setStyle(style); QWidget *w = new QWidget(); setFrameless(w); QVBoxLayout *layout = new QVBoxLayout(); QPushButton *pb1 = new QPushButton(QLatin1String("Push 1"), w); QGroupBox *g1 = new QGroupBox(QLatin1String("GroupBox 1"), w); QRadioButton *rb = new QRadioButton(QLatin1String("Radio 1"), g1); QVBoxLayout *g1layout = new QVBoxLayout(); g1layout->addWidget(rb); g1->setLayout(g1layout); QPushButton *pb3 = new QPushButton(QLatin1String("Push 3"), w); pb1->setAttribute(Qt::WA_LayoutUsesWidgetRect, false); g1->setAttribute(Qt::WA_LayoutUsesWidgetRect, false); pb3->setAttribute(Qt::WA_LayoutUsesWidgetRect, false); layout->addWidget(pb1); layout->addWidget(g1 ); layout->addWidget(pb3); w->setLayout(layout); QSize psh = pb1->sizeHint(); QSize gsh = g1->sizeHint(); QTest::newRow("subElementRect1, use layoutItemRect") << w << (PointList() << QPoint(0, 3) << QPoint(0, 3 + psh.height() + 10 - 10) << QPoint(0, 3 + psh.height() + 10 - 10 + gsh.height() + 10) ) << style->hspacing << style->vspacing << style->reimplementSubelementRect; } { /* A 3x4 gridlayout, modified arrowpad example: * [PB] * [PB] [PB] * |PB| * | | * | | * * Here the bottom pushbutton has a span */ style->hspacing = -1; style->vspacing = -1; style->reimplementSubelementRect = false; QApplication::setStyle(style); QWidget *w = new QWidget(); setFrameless(w); QGridLayout *layout = new QGridLayout(); QPushButton *left = new QPushButton(w); QPushButton *up = new QPushButton(w); QPushButton *right = new QPushButton(w); QPushButton *down = new QPushButton(w); layout->addWidget(up, 0, 1); layout->addWidget(left, 1, 0); layout->addWidget(right, 1, 2); layout->addWidget(down, 2, 1, 3, 1); w->setLayout(layout); int pw = up->sizeHint().width(); int ph = up->sizeHint().height(); QTest::newRow("arrowpad with span") << w << (PointList() << QPoint(0 + pw + 5, 3) << QPoint(0, 3 + ph + 10) << QPoint(0 + pw + 5 + pw + 5, 3 + ph + 10) << QPoint(0 + pw + 5, 3 + ph + 10 + ph + 10) ) << style->hspacing << style->vspacing << style->reimplementSubelementRect; } for (int yoff = 0; yoff < 5; ++yoff) { for (int xoff = 0; xoff < 5; ++xoff) { /* A 3x4 gridlayout, modified arrowpad example: * [empty cells] * [PB] * [PB] [PB] * [PB] * * It has 0-4 empty rows at the top and 0-4 empty columns to the left. */ style->hspacing = -1; style->vspacing = -1; style->reimplementSubelementRect = false; QApplication::setStyle(style); QWidget *w = new QWidget(); setFrameless(w); QGridLayout *layout = new QGridLayout(); QPushButton *left = new QPushButton(w); QPushButton *up = new QPushButton(w); QPushButton *right = new QPushButton(w); QPushButton *down = new QPushButton(w); layout->addWidget(up, yoff + 0, xoff + 1); layout->addWidget(left, yoff + 1, xoff + 0); layout->addWidget(right, yoff + 1, xoff + 2); layout->addWidget(down, yoff + 2, xoff + 1, 3, 1); w->setLayout(layout); int pw = up->sizeHint().width(); int ph = up->sizeHint().height(); QByteArray testName = "arrowpad with " + QByteArray::number(yoff) + " empty rows, " + QByteArray::number(xoff) + " empty columns"; QTest::newRow(testName.constData()) << w << (PointList() << QPoint(0 + pw + 5, 3) << QPoint(0, 3 + ph + 10) << QPoint(0 + pw + 5 + pw + 5, 3 + ph + 10) << QPoint(0 + pw + 5, 3 + ph + 10 + ph + 10) ) << style->hspacing << style->vspacing << style->reimplementSubelementRect; } } } void tst_QGridLayout::layoutSpacing() { QFETCH(QWidget *, widget); QFETCH(PointList, expectedpositions); QFETCH(int, hSpacing); QFETCH(int, vSpacing); QFETCH(bool, customSubElementRect); QWidget toplevel; setFrameless(&toplevel); CustomLayoutStyle *style = new CustomLayoutStyle(); style->hspacing = hSpacing; style->vspacing = vSpacing; style->reimplementSubelementRect = customSubElementRect; QApplication::setStyle(style); widget->setParent(&toplevel); widget->resize(widget->sizeHint()); toplevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&toplevel)); QLayout *layout = widget->layout(); QVERIFY(layout); for (int pi = 0; pi < expectedpositions.size(); ++pi) { QLayoutItem *item = layout->itemAt(pi); //qDebug() << item->widget()->pos(); QCOMPARE(item->widget()->pos(), expectedpositions.at(pi)); } } void tst_QGridLayout::spacing() { QWidget w; setFrameless(&w); CustomLayoutStyle *style = new CustomLayoutStyle(); style->hspacing = 5; style->vspacing = 10; w.setStyle(style); QGridLayout grid(&w); QCOMPARE(style->hspacing, grid.horizontalSpacing()); QCOMPARE(style->vspacing, grid.verticalSpacing()); QCOMPARE(grid.spacing(), -1); grid.setVerticalSpacing(5); QCOMPARE(5, grid.horizontalSpacing()); QCOMPARE(5, grid.verticalSpacing()); QCOMPARE(grid.spacing(), 5); grid.setVerticalSpacing(-1); QCOMPARE(style->hspacing, grid.horizontalSpacing()); QCOMPARE(style->vspacing, grid.verticalSpacing()); style->hspacing = 5; style->vspacing = 5; QCOMPARE(grid.spacing(), 5); grid.setHorizontalSpacing(20); QCOMPARE(grid.spacing(), -1); style->vspacing = 20; QCOMPARE(grid.horizontalSpacing(), 20); QCOMPARE(grid.verticalSpacing(), 20); QCOMPARE(grid.spacing(), 20); grid.setHorizontalSpacing(-1); QCOMPARE(grid.spacing(), -1); style->hspacing = 20; QCOMPARE(grid.spacing(), 20); delete style; } void populate(QGridLayout *layout, int row, int kind) { if (kind == 0) { QWidget *widget = new QWidget; widget->setFixedSize(100, 100); layout->addWidget(widget, row, 0); } else if (kind == 1) { layout->addItem(new QSpacerItem(10, 10), row, 0); } } void tst_QGridLayout::spacerWithSpacing() { // Tests all combinations of widget (w), spacer (s) and no layoutitem (-) // to see if they are laid out correctly. // Note that a combination of "s-" or "-s" should only give the height of "s" const int expectedHeight[] = { 302,// www 211,// wws 201,// ww- 211,// wsw 120,// wss 110,// ws- 201,// w-w 110,// w-s 100,// w-- 211,// sww 120,// sws 110,// sw- 120,// ssw 30,// sss 20,// ss- 110,// s-w 20,// s-s 10,// s-- 201,// -ww 110,// -ws 100,// -w- 110,// -sw 20,// -ss 10,// -s- 100,// --w 10,// --s 000 // --- }; int ii = 0; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { for (int k = 0; k < 3; ++k) { QWidget window; QGridLayout layout(&window); layout.setSpacing(1); layout.setContentsMargins(0, 0, 0, 0); populate(&layout, 0, i); populate(&layout, 1, j); populate(&layout, 2, k); QCOMPARE(window.sizeHint().height(), expectedHeight[ii]); ++ii; } } } } void tst_QGridLayout::contentsRect() { QWidget w; setFrameless(&w); QGridLayout grid; w.setLayout(&grid); grid.addWidget(new QPushButton(&w)); w.show(); QVERIFY(QTest::qWaitForWindowExposed(&w)); int l, t, r, b; grid.getContentsMargins(&l, &t, &r, &b); QRect geom = grid.geometry(); QCOMPARE(geom.adjusted(+l, +t, -r, -b), grid.contentsRect()); } void tst_QGridLayout::distributeMultiCell() { QWidget w; Qt42Style style; style.spacing = 9; w.setStyle(&style); QGridLayout grid; w.setLayout(&grid); SizeHinter le1(200, 20, &w); le1.setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); SizeHinter le2(200, 20, &w); le2.setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); SizeHinter box(80, 57, &w); box.setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding)); box.setMinimumSize(80, 57); grid.addWidget(&le1, 0, 0, 1, 1); grid.addWidget(&le2, 1, 0, 1, 1); grid.addWidget(&box, 0, 1, 2, 1); QCOMPARE(box.sizeHint().height(), 57); QCOMPARE(w.sizeHint().height(), 11 + 57 + 11); } void tst_QGridLayout::taskQTBUG_27420_takeAtShouldUnparentLayout() { QSharedPointer outer(new QGridLayout); QPointer inner = new QGridLayout; outer->addLayout(inner, 0, 0); QCOMPARE(outer->count(), 1); QCOMPARE(inner->parent(), outer.data()); QLayoutItem *item = outer->takeAt(0); QCOMPARE(item->layout(), inner.data()); QVERIFY(!item->layout()->parent()); outer.reset(); if (inner) delete item; // success: a taken item/layout should not be deleted when the old parent is deleted else QVERIFY(!inner.isNull()); } void tst_QGridLayout::taskQTBUG_40609_addingWidgetToItsOwnLayout(){ QWidget widget; widget.setObjectName("9bb37ca762aeb7269b8"); QGridLayout layout(&widget); layout.setObjectName("d631e91a35f2b66a6dff35"); QTest::ignoreMessage(QtWarningMsg, "QLayout: Cannot add a null widget to QGridLayout/d631e91a35f2b66a6dff35"); layout.addWidget(nullptr, 0, 0); QCOMPARE(layout.count(), 0); QTest::ignoreMessage(QtWarningMsg, "QLayout: Cannot add parent widget QWidget/9bb37ca762aeb7269b8 to its child layout QGridLayout/d631e91a35f2b66a6dff35"); layout.addWidget(&widget, 0, 0); QCOMPARE(layout.count(), 0); } void tst_QGridLayout::taskQTBUG_40609_addingLayoutToItself(){ QWidget widget; widget.setObjectName("0373d417fffe2c59c6fe543"); QGridLayout layout(&widget); layout.setObjectName("5d79e1b0aed83f100e3c2"); QTest::ignoreMessage(QtWarningMsg, "QLayout: Cannot add a null layout to QGridLayout/5d79e1b0aed83f100e3c2"); layout.addLayout(nullptr, 0, 0); QCOMPARE(layout.count(), 0); QTest::ignoreMessage(QtWarningMsg, "QLayout: Cannot add layout QGridLayout/5d79e1b0aed83f100e3c2 to itself"); layout.addLayout(&layout, 0, 0); QCOMPARE(layout.count(), 0); } void tst_QGridLayout::taskQTBUG_52357_spacingWhenItemIsHidden() { QWidget widget; setFrameless(&widget); QGridLayout layout(&widget); layout.setContentsMargins(0, 0, 0, 0); layout.setSpacing(5); QPushButton button1; layout.addWidget(&button1, 0, 0); QPushButton button2; layout.addWidget(&button2, 0, 1); QPushButton button3; layout.addWidget(&button3, 0, 2); widget.show(); QVERIFY(QTest::qWaitForWindowExposed(&widget)); int tempWidth = button1.width() + button2.width() + button3.width() + 2 * layout.spacing(); button2.hide(); QTRY_COMPARE_WITH_TIMEOUT(tempWidth, button1.width() + button3.width() + layout.spacing(), 1000); } void tst_QGridLayout::taskQTBUG_91261_itemIndexRange() { QWidget widget; QGridLayout lay(&widget); QPushButton *btn = new QPushButton(&widget); lay.addWidget(btn, 0, 0); { auto ptr = lay.itemAt(-1); QCOMPARE(ptr, nullptr); ptr = lay.itemAt(0); QCOMPARE(ptr->widget(), btn); ptr = lay.itemAt(1); QCOMPARE(ptr, nullptr); } { int row = -1; int column = -1; int rowSpan; int columnSpan; lay.getItemPosition(-1, &row, &column, &rowSpan, &columnSpan); QCOMPARE(row, -1); QCOMPARE(column, -1); lay.getItemPosition(1, &row, &column, &rowSpan, &columnSpan); QCOMPARE(row, -1); QCOMPARE(column, -1); lay.getItemPosition(0, &row, &column, &rowSpan, &columnSpan); QCOMPARE(row, 0); QCOMPARE(column, 0); } { auto ptr = lay.takeAt(-1); QCOMPARE(ptr, nullptr); ptr = lay.takeAt(1); QCOMPARE(ptr, nullptr); ptr = lay.takeAt(0); QCOMPARE(ptr->widget(), btn); delete ptr; } } void tst_QGridLayout::replaceWidget() { QWidget wdg; QGridLayout *l = new QGridLayout(); const int itemCount = 9; QLabel *labels[itemCount]; // setup layout for (int n = 0; n < itemCount; ++n) { int x = n % 3; int y = n / 3; labels[n] = new QLabel(QLatin1String("label ") + QString::number(n)); Qt::Alignment align = (n % 3 ? Qt::AlignLeft : Qt::AlignRight); l->addWidget(labels[n], x * 3, y * 3, (n % 2) + 1, (n + 1) % 2 + 1, align); } wdg.setLayout(l); // iterate and replace for (int n = 0; n < itemCount; n += 2) { int i = l->indexOf(labels[n]); int fromRow, fromCol, fromRowSpan, fromColSpan; l->getItemPosition(i, &fromRow, &fromCol, &fromRowSpan, &fromColSpan); Qt::Alignment fromAlign = l->itemAt(i)->alignment(); // do replace QPushButton *pb = new QPushButton("replaced"); QLayoutItem *olditem = l->replaceWidget(labels[n], pb); // verify QCOMPARE(i, l->indexOf(pb)); QVERIFY(olditem != 0); QCOMPARE(l->indexOf(labels[n]), -1); int toRow, toCol, toRowSpan, toColSpan; l->getItemPosition(i, &toRow, &toCol, &toRowSpan, &toColSpan); QCOMPARE(fromRow, toRow); QCOMPARE(fromCol, toCol); QCOMPARE(fromRowSpan, toRowSpan); QCOMPARE(fromColSpan, toColSpan); Qt::Alignment toAlign = l->itemAt(i)->alignment(); QCOMPARE(fromAlign, toAlign); // clean up olditem->widget()->deleteLater(); delete olditem; } } void tst_QGridLayout::dontCrashWhenExtendsToEnd() { QWidget window; window.resize(320,200); QWidget parent(&window); QLabel *lbl0 = new QLabel(QLatin1String("lbl0:")); QLabel *lbl1 = new QLabel(QLatin1String("lbl1:")); QPushButton *pb = new QPushButton(QLatin1String("pb1")); QGridLayout *l = new QGridLayout(&parent); l->addWidget(lbl0, 0, 0); l->addWidget(lbl1, 1, 0); // adding an item in the bottom right corner than spans to the end (!)... l->addWidget(pb, 1, 1, -1, -1); // ...should not cause a crash when the items are distributed.... l->setGeometry(QRect(0, 0, 200, 50)); // DONT CRASH HERE } QTEST_MAIN(tst_QGridLayout) #include "tst_qgridlayout.moc"