1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#include <qcoreapplication.h>
33#include <qdebug.h>
34#include <qboxlayout.h>
35#include <qmenubar.h>
36#include <qdialog.h>
37#include <qsizegrip.h>
38#include <qlabel.h>
39#include <QtWidgets/QFrame>
40#include <QtWidgets/QStyleFactory>
41#include <QtWidgets/QSizePolicy>
42#include <QtWidgets/QComboBox>
43#include <QPushButton>
44#include <QRadioButton>
45#include <private/qlayoutengine_p.h>
46
47#include <QtTest/private/qtesthelpers_p.h>
48
49using namespace QTestPrivate;
50
51class tst_QLayout : public QObject
52{
53Q_OBJECT
54
55public:
56 tst_QLayout();
57 virtual ~tst_QLayout();
58
59private slots:
60 void cleanup() { QVERIFY(QApplication::topLevelWidgets().isEmpty()); }
61 void getSetCheck();
62 void geometry();
63 void smartMaxSize();
64 void setLayoutBugs();
65 void setContentsMargins();
66 void layoutItemRect();
67 void warnIfWrongParent();
68 void controlTypes();
69 void controlTypes2();
70 void adjustSizeShouldMakeSureLayoutIsActivated();
71 void testRetainSizeWhenHidden();
72 void removeWidget();
73};
74
75tst_QLayout::tst_QLayout()
76{
77}
78
79tst_QLayout::~tst_QLayout()
80{
81}
82
83// Testing get/set functions
84void tst_QLayout::getSetCheck()
85{
86 QBoxLayout obj1(QBoxLayout::LeftToRight);
87 // QWidget * QLayout::menuBar()
88 // void QLayout::setMenuBar(QWidget *)
89 QMenuBar *var1 = new QMenuBar();
90 obj1.setMenuBar(var1);
91 QCOMPARE(static_cast<QWidget *>(var1), obj1.menuBar());
92 obj1.setMenuBar((QWidget *)0);
93 QCOMPARE((QWidget *)0, obj1.menuBar());
94 delete var1;
95}
96
97class SizeHinterFrame : public QFrame
98{
99public:
100 SizeHinterFrame(const QSize &sh, const QSize &msh = QSize())
101 : QFrame(0), sh(sh), msh(msh) {
102 setFrameStyle(QFrame::Box | QFrame::Plain);
103 }
104
105
106
107 void setSizeHint(const QSize &s) { sh = s; }
108 QSize sizeHint() const { return sh; }
109 QSize minimumSizeHint() const { return msh; }
110
111private:
112 QSize sh;
113 QSize msh;
114};
115
116
117void tst_QLayout::geometry()
118{
119 // For QWindowsStyle we know that QWidgetItem::geometry() and QWidget::geometry()
120 // should be the same.
121 QApplication::setStyle(QStyleFactory::create(QLatin1String("Windows")));
122 QWidget topLevel;
123 setFrameless(&topLevel);
124 QWidget w(&topLevel);
125 QVBoxLayout layout(&w);
126 SizeHinterFrame widget(QSize(100,100));
127 layout.addWidget(&widget);
128 QLayoutItem *item = layout.itemAt(0);
129 topLevel.show();
130 QApplication::processEvents();
131 QCOMPARE(item->geometry().size(), QSize(100,100));
132
133 widget.setMinimumSize(QSize(110,110));
134 QCOMPARE(item->geometry().size(), QSize(110,110));
135
136 widget.setMinimumSize(QSize(0,0));
137 widget.setMaximumSize(QSize(90,90));
138 widget.setSizeHint(QSize(100,100));
139 QCOMPARE(item->geometry().size(), QSize(90,90));
140}
141
142void tst_QLayout::smartMaxSize()
143{
144 QVector<int> expectedWidths;
145
146 QFile f(QFINDTESTDATA("baseline/smartmaxsize"));
147
148 QCOMPARE(f.open(QIODevice::ReadOnly | QIODevice::Text), true);
149
150 QTextStream stream(&f);
151
152 while(!stream.atEnd()) {
153 QString line = stream.readLine(maxlen: 200);
154 expectedWidths.append(t: line.section(asep: QLatin1Char(' '), astart: 6, aend: -1, aflags: QString::SectionSkipEmpty).toInt());
155 }
156 f.close();
157
158 int sizeCombinations[] = { 0, 10, 20, QWIDGETSIZE_MAX};
159 QSizePolicy::Policy policies[] = { QSizePolicy::Fixed,
160 QSizePolicy::Minimum,
161 QSizePolicy::Maximum,
162 QSizePolicy::Preferred,
163 QSizePolicy::Expanding,
164 QSizePolicy::MinimumExpanding,
165 QSizePolicy::Ignored
166 };
167 Qt::Alignment alignments[] = { Qt::Alignment{},
168 Qt::AlignLeft,
169 Qt::AlignRight,
170 Qt::AlignHCenter
171 };
172
173 int expectedIndex = 0;
174 int regressionCount = 0;
175 for (size_t p = 0; p < sizeof(policies)/sizeof(QSizePolicy::Policy); ++p) {
176 QSizePolicy sizePolicy;
177 sizePolicy.setHorizontalPolicy(policies[p]);
178 for (size_t min = 0; min < sizeof(sizeCombinations)/sizeof(int); ++min) {
179 int minSize = sizeCombinations[min];
180 for (size_t max = 0; max < sizeof(sizeCombinations)/sizeof(int); ++max) {
181 int maxSize = sizeCombinations[max];
182 for (size_t sh = 0; sh < sizeof(sizeCombinations)/sizeof(int); ++sh) {
183 int sizeHint = sizeCombinations[sh];
184 for (size_t a = 0; a < sizeof(alignments)/sizeof(int); ++a) {
185 Qt::Alignment align = alignments[a];
186 QSize sz = qSmartMaxSize(sizeHint: QSize(sizeHint, 1), minSize: QSize(minSize, 1), maxSize: QSize(maxSize, 1), sizePolicy, align);
187 int width = sz.width();
188 int expectedWidth = expectedWidths[expectedIndex];
189 if (width != expectedWidth) {
190 qDebug() << "error at index" << expectedIndex << ':' << sizePolicy.horizontalPolicy() << align << minSize << sizeHint << maxSize << width;
191 ++regressionCount;
192 }
193 ++expectedIndex;
194 }
195 }
196 }
197 }
198 }
199 QCOMPARE(regressionCount, 0);
200}
201
202void tst_QLayout::setLayoutBugs()
203{
204 QWidget widget(0);
205 QHBoxLayout *hBoxLayout = new QHBoxLayout(&widget);
206
207 for(int i = 0; i < 6; ++i) {
208 QPushButton *pushButton = new QPushButton("Press me!", &widget);
209 hBoxLayout->addWidget(pushButton);
210 }
211
212 widget.setLayout(hBoxLayout);
213 QCOMPARE(widget.layout(), hBoxLayout);
214
215 QWidget containerWidget(0);
216 containerWidget.setLayout(widget.layout());
217 QVERIFY(!widget.layout());
218 QCOMPARE(containerWidget.layout(), hBoxLayout);
219}
220
221class MyLayout : public QLayout
222{
223 public:
224 MyLayout() : invalidated(false) {}
225 virtual void invalidate() {invalidated = true;}
226 bool invalidated;
227 QSize sizeHint() const {return QSize();}
228 void addItem(QLayoutItem*) {}
229 QLayoutItem* itemAt(int) const {return 0;}
230 QLayoutItem* takeAt(int) {return 0;}
231 int count() const {return 0;}
232};
233
234void tst_QLayout::setContentsMargins()
235{
236 MyLayout layout;
237 layout.invalidated = false;
238 int left, top, right, bottom;
239
240 layout.setContentsMargins(left: 52, top: 53, right: 54, bottom: 55);
241 QVERIFY(layout.invalidated);
242 layout.invalidated = false;
243
244 layout.getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
245 QCOMPARE(left, 52);
246 QCOMPARE(top, 53);
247 QCOMPARE(right, 54);
248 QCOMPARE(bottom, 55);
249
250 layout.setContentsMargins(left: 52, top: 53, right: 54, bottom: 55);
251 QVERIFY(!layout.invalidated);
252}
253
254class EventReceiver : public QObject
255{
256public:
257 bool eventFilter(QObject *watched, QEvent *event)
258 {
259 if (event->type() == QEvent::Show) {
260 geom = static_cast<QWidget*>(watched)->geometry();
261 }
262 return false;
263 }
264 QRect geom;
265};
266
267void tst_QLayout::layoutItemRect()
268{
269#ifdef Q_OS_MAC
270 if (QApplication::style()->inherits("QMacStyle")) {
271 QWidget *window = new QWidget;
272 QRadioButton *radio = new QRadioButton(window);
273 QWidgetItem item(radio);
274 EventReceiver eventReceiver;
275 radio->installEventFilter(&eventReceiver);
276
277 radio->show();
278 QApplication::processEvents();
279 QApplication::processEvents();
280 QSize s = item.sizeHint();
281
282 item.setAlignment(Qt::AlignVCenter);
283 item.setGeometry(QRect(QPoint(0, 0), s));
284
285 QCOMPARE(radio->geometry().size(), radio->sizeHint());
286 delete radio;
287 }
288#endif
289}
290
291void tst_QLayout::warnIfWrongParent()
292{
293 QWidget root;
294 QHBoxLayout lay;
295 lay.setParent(&root);
296 QTest::ignoreMessage(type: QtWarningMsg, message: "QLayout::parentWidget: A layout can only have another layout as a parent.");
297 QCOMPARE(lay.parentWidget(), nullptr);
298}
299
300void tst_QLayout::controlTypes()
301{
302 QVBoxLayout layout;
303 QCOMPARE(layout.controlTypes(), QSizePolicy::DefaultType);
304 QSizePolicy p;
305 QCOMPARE(p.controlType(),QSizePolicy::DefaultType);
306}
307
308void tst_QLayout::controlTypes2()
309{
310 QWidget main;
311 QVBoxLayout *const layout = new QVBoxLayout(&main);
312 layout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
313 QComboBox *combo = new QComboBox(&main);
314 layout->addWidget(combo);
315 QCOMPARE(layout->controlTypes(), QSizePolicy::ComboBox);
316}
317
318void tst_QLayout::adjustSizeShouldMakeSureLayoutIsActivated()
319{
320 QWidget main;
321
322 QVBoxLayout *const layout = new QVBoxLayout(&main);
323 layout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
324 SizeHinterFrame *frame = new SizeHinterFrame(QSize(200, 10), QSize(200, 8));
325 frame->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Preferred);
326 layout->addWidget(frame);
327
328 SizeHinterFrame *frame2 = new SizeHinterFrame(QSize(200, 10), QSize(200, 8));
329 frame2->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Preferred);
330 layout->addWidget(frame2);
331
332 main.show();
333
334 frame2->hide();
335 main.adjustSize();
336 QCOMPARE(main.size(), QSize(200, 10));
337}
338
339void tst_QLayout::testRetainSizeWhenHidden()
340{
341#if (defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)) || defined(Q_OS_WINRT)
342 QSKIP("Test does not work on platforms which default to showMaximized()");
343#endif
344
345 QWidget widget;
346 QBoxLayout layout(QBoxLayout::TopToBottom, &widget);
347
348 QLabel *label1 = new QLabel("label1 text", &widget);
349 layout.addWidget(label1);
350 QLabel *label2 = new QLabel("label2 text", &widget);
351 layout.addWidget(label2);
352
353 widget.show();
354 QVERIFY(QTest::qWaitForWindowExposed(&widget));
355 int normalHeight = widget.height();
356
357 // a. Verify that a removed visible will mean lesser size after adjust
358 label1->hide();
359 widget.adjustSize();
360 int heightWithoutLabel1 = widget.height();
361 QVERIFY(heightWithoutLabel1 < normalHeight);
362
363 // b restore with verify that the size is the same
364 label1->show();
365 QCOMPARE(widget.sizeHint().height(), normalHeight);
366
367 // c verify that a policy with retainSizeWhenHidden is respected
368 QSizePolicy sp_remove = label1->sizePolicy();
369 QSizePolicy sp_retain = label1->sizePolicy();
370 sp_retain.setRetainSizeWhenHidden(true);
371
372 label1->setSizePolicy(sp_retain);
373 label1->hide();
374 QCOMPARE(widget.sizeHint().height(), normalHeight);
375
376 // d check that changing the policy to not wanting size will result in lesser size
377 label1->setSizePolicy(sp_remove);
378 QCOMPARE(widget.sizeHint().height(), heightWithoutLabel1);
379
380 // e verify that changing back the hidden widget to want the hidden size will ensure that it gets more size
381 label1->setSizePolicy(sp_retain);
382 QCOMPARE(widget.sizeHint().height(), normalHeight);
383}
384
385void tst_QLayout::removeWidget()
386{
387 QHBoxLayout layout;
388 QCOMPARE(layout.count(), 0);
389 QWidget w;
390 layout.addWidget(&w);
391 QCOMPARE(layout.count(), 1);
392 layout.removeWidget(w: &w);
393 QCOMPARE(layout.count(), 0);
394
395 QPointer<QLayout> childLayout(new QHBoxLayout);
396 layout.addLayout(layout: childLayout);
397 QCOMPARE(layout.count(), 1);
398
399 layout.removeWidget(w: nullptr);
400 QCOMPARE(layout.count(), 1);
401
402 layout.removeItem(childLayout);
403 QCOMPARE(layout.count(), 0);
404
405 QVERIFY(!childLayout.isNull());
406}
407
408QTEST_MAIN(tst_QLayout)
409#include "tst_qlayout.moc"
410

source code of qtbase/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp