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 | |
35 | class tst_QGraphicsLayout : public QObject |
36 | { |
37 | Q_OBJECT |
38 | |
39 | public: |
40 | tst_QGraphicsLayout(); |
41 | virtual ~tst_QGraphicsLayout(); |
42 | |
43 | private 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 | |
55 | tst_QGraphicsLayout::tst_QGraphicsLayout() |
56 | { |
57 | } |
58 | |
59 | tst_QGraphicsLayout::~tst_QGraphicsLayout() |
60 | { |
61 | } |
62 | |
63 | void 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 | |
83 | enum FunctionType { |
84 | SetGeometry = 0, |
85 | Invalidate, |
86 | NumFunctionTypes |
87 | }; |
88 | |
89 | |
90 | |
91 | class TestGraphicsWidget : public QGraphicsWidget { |
92 | public: |
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; |
126 | private: |
127 | QMap<int, int> m_eventCount; |
128 | }; |
129 | |
130 | void 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 | |
149 | void 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 | |
228 | class 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 | |
257 | void 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 | |
279 | void 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 | |
302 | static 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 | |
324 | static 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 | |
334 | void 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 | |
567 | class Layout : public QGraphicsLayout |
568 | { |
569 | public: |
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 | |
591 | protected: |
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 | |
601 | void 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 | |
629 | class AnimatedLayoutItem : public QGraphicsLayoutItem { |
630 | public: |
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; |
646 | private: |
647 | AnimatedLayoutItem() {} |
648 | }; |
649 | |
650 | void AnimatedLayoutItem::setGeometry(const QRectF &geom) |
651 | { |
652 | QGraphicsLayoutItem::setGeometry(geom); |
653 | } |
654 | |
655 | QSizeF 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 | |
669 | class AnimatedLayout : public QObject, public QGraphicsLinearLayout { |
670 | Q_OBJECT |
671 | public: |
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 | |
692 | private 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 | } |
704 | private: |
705 | QTimeLine m_timeline; |
706 | QVector<QRectF> fromGeoms; |
707 | QVector<QRectF> toGeoms; |
708 | }; |
709 | |
710 | |
711 | void 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 | |
753 | class CustomLayoutItem : public QGraphicsLayoutItem { |
754 | public: |
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 | } |
767 | private: |
768 | QSet<QGraphicsLayoutItem*> *m_destructedSet; |
769 | }; |
770 | |
771 | QSizeF 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 | |
785 | class CustomGraphicsWidget : public QGraphicsWidget { |
786 | public: |
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 | } |
807 | private: |
808 | QSet<QGraphicsLayoutItem*> *m_destructedSet; |
809 | }; |
810 | |
811 | QSizeF 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 | |
825 | static 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 | |
834 | class CustomLayout : public QGraphicsLayout |
835 | { |
836 | public : |
837 | CustomLayout(QGraphicsLayoutItem *parent) |
838 | : QGraphicsLayout(parent) |
839 | { |
840 | } |
841 | |
842 | |
843 | ~CustomLayout() |
844 | { |
845 | } |
846 | |
847 | int count() const |
848 | { |
849 | return items.count(); |
850 | } |
851 | |
852 | QGraphicsLayoutItem* itemAt(int index) const |
853 | { |
854 | return items.at(i: index); |
855 | } |
856 | |
857 | |
858 | void removeAt(int index) |
859 | { |
860 | items.removeAt(i: index); |
861 | } |
862 | |
863 | void addItem(QGraphicsLayoutItem *item) |
864 | { |
865 | insertItem(index: items.count(), item); |
866 | } |
867 | |
868 | void 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 | |
888 | void 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 | |
900 | QSizeF sizeHint(Qt::SizeHint /* which */, const QSizeF & /* constraint */) const |
901 | { |
902 | return QSizeF(50,50); |
903 | } |
904 | |
905 | QList<QGraphicsLayoutItem*> items; |
906 | |
907 | }; |
908 | |
909 | void 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 | |
1000 | QTEST_MAIN(tst_QGraphicsLayout) |
1001 | #include "tst_qgraphicslayout.moc" |
1002 | |