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#include <QtGui>
32#include <QtWidgets>
33#include <math.h>
34
35class tst_QGraphicsLayout : public QObject
36{
37Q_OBJECT
38
39public:
40 tst_QGraphicsLayout();
41 virtual ~tst_QGraphicsLayout();
42
43private slots:
44 void sizeHints();
45 void compressLayoutRequest();
46 void automaticReparenting();
47 void verifyActivate();
48 void sizeHintOfHiddenLayout();
49 void invalidate();
50 void constructors();
51 void alternativeLayoutItems();
52 void ownership();
53};
54
55tst_QGraphicsLayout::tst_QGraphicsLayout()
56{
57}
58
59tst_QGraphicsLayout::~tst_QGraphicsLayout()
60{
61}
62
63void tst_QGraphicsLayout::sizeHints()
64{
65
66 QGraphicsView view;
67 QGraphicsScene scene;
68 QGraphicsWidget *window = new QGraphicsWidget();
69 scene.addItem(item: window);
70 QGraphicsLinearLayout *lout = new QGraphicsLinearLayout(window);
71 lout->setContentsMargins(left: 0,top: 0,right: 0,bottom: 0);
72 QGraphicsWidget *gw = new QGraphicsWidget(window);
73 gw->setMinimumSize(QSizeF(10,10));
74 gw->setPreferredSize(QSizeF(100,100));
75 gw->setMaximumSize(QSizeF(500,500));
76 lout->addItem(item: gw);
77 QCOMPARE(lout->effectiveSizeHint(Qt::MinimumSize), gw->effectiveSizeHint(Qt::MinimumSize));
78 QCOMPARE(lout->effectiveSizeHint(Qt::PreferredSize), gw->effectiveSizeHint(Qt::PreferredSize));
79 QCOMPARE(lout->effectiveSizeHint(Qt::MaximumSize), gw->effectiveSizeHint(Qt::MaximumSize));
80
81}
82
83enum FunctionType {
84 SetGeometry = 0,
85 Invalidate,
86 NumFunctionTypes
87};
88
89
90
91class TestGraphicsWidget : public QGraphicsWidget {
92public:
93 TestGraphicsWidget(QGraphicsWidget *parent = 0) : QGraphicsWidget(parent)
94 { }
95
96 bool event(QEvent *e) {
97 ++(m_eventCount[int(e->type())]);
98 return QGraphicsWidget::event(event: e);
99 }
100
101 int eventCount(QEvent::Type type) {
102 return m_eventCount.value(akey: int(type));
103 }
104
105 void clearEventCount() {
106 m_eventCount.clear();
107 }
108
109 void clearCounters() {
110 m_eventCount.clear();
111 functionCount.clear();
112 }
113
114 void setGeometry(const QRectF &rect)
115 {
116 QGraphicsWidget::setGeometry(rect);
117 ++(functionCount[SetGeometry]);
118 }
119
120 void callUpdateGeometry()
121 {
122 // updateGeometry() is protected
123 QGraphicsWidget::updateGeometry();
124 }
125 QMap<FunctionType, int> functionCount;
126private:
127 QMap<int, int> m_eventCount;
128};
129
130void tst_QGraphicsLayout::compressLayoutRequest()
131{
132 QGraphicsView view;
133 QGraphicsScene scene;
134 TestGraphicsWidget *tw = new TestGraphicsWidget();
135 scene.addItem(item: tw);
136 view.show();
137
138 QVERIFY(QTest::qWaitForWindowExposed(&view));
139 QGraphicsLinearLayout *lout = new QGraphicsLinearLayout(tw);
140 for (int i = 0; i < 4; ++i) {
141 QGraphicsWidget *gw = new QGraphicsWidget(tw);
142 gw->setPreferredSize(QSizeF(50, 50));
143 lout->addItem(item: gw);
144 }
145 QApplication::processEvents();
146 QCOMPARE(tw->eventCount(QEvent::LayoutRequest), 1);
147}
148
149void tst_QGraphicsLayout::automaticReparenting()
150{
151 QGraphicsView view;
152 QGraphicsScene scene;
153 {
154 QGraphicsWidget *w = new QGraphicsWidget();
155 QGraphicsLinearLayout *l = new QGraphicsLinearLayout(w);
156 QGraphicsWidget *w1 = new QGraphicsWidget;
157 l->addItem(item: w1);
158 scene.addItem(item: w);
159 QCOMPARE(w1->parentWidget(), w);
160 delete w;
161 }
162 {
163 QGraphicsWidget *w = new QGraphicsWidget();
164 QGraphicsLinearLayout *l = new QGraphicsLinearLayout(w);
165 QGraphicsWidget *w1 = new QGraphicsWidget;
166 l->addItem(item: w1);
167 scene.addItem(item: w);
168 QCOMPARE(w1->parentWidget(), w);
169
170 QGraphicsWidget *ww = new QGraphicsWidget();
171 QGraphicsLinearLayout *l1 = new QGraphicsLinearLayout(ww);
172#if !defined(Q_OS_MAC) && defined(QT_DEBUG)
173 QTest::ignoreMessage(type: QtWarningMsg, message: "QGraphicsLayout::addChildLayoutItem: QGraphicsWidget \"\""
174 " in wrong parent; moved to correct parent");
175#endif
176 l1->addItem(item: w1);
177 QCOMPARE(w1->parentWidget(), ww);
178 delete w;
179 }
180
181 QGraphicsWidget *window = new QGraphicsWidget();
182 scene.addItem(item: window);
183 view.show();
184 QGraphicsLinearLayout *l1 = new QGraphicsLinearLayout();
185 QGraphicsWidget *w1 = new QGraphicsWidget();
186 l1->addItem(item: w1);
187 QGraphicsWidget *w2 = new QGraphicsWidget();
188 l1->addItem(item: w2);
189 QCOMPARE(w1->parentItem(), nullptr);
190 QCOMPARE(w2->parentItem(), nullptr);
191 scene.addItem(item: w1);
192 QCOMPARE(w1->parentItem(), nullptr);
193 window->setLayout(l1);
194 QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window));
195 QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window));
196
197 // Sublayouts
198 QGraphicsLinearLayout *l2 = new QGraphicsLinearLayout();
199 QGraphicsWidget *w3 = new QGraphicsWidget();
200 l2->addItem(item: w3);
201 QGraphicsWidget *w4 = new QGraphicsWidget();
202 l2->addItem(item: w4);
203 QGraphicsLinearLayout *l3 = new QGraphicsLinearLayout();
204 l2->addItem(item: l3);
205 QGraphicsWidget *window2 = new QGraphicsWidget();
206 scene.addItem(item: window2);
207 window2->setLayout(l2);
208
209 QCOMPARE(w3->parentItem(), static_cast<QGraphicsItem*>(window2));
210 QCOMPARE(w4->parentItem(), static_cast<QGraphicsItem*>(window2));
211
212 // graphics item with another parent
213 QGraphicsLinearLayout *l5 = new QGraphicsLinearLayout();
214 l5->addItem(item: w1);
215 l5->addItem(item: w2);
216 QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window));
217 QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window));
218 QGraphicsLinearLayout *l4 = new QGraphicsLinearLayout();
219 l4->addItem(item: l5);
220 QGraphicsWidget *window3 = new QGraphicsWidget();
221 scene.addItem(item: window3);
222 window3->setLayout(l4);
223
224 QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window3));
225 QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window3));
226}
227
228class TestLayout : public QGraphicsLinearLayout
229{
230 public:
231 TestLayout(QGraphicsLayoutItem *parent = 0)
232 : QGraphicsLinearLayout(parent)
233 {
234 setContentsMargins(left: 0,top: 0,right: 0,bottom: 0);
235 setSpacing(0);
236 }
237
238 void setGeometry(const QRectF &rect)
239 {
240 ++(functionCount[SetGeometry]);
241 QGraphicsLinearLayout::setGeometry(rect);
242 }
243
244 void invalidate()
245 {
246 ++(functionCount[Invalidate]);
247 QGraphicsLinearLayout::invalidate();
248 }
249
250 void clearCounters() {
251 functionCount.clear();
252 }
253
254 QMap<FunctionType, int> functionCount;
255};
256
257void tst_QGraphicsLayout::verifyActivate()
258{
259 QGraphicsScene scene;
260 QGraphicsView view(&scene);
261
262 QGraphicsWidget *window = new QGraphicsWidget();
263 scene.addItem(item: window);
264 TestLayout *lout = new TestLayout(window);
265 QGraphicsWidget *w = new QGraphicsWidget();
266 lout->addItem(item: w);
267 window->setLayout(lout);
268
269 QCOMPARE(lout->functionCount[SetGeometry], 0);
270 window->setVisible(false);
271 QCOMPARE(lout->functionCount[SetGeometry], 0);
272 window->setVisible(true);
273 // on polish or the first time a widget is shown, the widget is resized.
274 QCOMPARE(lout->functionCount[SetGeometry], 1);
275
276}
277
278
279void tst_QGraphicsLayout::sizeHintOfHiddenLayout()
280{
281 QGraphicsScene scene;
282 QGraphicsView view(&scene);
283
284 QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window);
285 scene.addItem(item: window);
286 TestLayout *lout = new TestLayout(window);
287 lout->setContentsMargins(left: 1,top: 2,right: 2,bottom: 1);
288 QGraphicsWidget *w = new QGraphicsWidget;
289 w->setPreferredSize(aw: 20, ah: 20);
290 w->setMaximumSize(aw: 50, ah: 50);
291 lout->addItem(item: w);
292 window->setLayout(lout);
293
294 for (int pass = 0; pass < 3; ++pass) {
295 QCOMPARE(lout->sizeHint(Qt::MinimumSize), QSizeF(3,3));
296 QCOMPARE(lout->sizeHint(Qt::PreferredSize), QSizeF(23,23));
297 QCOMPARE(lout->sizeHint(Qt::MaximumSize), QSizeF(53,53));
298 window->setVisible(pass % 2);
299 }
300}
301
302static void clearAllCounters(TestGraphicsWidget *widget)
303{
304 if (!widget)
305 return;
306 widget->clearCounters();
307 TestLayout *layout = static_cast<TestLayout *>(widget->layout());
308 if (layout) {
309 layout->clearCounters();
310 for (int i = layout->count() - 1; i >=0; --i) {
311 QGraphicsLayoutItem *item = layout->itemAt(index: i);
312 if (item->isLayout()) {
313 // ### Not used ATM
314 //TestLayout *lay = static_cast<TestLayout*>(static_cast<QGraphicsLayout*>(item));
315 //clearAllCounters(lay);
316 } else {
317 TestGraphicsWidget *wid = static_cast<TestGraphicsWidget *>(item);
318 clearAllCounters(widget: wid);
319 }
320 }
321 }
322}
323
324static void activateAndReset(TestGraphicsWidget *widget)
325{
326 QApplication::sendPostedEvents();
327 QApplication::processEvents();
328 if (widget->layout())
329 widget->layout()->activate();
330 clearAllCounters(widget);
331}
332
333
334void tst_QGraphicsLayout::invalidate()
335{
336 QGraphicsLayout::setInstantInvalidatePropagation(true);
337 QGraphicsScene scene;
338 QGraphicsView view(&scene);
339
340 TestGraphicsWidget *a = new TestGraphicsWidget;
341 a->setData(key: 0, value: QString("a"));
342 scene.addItem(item: a);
343 TestLayout *alay = new TestLayout(a);
344 TestGraphicsWidget *b = new TestGraphicsWidget;
345 b->setData(key: 0, value: QString("b"));
346 alay->addItem(item: b);
347 TestLayout *blay = new TestLayout(b);
348 TestGraphicsWidget *e = new TestGraphicsWidget;
349 e->setData(key: 0, value: QString("e"));
350 blay->addItem(item: e);
351
352
353 TestGraphicsWidget *c = new TestGraphicsWidget;
354 c->setData(key: 0, value: QString("c"));
355 alay->addItem(item: c);
356 TestLayout *clay = new TestLayout(c);
357 TestGraphicsWidget *f = new TestGraphicsWidget;
358 f->setData(key: 0, value: QString("f"));
359 clay->addItem(item: f);
360
361 TestGraphicsWidget *d = new TestGraphicsWidget;
362 d->setData(key: 0, value: QString("d"));
363 alay->addItem(item: d);
364 TestLayout *dlay = new TestLayout(d);
365 TestGraphicsWidget *g = new TestGraphicsWidget;
366 g->setData(key: 0, value: QString("g"));
367 dlay->addItem(item: g);
368
369 view.show();
370
371 {
372 clearAllCounters(widget: a);
373
374 QCoreApplication::sendPostedEvents();
375 QCoreApplication::processEvents();
376
377 alay->activate();
378 QCOMPARE(alay->isActivated(), true);
379 QCOMPARE(blay->isActivated(), true);
380 QCOMPARE(clay->isActivated(), true);
381 QCOMPARE(dlay->isActivated(), true);
382 }
383
384 {
385 clearAllCounters(widget: a);
386 e->callUpdateGeometry();
387 QCOMPARE(alay->isActivated(), false);
388 QCOMPARE(blay->isActivated(), false);
389 QCOMPARE(clay->isActivated(), true);
390 QCOMPARE(dlay->isActivated(), true);
391 QCOMPARE(a->eventCount(QEvent::LayoutRequest), 0);
392 QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
393 QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
394 QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
395
396 // should only invalidate ascendants of e
397 QCOMPARE(blay->functionCount[Invalidate], 1);
398 QCOMPARE(alay->functionCount[Invalidate], 1);
399 // not siblings
400 QCOMPARE(clay->functionCount[Invalidate], 0);
401 QCOMPARE(dlay->functionCount[Invalidate], 0);
402
403 QApplication::sendPostedEvents();
404 QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
405 QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1);
406 QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
407 QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
408 }
409
410
411 {
412 activateAndReset(widget: a);
413 f->callUpdateGeometry();
414 QCOMPARE(alay->isActivated(), false);
415 QCOMPARE(blay->isActivated(), true);
416 QCOMPARE(clay->isActivated(), false);
417 QCOMPARE(dlay->isActivated(), true);
418
419 QCoreApplication::sendPostedEvents();
420 QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
421 QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
422 QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
423 QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
424
425 QCOMPARE(a->functionCount[SetGeometry], 1);
426 QCOMPARE(alay->functionCount[SetGeometry], 1);
427
428 QCOMPARE(b->functionCount[SetGeometry], 1);
429 QCOMPARE(c->functionCount[SetGeometry], 1);
430 QCOMPARE(d->functionCount[SetGeometry], 1);
431 // Since nothing really changed, blay and dlay don't need
432 // to be resized.
433 QCOMPARE(blay->functionCount[SetGeometry], 0);
434 QCOMPARE(clay->functionCount[SetGeometry], 1);
435 QCOMPARE(dlay->functionCount[SetGeometry], 0);
436
437 QCOMPARE(f->functionCount[SetGeometry], 1);
438
439 QCOMPARE(a->size(), QSizeF(150, 50));
440 }
441
442 {
443 activateAndReset(widget: a);
444 f->setPreferredSize(QSizeF(60,50));
445 QCOMPARE(alay->isActivated(), false);
446 QCOMPARE(blay->isActivated(), true);
447 QCOMPARE(clay->isActivated(), false);
448 QCOMPARE(dlay->isActivated(), true);
449
450 QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
451 QCoreApplication::sendPostedEvents();
452 QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
453 QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
454 QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
455 QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
456
457 QCOMPARE(a->functionCount[SetGeometry], 1);
458 QCOMPARE(alay->functionCount[SetGeometry], 1);
459
460 QCOMPARE(b->functionCount[SetGeometry], 1);
461 QCOMPARE(c->functionCount[SetGeometry], 1);
462 QCOMPARE(d->functionCount[SetGeometry], 1);
463 // f actually got wider, need to rearrange its siblings
464 QCOMPARE(blay->functionCount[SetGeometry], 1);
465 QCOMPARE(clay->functionCount[SetGeometry], 1);
466 QCOMPARE(dlay->functionCount[SetGeometry], 1);
467
468 QCOMPARE(e->functionCount[SetGeometry], 1);
469 QCOMPARE(f->functionCount[SetGeometry], 1);
470 QCOMPARE(g->functionCount[SetGeometry], 1);
471
472 QVERIFY(e->size().width() < f->size().width());
473 QVERIFY(g->size().width() < f->size().width());
474 }
475
476 {
477 // resize f so much that it'll force a resize of the top widget
478 // this will currently generate two setGeometry() calls on the child layout
479 // of the top widget.
480 activateAndReset(widget: a);
481 f->setPreferredSize(QSizeF());
482 f->setMinimumSize(QSizeF(200,50));
483 QCOMPARE(alay->isActivated(), false);
484 QCOMPARE(blay->isActivated(), true);
485 QCOMPARE(clay->isActivated(), false);
486 QCOMPARE(dlay->isActivated(), true);
487
488 QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
489 QCoreApplication::sendPostedEvents();
490 QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
491 QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
492 QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
493 QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
494
495 QCOMPARE(a->functionCount[SetGeometry], 1);
496
497 /* well, ideally one call to setGeometry(), but it will currently
498 * get two calls to setGeometry():
499 * 1. The first LayoutRequest will call activate() - that will call
500 * setGeometry() on the layout. This geometry will be based on
501 * the widget geometry which is not correct at this moment.
502 * (it is still 150 wide)
503 * 2. Next, we check if the widget is top level, and then we call
504 * parentWidget->resize(parentWidget->size());
505 * This will be adjusted to be minimum 200 pixels wide.
506 * The new size will then be propagated down to the layout
507 *
508 */
509 QCOMPARE(alay->functionCount[SetGeometry], 2);
510
511 QCOMPARE(b->functionCount[SetGeometry], 2);
512 QCOMPARE(c->functionCount[SetGeometry], 2);
513 QCOMPARE(d->functionCount[SetGeometry], 2);
514 // f actually got wider, need to rearrange its siblings
515 QCOMPARE(blay->functionCount[SetGeometry], 1);
516 QCOMPARE(clay->functionCount[SetGeometry], 1);
517 QCOMPARE(dlay->functionCount[SetGeometry], 1);
518
519 QCOMPARE(e->functionCount[SetGeometry], 1);
520 QCOMPARE(f->functionCount[SetGeometry], 1);
521 QCOMPARE(g->functionCount[SetGeometry], 1);
522
523 QVERIFY(e->size().width() < f->size().width());
524 QVERIFY(g->size().width() < f->size().width());
525 }
526
527 {
528 f->setPreferredSize(QSizeF());
529 f->setMinimumSize(QSizeF());
530 a->adjustSize();
531 activateAndReset(widget: a);
532 // update two different leaf widgets,
533 // eventCount and functionCount should never be >= 2
534 e->callUpdateGeometry();
535 g->callUpdateGeometry();
536 QCOMPARE(alay->isActivated(), false);
537 QCOMPARE(blay->isActivated(), false);
538 QCOMPARE(clay->isActivated(), true);
539 QCOMPARE(dlay->isActivated(), false);
540
541 QCoreApplication::sendPostedEvents();
542 QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
543 QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1);
544 QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
545 QCOMPARE(d->eventCount(QEvent::LayoutRequest), 1);
546
547 QCOMPARE(a->functionCount[SetGeometry], 1);
548 QCOMPARE(alay->functionCount[SetGeometry], 1);
549
550 QCOMPARE(b->functionCount[SetGeometry], 1);
551 QCOMPARE(c->functionCount[SetGeometry], 1);
552 QCOMPARE(d->functionCount[SetGeometry], 1);
553 // f actually got wider, need to rearrange its siblings
554 QCOMPARE(blay->functionCount[SetGeometry], 1);
555 QCOMPARE(clay->functionCount[SetGeometry], 0);
556 QCOMPARE(dlay->functionCount[SetGeometry], 1);
557
558 QCOMPARE(e->functionCount[SetGeometry], 1);
559 QCOMPARE(f->functionCount[SetGeometry], 0);
560 QCOMPARE(g->functionCount[SetGeometry], 1);
561
562 }
563
564 QGraphicsLayout::setInstantInvalidatePropagation(false);
565}
566
567class Layout : public QGraphicsLayout
568{
569public:
570 Layout(QGraphicsLayoutItem *parentItem = 0) : QGraphicsLayout(parentItem) {}
571
572 void setGeometry(const QRectF &rect)
573 {
574 QGraphicsLayout::setGeometry(rect);
575 }
576
577 int count() const {
578 return 0;
579 }
580
581 QGraphicsLayoutItem *itemAt(int index) const {
582 Q_UNUSED(index);
583 return 0;
584 }
585
586 void removeAt(int index)
587 {
588 Q_UNUSED(index);
589 }
590
591protected:
592 QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const
593 {
594 Q_UNUSED(constraint);
595 Q_UNUSED(which);
596 return QSizeF(100,100);
597 }
598
599};
600
601void tst_QGraphicsLayout::constructors()
602{
603 // Strange test, but see the fix that was with this submit
604 QVector<Layout*> layouts;
605 for (int pass = 0; pass < 5; ++pass) {
606 Layout *lay = new Layout();
607 layouts << lay;
608 qreal left, top, right, bottom;
609 lay->getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
610 // Test if the style defaults are sane (should always be ints)
611 double intpart;
612 QVERIFY(modf(left, &intpart) == 0.0);
613 QVERIFY(modf(top, &intpart) == 0.0);
614 QVERIFY(modf(right, &intpart) == 0.0);
615 QVERIFY(modf(bottom, &intpart) == 0.0);
616
617 lay->setContentsMargins(left: 1, top: 2, right: 4, bottom: 8);
618 lay->getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
619
620 QCOMPARE(int(left), 1);
621 QCOMPARE(int(top), 2);
622 QCOMPARE(int(right), 4);
623 QCOMPARE(int(bottom), 8);
624 }
625
626 qDeleteAll(c: layouts);
627}
628
629class AnimatedLayoutItem : public QGraphicsLayoutItem {
630public:
631 AnimatedLayoutItem(QGraphicsRectItem *item)
632 : QGraphicsLayoutItem()
633 {
634 setGraphicsItem(item);
635 }
636
637 void setGeometry(const QRectF &geom);
638
639 QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const;
640
641 inline QGraphicsRectItem *rectItem() {
642 return static_cast<QGraphicsRectItem *>(graphicsItem());
643 }
644
645 QRectF m_geom;
646private:
647 AnimatedLayoutItem() {}
648};
649
650void AnimatedLayoutItem::setGeometry(const QRectF &geom)
651{
652 QGraphicsLayoutItem::setGeometry(geom);
653}
654
655QSizeF AnimatedLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const
656{
657 switch (which) {
658 case Qt::MinimumSize:
659 return QSizeF(32,32);
660 case Qt::PreferredSize:
661 return QSizeF(160,90);
662 case Qt::MaximumSize:
663 return QSizeF(1000,1000);
664 default:
665 return QSizeF(300, 300);
666 }
667}
668
669class AnimatedLayout : public QObject, public QGraphicsLinearLayout {
670 Q_OBJECT
671public:
672 AnimatedLayout(QGraphicsWidget *widget) : QGraphicsLinearLayout(widget), m_timeline(500, this)
673 {
674 connect(sender: &m_timeline, SIGNAL(valueChanged(qreal)), receiver: this, SLOT(valueChanged(qreal)));
675 }
676
677 void setGeometry(const QRectF &geom) {
678 fromGeoms.clear();
679 toGeoms.clear();
680 for (int i = 0; i < count(); ++i) {
681 fromGeoms << itemAt(index: i)->geometry();
682 }
683
684 QGraphicsLinearLayout::setGeometry(geom);
685
686 for (int i = 0; i < count(); ++i) {
687 toGeoms << itemAt(index: i)->geometry();
688 }
689 m_timeline.start();
690 }
691
692private slots:
693 void valueChanged(qreal value) {
694 for (int i = 0; i < fromGeoms.count(); ++i) {
695 QGraphicsLayoutItem *li = itemAt(index: i);
696 QRectF from = fromGeoms.at(i);
697 QRectF to = toGeoms.at(i);
698
699 QRectF geom(from.topLeft() + (to.topLeft() - from.topLeft()) * value,
700 from.size() + (to.size() - from.size()) * value);
701 static_cast<QGraphicsRectItem*>(li->graphicsItem())->setRect(geom);
702 }
703 }
704private:
705 QTimeLine m_timeline;
706 QVector<QRectF> fromGeoms;
707 QVector<QRectF> toGeoms;
708};
709
710
711void tst_QGraphicsLayout::alternativeLayoutItems()
712{
713 QGraphicsScene scene;
714 QGraphicsView view(&scene);
715
716 QGraphicsWidget *window = new QGraphicsWidget;
717 scene.addItem(item: window);
718 AnimatedLayout *lout = new AnimatedLayout(window);
719 lout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
720 lout->setSpacing(0);
721
722 QGraphicsRectItem *item1 = new QGraphicsRectItem;
723 AnimatedLayoutItem *li1 = new AnimatedLayoutItem(item1);
724 lout->addItem(item: li1);
725
726 QGraphicsRectItem *item2 = new QGraphicsRectItem;
727 AnimatedLayoutItem *li2 = new AnimatedLayoutItem(item2);
728 lout->addItem(item: li2);
729
730 QGraphicsRectItem *item3 = new QGraphicsRectItem;
731 AnimatedLayoutItem *li3 = new AnimatedLayoutItem(item3);
732 lout->addItem(item: li3);
733
734 window->setLayout(lout);
735
736 window->setGeometry(ax: 0, ay: 0, aw: 99, ah: 99);
737 view.setSceneRect(QRectF(-10, -10, 110, 110));
738 view.resize(w: 150, h: 150);
739 view.show();
740
741 QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li1->graphicsItem())->rect(), QRectF( 0, 0, 33, 99));
742 QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li2->graphicsItem())->rect(), QRectF(33, 0, 33, 99));
743 QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li3->graphicsItem())->rect(), QRectF(66, 0, 33, 99));
744
745 lout->setOrientation(Qt::Vertical);
746
747 QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li1->graphicsItem())->rect(), QRectF(0, 0, 99, 33));
748 QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li2->graphicsItem())->rect(), QRectF(0, 33, 99, 33));
749 QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li3->graphicsItem())->rect(), QRectF(0, 66, 99, 33));
750
751}
752
753class CustomLayoutItem : public QGraphicsLayoutItem {
754public:
755 CustomLayoutItem(QSet<QGraphicsLayoutItem*> *destructedSet)
756 : QGraphicsLayoutItem()
757 {
758 m_destructedSet = destructedSet;
759 setOwnedByLayout(true);
760 }
761
762 QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const;
763
764 ~CustomLayoutItem() {
765 m_destructedSet->insert(value: this);
766 }
767private:
768 QSet<QGraphicsLayoutItem*> *m_destructedSet;
769};
770
771QSizeF CustomLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const
772{
773 switch (which) {
774 case Qt::MinimumSize:
775 return QSizeF(32,32);
776 case Qt::PreferredSize:
777 return QSizeF(160,90);
778 case Qt::MaximumSize:
779 return QSizeF(1000,1000);
780 default:
781 return QSizeF(300, 300);
782 }
783}
784
785class CustomGraphicsWidget : public QGraphicsWidget {
786public:
787 CustomGraphicsWidget(QSet<QGraphicsLayoutItem*> *destructedSet = 0)
788 : QGraphicsWidget()
789 {
790 m_destructedSet = destructedSet;
791 }
792
793 QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const;
794
795 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * = 0)
796 {
797 const QRect r = option->rect.adjusted(xp1: 0, yp1: 0, xp2: -1, yp2: -1);
798 painter->drawLine(p1: r.topLeft(), p2: r.bottomRight());
799 painter->drawLine(p1: r.bottomLeft(), p2: r.topRight());
800 painter->drawRect(r);
801 }
802
803 ~CustomGraphicsWidget() {
804 if (m_destructedSet)
805 m_destructedSet->insert(value: this);
806 }
807private:
808 QSet<QGraphicsLayoutItem*> *m_destructedSet;
809};
810
811QSizeF CustomGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const
812{
813 switch (which) {
814 case Qt::MinimumSize:
815 return QSizeF(32,32);
816 case Qt::PreferredSize:
817 return QSizeF(160,90);
818 case Qt::MaximumSize:
819 return QSizeF(1000,1000);
820 default:
821 return QSizeF(300, 300);
822 }
823}
824
825static bool compareSets(const QSet<QGraphicsLayoutItem*> &actual, const QSet<QGraphicsLayoutItem*> &expected)
826{
827 if (actual != expected) {
828 qDebug() << "actual:" << actual << "expected:" << expected;
829 return false;
830 }
831 return true;
832}
833
834class CustomLayout : public QGraphicsLayout
835{
836public :
837CustomLayout(QGraphicsLayoutItem *parent)
838 : QGraphicsLayout(parent)
839{
840}
841
842
843~CustomLayout()
844{
845}
846
847int count() const
848{
849 return items.count();
850}
851
852QGraphicsLayoutItem* itemAt(int index) const
853{
854 return items.at(i: index);
855}
856
857
858void removeAt(int index)
859{
860 items.removeAt(i: index);
861}
862
863void addItem(QGraphicsLayoutItem *item)
864{
865 insertItem(index: items.count(), item);
866}
867
868void insertItem(int index, QGraphicsLayoutItem *item)
869{
870 index = qBound(min: 0, val: index, max: items.count());
871
872 item->setParentLayoutItem(this);
873
874 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
875 updateParentWidget(item: widget);
876
877
878 if (index == items.count()) {
879 items.append(t: item);
880 } else {
881 items.insert(i: index, t: item);
882 }
883
884 updateGeometry();
885 activate();
886}
887
888void updateParentWidget(QGraphicsWidget *item)
889{
890 QGraphicsLayoutItem *parentItem = parentLayoutItem();
891 while (parentItem && parentItem->isLayout()) {
892 parentItem = parentItem->parentLayoutItem();
893 }
894
895 if (parentItem) {
896 item->setParentItem(static_cast<QGraphicsWidget*>(parentItem));
897 }
898}
899
900QSizeF sizeHint(Qt::SizeHint /* which */, const QSizeF & /* constraint */) const
901{
902 return QSizeF(50,50);
903}
904
905QList<QGraphicsLayoutItem*> items;
906
907};
908
909void tst_QGraphicsLayout::ownership()
910{
911 QGraphicsScene scene;
912 QGraphicsView view(&scene);
913
914 {
915 QGraphicsLinearLayout *lay = new QGraphicsLinearLayout;
916 QSet<QGraphicsLayoutItem*> destructedSet;
917 CustomLayoutItem *li1 = new CustomLayoutItem(&destructedSet);
918 lay->addItem(item: li1);
919 CustomLayoutItem *li2 = new CustomLayoutItem(&destructedSet);
920 lay->addItem(item: li2);
921 CustomLayoutItem *li3 = new CustomLayoutItem(&destructedSet);
922 lay->addItem(item: li3);
923 destructedSet.clear();
924
925 delete lay;
926 QSet<QGraphicsLayoutItem*> expected;
927 expected << li1 << li2 << li3;
928 QVERIFY(compareSets(destructedSet, expected));
929 }
930
931 {
932 QGraphicsWidget *window = new QGraphicsWidget;
933 QGraphicsLinearLayout *lay = new QGraphicsLinearLayout;
934 QSet<QGraphicsLayoutItem*> destructedSet;
935 CustomGraphicsWidget *li1 = new CustomGraphicsWidget(&destructedSet);
936 lay->addItem(item: li1);
937 CustomGraphicsWidget *li2 = new CustomGraphicsWidget(&destructedSet);
938 lay->addItem(item: li2);
939 CustomGraphicsWidget *li3 = new CustomGraphicsWidget(&destructedSet);
940 lay->addItem(item: li3);
941 window->setLayout(lay);
942 scene.addItem(item: window);
943
944 destructedSet.clear();
945 window->setLayout(0);
946 QCOMPARE(destructedSet.count(), 0);
947 delete window;
948 }
949
950 {
951 QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window);
952 QGraphicsLinearLayout *lay = new QGraphicsLinearLayout;
953
954 CustomGraphicsWidget *li1 = new CustomGraphicsWidget;
955 lay->addItem(item: li1);
956
957 QGraphicsLinearLayout *li2 = new QGraphicsLinearLayout;
958 CustomGraphicsWidget *li2_1 = new CustomGraphicsWidget;
959 li2->addItem(item: li2_1);
960 CustomGraphicsWidget *li2_2 = new CustomGraphicsWidget;
961 li2->addItem(item: li2_2);
962 CustomGraphicsWidget *li2_3 = new CustomGraphicsWidget;
963 li2->addItem(item: li2_3);
964 lay->addItem(item: li2);
965
966 CustomGraphicsWidget *li3 = new CustomGraphicsWidget;
967 lay->addItem(item: li3);
968
969 window->setLayout(lay);
970 scene.addItem(item: window);
971 view.resize(w: 500, h: 200);
972 view.show();
973
974 for (int i = li2->count(); i > 0; --i) {
975 QCOMPARE(li2->count(), i);
976 delete li2->itemAt(index: 0);
977 }
978
979 for (int i = lay->count(); i > 0; --i) {
980 QCOMPARE(lay->count(), i);
981 delete lay->itemAt(index: 0);
982 }
983
984 delete window;
985 }
986
987 {
988 QGraphicsWidget *top = new QGraphicsWidget;
989 QGraphicsWidget *w = new QGraphicsWidget;
990 QGraphicsWidget *w2 = new QGraphicsWidget;
991 CustomLayout *layout = new CustomLayout(top);
992 layout->addItem(item: w);
993 layout->addItem(item: w2);
994 top->setLayout(layout);
995 delete top;
996 //don't crash after that.
997 }
998}
999
1000QTEST_MAIN(tst_QGraphicsLayout)
1001#include "tst_qgraphicslayout.moc"
1002

source code of qtbase/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp