1/****************************************************************************
2**
3** Copyright (C) 2019 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#include <QtWidgets/QApplication>
30#include <QtWidgets/QDial>
31#include <QtWidgets/QLabel>
32#include <QtWidgets/QLineEdit>
33#include <QtWidgets/QGraphicsView>
34#include <QtWidgets/QGraphicsScene>
35#include <QtWidgets/QGraphicsPixmapItem>
36#include <QtWidgets/QStyle>
37#include <QtWidgets/QStyleFactory>
38#include <QtWidgets/QVBoxLayout>
39
40#include <QtGui/QPainterPath>
41#include <QtGui/QScreen>
42
43#include <QtTest/QtTest>
44
45#include <QtCore/QDebug>
46#include <QtCore/QLoggingCategory>
47
48#include <private/qgraphicsscene_p.h>
49#include <private/qgraphicssceneindex_p.h>
50#include <math.h>
51#include "../../../gui/painting/qpathclipper/pathcompare.h"
52#include "../../../shared/platforminputcontext.h"
53#include <private/qinputmethod_p.h>
54
55#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
56#include <QtCore/qt_windows.h>
57#define Q_CHECK_PAINTEVENTS \
58 if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
59 QSKIP("The Graphics View doesn't get the paint events");
60#else
61#define Q_CHECK_PAINTEVENTS
62#endif
63
64Q_DECLARE_METATYPE(Qt::FocusReason)
65Q_DECLARE_METATYPE(QPainterPath)
66Q_DECLARE_METATYPE(Qt::AspectRatioMode)
67Q_DECLARE_METATYPE(Qt::ItemSelectionMode)
68Q_DECLARE_METATYPE(QGraphicsItem::GraphicsItemFlags)
69
70static const int randomX[] = {276, 40, 250, 864, -56, 426, 855, 825, 184, 955, -798, -804, 773,
71 282, 489, 686, 780, -220, 50, 749, -856, -205, 81, 492, -819, 518,
72 895, 57, -559, 788, -965, 68, -442, -247, -339, -648, 292, 891,
73 -865, 462, 864, 673, 640, 523, 194, 500, -727, 307, -243, 320,
74 -545, 415, 448, 341, -619, 652, 892, -16, -14, -659, -101, -934,
75 532, 356, 824, 132, 160, 130, 104, 886, -179, -174, 543, -644, 60,
76 -470, -354, -728, 689, 682, -587, -694, -221, -741, 37, 372, -289,
77 741, -300, 858, -320, 729, -602, -956, -544, -403, 203, 398, 284,
78 -972, -572, -946, 81, 51, -403, -580, 867, 546, 565, -580, -484,
79 659, 982, -518, -976, 423, -800, 659, -297, 712, 938, -19, -16,
80 824, -252, 197, 321, -837, 824, 136, 226, -980, -909, -826, -479,
81 -835, -503, -828, -901, -810, -641, -548, -179, 194, 749, -296, 539,
82 -37, -599, -235, 121, 35, -230, -915, 789, 764, -622, -382, -90, -701,
83 676, -407, 998, 267, 913, 817, -748, -370, -162, -797, 19, -556, 933,
84 -670, -101, -765, -941, -17, 360, 31, 960, 509, 933, -35, 974, -924,
85 -734, 589, 963, 724, 794, 843, 16, -272, -811, 721, 99, -122, 216,
86 -404, 158, 787, -443, -437, -337, 383, -342, 538, -641, 791, 637,
87 -848, 397, 820, 109, 11, 45, 809, 591, 933, 961, 625, -140, -592,
88 -694, -969, 317, 293, 777, -18, -282, 835, -455, -708, -407, -204,
89 748, 347, -501, -545, 292, -362, 176, 546, -573, -38, -854, -395,
90 560, -624, -940, -971, 66, -910, 782, 985};
91
92static const int randomY[] = {603, 70, -318, 843, 450, -637, 199, -527, 407, 964, -54, 620, -207,
93 -736, -700, -476, -706, -142, 837, 621, 522, -98, 232, 292, -267, 900,
94 615, -356, -415, 783, 290, 462, -857, -314, 677, 36, 772, 424, -72,
95 -121, 547, -533, 537, -656, 289, 508, 914, 601, 434, 588, -779, -714,
96 -368, 628, -276, 432, -1, -929, 638, -36, 253, -922, -943, 979, -34,
97 -268, -193, 601, 686, -330, 165, 98, 75, -691, -605, 617, 773, 617,
98 619, 238, -42, -405, 17, 384, -472, -846, 520, 110, 591, -217, 936,
99 -373, 731, 734, 810, 961, 881, 939, 379, -905, -137, 437, 298, 688,
100 -71, -204, 573, -120, -821, 489, -722, -926, 529, -113, -243, 543,
101 868, -301, -781, -549, -842, -489, -80, -910, -928, 51, -91, 324,
102 204, -92, 867, 723, 248, 709, -357, 591, -365, -379, 266, -649, -95,
103 205, 551, 355, -631, 79, -186, 795, -7, -225, 46, -410, 665, -874,
104 -618, 845, -548, 443, 471, -644, 606, -607, 59, -619, 288, -244, 529,
105 690, 349, -738, -611, -879, -642, 801, -178, 823, -748, -552, -247,
106 -223, -408, 651, -62, 949, -795, 171, -107, -210, -207, -842, -86,
107 436, 528, 366, -178, 245, -695, 665, 613, -948, 667, -620, -979, -949,
108 905, 181, -412, -467, -437, -774, 750, -10, 54, 205, -674, -290, -924,
109 -361, -463, 912, -702, 622, -542, 220, 115, 832, 451, -38, -952, -230,
110 -588, 864, 234, 225, -303, 493, 246, 153, 338, -378, 377, -819, 140, 136,
111 467, -849, -326, -533, 166, 252, -994, -699, 904, -566, 621, -752};
112
113Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests")
114
115class HoverItem : public QGraphicsRectItem
116{
117public:
118 HoverItem()
119 : QGraphicsRectItem(QRectF(-10, -10, 20, 20))
120 { setAcceptHoverEvents(true); }
121
122 bool isHovered = false;
123
124protected:
125 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
126 {
127 isHovered = (option->state & QStyle::State_MouseOver);
128
129 painter->setOpacity(0.75);
130 painter->setPen(Qt::NoPen);
131 painter->setBrush(Qt::darkGray);
132 painter->drawRoundedRect(rect: boundingRect().adjusted(xp1: 3, yp1: 3, xp2: -3, yp2: -3), xRadius: 25, yRadius: 25, mode: Qt::RelativeSize);
133 painter->setPen(Qt::black);
134 if (isHovered) {
135 painter->setBrush(QColor(Qt::blue).lighter(f: 120));
136 } else {
137 painter->setBrush(Qt::gray);
138 }
139 painter->drawRoundedRect(rect: boundingRect().adjusted(xp1: 0, yp1: 0, xp2: -5, yp2: -5), xRadius: 25, yRadius: 25, mode: Qt::RelativeSize);
140 }
141};
142
143class EventSpy : public QGraphicsWidget
144{
145 Q_OBJECT
146public:
147 EventSpy(QObject *watched, QEvent::Type type)
148 : spied(type)
149 {
150 watched->installEventFilter(filterObj: this);
151 }
152
153 EventSpy(QGraphicsScene *scene, QGraphicsItem *watched, QEvent::Type type)
154 : spied(type)
155 {
156 scene->addItem(item: this);
157 watched->installSceneEventFilter(filterItem: this);
158 }
159
160 int count() const { return _count; }
161
162protected:
163 bool eventFilter(QObject *watched, QEvent *event) override
164 {
165 Q_UNUSED(watched);
166 if (event->type() == spied)
167 ++_count;
168 return false;
169 }
170
171 bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
172 {
173 Q_UNUSED(watched);
174 if (event->type() == spied)
175 ++_count;
176 return false;
177 }
178
179 int _count = 0;
180 const QEvent::Type spied;
181};
182
183class tst_QGraphicsScene : public QObject
184{
185 Q_OBJECT
186public:
187 tst_QGraphicsScene();
188 static void initMain() { QCoreApplication::setAttribute(attribute: Qt::AA_DisableHighDpiScaling); }
189
190public slots:
191 void cleanup();
192
193private slots:
194 void construction();
195 void sceneRect();
196 void itemIndexMethod();
197 void bspTreeDepth();
198 void itemsBoundingRect_data();
199 void itemsBoundingRect();
200 void items();
201 void items_QPointF_data();
202 void items_QPointF();
203 void items_QRectF();
204 void items_QRectF_2_data();
205 void items_QRectF_2();
206 void items_QPolygonF();
207 void items_QPolygonF_2();
208 void items_QPainterPath();
209 void items_QPainterPath_2();
210 void selectionChanged();
211 void selectionChanged2();
212 void addItem();
213 void addEllipse();
214 void addLine();
215 void addPath();
216 void addPixmap();
217 void addRect();
218 void addText();
219 void removeItem();
220 void clear();
221 void focusItem();
222 void focusItemLostFocus();
223 void setFocusItem();
224 void setFocusItem_inactive();
225 void mouseGrabberItem();
226 void hoverEvents_siblings();
227 void hoverEvents_parentChild();
228 void createItemGroup();
229 void mouseEventPropagation();
230 void mouseEventPropagation_ignore();
231 void mouseEventPropagation_focus();
232 void mouseEventPropagation_doubleclick();
233 void mouseEventPropagation_mouseMove();
234#if QT_CONFIG(draganddrop)
235 void dragAndDrop_simple();
236 void dragAndDrop_disabledOrInvisible();
237 void dragAndDrop_propagate();
238#endif
239 void render_data();
240 void render();
241 void renderItemsWithNegativeWidthOrHeight();
242#ifndef QT_NO_CONTEXTMENU
243 void contextMenuEvent();
244 void contextMenuEvent_ItemIgnoresTransformations();
245#endif
246 void update();
247 void update2();
248 void views();
249 void testEvent();
250 void eventsToDisabledItems();
251 void exposedRect();
252 void tabFocus_emptyScene();
253 void tabFocus_sceneWithFocusableItems();
254 void tabFocus_sceneWithFocusWidgets();
255 void tabFocus_sceneWithNestedFocusWidgets();
256 void style();
257 void sorting();
258 void insertionOrder();
259 void changedSignal_data();
260 void changedSignal();
261 void stickyFocus_data();
262 void stickyFocus();
263 void sendEvent();
264 void inputMethod_data();
265 void inputMethod();
266 void dispatchHoverOnPress();
267 void initialFocus_data();
268 void initialFocus();
269 void polishItems();
270 void polishItems2();
271 void isActive();
272 void siblingIndexAlwaysValid();
273 void removeFullyTransparentItem();
274 void zeroScale();
275 void focusItemChangedSignal();
276 void minimumRenderSize();
277 void focusOnTouch();
278
279 // task specific tests below me
280 void task139710_bspTreeCrash();
281 void task139782_containsItemBoundingRect();
282 void task176178_itemIndexMethodBreaksSceneRect();
283 void task160653_selectionChanged();
284 void task250680_childClip();
285 void taskQTBUG_5904_crashWithDeviceCoordinateCache();
286 void taskQT657_paintIntoCacheWithTransparentParts();
287 void taskQTBUG_7863_paintIntoCacheWithTransparentParts();
288 void taskQT_3674_doNotCrash();
289 void taskQTBUG_15977_renderWithDeviceCoordinateCache();
290 void taskQTBUG_16401_focusItem();
291 void taskQTBUG_42915_focusNextPrevChild();
292
293private:
294 QRect m_availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
295 QSize m_testSize;
296};
297
298tst_QGraphicsScene::tst_QGraphicsScene()
299{
300 const int testSize = qMax(a: 200, b: m_availableGeometry.width() / 10);
301 m_testSize.setWidth(testSize);
302 m_testSize.setHeight(testSize);
303}
304
305void tst_QGraphicsScene::cleanup()
306{
307 // ensure not even skipped tests with custom input context leave it dangling
308 QInputMethodPrivate *inputMethodPrivate =
309 QInputMethodPrivate::get(inputMethod: QGuiApplication::inputMethod());
310 inputMethodPrivate->testContext = nullptr;
311 QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty());
312}
313
314void tst_QGraphicsScene::construction()
315{
316 QGraphicsScene scene;
317 QCOMPARE(scene.itemsBoundingRect(), QRectF());
318 QVERIFY(scene.items().isEmpty());
319 QVERIFY(scene.items(QPointF()).isEmpty());
320 QVERIFY(scene.items(QRectF()).isEmpty());
321 QVERIFY(scene.items(QPolygonF()).isEmpty());
322 QVERIFY(scene.items(QPainterPath()).isEmpty());
323 QTest::ignoreMessage(type: QtWarningMsg, message: "QGraphicsScene::collidingItems: cannot find collisions for null item");
324 QVERIFY(scene.collidingItems(nullptr).isEmpty());
325 QVERIFY(scene.items(QPointF()).isEmpty());
326 QVERIFY(scene.selectedItems().isEmpty());
327 QVERIFY(!scene.focusItem());
328}
329
330static inline const QGraphicsItem *itemAt(const QGraphicsScene &scene, qreal x, qreal y)
331{
332 return scene.items(pos: QPointF(x, y)).value(i: 0, defaultValue: nullptr);
333}
334
335void tst_QGraphicsScene::sceneRect()
336{
337 QGraphicsScene scene;
338 QSignalSpy sceneRectChanged(&scene, &QGraphicsScene::sceneRectChanged);
339 QCOMPARE(scene.sceneRect(), QRectF());
340 QCOMPARE(sceneRectChanged.count(), 0);
341
342 QGraphicsRectItem *item = scene.addRect(rect: QRectF(0, 0, 10, 10));
343 item->setPen(QPen(Qt::black, 0));
344 item->setPos(ax: -5, ay: -5);
345 QCOMPARE(sceneRectChanged.count(), 0);
346
347 QCOMPARE(itemAt(scene, 0, 0), item);
348 QVERIFY(scene.items(QPointF(10, 10)).isEmpty());
349 QCOMPARE(sceneRectChanged.count(), 0);
350 QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 10, 10));
351 QCOMPARE(sceneRectChanged.count(), 1);
352 QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect());
353
354 item->setPos(ax: 0, ay: 0);
355 QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 15, 15));
356 QCOMPARE(sceneRectChanged.count(), 2);
357 QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect());
358
359 scene.setSceneRect(x: -100, y: -100, w: 10, h: 10);
360 QCOMPARE(sceneRectChanged.count(), 3);
361 QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect());
362
363 QCOMPARE(itemAt(scene, 0, 0), item);
364 QVERIFY(scene.items(QPointF(10, 10)).isEmpty());
365 QCOMPARE(scene.sceneRect(), QRectF(-100, -100, 10, 10));
366 item->setPos(ax: 10, ay: 10);
367 QCOMPARE(scene.sceneRect(), QRectF(-100, -100, 10, 10));
368 QCOMPARE(sceneRectChanged.count(), 3);
369 QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect());
370
371 scene.setSceneRect(QRectF());
372
373 QCOMPARE(itemAt(scene, 10, 10), item);
374 QVERIFY(scene.items(QPointF(20, 20)).isEmpty());
375 QCOMPARE(sceneRectChanged.count(), 4);
376 QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 25, 25));
377 QCOMPARE(sceneRectChanged.count(), 5);
378 QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect());
379}
380
381void tst_QGraphicsScene::itemIndexMethod()
382{
383 QGraphicsScene scene;
384 QCOMPARE(scene.itemIndexMethod(), QGraphicsScene::BspTreeIndex);
385
386#ifdef Q_PROCESSOR_ARM
387 const int minY = -500;
388 const int maxY = 500;
389 const int minX = -500;
390 const int maxX = 500;
391#else
392 const int minY = -1000;
393 const int maxY = 2000;
394 const int minX = -1000;
395 const int maxX = 2000;
396#endif
397
398 QList<QGraphicsItem *> items;
399 for (int y = minY; y < maxY; y += 100) {
400 for (int x = minX; x < maxX; x += 100) {
401 QGraphicsItem *item = scene.addRect(rect: QRectF(0, 0, 10, 10));
402 item->setPos(ax: x, ay: y);
403 QCOMPARE(itemAt(scene, x, y), item);
404 items << item;
405 }
406 }
407
408 int n = 0;
409 for (int y = minY; y < maxY; y += 100) {
410 for (int x = minX; x < maxX; x += 100)
411 QCOMPARE(itemAt(scene, x, y), items.at(n++));
412 }
413
414 scene.setItemIndexMethod(QGraphicsScene::NoIndex);
415 QCOMPARE(scene.itemIndexMethod(), QGraphicsScene::NoIndex);
416
417 n = 0;
418 for (int y = minY; y < maxY; y += 100) {
419 for (int x = minX; x < maxX; x += 100)
420 QCOMPARE(itemAt(scene, x, y), items.at(n++));
421 }
422
423 scene.setItemIndexMethod(QGraphicsScene::BspTreeIndex);
424 QCOMPARE(scene.itemIndexMethod(), QGraphicsScene::BspTreeIndex);
425
426 n = 0;
427 for (int y = minY; y < maxY; y += 100) {
428 for (int x = minX; x < maxX; x += 100)
429 QCOMPARE(itemAt(scene, x, y), items.at(n++));
430 }
431}
432
433void tst_QGraphicsScene::bspTreeDepth()
434{
435 QGraphicsScene scene;
436 QCOMPARE(scene.itemIndexMethod(), QGraphicsScene::BspTreeIndex);
437 QCOMPARE(scene.bspTreeDepth(), 0);
438 scene.setBspTreeDepth(1);
439 QCOMPARE(scene.bspTreeDepth(), 1);
440 QTest::ignoreMessage(type: QtWarningMsg, message: "QGraphicsScene::setBspTreeDepth: invalid depth -1 ignored; must be >= 0");
441 scene.setBspTreeDepth(-1);
442 QCOMPARE(scene.bspTreeDepth(), 1);
443}
444
445void tst_QGraphicsScene::items()
446{
447#ifdef Q_PROCESSOR_ARM
448 const int minY = -500;
449 const int maxY = 500;
450 const int minX = -500;
451 const int maxX = 500;
452#else
453 const int minY = -1000;
454 const int maxY = 2000;
455 const int minX = -1000;
456 const int maxX = 2000;
457#endif
458
459 {
460 QGraphicsScene scene;
461
462 QList<QGraphicsItem *> items;
463 for (int y = minY; y < maxY; y += 100) {
464 for (int x = minX; x < maxX; x += 100)
465 items << scene.addRect(rect: QRectF(0, 0, 10, 10));
466 }
467 QCOMPARE(scene.items().size(), items.size());
468 itemAt(scene, x: 0, y: 0); // trigger indexing
469
470 scene.removeItem(item: items.at(i: 5));
471 delete items.at(i: 5);
472 QVERIFY(!scene.items().contains(nullptr));
473 delete items.at(i: 7);
474 QVERIFY(!scene.items().contains(nullptr));
475 }
476 {
477 QGraphicsScene scene;
478 QGraphicsLineItem *l1 = scene.addLine(x1: -5, y1: 0, x2: 5, y2: 0);
479 l1->setPen(QPen(Qt::black, 0));
480 QGraphicsLineItem *l2 = scene.addLine(x1: 0, y1: -5, x2: 0, y2: 5);
481 l2->setPen(QPen(Qt::black, 0));
482 QVERIFY(!l1->sceneBoundingRect().intersects(l2->sceneBoundingRect()));
483 QVERIFY(!l2->sceneBoundingRect().intersects(l1->sceneBoundingRect()));
484 QList<QGraphicsItem *> items;
485 items<<l1<<l2;
486 QCOMPARE(scene.items().size(), items.size());
487 QVERIFY(scene.items(QRectF(-1, -1, 2, 2)).contains(l1));
488 QVERIFY(scene.items(QRectF(-1, -1, 2, 2)).contains(l2));
489 }
490}
491
492void tst_QGraphicsScene::itemsBoundingRect_data()
493{
494 QTest::addColumn<QList<QRectF> >(name: "rects");
495 QTest::addColumn<QTransform>(name: "transform");
496 QTest::addColumn<QRectF>(name: "boundingRect");
497
498 QTransform transformation;
499 transformation.translate(dx: 50, dy: -50);
500 transformation.scale(sx: 2, sy: 2);
501 transformation.rotate(a: 90);
502
503 QTest::newRow(dataTag: "none")
504 << QList<QRectF>()
505 << QTransform()
506 << QRectF();
507 QTest::newRow(dataTag: "{{0, 0, 10, 10}}")
508 << (QList<QRectF>() << QRectF(0, 0, 10, 10))
509 << QTransform()
510 << QRectF(0, 0, 10, 10);
511 QTest::newRow(dataTag: "{{-10, -10, 10, 10}}")
512 << (QList<QRectF>() << QRectF(-10, -10, 10, 10))
513 << QTransform()
514 << QRectF(-10, -10, 10, 10);
515 QTest::newRow(dataTag: "{{-1000, -1000, 1, 1}, {-10, -10, 10, 10}}")
516 << (QList<QRectF>() << QRectF(-1000, -1000, 1, 1) << QRectF(-10, -10, 10, 10))
517 << QTransform()
518 << QRectF(-1000, -1000, 1000, 1000);
519 QTest::newRow(dataTag: "transformed {{0, 0, 10, 10}}")
520 << (QList<QRectF>() << QRectF(0, 0, 10, 10))
521 << transformation
522 << QRectF(30, -50, 20, 20);
523 QTest::newRow(dataTag: "transformed {{-10, -10, 10, 10}}")
524 << (QList<QRectF>() << QRectF(-10, -10, 10, 10))
525 << transformation
526 << QRectF(50, -70, 20, 20);
527 QTest::newRow(dataTag: "transformed {{-1000, -1000, 1, 1}, {-10, -10, 10, 10}}")
528 << (QList<QRectF>() << QRectF(-1000, -1000, 1, 1) << QRectF(-10, -10, 10, 10))
529 << transformation
530 << QRectF(50, -2050, 2000, 2000);
531
532 QList<QRectF> all;
533 for (int i = 0; i < 256; ++i)
534 all << QRectF(randomX[i], randomY[i], 10, 10);
535 QTest::newRow(dataTag: "all")
536 << all
537 << QTransform()
538 << QRectF(-980, -994, 1988, 1983);
539 QTest::newRow(dataTag: "transformed all")
540 << all
541 << transformation
542 << QRectF(-1928, -2010, 3966, 3976);
543}
544
545void tst_QGraphicsScene::itemsBoundingRect()
546{
547 QFETCH(QList<QRectF>, rects);
548 QFETCH(QTransform, transform);
549 QFETCH(QRectF, boundingRect);
550
551 QGraphicsScene scene;
552
553 for (const auto &rect : qAsConst(t&: rects)) {
554 QPainterPath path;
555 path.addRect(rect);
556 QGraphicsPathItem *item = scene.addPath(path);
557 item->setPen(QPen(Qt::black, 0));
558 item->setTransform(matrix: transform);
559 }
560
561 QCOMPARE(scene.itemsBoundingRect(), boundingRect);
562}
563
564void tst_QGraphicsScene::items_QPointF_data()
565{
566 QTest::addColumn<QList<QRectF> >(name: "items");
567 QTest::addColumn<QPointF>(name: "point");
568 QTest::addColumn<QList<int> >(name: "itemsAtPoint");
569
570 QTest::newRow(dataTag: "empty")
571 << QList<QRectF>()
572 << QPointF()
573 << QList<int>();
574 QTest::newRow(dataTag: "1")
575 << (QList<QRectF>() << QRectF(0, 0, 10, 10))
576 << QPointF(0, 0)
577 << (QList<int>() << 0);
578 QTest::newRow(dataTag: "2")
579 << (QList<QRectF>() << QRectF(0, 0, 10, 10))
580 << QPointF(5, 5)
581 << (QList<int>() << 0);
582 QTest::newRow(dataTag: "3")
583 << (QList<QRectF>() << QRectF(0, 0, 10, 10))
584 << QPointF(9.9, 9.9)
585 << (QList<int>() << 0);
586 QTest::newRow(dataTag: "3.5")
587 << (QList<QRectF>() << QRectF(0, 0, 10, 10))
588 << QPointF(10, 10)
589 << QList<int>();
590 QTest::newRow(dataTag: "4")
591 << (QList<QRectF>() << QRectF(0, 0, 10, 10) << QRectF(9.9, 9.9, 10, 10))
592 << QPointF(9.9, 9.9)
593 << (QList<int>() << 1 << 0);
594 QTest::newRow(dataTag: "4.5")
595 << (QList<QRectF>() << QRectF(0, 0, 10, 10) << QRectF(10, 10, 10, 10))
596 << QPointF(10, 10)
597 << (QList<int>() << 1);
598 QTest::newRow(dataTag: "5")
599 << (QList<QRectF>() << QRectF(5, 5, 10, 10) << QRectF(10, 10, 10, 10))
600 << QPointF(10, 10)
601 << (QList<int>() << 1 << 0);
602 QTest::newRow(dataTag: "6")
603 << (QList<QRectF>() << QRectF(5, 5, 10, 10) << QRectF(10, 10, 10, 10) << QRectF(0, 0, 20, 30))
604 << QPointF(10, 10)
605 << (QList<int>() << 2 << 1 << 0);
606}
607
608void tst_QGraphicsScene::items_QPointF()
609{
610 QFETCH(QList<QRectF>, items);
611 QFETCH(QPointF, point);
612 QFETCH(QList<int>, itemsAtPoint);
613
614 QGraphicsScene scene;
615
616 int n = 0;
617 QList<QGraphicsItem *> addedItems;
618 for (const auto &rect : qAsConst(t&: items)) {
619 QPainterPath path;
620 path.addRect(x: 0, y: 0, w: rect.width(), h: rect.height());
621
622 QGraphicsPathItem *item = scene.addPath(path);
623 item->setPen(QPen(Qt::black, 0));
624 item->setZValue(n++);
625 item->setPos(rect.topLeft());
626 addedItems << item;
627 }
628
629 QList<int> itemIndexes;
630 const auto &actualItemsAtPoint = scene.items(pos: point);
631 for (QGraphicsItem *item : actualItemsAtPoint)
632 itemIndexes << addedItems.indexOf(t: item);
633
634 QCOMPARE(itemIndexes, itemsAtPoint);
635}
636
637void tst_QGraphicsScene::items_QRectF()
638{
639 QGraphicsScene scene;
640 QGraphicsItem *item1 = scene.addRect(rect: QRectF(-10, -10, 10, 10));
641 QGraphicsItem *item2 = scene.addRect(rect: QRectF(10, -10, 10, 10));
642 QGraphicsItem *item3 = scene.addRect(rect: QRectF(10, 10, 10, 10));
643 QGraphicsItem *item4 = scene.addRect(rect: QRectF(-10, 10, 10, 10));
644
645 item1->setZValue(0);
646 item2->setZValue(1);
647 item3->setZValue(2);
648 item4->setZValue(3);
649
650 QCOMPARE(scene.items(QRectF(-10, -10, 10, 10)), QList<QGraphicsItem *>() << item1);
651 QCOMPARE(scene.items(QRectF(10, -10, 10, 10)), QList<QGraphicsItem *>() << item2);
652 QCOMPARE(scene.items(QRectF(10, 10, 10, 10)), QList<QGraphicsItem *>() << item3);
653 QCOMPARE(scene.items(QRectF(-10, 10, 10, 10)), QList<QGraphicsItem *>() << item4);
654 QCOMPARE(scene.items(QRectF(-10, -10, 1, 1)), QList<QGraphicsItem *>() << item1);
655 QCOMPARE(scene.items(QRectF(10, -10, 1, 1)), QList<QGraphicsItem *>() << item2);
656 QCOMPARE(scene.items(QRectF(10, 10, 1, 1)), QList<QGraphicsItem *>() << item3);
657 QCOMPARE(scene.items(QRectF(-10, 10, 1, 1)), QList<QGraphicsItem *>() << item4);
658
659 QCOMPARE(scene.items(QRectF(-10, -10, 40, 10)), QList<QGraphicsItem *>() << item2 << item1);
660 QCOMPARE(scene.items(QRectF(-10, 10, 40, 10)), QList<QGraphicsItem *>() << item4 << item3);
661
662 item1->setZValue(3);
663 item2->setZValue(2);
664 item3->setZValue(1);
665 item4->setZValue(0);
666
667 QCOMPARE(scene.items(QRectF(-10, -10, 40, 10)), QList<QGraphicsItem *>() << item1 << item2);
668 QCOMPARE(scene.items(QRectF(-10, 10, 40, 10)), QList<QGraphicsItem *>() << item3 << item4);
669}
670
671void tst_QGraphicsScene::items_QRectF_2_data()
672{
673 QTest::addColumn<QRectF>(name: "ellipseRect");
674 QTest::addColumn<QRectF>(name: "sceneRect");
675 QTest::addColumn<Qt::ItemSelectionMode>(name: "selectionMode");
676 QTest::addColumn<bool>(name: "contained");
677 QTest::addColumn<bool>(name: "containedRotated");
678
679 // None of the rects contain the ellipse's shape nor bounding rect
680 QTest::newRow(dataTag: "1") << QRectF(0, 0, 100, 100) << QRectF(1, 1, 10, 10) << Qt::ContainsItemShape << false << false;
681 QTest::newRow(dataTag: "2") << QRectF(0, 0, 100, 100) << QRectF(1, 89, 10, 10) << Qt::ContainsItemShape << false << false;
682 QTest::newRow(dataTag: "3") << QRectF(0, 0, 100, 100) << QRectF(89, 1, 10, 10) << Qt::ContainsItemShape << false << false;
683 QTest::newRow(dataTag: "4") << QRectF(0, 0, 100, 100) << QRectF(89, 89, 10, 10) << Qt::ContainsItemShape << false << false;
684 QTest::newRow(dataTag: "5") << QRectF(0, 0, 100, 100) << QRectF(1, 1, 10, 10) << Qt::ContainsItemBoundingRect << false << false;
685 QTest::newRow(dataTag: "6") << QRectF(0, 0, 100, 100) << QRectF(1, 89, 10, 10) << Qt::ContainsItemBoundingRect << false << false;
686 QTest::newRow(dataTag: "7") << QRectF(0, 0, 100, 100) << QRectF(89, 1, 10, 10) << Qt::ContainsItemBoundingRect << false << false;
687 QTest::newRow(dataTag: "8") << QRectF(0, 0, 100, 100) << QRectF(89, 89, 10, 10) << Qt::ContainsItemBoundingRect << false << false;
688 QTest::newRow(dataTag: "9") << QRectF(0, 0, 100, 100) << QRectF(0, 0, 50, 50) << Qt::ContainsItemShape << false << false;
689 QTest::newRow(dataTag: "10") << QRectF(0, 0, 100, 100) << QRectF(0, 50, 50, 50) << Qt::ContainsItemShape << false << false;
690 QTest::newRow(dataTag: "11") << QRectF(0, 0, 100, 100) << QRectF(50, 0, 50, 50) << Qt::ContainsItemShape << false << false;
691 QTest::newRow(dataTag: "12") << QRectF(0, 0, 100, 100) << QRectF(50, 50, 50, 50) << Qt::ContainsItemShape << false << false;
692 QTest::newRow(dataTag: "13") << QRectF(0, 0, 100, 100) << QRectF(0, 0, 50, 50) << Qt::ContainsItemBoundingRect << false << false;
693 QTest::newRow(dataTag: "14") << QRectF(0, 0, 100, 100) << QRectF(0, 50, 50, 50) << Qt::ContainsItemBoundingRect << false << false;
694 QTest::newRow(dataTag: "15") << QRectF(0, 0, 100, 100) << QRectF(50, 0, 50, 50) << Qt::ContainsItemBoundingRect << false << false;
695 QTest::newRow(dataTag: "16") << QRectF(0, 0, 100, 100) << QRectF(50, 50, 50, 50) << Qt::ContainsItemBoundingRect << false << false;
696 QTest::newRow(dataTag: "17") << QRectF(0, 0, 100, 100) << QRectF(-50, -50, 100, 100) << Qt::ContainsItemShape << false << false;
697 QTest::newRow(dataTag: "18") << QRectF(0, 0, 100, 100) << QRectF(0, -50, 100, 100) << Qt::ContainsItemShape << false << false;
698 QTest::newRow(dataTag: "19") << QRectF(0, 0, 100, 100) << QRectF(-50, 0, 100, 100) << Qt::ContainsItemShape << false << false;
699 QTest::newRow(dataTag: "20") << QRectF(0, 0, 100, 100) << QRectF(0, 0, 100, 100) << Qt::ContainsItemShape << false << false;
700 QTest::newRow(dataTag: "21") << QRectF(0, 0, 100, 100) << QRectF(-50, -50, 100, 100) << Qt::ContainsItemBoundingRect << false << false;
701 QTest::newRow(dataTag: "22") << QRectF(0, 0, 100, 100) << QRectF(0, -50, 100, 100) << Qt::ContainsItemBoundingRect << false << false;
702 QTest::newRow(dataTag: "23") << QRectF(0, 0, 100, 100) << QRectF(-50, 0, 100, 100) << Qt::ContainsItemBoundingRect << false << false;
703
704 // The rect is the same as the ellipse's bounding rect
705 QTest::newRow(dataTag: "24") << QRectF(0, 0, 100, 100) << QRectF(0, 0, 100, 100) << Qt::ContainsItemBoundingRect << false << false;
706
707 // None intersects with the item's shape, but they all intersects with the
708 // item's bounding rect.
709 QTest::newRow(dataTag: "25") << QRectF(0, 0, 100, 100) << QRectF(1, 1, 10, 10) << Qt::IntersectsItemShape << false << false;
710 QTest::newRow(dataTag: "26") << QRectF(0, 0, 100, 100) << QRectF(1, 89, 10, 10) << Qt::IntersectsItemShape << false << true;
711 QTest::newRow(dataTag: "27") << QRectF(0, 0, 100, 100) << QRectF(89, 1, 10, 10) << Qt::IntersectsItemShape << false << false;
712 QTest::newRow(dataTag: "28") << QRectF(0, 0, 100, 100) << QRectF(89, 89, 10, 10) << Qt::IntersectsItemShape << false << false;
713 QTest::newRow(dataTag: "29") << QRectF(0, 0, 100, 100) << QRectF(1, 1, 10, 10) << Qt::IntersectsItemBoundingRect << true << true;
714 QTest::newRow(dataTag: "30") << QRectF(0, 0, 100, 100) << QRectF(1, 89, 10, 10) << Qt::IntersectsItemBoundingRect << true << true;
715 QTest::newRow(dataTag: "31") << QRectF(0, 0, 100, 100) << QRectF(89, 1, 10, 10) << Qt::IntersectsItemBoundingRect << true << false;
716 QTest::newRow(dataTag: "32") << QRectF(0, 0, 100, 100) << QRectF(89, 89, 10, 10) << Qt::IntersectsItemBoundingRect << true << false;
717
718 // This rect does not contain the shape nor the bounding rect
719 QTest::newRow(dataTag: "33") << QRectF(0, 0, 100, 100) << QRectF(5, 5, 90, 90) << Qt::ContainsItemShape << false << false;
720 QTest::newRow(dataTag: "34") << QRectF(0, 0, 100, 100) << QRectF(5, 5, 90, 90) << Qt::ContainsItemBoundingRect << false << false;
721
722 // It will, however, intersect with both
723 QTest::newRow(dataTag: "35") << QRectF(0, 0, 100, 100) << QRectF(5, 5, 90, 90) << Qt::IntersectsItemShape << true << true;
724 QTest::newRow(dataTag: "36") << QRectF(0, 0, 100, 100) << QRectF(5, 5, 90, 90) << Qt::IntersectsItemBoundingRect << true << true;
725
726 // A rect that contains the whole ellipse will both contain and intersect
727 // with both the ellipse's shape and bounding rect.
728 QTest::newRow(dataTag: "37") << QRectF(0, 0, 100, 100) << QRectF(-5, -5, 110, 110) << Qt::IntersectsItemBoundingRect << true << true;
729 QTest::newRow(dataTag: "38") << QRectF(0, 0, 100, 100) << QRectF(-5, -5, 110, 110) << Qt::IntersectsItemShape << true << true;
730 QTest::newRow(dataTag: "39") << QRectF(0, 0, 100, 100) << QRectF(-5, -5, 110, 110) << Qt::ContainsItemBoundingRect << true << false;
731 QTest::newRow(dataTag: "40") << QRectF(0, 0, 100, 100) << QRectF(-5, -5, 110, 110) << Qt::ContainsItemShape << true << false;
732
733 // A rect that is fully contained within the ellipse will intersect only
734 QTest::newRow(dataTag: "41") << QRectF(0, 0, 100, 100) << QRectF(40, 40, 20, 20) << Qt::ContainsItemShape << false << false;
735 QTest::newRow(dataTag: "42") << QRectF(0, 0, 100, 100) << QRectF(40, 40, 20, 20) << Qt::ContainsItemBoundingRect << false << false;
736 QTest::newRow(dataTag: "43") << QRectF(0, 0, 100, 100) << QRectF(40, 40, 20, 20) << Qt::IntersectsItemShape << true << true;
737 QTest::newRow(dataTag: "44") << QRectF(0, 0, 100, 100) << QRectF(40, 40, 20, 20) << Qt::IntersectsItemBoundingRect << true << true;
738}
739
740void tst_QGraphicsScene::items_QRectF_2()
741{
742 QFETCH(QRectF, ellipseRect);
743 QFETCH(QRectF, sceneRect);
744 QFETCH(Qt::ItemSelectionMode, selectionMode);
745 QFETCH(bool, contained);
746 QFETCH(bool, containedRotated);
747
748 QGraphicsScene scene;
749 QGraphicsItem *item = scene.addEllipse(rect: ellipseRect);
750
751 QCOMPARE(!scene.items(sceneRect, selectionMode).isEmpty(), contained);
752 item->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
753 QCOMPARE(!scene.items(sceneRect, selectionMode).isEmpty(), containedRotated);
754}
755
756void tst_QGraphicsScene::items_QPolygonF()
757{
758 QGraphicsScene scene;
759 QGraphicsItem *item1 = scene.addRect(rect: QRectF(-10, -10, 10, 10));
760 QGraphicsItem *item2 = scene.addRect(rect: QRectF(10, -10, 10, 10));
761 QGraphicsItem *item3 = scene.addRect(rect: QRectF(10, 10, 10, 10));
762 QGraphicsItem *item4 = scene.addRect(rect: QRectF(-10, 10, 10, 10));
763
764 item1->setZValue(0);
765 item2->setZValue(1);
766 item3->setZValue(2);
767 item4->setZValue(3);
768
769 QPolygonF poly1(item1->boundingRect());
770 QPolygonF poly2(item2->boundingRect());
771 QPolygonF poly3(item3->boundingRect());
772 QPolygonF poly4(item4->boundingRect());
773
774 QCOMPARE(scene.items(poly1), QList<QGraphicsItem *>() << item1);
775 QCOMPARE(scene.items(poly2), QList<QGraphicsItem *>() << item2);
776 QCOMPARE(scene.items(poly3), QList<QGraphicsItem *>() << item3);
777 QCOMPARE(scene.items(poly4), QList<QGraphicsItem *>() << item4);
778
779 poly1 = QPolygonF(QRectF(-10, -10, 1, 1));
780 poly2 = QPolygonF(QRectF(10, -10, 1, 1));
781 poly3 = QPolygonF(QRectF(10, 10, 1, 1));
782 poly4 = QPolygonF(QRectF(-10, 10, 1, 1));
783
784 QCOMPARE(scene.items(poly1), QList<QGraphicsItem *>() << item1);
785 QCOMPARE(scene.items(poly2), QList<QGraphicsItem *>() << item2);
786 QCOMPARE(scene.items(poly3), QList<QGraphicsItem *>() << item3);
787 QCOMPARE(scene.items(poly4), QList<QGraphicsItem *>() << item4);
788
789 poly1 = QPolygonF(QRectF(-10, -10, 40, 10));
790 poly2 = QPolygonF(QRectF(-10, 10, 40, 10));
791
792 QCOMPARE(scene.items(poly1), QList<QGraphicsItem *>() << item2 << item1);
793 QCOMPARE(scene.items(poly2), QList<QGraphicsItem *>() << item4 << item3);
794
795 item1->setZValue(3);
796 item2->setZValue(2);
797 item3->setZValue(1);
798 item4->setZValue(0);
799
800 QCOMPARE(scene.items(poly1), QList<QGraphicsItem *>() << item1 << item2);
801 QCOMPARE(scene.items(poly2), QList<QGraphicsItem *>() << item3 << item4);
802}
803
804void tst_QGraphicsScene::items_QPolygonF_2()
805{
806 QGraphicsScene scene;
807 QGraphicsItem *ellipse = scene.addEllipse(rect: QRectF(0, 0, 100, 100));
808
809 // None of the rects contain the ellipse's shape nor bounding rect
810 QVERIFY(scene.items(QPolygonF(QRectF(1, 1, 10, 10)), Qt::ContainsItemShape).isEmpty());
811 QVERIFY(scene.items(QPolygonF(QRectF(1, 89, 10, 10)), Qt::ContainsItemShape).isEmpty());
812 QVERIFY(scene.items(QPolygonF(QRectF(89, 1, 10, 10)), Qt::ContainsItemShape).isEmpty());
813 QVERIFY(scene.items(QPolygonF(QRectF(89, 89, 10, 10)), Qt::ContainsItemShape).isEmpty());
814 QVERIFY(scene.items(QPolygonF(QRectF(1, 1, 10, 10)), Qt::ContainsItemBoundingRect).isEmpty());
815 QVERIFY(scene.items(QPolygonF(QRectF(1, 89, 10, 10)), Qt::ContainsItemBoundingRect).isEmpty());
816 QVERIFY(scene.items(QPolygonF(QRectF(89, 1, 10, 10)), Qt::ContainsItemBoundingRect).isEmpty());
817 QVERIFY(scene.items(QPolygonF(QRectF(89, 89, 10, 10)), Qt::ContainsItemBoundingRect).isEmpty());
818
819 // None intersects with the item's shape, but they all intersects with the
820 // item's bounding rect.
821 QVERIFY(scene.items(QPolygonF(QRectF(1, 1, 10, 10)), Qt::IntersectsItemShape).isEmpty());
822 QVERIFY(scene.items(QPolygonF(QRectF(1, 89, 10, 10)), Qt::IntersectsItemShape).isEmpty());
823 QVERIFY(scene.items(QPolygonF(QRectF(89, 1, 10, 10)), Qt::IntersectsItemShape).isEmpty());
824 QVERIFY(scene.items(QPolygonF(QRectF(89, 89, 10, 10)), Qt::IntersectsItemShape).isEmpty());
825 QCOMPARE(scene.items(QPolygonF(QRectF(1, 1, 10, 10)), Qt::IntersectsItemBoundingRect).first(), ellipse);
826 QCOMPARE(scene.items(QPolygonF(QRectF(1, 89, 10, 10)), Qt::IntersectsItemBoundingRect).first(), ellipse);
827 QCOMPARE(scene.items(QPolygonF(QRectF(89, 1, 10, 10)), Qt::IntersectsItemBoundingRect).first(), ellipse);
828 QCOMPARE(scene.items(QPolygonF(QRectF(89, 89, 10, 10)), Qt::IntersectsItemBoundingRect).first(), ellipse);
829
830 // This rect does not contain the shape nor the bounding rect
831 QVERIFY(scene.items(QPolygonF(QRectF(5, 5, 90, 90)), Qt::ContainsItemShape).isEmpty());
832 QVERIFY(scene.items(QPolygonF(QRectF(5, 5, 90, 90)), Qt::ContainsItemBoundingRect).isEmpty());
833
834 // It will, however, intersect with both
835 QCOMPARE(scene.items(QPolygonF(QRectF(5, 5, 90, 90)), Qt::IntersectsItemShape).first(), ellipse);
836 QCOMPARE(scene.items(QPolygonF(QRectF(5, 5, 90, 90)), Qt::IntersectsItemBoundingRect).first(), ellipse);
837
838 // A rect that contains the whole ellipse will both contain and intersect
839 // with both the ellipse's shape and bounding rect.
840 QCOMPARE(scene.items(QPolygonF(QRectF(-5, -5, 110, 110)), Qt::IntersectsItemShape).first(), ellipse);
841 QCOMPARE(scene.items(QPolygonF(QRectF(-5, -5, 110, 110)), Qt::ContainsItemShape).first(), ellipse);
842 QCOMPARE(scene.items(QPolygonF(QRectF(-5, -5, 110, 110)), Qt::IntersectsItemBoundingRect).first(), ellipse);
843 QCOMPARE(scene.items(QPolygonF(QRectF(-5, -5, 110, 110)), Qt::ContainsItemBoundingRect).first(), ellipse);
844}
845
846void tst_QGraphicsScene::items_QPainterPath()
847{
848 QGraphicsScene scene;
849 QGraphicsItem *item1 = scene.addRect(rect: QRectF(-10, -10, 10, 10));
850 QGraphicsItem *item2 = scene.addRect(rect: QRectF(10, -10, 10, 10));
851 QGraphicsItem *item3 = scene.addRect(rect: QRectF(10, 10, 10, 10));
852 QGraphicsItem *item4 = scene.addRect(rect: QRectF(-10, 10, 10, 10));
853
854 item1->setZValue(0);
855 item2->setZValue(1);
856 item3->setZValue(2);
857 item4->setZValue(3);
858
859 QPainterPath path1; path1.addEllipse(rect: item1->boundingRect());
860 QPainterPath path2; path2.addEllipse(rect: item2->boundingRect());
861 QPainterPath path3; path3.addEllipse(rect: item3->boundingRect());
862 QPainterPath path4; path4.addEllipse(rect: item4->boundingRect());
863
864 QCOMPARE(scene.items(path1), QList<QGraphicsItem *>() << item1);
865 QCOMPARE(scene.items(path2), QList<QGraphicsItem *>() << item2);
866 QCOMPARE(scene.items(path3), QList<QGraphicsItem *>() << item3);
867 QCOMPARE(scene.items(path4), QList<QGraphicsItem *>() << item4);
868
869 path1 = QPainterPath(); path1.addEllipse(rect: QRectF(-10, -10, 1, 1));
870 path2 = QPainterPath(); path2.addEllipse(rect: QRectF(10, -10, 1, 1));
871 path3 = QPainterPath(); path3.addEllipse(rect: QRectF(10, 10, 1, 1));
872 path4 = QPainterPath(); path4.addEllipse(rect: QRectF(-10, 10, 1, 1));
873
874 QCOMPARE(scene.items(path1), QList<QGraphicsItem *>() << item1);
875 QCOMPARE(scene.items(path2), QList<QGraphicsItem *>() << item2);
876 QCOMPARE(scene.items(path3), QList<QGraphicsItem *>() << item3);
877 QCOMPARE(scene.items(path4), QList<QGraphicsItem *>() << item4);
878
879 path1 = QPainterPath(); path1.addRect(rect: QRectF(-10, -10, 40, 10));
880 path2 = QPainterPath(); path2.addRect(rect: QRectF(-10, 10, 40, 10));
881
882 QCOMPARE(scene.items(path1), QList<QGraphicsItem *>() << item2 << item1);
883 QCOMPARE(scene.items(path2), QList<QGraphicsItem *>() << item4 << item3);
884
885 item1->setZValue(3);
886 item2->setZValue(2);
887 item3->setZValue(1);
888 item4->setZValue(0);
889
890 QCOMPARE(scene.items(path1), QList<QGraphicsItem *>() << item1 << item2);
891 QCOMPARE(scene.items(path2), QList<QGraphicsItem *>() << item3 << item4);
892}
893
894void tst_QGraphicsScene::items_QPainterPath_2()
895{
896 QGraphicsScene scene;
897 QGraphicsItem *ellipse = scene.addEllipse(rect: QRectF(0, 0, 100, 100));
898
899 QPainterPath p1; p1.addRect(rect: QRectF(1, 1, 10, 10));
900 QPainterPath p2; p2.addRect(rect: QRectF(1, 89, 10, 10));
901 QPainterPath p3; p3.addRect(rect: QRectF(89, 1, 10, 10));
902 QPainterPath p4; p4.addRect(rect: QRectF(89, 89, 10, 10));
903
904 // None of the rects contain the ellipse's shape nor bounding rect
905 QVERIFY(scene.items(p1, Qt::ContainsItemShape).isEmpty());
906 QVERIFY(scene.items(p2, Qt::ContainsItemShape).isEmpty());
907 QVERIFY(scene.items(p3, Qt::ContainsItemShape).isEmpty());
908 QVERIFY(scene.items(p4, Qt::ContainsItemShape).isEmpty());
909 QVERIFY(scene.items(p1, Qt::ContainsItemBoundingRect).isEmpty());
910 QVERIFY(scene.items(p2, Qt::ContainsItemBoundingRect).isEmpty());
911 QVERIFY(scene.items(p3, Qt::ContainsItemBoundingRect).isEmpty());
912 QVERIFY(scene.items(p4, Qt::ContainsItemBoundingRect).isEmpty());
913
914 // None intersects with the item's shape, but they all intersects with the
915 // item's bounding rect.
916 QVERIFY(scene.items(p1, Qt::IntersectsItemShape).isEmpty());
917 QVERIFY(scene.items(p2, Qt::IntersectsItemShape).isEmpty());
918 QVERIFY(scene.items(p3, Qt::IntersectsItemShape).isEmpty());
919 QVERIFY(scene.items(p4, Qt::IntersectsItemShape).isEmpty());
920 QCOMPARE(scene.items(p1, Qt::IntersectsItemBoundingRect).first(), ellipse);
921 QCOMPARE(scene.items(p2, Qt::IntersectsItemBoundingRect).first(), ellipse);
922 QCOMPARE(scene.items(p3, Qt::IntersectsItemBoundingRect).first(), ellipse);
923 QCOMPARE(scene.items(p4, Qt::IntersectsItemBoundingRect).first(), ellipse);
924
925 QPainterPath p5;
926 p5.addRect(rect: QRectF(5, 5, 90, 90));
927
928 // This rect does not contain the shape nor the bounding rect
929 QVERIFY(scene.items(p5, Qt::ContainsItemShape).isEmpty());
930 QVERIFY(scene.items(p5, Qt::ContainsItemBoundingRect).isEmpty());
931
932 // It will, however, intersect with both
933 QCOMPARE(scene.items(p5, Qt::IntersectsItemShape).first(), ellipse);
934 QCOMPARE(scene.items(p5, Qt::IntersectsItemBoundingRect).first(), ellipse);
935
936 QPainterPath p6;
937 p6.addRect(rect: QRectF(-5, -5, 110, 110));
938
939 // A rect that contains the whole ellipse will both contain and intersect
940 // with both the ellipse's shape and bounding rect.
941 QCOMPARE(scene.items(p6, Qt::IntersectsItemShape).first(), ellipse);
942 QCOMPARE(scene.items(p6, Qt::ContainsItemShape).first(), ellipse);
943 QCOMPARE(scene.items(p6, Qt::IntersectsItemBoundingRect).first(), ellipse);
944 QCOMPARE(scene.items(p6, Qt::ContainsItemBoundingRect).first(), ellipse);
945}
946
947class CustomView : public QGraphicsView
948{
949public:
950 using QGraphicsView::QGraphicsView;
951
952 int repaints = 0;
953protected:
954 void paintEvent(QPaintEvent *event) override
955 {
956 ++repaints;
957 QGraphicsView::paintEvent(event);
958 }
959};
960
961void tst_QGraphicsScene::selectionChanged()
962{
963 QGraphicsScene scene(0, 0, 1000, 1000);
964 QSignalSpy spy(&scene, &QGraphicsScene::selectionChanged);
965 QCOMPARE(spy.count(), 0);
966
967 QPainterPath path;
968 path.addRect(rect: scene.sceneRect());
969 QCOMPARE(scene.selectionArea(), QPainterPath());
970 scene.setSelectionArea(path);
971 QCOMPARE(scene.selectionArea(), path);
972 QCOMPARE(spy.count(), 0); // selection didn't change
973 QVERIFY(scene.selectedItems().isEmpty());
974
975 QGraphicsItem *rect = scene.addRect(rect: QRectF(0, 0, 100, 100));
976 QCOMPARE(spy.count(), 0); // selection didn't change
977
978 rect->setSelected(true);
979 QVERIFY(!rect->isSelected());
980 QCOMPARE(spy.count(), 0); // selection didn't change, item isn't selectable
981
982 rect->setFlag(flag: QGraphicsItem::ItemIsSelectable);
983 rect->setSelected(true);
984 QVERIFY(rect->isSelected());
985 QCOMPARE(spy.count(), 1); // selection changed
986 QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>() << rect);
987
988 rect->setSelected(false);
989 QVERIFY(!rect->isSelected());
990 QCOMPARE(spy.count(), 2); // selection changed
991 QVERIFY(scene.selectedItems().isEmpty());
992
993 QGraphicsEllipseItem *parentItem = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100));
994 QGraphicsEllipseItem *childItem = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100), parentItem);
995 QGraphicsEllipseItem *grandChildItem = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100), childItem);
996 grandChildItem->setFlag(flag: QGraphicsItem::ItemIsSelectable);
997 grandChildItem->setSelected(true);
998 grandChildItem->setSelected(false);
999 grandChildItem->setSelected(true);
1000 scene.addItem(item: parentItem);
1001
1002 QCOMPARE(spy.count(), 3); // the grandchild was added, so the selection changed once
1003
1004 scene.removeItem(item: parentItem);
1005 QCOMPARE(spy.count(), 4); // the grandchild was removed, so the selection changed
1006
1007 rect->setSelected(true);
1008 QCOMPARE(spy.count(), 5); // the rect was reselected, so the selection changed
1009
1010 scene.clearSelection();
1011 QCOMPARE(spy.count(), 6); // the scene selection was cleared
1012
1013 rect->setSelected(true);
1014 QCOMPARE(spy.count(), 7); // the rect was reselected, so the selection changed
1015
1016 rect->setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: false);
1017 QCOMPARE(spy.count(), 8); // the rect was unselected, so the selection changed
1018
1019 rect->setSelected(true);
1020 QCOMPARE(spy.count(), 8); // the rect is not longer selectable, so the selection does not change
1021
1022
1023 rect->setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: true);
1024 rect->setSelected(true);
1025 QCOMPARE(spy.count(), 9); // the rect is again selectable, so the selection changed
1026
1027 delete rect;
1028 QCOMPARE(spy.count(), 10); // a selected item was deleted; selection changed
1029}
1030
1031void tst_QGraphicsScene::selectionChanged2()
1032{
1033 QGraphicsScene scene;
1034 QSignalSpy spy(&scene, &QGraphicsScene::selectionChanged);
1035
1036 QGraphicsItem *item1 = scene.addRect(x: 0, y: 0, w: 100, h: 100);
1037 QGraphicsItem *item2 = scene.addRect(x: 100, y: 100, w: 100, h: 100);
1038 item1->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1039 item2->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1040
1041 QCOMPARE(spy.count(), 0);
1042 {
1043 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
1044 event.setScenePos(QPointF(50, 50));
1045 event.setButton(Qt::LeftButton);
1046 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1047 }
1048 {
1049 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
1050 event.setScenePos(QPointF(50, 50));
1051 event.setButton(Qt::LeftButton);
1052 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1053 }
1054 QVERIFY(item1->isSelected());
1055 QVERIFY(!item2->isSelected());
1056 QCOMPARE(spy.count(), 1);
1057 {
1058 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
1059 event.setScenePos(QPointF(150, 150));
1060 event.setButton(Qt::LeftButton);
1061 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1062 }
1063 {
1064 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
1065 event.setScenePos(QPointF(150, 150));
1066 event.setButton(Qt::LeftButton);
1067 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1068 }
1069 QVERIFY(!item1->isSelected());
1070 QVERIFY(item2->isSelected());
1071 QCOMPARE(spy.count(), 2);
1072 {
1073 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
1074 event.setScenePos(QPointF(50, 50));
1075 event.setButton(Qt::LeftButton);
1076 event.setModifiers(Qt::ControlModifier);
1077 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1078 }
1079 QVERIFY(!item1->isSelected());
1080 QVERIFY(item2->isSelected());
1081 QCOMPARE(spy.count(), 2);
1082 {
1083 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
1084 event.setScenePos(QPointF(50, 50));
1085 event.setButton(Qt::LeftButton);
1086 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1087 }
1088 QVERIFY(item1->isSelected());
1089 QVERIFY(!item2->isSelected());
1090 QCOMPARE(spy.count(), 3);
1091}
1092
1093void tst_QGraphicsScene::addItem()
1094{
1095 Q_CHECK_PAINTEVENTS
1096 {
1097 // 1) Create item, then scene, then add item
1098 QGraphicsItem *path = new QGraphicsEllipseItem(QRectF(-10, -10, 20, 20));
1099 QGraphicsScene scene;
1100
1101 CustomView view;
1102 view.setWindowTitle(QTest::currentTestFunction());
1103 view.setScene(&scene);
1104 view.resize(m_testSize);
1105 view.show();
1106 QVERIFY(QTest::qWaitForWindowExposed(&view));
1107 QCoreApplication::processEvents();
1108 view.repaints = 0;
1109
1110 scene.addItem(item: path);
1111
1112 // Adding an item should always issue a repaint.
1113 QTRY_VERIFY(view.repaints > 0);
1114 view.repaints = 0;
1115
1116 QCOMPARE(itemAt(scene, 0, 0), path);
1117
1118 QGraphicsItem *path2 = new QGraphicsEllipseItem(QRectF(-10, -10, 20, 20));
1119 path2->setPos(ax: 100, ay: 100);
1120
1121 QCOMPARE(itemAt(scene, 0, 0), path);
1122 QVERIFY(scene.items(QPointF(100, 100)).isEmpty());
1123 scene.addItem(item: path2);
1124
1125 // Adding an item should always issue a repaint.
1126 QTRY_VERIFY(view.repaints > 0);
1127
1128 QCOMPARE(itemAt(scene, 100, 100), path2);
1129 }
1130 {
1131 // 2) Create scene, then item, then add item
1132 QGraphicsScene scene;
1133 QGraphicsItem *path = new QGraphicsEllipseItem(QRectF(-10, -10, 20, 20));
1134 scene.addItem(item: path);
1135
1136 QGraphicsItem *path2 = new QGraphicsEllipseItem(QRectF(-10, -10, 20, 20));
1137 path2->setPos(ax: 100, ay: 100);
1138 scene.addItem(item: path2);
1139
1140 QCOMPARE(itemAt(scene, 0, 0), path);
1141 QCOMPARE(itemAt(scene, 100, 100), path2);
1142 }
1143}
1144
1145void tst_QGraphicsScene::addEllipse()
1146{
1147 QGraphicsScene scene;
1148 QGraphicsEllipseItem *ellipse = scene.addEllipse(rect: QRectF(-10, -10, 20, 20),
1149 pen: QPen(Qt::red), brush: QBrush(Qt::blue));
1150 QCOMPARE(ellipse->pos(), QPointF());
1151 QCOMPARE(ellipse->pen(), QPen(Qt::red));
1152 QCOMPARE(ellipse->brush(), QBrush(Qt::blue));
1153 QCOMPARE(ellipse->rect(), QRectF(-10, -10, 20, 20));
1154 QCOMPARE(itemAt(scene, 0, 0), ellipse);
1155 QVERIFY(scene.items(QPointF(-10, -10)).isEmpty());
1156 QCOMPARE(itemAt(scene, -9.9, 0), ellipse);
1157 QVERIFY(scene.items(QPointF(-10, 10)).isEmpty());
1158 QCOMPARE(itemAt(scene, 0, -9.9), ellipse);
1159 QCOMPARE(itemAt(scene, 0, 9.9), ellipse);
1160 QVERIFY(scene.items(QPointF(10, -10)).isEmpty());
1161 QCOMPARE(itemAt(scene, 9.9, 0), ellipse);
1162 QVERIFY(scene.items(QPointF(10, 10)).isEmpty());
1163}
1164
1165void tst_QGraphicsScene::addLine()
1166{
1167 QGraphicsScene scene;
1168 QPen pen(Qt::red);
1169 pen.setWidthF(1.0);
1170 QGraphicsLineItem *line = scene.addLine(line: QLineF(-10, -10, 20, 20),
1171 pen);
1172 QCOMPARE(line->pos(), QPointF());
1173 QCOMPARE(line->pen(), pen);
1174 QCOMPARE(line->line(), QLineF(-10, -10, 20, 20));
1175 QCOMPARE(itemAt(scene, 0, 0), line);
1176 QCOMPARE(itemAt(scene, -10, -10), line);
1177 QVERIFY(scene.items(QPointF(-9.9, 0)).isEmpty());
1178 QVERIFY(scene.items(QPointF(-10, 10)).isEmpty());
1179 QVERIFY(scene.items(QPointF(0, -9.9)).isEmpty());
1180 QVERIFY(scene.items(QPointF(0, 9.9)).isEmpty());
1181 QVERIFY(scene.items(QPointF(10, -10)).isEmpty());
1182 QVERIFY(scene.items(QPointF(9.9, 0)).isEmpty());
1183 QCOMPARE(itemAt(scene, 10, 10), line);
1184}
1185
1186void tst_QGraphicsScene::addPath()
1187{
1188 QGraphicsScene scene;
1189 QPainterPath p;
1190 p.addEllipse(rect: QRectF(-10, -10, 20, 20));
1191 p.addEllipse(rect: QRectF(-10, 20, 20, 20));
1192
1193 QGraphicsPathItem *path = scene.addPath(path: p, pen: QPen(Qt::red), brush: QBrush(Qt::blue));
1194 QCOMPARE(path->pos(), QPointF());
1195 QCOMPARE(path->pen(), QPen(Qt::red));
1196 QCOMPARE(path->path(), p);
1197 QCOMPARE(path->brush(), QBrush(Qt::blue));
1198
1199 path->setPen(QPen(Qt::red, 0));
1200
1201 QCOMPARE(itemAt(scene, 0, 0), path);
1202 QCOMPARE(itemAt(scene, -9.9, 0), path);
1203 QCOMPARE(itemAt(scene, 9.9, 0), path);
1204 QCOMPARE(itemAt(scene, 0, -9.9), path);
1205 QCOMPARE(itemAt(scene, 0, 9.9), path);
1206 QCOMPARE(itemAt(scene, 0, 30), path);
1207 QCOMPARE(itemAt(scene, -9.9, 30), path);
1208 QCOMPARE(itemAt(scene, 9.9, 30), path);
1209 QCOMPARE(itemAt(scene, 0, 20.1), path);
1210 QCOMPARE(itemAt(scene, 0, 39.9), path);
1211 QVERIFY(scene.items(QPointF(-10, -10)).isEmpty());
1212 QVERIFY(scene.items(QPointF(10, -10)).isEmpty());
1213 QVERIFY(scene.items(QPointF(-10, 10)).isEmpty());
1214 QVERIFY(scene.items(QPointF(10, 10)).isEmpty());
1215 QVERIFY(scene.items(QPointF(-10, 20)).isEmpty());
1216 QVERIFY(scene.items(QPointF(10, 20)).isEmpty());
1217if (sizeof(qreal) != sizeof(double))
1218 QWARN("Skipping test because of rounding errors when qreal != double");
1219else
1220 QVERIFY(scene.items(QPointF(-10, 30)).isEmpty());
1221 QVERIFY(scene.items(QPointF(10.1, 30)).isEmpty());
1222}
1223
1224void tst_QGraphicsScene::addPixmap()
1225{
1226 QGraphicsScene scene;
1227 QPixmap pix(":/Ash_European.jpg");
1228 QGraphicsPixmapItem *pixmap = scene.addPixmap(pixmap: pix);
1229
1230 QCOMPARE(pixmap->pos(), QPointF());
1231 QCOMPARE(pixmap->pixmap(), pix);
1232 QCOMPARE(itemAt(scene, 0, 0), pixmap);
1233 QCOMPARE(itemAt(scene, pix.width() - 1, 0), pixmap);
1234 QCOMPARE(itemAt(scene, 0, pix.height() - 1), pixmap);
1235 QCOMPARE(itemAt(scene, pix.width() - 1, pix.height() - 1), pixmap);
1236
1237 QVERIFY(scene.items(QPointF(-1, -1)).isEmpty());
1238 QVERIFY(scene.items(QPointF(pix.width() - 1, -1)).isEmpty());
1239 QVERIFY(scene.items(QPointF(-1, pix.height() - 1)).isEmpty());
1240 QVERIFY(scene.items(QPointF(pix.width(), pix.height())).isEmpty());
1241 QVERIFY(scene.items(QPointF(0, pix.height())).isEmpty());
1242 QVERIFY(scene.items(QPointF(pix.width(), 0)).isEmpty());
1243}
1244
1245void tst_QGraphicsScene::addRect()
1246{
1247 QGraphicsScene scene;
1248 QGraphicsRectItem *rect = scene.addRect(rect: QRectF(-10, -10, 20, 20),
1249 pen: QPen(Qt::red), brush: QBrush(Qt::blue));
1250 QCOMPARE(rect->pos(), QPointF());
1251 QCOMPARE(rect->pen(), QPen(Qt::red));
1252 QCOMPARE(rect->brush(), QBrush(Qt::blue));
1253 QCOMPARE(rect->rect(), QRectF(-10, -10, 20, 20));
1254
1255 rect->setPen(QPen(Qt::red, 0));
1256
1257 QCOMPARE(itemAt(scene, 0, 0),rect);
1258 QCOMPARE(itemAt(scene, -10, -10), rect);
1259 QCOMPARE(itemAt(scene, -9.9, 0), rect);
1260 QVERIFY(scene.items(QPointF(-10, 10)).isEmpty());
1261 QCOMPARE(itemAt(scene, 0, -9.9), rect);
1262 QCOMPARE(itemAt(scene, 0, 9.9), rect);
1263 QVERIFY(scene.items(QPointF(10, -10)).isEmpty());
1264 QCOMPARE(itemAt(scene, 9.9, 0), rect);
1265 QVERIFY(scene.items(QPointF(10, 10)).isEmpty());
1266}
1267
1268void tst_QGraphicsScene::addText()
1269{
1270 QGraphicsScene scene;
1271 QGraphicsTextItem *text = scene.addText(text: "Qt", font: QFont());
1272 QCOMPARE(text->pos(), QPointF());
1273 QCOMPARE(text->toPlainText(), QString("Qt"));
1274 QCOMPARE(text->font(), QFont());
1275}
1276
1277void tst_QGraphicsScene::removeItem()
1278{
1279#if defined(Q_OS_ANDROID)
1280 QSKIP("No mouse cursor support");
1281#endif
1282 QGraphicsScene scene;
1283 QGraphicsItem *item = scene.addRect(rect: QRectF(0, 0, 10, 10));
1284 QCOMPARE(itemAt(scene, 0, 0), item); // forces indexing
1285 scene.removeItem(item);
1286 QVERIFY(scene.items(QPointF(0, 0)).isEmpty());
1287 delete item;
1288
1289 QGraphicsItem *item2 = scene.addRect(rect: QRectF(0, 0, 10, 10));
1290 item2->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1291 QCOMPARE(itemAt(scene, 0, 0), item2);
1292
1293 // Removing a selected item
1294 QVERIFY(scene.selectedItems().isEmpty());
1295 item2->setSelected(true);
1296 QVERIFY(scene.selectedItems().contains(item2));
1297 scene.removeItem(item: item2);
1298 QVERIFY(scene.selectedItems().isEmpty());
1299
1300 // Check that we are in a state that can receive paint events
1301 // (i.e., not logged out on Windows).
1302 Q_CHECK_PAINTEVENTS
1303
1304 // Removing a hovered item
1305 HoverItem *hoverItem = new HoverItem;
1306 scene.addItem(item: hoverItem);
1307 scene.setSceneRect(x: -50, y: -50, w: 100, h: 100);
1308
1309 QGraphicsView view(&scene);
1310 view.setWindowTitle(QTest::currentTestFunction());
1311 view.setFixedSize(w: 150, h: 150);
1312 view.show();
1313 QApplication::setActiveWindow(&view);
1314 QVERIFY(QTest::qWaitForWindowActive(&view));
1315 QTest::mouseMove(window: view.windowHandle(), pos: view.mapFromScene(point: hoverItem->scenePos() + QPointF(20, 20)));
1316 QTRY_VERIFY(!hoverItem->isHovered);
1317
1318 QTest::mouseMove(window: view.windowHandle(), pos: view.mapFromScene(point: hoverItem->scenePos()));
1319 QTRY_VERIFY(hoverItem->isHovered);
1320
1321 scene.removeItem(item: hoverItem);
1322 hoverItem->setAcceptHoverEvents(false);
1323 scene.addItem(item: hoverItem);
1324 QTRY_VERIFY(!hoverItem->isHovered);
1325}
1326
1327void tst_QGraphicsScene::focusItem()
1328{
1329 QGraphicsScene scene;
1330 QEvent activate(QEvent::WindowActivate);
1331 QApplication::sendEvent(receiver: &scene, event: &activate);
1332
1333 QVERIFY(!scene.focusItem());
1334 QGraphicsItem *item = scene.addText(text: "Qt");
1335 QVERIFY(!scene.focusItem());
1336 item->setFocus();
1337 QVERIFY(!scene.focusItem());
1338 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
1339 QVERIFY(!scene.focusItem());
1340 item->setFocus();
1341 QCOMPARE(scene.focusItem(), item);
1342
1343 QFocusEvent focusOut(QEvent::FocusOut);
1344 QApplication::sendEvent(receiver: &scene, event: &focusOut);
1345
1346 QVERIFY(!scene.focusItem());
1347
1348 QFocusEvent focusIn(QEvent::FocusIn);
1349 QApplication::sendEvent(receiver: &scene, event: &focusIn);
1350 QCOMPARE(scene.focusItem(), item);
1351
1352 QGraphicsItem *item2 = scene.addText(text: "Qt");
1353 item2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
1354 QCOMPARE(scene.focusItem(), item);
1355
1356 item2->setFocus();
1357 QCOMPARE(scene.focusItem(), item2);
1358 item->setFocus();
1359 QCOMPARE(scene.focusItem(), item);
1360
1361 item2->setFocus();
1362 QCOMPARE(scene.focusItem(), item2);
1363 QApplication::sendEvent(receiver: &scene, event: &focusOut);
1364 QVERIFY(!scene.hasFocus());
1365 QVERIFY(!scene.focusItem());
1366 QApplication::sendEvent(receiver: &scene, event: &focusIn);
1367 QCOMPARE(scene.focusItem(), item2);
1368
1369 QApplication::sendEvent(receiver: &scene, event: &focusOut);
1370
1371 QVERIFY(!scene.focusItem());
1372 scene.removeItem(item: item2);
1373 delete item2;
1374
1375 QApplication::sendEvent(receiver: &scene, event: &focusIn);
1376 QVERIFY(!scene.focusItem());
1377}
1378
1379class FocusItem : public QGraphicsTextItem
1380{
1381protected:
1382 void focusOutEvent(QFocusEvent *) override
1383 {
1384 QVERIFY(!scene()->focusItem());
1385 }
1386};
1387
1388void tst_QGraphicsScene::focusItemLostFocus()
1389{
1390 QGraphicsScene scene;
1391 QEvent activate(QEvent::WindowActivate);
1392 QApplication::sendEvent(receiver: &scene, event: &activate);
1393
1394 FocusItem *item = new FocusItem;
1395 item->setTextInteractionFlags(Qt::TextEditorInteraction);
1396 scene.addItem(item);
1397
1398 item->setFocus();
1399 QCOMPARE(scene.focusItem(), item);
1400 item->clearFocus();
1401}
1402
1403class ClearTestItem : public QGraphicsRectItem
1404{
1405public:
1406 using QGraphicsRectItem::QGraphicsRectItem;
1407 ~ClearTestItem() { qDeleteAll(c: items); }
1408 QList<QGraphicsItem *> items;
1409};
1410
1411void tst_QGraphicsScene::clear()
1412{
1413 QGraphicsScene scene;
1414 scene.clear();
1415 QVERIFY(scene.items().isEmpty());
1416 scene.addRect(x: 0, y: 0, w: 100, h: 100)->setPen(QPen(Qt::black, 0));
1417 QCOMPARE(scene.sceneRect(), QRectF(0, 0, 100, 100));
1418 scene.clear();
1419 QVERIFY(scene.items().isEmpty());
1420 QCOMPARE(scene.sceneRect(), QRectF(0, 0, 100, 100));
1421
1422 ClearTestItem *firstItem = new ClearTestItem;
1423 QGraphicsItem *secondItem = new QGraphicsRectItem;
1424 firstItem->items += secondItem;
1425
1426 scene.setItemIndexMethod(QGraphicsScene::NoIndex);
1427 scene.addItem(item: firstItem);
1428 scene.addItem(item: secondItem);
1429 QCOMPARE(scene.items().at(0), firstItem);
1430 QCOMPARE(scene.items().at(1), secondItem);
1431
1432 ClearTestItem *thirdItem = new ClearTestItem(firstItem);
1433 QGraphicsItem *forthItem = new QGraphicsRectItem(firstItem);
1434 thirdItem->items += forthItem;
1435
1436 // must not crash even if firstItem deletes secondItem
1437 scene.clear();
1438 QVERIFY(scene.items().isEmpty());
1439}
1440
1441void tst_QGraphicsScene::setFocusItem()
1442{
1443 QGraphicsScene scene;
1444 QEvent activate(QEvent::WindowActivate);
1445 QApplication::sendEvent(receiver: &scene, event: &activate);
1446
1447 QGraphicsItem *item = scene.addText(text: "Qt");
1448 QVERIFY(!scene.focusItem());
1449 QVERIFY(!scene.hasFocus());
1450 scene.setFocusItem(item);
1451 QVERIFY(!scene.hasFocus());
1452 QVERIFY(!scene.focusItem());
1453 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
1454
1455 for (int i = 0; i < 3; ++i) {
1456 scene.setFocusItem(item);
1457 QVERIFY(scene.hasFocus());
1458 QCOMPARE(scene.focusItem(), item);
1459 QVERIFY(item->hasFocus());
1460 }
1461
1462 QGraphicsItem *item2 = scene.addText(text: "Qt");
1463 item2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
1464
1465 scene.setFocusItem(item: item2);
1466 QVERIFY(!item->hasFocus());
1467 QVERIFY(item2->hasFocus());
1468
1469 scene.setFocusItem(item);
1470 QVERIFY(item->hasFocus());
1471 QVERIFY(!item2->hasFocus());
1472
1473 scene.clearFocus();
1474 QVERIFY(!item->hasFocus());
1475 QVERIFY(!item2->hasFocus());
1476
1477 scene.setFocus();
1478 QVERIFY(item->hasFocus());
1479 QVERIFY(!item2->hasFocus());
1480
1481 scene.setFocusItem(item: nullptr);
1482 QVERIFY(!item->hasFocus());
1483 QVERIFY(!item2->hasFocus());
1484
1485 scene.setFocus();
1486 QVERIFY(!item->hasFocus());
1487 QVERIFY(!item2->hasFocus());
1488}
1489
1490void tst_QGraphicsScene::setFocusItem_inactive()
1491{
1492 QGraphicsScene scene;
1493 QGraphicsItem *item = scene.addText(text: "Qt");
1494 QVERIFY(!scene.focusItem());
1495 QVERIFY(!scene.hasFocus());
1496 scene.setFocusItem(item);
1497 QVERIFY(!scene.hasFocus());
1498 QVERIFY(!scene.focusItem());
1499 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
1500
1501 for (int i = 0; i < 3; ++i) {
1502 scene.setFocusItem(item);
1503 QCOMPARE(scene.focusItem(), item);
1504 QVERIFY(!item->hasFocus());
1505 }
1506
1507}
1508
1509
1510void tst_QGraphicsScene::mouseGrabberItem()
1511{
1512 QGraphicsScene scene;
1513 QVERIFY(!scene.mouseGrabberItem());
1514
1515 QGraphicsItem *item = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1516 item->setFlag(flag: QGraphicsItem::ItemIsMovable);
1517 item->setZValue(1);
1518
1519 QGraphicsItem *item2 = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1520 item2->setFlag(flag: QGraphicsItem::ItemIsMovable);
1521 item2->setZValue(0);
1522
1523 for (int i = 0; i < 3; ++i) {
1524 item->setPos(ax: 0, ay: 0);
1525 item2->setPos(ax: 0, ay: 0);
1526 item->setZValue((i & 1) ? 0 : 1);
1527 item2->setZValue((i & 1) ? 1 : 0);
1528 QGraphicsItem *topMostItem = (i & 1) ? item2 : item;
1529
1530 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
1531 pressEvent.setButton(Qt::LeftButton);
1532 pressEvent.setScenePos(QPointF(0, 0));
1533 pressEvent.setScreenPos(QPoint(100, 100));
1534
1535 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1536 QCOMPARE(scene.mouseGrabberItem(), topMostItem);
1537
1538 for (int i = 0; i < 1000; ++i) {
1539 QGraphicsSceneMouseEvent moveEvent(QEvent::GraphicsSceneMouseMove);
1540 moveEvent.setButtons(Qt::LeftButton);
1541 moveEvent.setScenePos(QPointF(i * 10, i * 10));
1542 moveEvent.setScreenPos(QPoint(100 + i * 10, 100 + i * 10));
1543 QApplication::sendEvent(receiver: &scene, event: &moveEvent);
1544 QCOMPARE(scene.mouseGrabberItem(), topMostItem);
1545
1546 // Geometrical changes should not affect the mouse grabber.
1547 item->setZValue(QRandomGenerator::global()->bounded(highest: 500));
1548 item2->setZValue(QRandomGenerator::global()->bounded(highest: 500));
1549 item->setPos(ax: QRandomGenerator::global()->bounded(highest: 50000), ay: QRandomGenerator::global()->bounded(highest: 50000));
1550 item2->setPos(ax: QRandomGenerator::global()->bounded(highest: 50000), ay: QRandomGenerator::global()->bounded(highest: 50000));
1551 }
1552
1553 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
1554 releaseEvent.setScenePos(QPointF(10000, 10000));
1555 releaseEvent.setScreenPos(QPoint(1000000, 1000000));
1556 QApplication::sendEvent(receiver: &scene, event: &releaseEvent);
1557 QVERIFY(!scene.mouseGrabberItem());
1558 }
1559
1560 // Structural change: deleting the mouse grabber
1561 item->setPos(ax: 0, ay: 0);
1562 item->setZValue(1);
1563 item2->setPos(ax: 0, ay: 0);
1564 item2->setZValue(0);
1565 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
1566 pressEvent.setButton(Qt::LeftButton);
1567 pressEvent.setScenePos(QPointF(0, 0));
1568 pressEvent.setScreenPos(QPoint(100, 100));
1569
1570 QGraphicsSceneMouseEvent moveEvent(QEvent::GraphicsSceneMouseMove);
1571 moveEvent.setButtons(Qt::LeftButton);
1572 moveEvent.setScenePos(QPointF(0, 0));
1573 moveEvent.setScreenPos(QPoint(100, 100));
1574
1575 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1576 QApplication::sendEvent(receiver: &scene, event: &moveEvent);
1577 QCOMPARE(scene.mouseGrabberItem(), item);
1578 item->setVisible(false);
1579 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1580 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1581 QCOMPARE(scene.mouseGrabberItem(), item2);
1582 item2->setVisible(false);
1583 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1584 QApplication::sendEvent(receiver: &scene, event: &moveEvent);
1585 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1586 item2->setVisible(true);
1587 QApplication::sendEvent(receiver: &scene, event: &moveEvent);
1588 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1589 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1590 QApplication::sendEvent(receiver: &scene, event: &moveEvent);
1591 QCOMPARE(scene.mouseGrabberItem(), item2);
1592 scene.removeItem(item: item2);
1593 delete item2;
1594 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1595}
1596
1597void tst_QGraphicsScene::hoverEvents_siblings()
1598{
1599 Q_CHECK_PAINTEVENTS
1600
1601 QGraphicsScene scene;
1602 QGraphicsItem *lastItem = nullptr;
1603 QList<HoverItem *> items;
1604 for (int i = 0; i < 15; ++i) {
1605 auto item = new HoverItem;
1606 scene.addItem(item);
1607 items << item;
1608 if (lastItem)
1609 item->setPos(lastItem->pos() + QPointF(sin(x: i / 3.0) * 17, cos(x: i / 3.0) * 17));
1610 item->setZValue(i);
1611 lastItem = item;
1612 }
1613
1614 QGraphicsView view(&scene);
1615 view.setWindowTitle(QTest::currentTestFunction());
1616 view.setRenderHint(hint: QPainter::Antialiasing, enabled: true);
1617 view.setMinimumSize(minw: 400, minh: 300);
1618 view.rotate(angle: 10);
1619 view.scale(sx: 1.7, sy: 1.7);
1620 view.show();
1621 QApplication::setActiveWindow(&view);
1622 view.activateWindow();
1623 QVERIFY(QTest::qWaitForWindowActive(&view));
1624
1625 QCursor::setPos(view.mapToGlobal(QPoint(-5, -5)));
1626
1627 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
1628 mouseEvent.setScenePos(QPointF(-1000, -1000));
1629 QApplication::sendEvent(receiver: &scene, event: &mouseEvent);
1630
1631 QTest::qWait(ms: 50);
1632
1633 for (int j = 1; j >= 0; --j) {
1634 int i = j ? 0 : 14;
1635 forever {
1636 if (j)
1637 QVERIFY(!items.at(i)->isHovered);
1638 else
1639 QVERIFY(!items.at(i)->isHovered);
1640 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
1641 mouseEvent.setScenePos(items.at(i)->mapToScene(ax: 0, ay: 0));
1642 QApplication::sendEvent(receiver: &scene, event: &mouseEvent);
1643
1644 QCoreApplication::processEvents(); // this posts updates from the scene to the view
1645 QCoreApplication::processEvents(); // which trigger a repaint here
1646
1647 QTRY_VERIFY(items.at(i)->isHovered);
1648 if (j && i > 0)
1649 QVERIFY(!items.at(i - 1)->isHovered);
1650 if (!j && i < 14)
1651 QVERIFY(!items.at(i + 1)->isHovered);
1652 i += j ? 1 : -1;
1653 if ((j && i == 15) || (!j && i == -1))
1654 break;
1655 }
1656
1657 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
1658 mouseEvent.setScenePos(QPointF(-1000, -1000));
1659 QApplication::sendEvent(receiver: &scene, event: &mouseEvent);
1660
1661 QCoreApplication::processEvents(); // this posts updates from the scene to the view
1662 QCoreApplication::processEvents(); // which trigger a repaint here
1663 }
1664}
1665
1666void tst_QGraphicsScene::hoverEvents_parentChild()
1667{
1668 Q_CHECK_PAINTEVENTS
1669
1670 QGraphicsScene scene;
1671 QGraphicsItem *lastItem = nullptr;
1672 QList<HoverItem *> items;
1673 for (int i = 0; i < 15; ++i) {
1674 auto item = new HoverItem;
1675 scene.addItem(item);
1676 items << item;
1677 if (lastItem) {
1678 item->setParentItem(lastItem);
1679 item->setPos(ax: sin(x: i / 3.0) * 17, ay: cos(x: i / 3.0) * 17);
1680 }
1681 lastItem = item;
1682 }
1683
1684 QGraphicsView view(&scene);
1685 view.setWindowTitle(QTest::currentTestFunction());
1686 view.setRenderHint(hint: QPainter::Antialiasing, enabled: true);
1687 view.setMinimumSize(minw: 400, minh: 300);
1688 view.rotate(angle: 10);
1689 view.scale(sx: 1.7, sy: 1.7);
1690 view.show();
1691 QApplication::setActiveWindow(&view);
1692 QVERIFY(QTest::qWaitForWindowActive(&view));
1693
1694 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
1695 mouseEvent.setScenePos(QPointF(-1000, -1000));
1696 QApplication::sendEvent(receiver: &scene, event: &mouseEvent);
1697
1698 for (int j = 1; j >= 0; --j) {
1699 int i = j ? 0 : 14;
1700 forever {
1701 if (j) {
1702 QVERIFY(!items.at(i)->isHovered);
1703 } else {
1704 if (i == 14)
1705 QVERIFY(!items.at(13)->isHovered);
1706 }
1707 mouseEvent.setScenePos(items.at(i)->mapToScene(ax: 0, ay: 0));
1708 QApplication::sendEvent(receiver: &scene, event: &mouseEvent);
1709
1710 QCoreApplication::processEvents(); // this posts updates from the scene to the view
1711 QCoreApplication::processEvents(); // which trigger a repaint here
1712
1713 QTRY_VERIFY(items.at(i)->isHovered);
1714 if (i < 14)
1715 QVERIFY(!items.at(i + 1)->isHovered);
1716 i += j ? 1 : -1;
1717 if ((j && i == 15) || (!j && i == -1))
1718 break;
1719 }
1720
1721 mouseEvent.setScenePos(QPointF(-1000, -1000));
1722 QApplication::sendEvent(receiver: &scene, event: &mouseEvent);
1723
1724 QCoreApplication::processEvents(); // this posts updates from the scene to the view
1725 QCoreApplication::processEvents(); // which trigger a repaint here
1726 }
1727}
1728
1729void tst_QGraphicsScene::createItemGroup()
1730{
1731 QGraphicsScene scene;
1732
1733 QList<QGraphicsItem *> children1;
1734 children1 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1735 children1 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1736 children1 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1737 children1 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1738
1739 QList<QGraphicsItem *> children2;
1740 children2 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1741 children2 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1742 children2 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1743 children2 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1744
1745 QList<QGraphicsItem *> children3;
1746 children3 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1747 children3 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1748 children3 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1749 children3 << scene.addRect(rect: QRectF(-10, -10, 20, 20));
1750
1751 // All items in children1 are children of parent1
1752 QGraphicsItem *parent1 = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1753 for (QGraphicsItem *item : qAsConst(t&: children1))
1754 item->setParentItem(parent1);
1755
1756 QGraphicsItemGroup *group = scene.createItemGroup(items: children1);
1757 QCOMPARE(group->parentItem(), parent1);
1758 QCOMPARE(children1.first()->parentItem(), group);
1759 scene.destroyItemGroup(group);
1760 QCOMPARE(children1.first()->parentItem(), parent1);
1761 group = scene.createItemGroup(items: children1);
1762 QCOMPARE(group->parentItem(), parent1);
1763 QCOMPARE(children1.first()->parentItem(), group);
1764 scene.destroyItemGroup(group);
1765 QCOMPARE(children1.first()->parentItem(), parent1);
1766
1767 // All items in children2 are children of parent2
1768 QGraphicsItem *parent2 = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1769 for (QGraphicsItem *item : qAsConst(t&: children2))
1770 item->setParentItem(parent2);
1771
1772 // Now make parent2 a child of parent1, so all children2 are also children
1773 // of parent1.
1774 parent2->setParentItem(parent1);
1775
1776 // The children2 group should still have parent2 as their common ancestor.
1777 group = scene.createItemGroup(items: children2);
1778 QCOMPARE(group->parentItem(), parent2);
1779 QCOMPARE(children2.first()->parentItem(), group);
1780 scene.destroyItemGroup(group);
1781 QCOMPARE(children2.first()->parentItem(), parent2);
1782
1783 // But the set of both children2 and children1 share only parent1.
1784 group = scene.createItemGroup(items: children2 + children1);
1785 QCOMPARE(group->parentItem(), parent1);
1786 QCOMPARE(children1.first()->parentItem(), group);
1787 QCOMPARE(children2.first()->parentItem(), group);
1788 scene.destroyItemGroup(group);
1789 QCOMPARE(children1.first()->parentItem(), parent1);
1790 QCOMPARE(children2.first()->parentItem(), parent1);
1791
1792 // Fixup the parent-child chain
1793 for (QGraphicsItem *item : qAsConst(t&: children2))
1794 item->setParentItem(parent2);
1795
1796 // These share no common parent
1797 group = scene.createItemGroup(items: children3);
1798 QCOMPARE(group->parentItem(), nullptr);
1799 scene.destroyItemGroup(group);
1800
1801 // Make children3 children of parent3
1802 QGraphicsItem *parent3 = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1803 for (QGraphicsItem *item : qAsConst(t&: children3))
1804 item->setParentItem(parent3);
1805
1806 // These should have parent3 as a parent
1807 group = scene.createItemGroup(items: children3);
1808 QCOMPARE(group->parentItem(), parent3);
1809 scene.destroyItemGroup(group);
1810
1811 // Now make them all children of parent1
1812 parent3->setParentItem(parent1);
1813
1814 group = scene.createItemGroup(items: children3);
1815 QCOMPARE(group->parentItem(), parent3);
1816 scene.destroyItemGroup(group);
1817
1818 group = scene.createItemGroup(items: children2);
1819 QCOMPARE(group->parentItem(), parent2);
1820 scene.destroyItemGroup(group);
1821
1822 group = scene.createItemGroup(items: children1);
1823 QCOMPARE(group->parentItem(), parent1);
1824 scene.destroyItemGroup(group);
1825
1826 QGraphicsItemGroup *emptyGroup = scene.createItemGroup(items: QList<QGraphicsItem *>());
1827 QVERIFY(emptyGroup->childItems().isEmpty());
1828 QVERIFY(!emptyGroup->parentItem());
1829 QCOMPARE(emptyGroup->scene(), &scene);
1830}
1831
1832class EventTester : public QGraphicsEllipseItem
1833{
1834public:
1835 EventTester() : QGraphicsEllipseItem(QRectF(-10, -10, 20, 20))
1836 { }
1837
1838 bool ignoreMouse = false;
1839 QVector<QEvent::Type> eventTypes;
1840
1841protected:
1842 bool sceneEvent(QEvent *event) override
1843 {
1844 eventTypes << QEvent::Type(event->type());
1845 switch (event->type()) {
1846 case QEvent::GraphicsSceneMousePress:
1847 case QEvent::GraphicsSceneMouseMove:
1848 case QEvent::GraphicsSceneMouseRelease:
1849 if (ignoreMouse) {
1850 event->ignore();
1851 return true;
1852 }
1853 break;
1854 default:
1855 break;
1856 }
1857
1858 return QGraphicsEllipseItem::sceneEvent(event);
1859 }
1860};
1861
1862void tst_QGraphicsScene::mouseEventPropagation()
1863{
1864 EventTester *a = new EventTester;
1865 EventTester *b = new EventTester;
1866 EventTester *c = new EventTester;
1867 EventTester *d = new EventTester;
1868 b->setParentItem(a);
1869 c->setParentItem(b);
1870 d->setParentItem(c);
1871
1872 a->setFlag(flag: QGraphicsItem::ItemIsMovable);
1873 b->setFlag(flag: QGraphicsItem::ItemIsMovable);
1874 c->setFlag(flag: QGraphicsItem::ItemIsMovable);
1875 d->setFlag(flag: QGraphicsItem::ItemIsMovable);
1876
1877 a->setData(key: 0, value: "A");
1878 b->setData(key: 0, value: "B");
1879 c->setData(key: 0, value: "C");
1880 d->setData(key: 0, value: "D");
1881
1882 // scene -> a -> b -> c -> d
1883 QGraphicsScene scene;
1884 QEvent activate(QEvent::WindowActivate);
1885 QApplication::sendEvent(receiver: &scene, event: &activate);
1886
1887 scene.addItem(item: a);
1888
1889 // Prepare some events
1890 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
1891 pressEvent.setButton(Qt::LeftButton);
1892 pressEvent.setScenePos(QPointF(0, 0));
1893 QGraphicsSceneMouseEvent moveEvent(QEvent::GraphicsSceneMouseMove);
1894 moveEvent.setButton(Qt::LeftButton);
1895 moveEvent.setScenePos(QPointF(0, 0));
1896 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
1897 releaseEvent.setButton(Qt::LeftButton);
1898 releaseEvent.setScenePos(QPointF(0, 0));
1899
1900 // Send a press
1901 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1902 QCOMPARE(d->eventTypes.size(), 2);
1903 QCOMPARE(d->eventTypes.at(0), QEvent::GrabMouse);
1904 QCOMPARE(d->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
1905 QCOMPARE(c->eventTypes.size(), 0);
1906 QCOMPARE(b->eventTypes.size(), 0);
1907 QCOMPARE(a->eventTypes.size(), 0);
1908 QCOMPARE(scene.mouseGrabberItem(), d);
1909
1910 // Send a move
1911 QApplication::sendEvent(receiver: &scene, event: &moveEvent);
1912 QCOMPARE(d->eventTypes.size(), 3);
1913 QCOMPARE(d->eventTypes.at(2), QEvent::GraphicsSceneMouseMove);
1914 QCOMPARE(c->eventTypes.size(), 0);
1915 QCOMPARE(b->eventTypes.size(), 0);
1916 QCOMPARE(a->eventTypes.size(), 0);
1917 QCOMPARE(scene.mouseGrabberItem(), d);
1918
1919 // Send a release
1920 QApplication::sendEvent(receiver: &scene, event: &releaseEvent);
1921 QCOMPARE(d->eventTypes.size(), 5);
1922 QCOMPARE(d->eventTypes.at(3), QEvent::GraphicsSceneMouseRelease);
1923 QCOMPARE(d->eventTypes.at(4), QEvent::UngrabMouse);
1924 QCOMPARE(c->eventTypes.size(), 0);
1925 QCOMPARE(b->eventTypes.size(), 0);
1926 QCOMPARE(a->eventTypes.size(), 0);
1927 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1928
1929 d->setAcceptedMouseButtons(Qt::RightButton);
1930
1931 // Send a press
1932 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1933 QCOMPARE(d->eventTypes.size(), 5);
1934 QCOMPARE(c->eventTypes.size(), 2);
1935 QCOMPARE(c->eventTypes.at(0), QEvent::GrabMouse);
1936 QCOMPARE(c->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
1937 QCOMPARE(b->eventTypes.size(), 0);
1938 QCOMPARE(a->eventTypes.size(), 0);
1939 QCOMPARE(scene.mouseGrabberItem(), c);
1940
1941 // Send another press, with a button that isn't actually accepted
1942 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1943 pressEvent.setButton(Qt::RightButton);
1944 QCOMPARE(d->eventTypes.size(), 5);
1945 QCOMPARE(c->eventTypes.size(), 3);
1946 QCOMPARE(c->eventTypes.at(2), QEvent::GraphicsSceneMousePress);
1947 QCOMPARE(b->eventTypes.size(), 0);
1948 QCOMPARE(a->eventTypes.size(), 0);
1949 QCOMPARE(scene.mouseGrabberItem(), c);
1950
1951 // Send a move
1952 QApplication::sendEvent(receiver: &scene, event: &moveEvent);
1953 QCOMPARE(d->eventTypes.size(), 5);
1954 QCOMPARE(c->eventTypes.size(), 4);
1955 QCOMPARE(c->eventTypes.at(3), QEvent::GraphicsSceneMouseMove);
1956 QCOMPARE(b->eventTypes.size(), 0);
1957 QCOMPARE(a->eventTypes.size(), 0);
1958 QCOMPARE(scene.mouseGrabberItem(), c);
1959
1960 // Send a release
1961 QApplication::sendEvent(receiver: &scene, event: &releaseEvent);
1962 QCOMPARE(d->eventTypes.size(), 5);
1963 QCOMPARE(c->eventTypes.size(), 6);
1964 QCOMPARE(c->eventTypes.at(4), QEvent::GraphicsSceneMouseRelease);
1965 QCOMPARE(c->eventTypes.at(5), QEvent::UngrabMouse);
1966 QCOMPARE(b->eventTypes.size(), 0);
1967 QCOMPARE(a->eventTypes.size(), 0);
1968 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1969
1970 // Disabled items eat events. c should not get this.
1971 d->setEnabled(false);
1972 d->setAcceptedMouseButtons(Qt::RightButton);
1973
1974 // Send a right press. This disappears in d.
1975 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1976 QCOMPARE(d->eventTypes.size(), 5);
1977 QCOMPARE(c->eventTypes.size(), 6);
1978 QCOMPARE(b->eventTypes.size(), 0);
1979 QCOMPARE(a->eventTypes.size(), 0);
1980 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1981
1982 // Send a left press. This goes to c.
1983 pressEvent.setButton(Qt::LeftButton);
1984 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
1985 QCOMPARE(d->eventTypes.size(), 5);
1986 QCOMPARE(c->eventTypes.size(), 8);
1987 QCOMPARE(c->eventTypes.at(6), QEvent::GrabMouse);
1988 QCOMPARE(c->eventTypes.at(7), QEvent::GraphicsSceneMousePress);
1989 QCOMPARE(b->eventTypes.size(), 0);
1990 QCOMPARE(a->eventTypes.size(), 0);
1991 QCOMPARE(scene.mouseGrabberItem(), c);
1992
1993 // Clicking outside the items removes the mouse grabber
1994}
1995
1996void tst_QGraphicsScene::mouseEventPropagation_ignore()
1997{
1998 EventTester *a = new EventTester;
1999 EventTester *b = new EventTester;
2000 EventTester *c = new EventTester;
2001 EventTester *d = new EventTester;
2002 b->setParentItem(a);
2003 c->setParentItem(b);
2004 d->setParentItem(c);
2005
2006 a->setFlags(QGraphicsItem::ItemIsMovable);
2007 b->setFlags(QGraphicsItem::ItemIsMovable);
2008 c->setFlags(QGraphicsItem::ItemIsMovable);
2009 d->setFlags(QGraphicsItem::ItemIsMovable);
2010
2011 // scene -> a -> b -> c -> d
2012 QGraphicsScene scene;
2013 scene.addItem(item: a);
2014
2015 // Prepare some events
2016 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
2017 pressEvent.setButton(Qt::LeftButton);
2018 pressEvent.setScenePos(QPointF(0, 0));
2019
2020 b->ignoreMouse = true;
2021 c->ignoreMouse = true;
2022 d->ignoreMouse = true;
2023
2024 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
2025 QCOMPARE(a->eventTypes.size(), 2);
2026 QCOMPARE(a->eventTypes.at(0), QEvent::GrabMouse);
2027 QCOMPARE(a->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
2028 QCOMPARE(b->eventTypes.size(), 3);
2029 QCOMPARE(b->eventTypes.at(0), QEvent::GrabMouse);
2030 QCOMPARE(b->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
2031 QCOMPARE(b->eventTypes.at(2), QEvent::UngrabMouse);
2032 QCOMPARE(c->eventTypes.size(), 3);
2033 QCOMPARE(c->eventTypes.at(0), QEvent::GrabMouse);
2034 QCOMPARE(c->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
2035 QCOMPARE(c->eventTypes.at(2), QEvent::UngrabMouse);
2036 QCOMPARE(d->eventTypes.size(), 3);
2037 QCOMPARE(d->eventTypes.at(0), QEvent::GrabMouse);
2038 QCOMPARE(d->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
2039 QCOMPARE(d->eventTypes.at(2), QEvent::UngrabMouse);
2040 QCOMPARE(scene.mouseGrabberItem(), a);
2041
2042 a->ignoreMouse = true;
2043
2044 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
2045 QCOMPARE(a->eventTypes.size(), 3);
2046 QCOMPARE(a->eventTypes.at(2), QEvent::GraphicsSceneMousePress);
2047 QCOMPARE(b->eventTypes.size(), 3);
2048 QCOMPARE(c->eventTypes.size(), 3);
2049 QCOMPARE(d->eventTypes.size(), 3);
2050
2051 QVERIFY(!pressEvent.isAccepted());
2052}
2053
2054void tst_QGraphicsScene::mouseEventPropagation_focus()
2055{
2056 EventTester *a = new EventTester;
2057 EventTester *b = new EventTester;
2058 EventTester *c = new EventTester;
2059 EventTester *d = new EventTester;
2060 b->setParentItem(a);
2061 c->setParentItem(b);
2062 d->setParentItem(c);
2063
2064 a->setFlag(flag: QGraphicsItem::ItemIsMovable);
2065 b->setFlag(flag: QGraphicsItem::ItemIsMovable);
2066 c->setFlag(flag: QGraphicsItem::ItemIsMovable);
2067 d->setFlag(flag: QGraphicsItem::ItemIsMovable);
2068
2069 // scene -> a -> b -> c -> d
2070 QGraphicsScene scene;
2071 QEvent activate(QEvent::WindowActivate);
2072 QApplication::sendEvent(receiver: &scene, event: &activate);
2073
2074 scene.addItem(item: a);
2075
2076 // Prepare some events
2077 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
2078 pressEvent.setButton(Qt::LeftButton);
2079 pressEvent.setScenePos(QPointF(0, 0));
2080
2081 a->setFlag(flag: QGraphicsItem::ItemIsFocusable);
2082 QVERIFY(!a->hasFocus());
2083
2084 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
2085
2086 QVERIFY(a->hasFocus());
2087 QCOMPARE(a->eventTypes.size(), 1);
2088 QCOMPARE(a->eventTypes.first(), QEvent::FocusIn);
2089 QCOMPARE(d->eventTypes.size(), 2);
2090 QCOMPARE(d->eventTypes.at(0), QEvent::GrabMouse);
2091 QCOMPARE(d->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
2092}
2093
2094void tst_QGraphicsScene::mouseEventPropagation_doubleclick()
2095{
2096 EventTester *a = new EventTester;
2097 EventTester *b = new EventTester;
2098 a->setFlags(QGraphicsItem::ItemIsMovable);
2099 b->setFlags(QGraphicsItem::ItemIsMovable);
2100
2101 a->setPos(ax: -50, ay: 0);
2102 b->setPos(ax: 50, ay: 0);
2103
2104 QGraphicsScene scene;
2105 scene.addItem(item: a);
2106 scene.addItem(item: b);
2107
2108 // Prepare some events
2109 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
2110 pressEvent.setButton(Qt::LeftButton);
2111 pressEvent.setScenePos(QPointF(0, 0));
2112 QGraphicsSceneMouseEvent doubleClickEvent(QEvent::GraphicsSceneMouseDoubleClick);
2113 doubleClickEvent.setButton(Qt::LeftButton);
2114 doubleClickEvent.setScenePos(QPointF(0, 0));
2115 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
2116 releaseEvent.setButton(Qt::LeftButton);
2117 releaseEvent.setScenePos(QPointF(0, 0));
2118
2119 // Send press to A
2120 pressEvent.setScenePos(a->mapToScene(ax: 0, ay: 0));
2121 QApplication::sendEvent(receiver: &scene, event: &pressEvent);
2122 QCOMPARE(a->eventTypes.size(), 2);
2123 QCOMPARE(a->eventTypes.at(0), QEvent::GrabMouse);
2124 QCOMPARE(a->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
2125
2126 // Send release to A
2127 releaseEvent.setScenePos(a->mapToScene(ax: 0, ay: 0));
2128 QApplication::sendEvent(receiver: &scene, event: &releaseEvent);
2129 QCOMPARE(a->eventTypes.size(), 4);
2130 QCOMPARE(a->eventTypes.at(2), QEvent::GraphicsSceneMouseRelease);
2131 QCOMPARE(a->eventTypes.at(3), QEvent::UngrabMouse);
2132
2133 // Send doubleclick to B
2134 doubleClickEvent.setScenePos(b->mapToScene(ax: 0, ay: 0));
2135 QApplication::sendEvent(receiver: &scene, event: &doubleClickEvent);
2136 QCOMPARE(a->eventTypes.size(), 4);
2137 QCOMPARE(b->eventTypes.size(), 2);
2138 QCOMPARE(b->eventTypes.at(0), QEvent::GrabMouse);
2139 QCOMPARE(b->eventTypes.at(1), QEvent::GraphicsSceneMousePress);
2140
2141 // Send release to B
2142 releaseEvent.setScenePos(b->mapToScene(ax: 0, ay: 0));
2143 QApplication::sendEvent(receiver: &scene, event: &releaseEvent);
2144 QCOMPARE(a->eventTypes.size(), 4);
2145 QCOMPARE(b->eventTypes.size(), 4);
2146 QCOMPARE(b->eventTypes.at(2), QEvent::GraphicsSceneMouseRelease);
2147 QCOMPARE(b->eventTypes.at(3), QEvent::UngrabMouse);
2148}
2149
2150class Scene : public QGraphicsScene
2151{
2152public:
2153 QVector<QPointF> mouseMovePoints;
2154
2155protected:
2156 void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override
2157 {
2158 mouseMovePoints << event->scenePos();
2159 }
2160};
2161
2162void tst_QGraphicsScene::mouseEventPropagation_mouseMove()
2163{
2164 Scene scene;
2165 scene.addRect(rect: QRectF(5, 0, 12, 12));
2166 scene.addRect(rect: QRectF(15, 0, 12, 12))->setAcceptHoverEvents(true);
2167 for (int i = 0; i < 30; ++i) {
2168 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
2169 event.setScenePos(QPointF(i, 5));
2170 QApplication::sendEvent(receiver: &scene, event: &event);
2171 }
2172
2173 QCOMPARE(scene.mouseMovePoints.size(), 30);
2174 for (int i = 0; i < 30; ++i)
2175 QCOMPARE(scene.mouseMovePoints.at(i), QPointF(i, 5));
2176}
2177
2178class DndTester : public QGraphicsEllipseItem
2179{
2180public:
2181 using QGraphicsEllipseItem::QGraphicsEllipseItem;
2182
2183 ~DndTester()
2184 {
2185 delete lastEvent;
2186 }
2187
2188 QGraphicsSceneDragDropEvent *lastEvent = nullptr;
2189 QList<QEvent::Type> eventList;
2190 bool ignoresDragEnter = false;
2191 bool ignoresDragMove = false;
2192
2193protected:
2194 void dragEnterEvent(QGraphicsSceneDragDropEvent *event)
2195 {
2196 storeLastEvent(event);
2197 event->setAccepted(!ignoresDragEnter);
2198 if (!ignoresDragEnter)
2199 event->setDropAction(Qt::IgnoreAction);
2200 eventList << event->type();
2201 }
2202
2203 void dragMoveEvent(QGraphicsSceneDragDropEvent *event)
2204 {
2205 storeLastEvent(event);
2206 event->setAccepted(!ignoresDragMove);
2207 eventList << event->type();
2208 }
2209
2210 void dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
2211 {
2212 storeLastEvent(event);
2213 eventList << event->type();
2214 }
2215
2216 void dropEvent(QGraphicsSceneDragDropEvent *event)
2217 {
2218 storeLastEvent(event);
2219 eventList << event->type();
2220 }
2221
2222private:
2223 void storeLastEvent(QGraphicsSceneDragDropEvent *event)
2224 {
2225 delete lastEvent;
2226 lastEvent = new QGraphicsSceneDragDropEvent(event->type());
2227 lastEvent->setScenePos(event->scenePos());
2228 lastEvent->setScreenPos(event->screenPos());
2229 lastEvent->setButtons(event->buttons());
2230 lastEvent->setModifiers(event->modifiers());
2231 lastEvent->setPossibleActions(event->possibleActions());
2232 lastEvent->setProposedAction(event->proposedAction());
2233 lastEvent->setDropAction(event->dropAction());
2234 lastEvent->setMimeData(event->mimeData());
2235 lastEvent->setWidget(event->widget());
2236 lastEvent->setSource(event->source());
2237 }
2238};
2239
2240#if QT_CONFIG(draganddrop)
2241void tst_QGraphicsScene::dragAndDrop_simple()
2242{
2243 DndTester *item = new DndTester(QRectF(-10, -10, 20, 20));
2244
2245 QGraphicsScene scene;
2246 scene.addItem(item);
2247
2248 QGraphicsView view(&scene);
2249 view.setFixedSize(w: 100, h: 100);
2250
2251 QMimeData mimeData;
2252
2253 // Initial drag enter for the scene
2254 QDragEnterEvent dragEnter(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2255 QApplication::sendEvent(receiver: view.viewport(), event: &dragEnter);
2256 QVERIFY(dragEnter.isAccepted());
2257 QCOMPARE(dragEnter.dropAction(), Qt::CopyAction);
2258
2259 {
2260 // Move outside the item
2261 QDragMoveEvent dragMove(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2262 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2263 QVERIFY(!dragMove.isAccepted());
2264 QCOMPARE(dragMove.dropAction(), Qt::CopyAction);
2265 }
2266 {
2267 // Move inside the item without setAcceptDrops
2268 QDragMoveEvent dragMove(view.mapFromScene(point: item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2269 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2270 QVERIFY(!dragMove.isAccepted());
2271 QCOMPARE(dragMove.dropAction(), Qt::CopyAction);
2272 QCOMPARE(item->eventList.size(), 0);
2273 }
2274 item->setAcceptDrops(true);
2275 {
2276 // Move inside the item with setAcceptDrops
2277 QDragMoveEvent dragMove(view.mapFromScene(point: item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2278 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2279 QVERIFY(dragMove.isAccepted());
2280 QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction);
2281 QCOMPARE(item->eventList.size(), 2);
2282 QCOMPARE(item->eventList.at(0), QEvent::GraphicsSceneDragEnter);
2283 QCOMPARE(item->eventList.at(1), QEvent::GraphicsSceneDragMove);
2284 QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(dragMove.pos()));
2285 QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(dragMove.pos()));
2286 QVERIFY(item->lastEvent->isAccepted());
2287 QCOMPARE(item->lastEvent->dropAction(), Qt::IgnoreAction);
2288 }
2289 {
2290 // Another move inside the item
2291 QDragMoveEvent dragMove(view.mapFromScene(point: item->mapToScene(ax: 5, ay: 5)), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2292 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2293 QVERIFY(dragMove.isAccepted());
2294 QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction);
2295 QCOMPARE(item->eventList.size(), 3);
2296 QCOMPARE(item->eventList.at(2), QEvent::GraphicsSceneDragMove);
2297 QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(dragMove.pos()));
2298 QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(dragMove.pos()));
2299 QVERIFY(item->lastEvent->isAccepted());
2300 QCOMPARE(item->lastEvent->dropAction(), Qt::IgnoreAction);
2301 }
2302 {
2303 // Move outside the item
2304 QDragMoveEvent dragMove(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2305 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2306 QVERIFY(!dragMove.isAccepted());
2307 QCOMPARE(dragMove.dropAction(), Qt::CopyAction);
2308 QCOMPARE(item->eventList.size(), 4);
2309 QCOMPARE(item->eventList.at(3), QEvent::GraphicsSceneDragLeave);
2310 QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(dragMove.pos()));
2311 QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(dragMove.pos()));
2312 QVERIFY(item->lastEvent->isAccepted());
2313 QCOMPARE(item->lastEvent->dropAction(), Qt::CopyAction);
2314 }
2315 {
2316 // Move inside the item again
2317 QDragMoveEvent dragMove(view.mapFromScene(point: item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2318 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2319 QVERIFY(dragMove.isAccepted());
2320 QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction);
2321 QCOMPARE(item->eventList.size(), 6);
2322 QCOMPARE(item->eventList.at(4), QEvent::GraphicsSceneDragEnter);
2323 QCOMPARE(item->eventList.at(5), QEvent::GraphicsSceneDragMove);
2324 QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(dragMove.pos()));
2325 QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(dragMove.pos()));
2326 QVERIFY(item->lastEvent->isAccepted());
2327 QCOMPARE(item->lastEvent->dropAction(), Qt::IgnoreAction);
2328 }
2329 {
2330 // Drop inside the item
2331 QDropEvent drop(view.mapFromScene(point: item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2332 QApplication::sendEvent(receiver: view.viewport(), event: &drop);
2333 QVERIFY(drop.isAccepted());
2334 QCOMPARE(drop.dropAction(), Qt::CopyAction);
2335 QCOMPARE(item->eventList.size(), 7);
2336 QCOMPARE(item->eventList.at(6), QEvent::GraphicsSceneDrop);
2337 QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(drop.pos()));
2338 QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(drop.pos()));
2339 QVERIFY(item->lastEvent->isAccepted());
2340 QCOMPARE(item->lastEvent->dropAction(), Qt::CopyAction);
2341 }
2342}
2343
2344void tst_QGraphicsScene::dragAndDrop_disabledOrInvisible()
2345{
2346 DndTester *item = new DndTester(QRectF(-10, -10, 20, 20));
2347 item->setAcceptDrops(true);
2348
2349 QGraphicsScene scene;
2350 scene.addItem(item);
2351
2352 QGraphicsView view(&scene);
2353 view.setFixedSize(w: 100, h: 100);
2354
2355 QMimeData mimeData;
2356
2357 // Initial drag enter for the scene
2358 QDragEnterEvent dragEnter(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2359 QApplication::sendEvent(receiver: view.viewport(), event: &dragEnter);
2360 QVERIFY(dragEnter.isAccepted());
2361 QCOMPARE(dragEnter.dropAction(), Qt::CopyAction);
2362 {
2363 // Move inside the item
2364 QDragMoveEvent dragMove(view.mapFromScene(point: item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2365 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2366 QVERIFY(dragMove.isAccepted());
2367 QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction);
2368 QCOMPARE(item->eventList.size(), 2);
2369 QCOMPARE(item->eventList.at(0), QEvent::GraphicsSceneDragEnter);
2370 QCOMPARE(item->eventList.at(1), QEvent::GraphicsSceneDragMove);
2371 }
2372 {
2373 // Move outside the item
2374 QDragMoveEvent dragMove(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2375 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2376 QVERIFY(!dragMove.isAccepted());
2377 QCOMPARE(dragMove.dropAction(), Qt::CopyAction);
2378 QCOMPARE(item->eventList.size(), 3);
2379 QCOMPARE(item->eventList.at(2), QEvent::GraphicsSceneDragLeave);
2380 }
2381
2382 // Now disable the item
2383 item->setEnabled(false);
2384 QVERIFY(!item->isEnabled());
2385 QVERIFY(item->isVisible());
2386
2387 {
2388 // Move inside the item
2389 QDragMoveEvent dragMove(view.mapFromScene(point: item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2390 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2391 QVERIFY(!dragMove.isAccepted());
2392 QCOMPARE(dragMove.dropAction(), Qt::CopyAction);
2393 QCOMPARE(item->eventList.size(), 3);
2394 QCOMPARE(item->eventList.at(2), QEvent::GraphicsSceneDragLeave);
2395 }
2396
2397 // Reenable it, and make it invisible
2398 item->setEnabled(true);
2399 item->setVisible(false);
2400 QVERIFY(item->isEnabled());
2401 QVERIFY(!item->isVisible());
2402
2403 {
2404 // Move inside the item
2405 QDragMoveEvent dragMove(view.mapFromScene(point: item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2406 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2407 QVERIFY(!dragMove.isAccepted());
2408 QCOMPARE(dragMove.dropAction(), Qt::CopyAction);
2409 QCOMPARE(item->eventList.size(), 3);
2410 QCOMPARE(item->eventList.at(2), QEvent::GraphicsSceneDragLeave);
2411 }
2412
2413 // Dummy drop event to keep the Mac from crashing.
2414 QDropEvent dropEvent(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2415 QApplication::sendEvent(receiver: view.viewport(), event: &dropEvent);
2416}
2417
2418void tst_QGraphicsScene::dragAndDrop_propagate()
2419{
2420 DndTester *item1 = new DndTester(QRectF(-10, -10, 20, 20));
2421 DndTester *item2 = new DndTester(QRectF(0, 0, 20, 20));
2422 item1->setAcceptDrops(true);
2423 item2->setAcceptDrops(true);
2424 item2->ignoresDragMove = true;
2425 item2->ignoresDragEnter = false;
2426 item2->setZValue(1);
2427
2428 item1->setData(key: 0, value: "item1");
2429 item2->setData(key: 0, value: "item2");
2430
2431 QGraphicsScene scene;
2432 scene.addItem(item: item1);
2433 scene.addItem(item: item2);
2434
2435 QGraphicsView view(&scene);
2436 view.setFixedSize(w: 100, h: 100);
2437
2438 QMimeData mimeData;
2439
2440 // Initial drag enter for the scene
2441 QDragEnterEvent dragEnter(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2442 QApplication::sendEvent(receiver: view.viewport(), event: &dragEnter);
2443 QVERIFY(dragEnter.isAccepted());
2444 QCOMPARE(dragEnter.dropAction(), Qt::CopyAction);
2445
2446 {
2447 // Move outside the items
2448 QDragMoveEvent dragMove(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2449 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2450 QVERIFY(!dragMove.isAccepted());
2451 QCOMPARE(dragMove.dropAction(), Qt::CopyAction);
2452 QVERIFY(item1->eventList.isEmpty());
2453 QVERIFY(item2->eventList.isEmpty());
2454 }
2455 {
2456 // Move inside item1
2457 QDragMoveEvent dragMove(view.mapFromScene(ax: -5, ay: -5), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2458 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2459 QVERIFY(dragMove.isAccepted());
2460 QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction);
2461 QCOMPARE(item1->eventList.size(), 2);
2462 QCOMPARE(item1->eventList.at(0), QEvent::GraphicsSceneDragEnter);
2463 QCOMPARE(item1->eventList.at(1), QEvent::GraphicsSceneDragMove);
2464 }
2465
2466 {
2467 // Move into the intersection item1-item2
2468 QDragMoveEvent dragMove(view.mapFromScene(ax: 5, ay: 5), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2469 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2470 QVERIFY(!dragMove.isAccepted()); // move does not propagate, (ignoresDragMove = true)
2471 QCOMPARE(item1->eventList.size(), 3);
2472 QCOMPARE(item1->eventList.at(2), QEvent::GraphicsSceneDragLeave);
2473 QCOMPARE(item2->eventList.size(), 2);
2474 QCOMPARE(item2->eventList.at(0), QEvent::GraphicsSceneDragEnter);
2475 QCOMPARE(item2->eventList.at(1), QEvent::GraphicsSceneDragMove);
2476 }
2477 {
2478 // Move into the item2
2479 QDragMoveEvent dragMove(view.mapFromScene(ax: 15, ay: 15), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2480 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2481 QVERIFY(!dragMove.isAccepted());
2482 QCOMPARE(dragMove.dropAction(), Qt::CopyAction);
2483 QCOMPARE(item1->eventList.size(), 3);
2484 QCOMPARE(item2->eventList.size(), 3);
2485 QCOMPARE(item2->eventList.at(2), QEvent::GraphicsSceneDragMove);
2486 }
2487 {
2488 // Move inside item1
2489 QDragMoveEvent dragMove(view.mapFromScene(ax: -5, ay: -5), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2490 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2491 QVERIFY(dragMove.isAccepted());
2492 QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction);
2493 QCOMPARE(item1->eventList.size(), 5);
2494 QCOMPARE(item1->eventList.at(3), QEvent::GraphicsSceneDragEnter);
2495 QCOMPARE(item1->eventList.at(4), QEvent::GraphicsSceneDragMove);
2496 QCOMPARE(item2->eventList.size(), 4);
2497 QCOMPARE(item2->eventList.at(3), QEvent::GraphicsSceneDragLeave);
2498 }
2499
2500 {
2501 item2->ignoresDragEnter = true;
2502 // Move into the intersection item1-item2
2503 QDragMoveEvent dragMove(view.mapFromScene(ax: 5, ay: 5), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2504 QApplication::sendEvent(receiver: view.viewport(), event: &dragMove);
2505 QVERIFY(dragMove.isAccepted()); // dragEnter propagates down to item1, which then accepts the move event.
2506 QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction);
2507 QCOMPARE(item1->eventList.size(), 6);
2508 QCOMPARE(item1->eventList.at(5), QEvent::GraphicsSceneDragMove);
2509 QCOMPARE(item2->eventList.size(), 5);
2510 QCOMPARE(item2->eventList.at(4), QEvent::GraphicsSceneDragEnter);
2511 }
2512
2513 {
2514 item2->ignoresDragEnter = false;
2515 // Drop on the intersection item1-item2
2516 QDropEvent drop(view.mapFromScene(ax: 5, ay: 5), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2517 QApplication::sendEvent(receiver: view.viewport(), event: &drop);
2518 QVERIFY(drop.isAccepted());
2519 QCOMPARE(drop.dropAction(), Qt::CopyAction);
2520
2521 QCOMPARE(item1->eventList.size(), 7);
2522 QCOMPARE(item1->eventList.at(6), QEvent::GraphicsSceneDrop);
2523 QCOMPARE(item2->eventList.size(), 5);
2524 }
2525
2526 // Dummy drop event to keep the Mac from crashing.
2527 QDropEvent dropEvent(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, {});
2528 QApplication::sendEvent(receiver: view.viewport(), event: &dropEvent);
2529}
2530#endif
2531
2532void tst_QGraphicsScene::render_data()
2533{
2534 QTest::addColumn<QRectF>(name: "targetRect");
2535 QTest::addColumn<QRectF>(name: "sourceRect");
2536 QTest::addColumn<Qt::AspectRatioMode>(name: "aspectRatioMode");
2537 QTest::addColumn<QTransform>(name: "transform");
2538 QTest::addColumn<QPainterPath>(name: "clip");
2539
2540 QPainterPath clip_rect;
2541 clip_rect.addRect(x: 50, y: 100, w: 200, h: 150);
2542
2543 QPainterPath clip_ellipse;
2544 clip_ellipse.addEllipse(x: 100,y: 50,w: 150,h: 200);
2545
2546 QTest::newRow(dataTag: "all-all-untransformed") << QRectF() << QRectF()
2547 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2548 QTest::newRow(dataTag: "all-topleft-untransformed") << QRectF(0, 0, 150, 150)
2549 << QRectF() << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2550 QTest::newRow(dataTag: "all-topright-untransformed") << QRectF(150, 0, 150, 150)
2551 << QRectF() << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2552 QTest::newRow(dataTag: "all-bottomleft-untransformed") << QRectF(0, 150, 150, 150)
2553 << QRectF() << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2554 QTest::newRow(dataTag: "all-bottomright-untransformed") << QRectF(150, 150, 150, 150)
2555 << QRectF() << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2556 QTest::newRow(dataTag: "topleft-all-untransformed") << QRectF() << QRectF(-10, -10, 10, 10)
2557 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2558 QTest::newRow(dataTag: "topright-all-untransformed") << QRectF() << QRectF(0, -10, 10, 10)
2559 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2560 QTest::newRow(dataTag: "bottomleft-all-untransformed") << QRectF() << QRectF(-10, 0, 10, 10)
2561 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2562 QTest::newRow(dataTag: "bottomright-all-untransformed") << QRectF() << QRectF(0, 0, 10, 10)
2563 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2564 QTest::newRow(dataTag: "topleft-topleft-untransformed") << QRectF(0, 0, 150, 150) << QRectF(-10, -10, 10, 10)
2565 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2566 QTest::newRow(dataTag: "topright-topleft-untransformed") << QRectF(150, 0, 150, 150) << QRectF(-10, -10, 10, 10)
2567 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2568 QTest::newRow(dataTag: "bottomleft-topleft-untransformed") << QRectF(0, 150, 150, 150) << QRectF(-10, -10, 10, 10)
2569 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2570 QTest::newRow(dataTag: "bottomright-topleft-untransformed") << QRectF(150, 150, 150, 150) << QRectF(-10, -10, 10, 10)
2571 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2572 QTest::newRow(dataTag: "top-topleft-untransformed") << QRectF(0, 0, 300, 150) << QRectF(-10, -10, 10, 10)
2573 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2574 QTest::newRow(dataTag: "bottom-topleft-untransformed") << QRectF(0, 150, 300, 150) << QRectF(-10, -10, 10, 10)
2575 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2576 QTest::newRow(dataTag: "left-topleft-untransformed") << QRectF(0, 0, 150, 300) << QRectF(-10, -10, 10, 10)
2577 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2578 QTest::newRow(dataTag: "right-topleft-untransformed") << QRectF(150, 0, 150, 300) << QRectF(-10, -10, 10, 10)
2579 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2580 QTest::newRow(dataTag: "top-bottomright-untransformed") << QRectF(0, 0, 300, 150) << QRectF(0, 0, 10, 10)
2581 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2582 QTest::newRow(dataTag: "bottom-bottomright-untransformed") << QRectF(0, 150, 300, 150) << QRectF(0, 0, 10, 10)
2583 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2584 QTest::newRow(dataTag: "left-bottomright-untransformed") << QRectF(0, 0, 150, 300) << QRectF(0, 0, 10, 10)
2585 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2586 QTest::newRow(dataTag: "right-bottomright-untransformed") << QRectF(150, 0, 150, 300) << QRectF(0, 0, 10, 10)
2587 << Qt::IgnoreAspectRatio << QTransform() << QPainterPath();
2588 QTest::newRow(dataTag: "all-all-45-deg-right") << QRectF() << QRectF()
2589 << Qt::IgnoreAspectRatio << QTransform().rotate(a: -45) << QPainterPath();
2590 QTest::newRow(dataTag: "all-all-45-deg-left") << QRectF() << QRectF()
2591 << Qt::IgnoreAspectRatio << QTransform().rotate(a: 45) << QPainterPath();
2592 QTest::newRow(dataTag: "all-all-scale-2x") << QRectF() << QRectF()
2593 << Qt::IgnoreAspectRatio << QTransform::fromScale(dx: 2, dy: 2) << QPainterPath();
2594 QTest::newRow(dataTag: "all-all-translate-50-0") << QRectF() << QRectF()
2595 << Qt::IgnoreAspectRatio << QTransform::fromTranslate(dx: 50, dy: 0) << QPainterPath();
2596 QTest::newRow(dataTag: "all-all-translate-0-50") << QRectF() << QRectF()
2597 << Qt::IgnoreAspectRatio << QTransform::fromTranslate(dx: 0, dy: 50) << QPainterPath();
2598 QTest::newRow(dataTag: "all-all-untransformed-clip-rect") << QRectF() << QRectF()
2599 << Qt::IgnoreAspectRatio << QTransform() << clip_rect;
2600 QTest::newRow(dataTag: "all-all-untransformed-clip-ellipse") << QRectF() << QRectF()
2601 << Qt::IgnoreAspectRatio << QTransform() << clip_ellipse;
2602}
2603
2604void tst_QGraphicsScene::render()
2605{
2606 QFETCH(QRectF, targetRect);
2607 QFETCH(QRectF, sourceRect);
2608 QFETCH(Qt::AspectRatioMode, aspectRatioMode);
2609 QFETCH(QTransform, transform);
2610 QFETCH(QPainterPath, clip);
2611
2612 QPixmap pix(30, 30);
2613 pix.fill(fillColor: Qt::blue);
2614
2615 QGraphicsView view;
2616 view.setWindowTitle(QTest::currentTestFunction());
2617 view.resize(m_testSize);
2618 QGraphicsScene scene(&view);
2619 scene.addEllipse(rect: QRectF(-10, -10, 20, 20), pen: QPen(Qt::black, 0), brush: QBrush(Qt::white));
2620 scene.addEllipse(rect: QRectF(-2, -7, 4, 4), pen: QPen(Qt::black, 0), brush: QBrush(Qt::yellow))->setZValue(1);
2621 QGraphicsPixmapItem *item = scene.addPixmap(pixmap: pix);
2622 item->setZValue(2);
2623 item->setOffset(QPointF(3, 3));
2624 view.show();
2625
2626 scene.setSceneRect(scene.itemsBoundingRect());
2627
2628 QImage bigImage(300, 300, QImage::Format_RGB32);
2629 bigImage.fill(pixel: 0);
2630 QPainter painter(&bigImage);
2631 painter.setPen(Qt::lightGray);
2632 for (int i = 0; i <= 300; i += 25) {
2633 painter.drawLine(x1: 0, y1: i, x2: 300, y2: i);
2634 painter.drawLine(x1: i, y1: 0, x2: i, y2: 300);
2635 }
2636 painter.setPen(QPen(Qt::darkGray, 2));
2637 painter.drawLine(x1: 0, y1: 150, x2: 300, y2: 150);
2638 painter.drawLine(x1: 150, y1: 0, x2: 150, y2: 300);
2639 painter.setTransform(transform);
2640 if (!clip.isEmpty()) painter.setClipPath(path: clip);
2641 scene.render(painter: &painter, target: targetRect, source: sourceRect, aspectRatioMode);
2642 painter.end();
2643
2644 QString fileName = QFINDTESTDATA(QString("/testData/render/%1.png").arg(QTest::currentDataTag()));
2645 QImage original(fileName);
2646 QVERIFY(!original.isNull());
2647
2648 // Compare
2649 int wrongPixels = 0;
2650 for (int y = 0; y < original.height(); ++y) {
2651 for (int x = 0; x < original.width(); ++x) {
2652 if (bigImage.pixel(x, y) != original.pixel(x, y))
2653 ++wrongPixels;
2654 }
2655 }
2656
2657 // This is a pixmap compare test - because of rounding errors on diverse
2658 // platforms, and especially because tests are compiled in release mode,
2659 // we set a 95% acceptance threshold for comparing images. This number may
2660 // have to be adjusted if this test fails.
2661 qreal threshold = 0.95;
2662 qreal similarity = (1 - (wrongPixels / qreal(original.width() * original.height())));
2663 if (similarity < threshold) {
2664#if 1
2665 // fail
2666 QLabel *expectedLabel = new QLabel;
2667 expectedLabel->setPixmap(QPixmap::fromImage(image: original));
2668
2669 QLabel *newLabel = new QLabel;
2670 newLabel->setPixmap(QPixmap::fromImage(image: bigImage));
2671
2672 QGridLayout *gridLayout = new QGridLayout;
2673 gridLayout->addWidget(new QLabel(tr(s: "MISMATCH: %1").arg(a: QTest::currentDataTag())), row: 0, column: 0, rowSpan: 1, columnSpan: 2);
2674 gridLayout->addWidget(new QLabel(tr(s: "Current")), row: 1, column: 0);
2675 gridLayout->addWidget(new QLabel(tr(s: "Expected")), row: 1, column: 1);
2676 gridLayout->addWidget(expectedLabel, row: 2, column: 1);
2677 gridLayout->addWidget(newLabel, row: 2, column: 0);
2678
2679 QWidget widget;
2680 widget.setWindowTitle(QTest::currentTestFunction());
2681 widget.setLayout(gridLayout);
2682 widget.show();
2683
2684 QTestEventLoop::instance().enterLoop(secs: 1);
2685
2686 QFAIL("Images are not identical.");
2687#else
2688 // generate
2689 qDebug() << "Updating" << QTest::currentDataTag() << ":" << bigImage.save(fileName, "png");
2690#endif
2691 }
2692}
2693
2694void tst_QGraphicsScene::renderItemsWithNegativeWidthOrHeight()
2695{
2696#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) || defined(Q_OS_WINRT)
2697 QSKIP("Test only works on platforms with resizable windows");
2698#endif
2699 QGraphicsScene scene(0, 0, m_testSize.width(), m_testSize.height());
2700
2701 // Add item with negative width.
2702 QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, -m_testSize.width(), 50);
2703 item1->setBrush(Qt::red);
2704 item1->setPos(ax: m_testSize.width(), ay: 50);
2705 scene.addItem(item: item1);
2706
2707 // Add item with negative height.
2708 QGraphicsRectItem *item2 = new QGraphicsRectItem(0, 0, 50, -m_testSize.height());
2709 item2->setBrush(Qt::blue);
2710 item2->setPos(ax: 50, ay: m_testSize.height());
2711 scene.addItem(item: item2);
2712
2713 QGraphicsView view(&scene);
2714 view.setWindowTitle(QTest::currentTestFunction());
2715 view.setFrameStyle(QFrame::NoFrame);
2716 view.show();
2717 QTRY_COMPARE(view.viewport()->size(), m_testSize);
2718
2719 QImage expected(view.viewport()->size(), QImage::Format_RGB32);
2720 view.viewport()->render(target: &expected);
2721
2722 // Make sure the scene background is the same as the viewport background.
2723 scene.setBackgroundBrush(view.viewport()->palette().brush(cr: view.viewport()->backgroundRole()));
2724 QImage actual(m_testSize, QImage::Format_RGB32);
2725 QPainter painter(&actual);
2726 scene.render(painter: &painter);
2727 painter.end();
2728
2729 QCOMPARE(actual, expected);
2730}
2731
2732#ifndef QT_NO_CONTEXTMENU
2733void tst_QGraphicsScene::contextMenuEvent()
2734{
2735 QGraphicsScene scene;
2736 QEvent activate(QEvent::WindowActivate);
2737 QApplication::sendEvent(receiver: &scene, event: &activate);
2738
2739 EventTester *item = new EventTester;
2740 scene.addItem(item);
2741 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
2742 item->setFocus();
2743
2744 QVERIFY(item->hasFocus());
2745 QVERIFY(scene.hasFocus());
2746
2747 QGraphicsView view(&scene);
2748 view.setWindowTitle(QTest::currentTestFunction());
2749 view.resize(m_testSize);
2750 view.show();
2751 view.activateWindow();
2752 QVERIFY(QTest::qWaitForWindowActive(&view));
2753 view.centerOn(item);
2754
2755 {
2756 QContextMenuEvent event(QContextMenuEvent::Keyboard, view.viewport()->rect().center(),
2757 view.mapToGlobal(view.viewport()->rect().center()));
2758 QApplication::sendEvent(receiver: view.viewport(), event: &event);
2759 QCOMPARE(item->eventTypes.last(), QEvent::GraphicsSceneContextMenu);
2760 }
2761}
2762
2763class ContextMenuItem : public QGraphicsRectItem
2764{
2765public:
2766 ContextMenuItem(const QSize &s) : QGraphicsRectItem(0, 0, s.width(), s.height())
2767 { setBrush(Qt::red); }
2768
2769protected:
2770 void contextMenuEvent(QGraphicsSceneContextMenuEvent *) override
2771 { /* just accept */ }
2772};
2773
2774void tst_QGraphicsScene::contextMenuEvent_ItemIgnoresTransformations()
2775{
2776#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
2777 QSKIP("Test fails on some Android devices (QTBUG-44430)");
2778#endif
2779
2780 QGraphicsScene scene(0, 0, m_testSize.width(), m_testSize.height());
2781 const QSize itemSize = m_testSize / 2;
2782 ContextMenuItem *item = new ContextMenuItem(itemSize);
2783 item->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
2784 scene.addItem(item);
2785
2786 QWidget topLevel;
2787 topLevel.setWindowTitle(QTest::currentTestFunction());
2788 topLevel.resize(m_testSize);
2789 QGraphicsView view(&scene, &topLevel);
2790 topLevel.show();
2791 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
2792
2793
2794
2795 {
2796 QPoint pos(itemSize.width() / 2, itemSize.height() / 2);
2797 QContextMenuEvent event(QContextMenuEvent::Keyboard, pos, view.viewport()->mapToGlobal(pos));
2798 event.ignore();
2799 QApplication::sendEvent(receiver: view.viewport(), event: &event);
2800 QVERIFY(event.isAccepted());
2801 }
2802 {
2803 QPoint pos(itemSize.width() * 3 / 2, itemSize.height() * 3 / 2);
2804 QContextMenuEvent event(QContextMenuEvent::Keyboard, pos, view.viewport()->mapToGlobal(pos));
2805 event.ignore();
2806 QApplication::sendEvent(receiver: view.viewport(), event: &event);
2807 QVERIFY(!event.isAccepted());
2808 }
2809 view.scale(sx: 1.5, sy: 1.5);
2810 {
2811 QPoint pos(itemSize.width() / 4, itemSize.height() / 4);
2812 QContextMenuEvent event(QContextMenuEvent::Keyboard, pos, view.viewport()->mapToGlobal(pos));
2813 event.ignore();
2814 QApplication::sendEvent(receiver: view.viewport(), event: &event);
2815 QVERIFY(event.isAccepted());
2816 }
2817 {
2818 QPoint pos(itemSize.width() / 2 + 5, itemSize.height() / 2 + 5);
2819 QContextMenuEvent event(QContextMenuEvent::Keyboard, pos, view.viewport()->mapToGlobal(pos));
2820 event.ignore();
2821 QApplication::sendEvent(receiver: view.viewport(), event: &event);
2822 QVERIFY(!event.isAccepted());
2823 }
2824}
2825#endif // QT_NO_CONTEXTMENU
2826
2827void tst_QGraphicsScene::update()
2828{
2829 QGraphicsScene scene;
2830
2831 QGraphicsRectItem *rect = new QGraphicsRectItem(0, 0, 100, 100);
2832 rect->setPen(QPen(Qt::black, 0));
2833 scene.addItem(item: rect);
2834 QCoreApplication::processEvents();
2835 rect->setPos(ax: -100, ay: -100);
2836
2837 // This function forces indexing
2838 itemAt(scene, x: 0, y: 0);
2839
2840 qRegisterMetaType<QList<QRectF> >(typeName: "QList<QRectF>");
2841 QSignalSpy spy(&scene, &QGraphicsScene::changed);
2842
2843 // We update the scene.
2844 scene.update();
2845
2846 // This function forces a purge, which will post an update signal
2847 itemAt(scene, x: 0, y: 0);
2848
2849 // This will process the pending update
2850 QCoreApplication::processEvents();
2851
2852 // Check that the update region is correct
2853 QCOMPARE(spy.count(), 1);
2854 QRectF region;
2855 const auto &rects = qvariant_cast<QList<QRectF> >(v: spy.at(i: 0).at(i: 0));
2856 for (const auto &rectF : rects)
2857 region |= rectF;
2858 QCOMPARE(region, QRectF(-100, -100, 200, 200));
2859}
2860
2861void tst_QGraphicsScene::update2()
2862{
2863 QGraphicsScene scene;
2864 scene.setSceneRect(x: -200, y: -200, w: 200, h: 200);
2865 CustomView view;
2866 view.setWindowTitle(QTest::currentTestFunction());
2867 view.resize(m_testSize);
2868 view.setScene(&scene);
2869 view.show();
2870 QApplication::setActiveWindow(&view);
2871 QVERIFY(QTest::qWaitForWindowActive(&view));
2872 QTRY_VERIFY(view.repaints >= 1);
2873 view.repaints = 0;
2874
2875 // Make sure QGraphicsScene::update only requires one event-loop iteration
2876 // before the view is updated.
2877 scene.update();
2878 QCoreApplication::processEvents();
2879 QTRY_COMPARE(view.repaints, 1);
2880 view.repaints = 0;
2881
2882 // The same for partial scene updates.
2883 scene.update(rect: QRectF(-100, -100, 100, 100));
2884 QCoreApplication::processEvents();
2885 QCOMPARE(view.repaints, 1);
2886}
2887
2888void tst_QGraphicsScene::views()
2889{
2890 QGraphicsScene scene;
2891 QGraphicsView view(&scene);
2892
2893 QCOMPARE(scene.views().size(), 1);
2894 QCOMPARE(scene.views().at(0), &view);
2895
2896 QGraphicsView view1(&scene);
2897 QCOMPARE(scene.views().size(), 2);
2898 QVERIFY(scene.views().contains(&view1));
2899
2900 view.setScene(nullptr);
2901 QCOMPARE(scene.views().size(), 1);
2902 QCOMPARE(scene.views().at(0), &view1);
2903
2904 QGraphicsView *view2 = new QGraphicsView(&scene);
2905 QCOMPARE(scene.views().size(), 2);
2906 QCOMPARE(scene.views().at(0), &view1);
2907 QCOMPARE(scene.views().at(1), view2);
2908
2909 delete view2;
2910
2911 QCOMPARE(scene.views().size(), 1);
2912 QCOMPARE(scene.views().at(0), &view1);
2913}
2914
2915class CustomScene : public QGraphicsScene
2916{
2917public:
2918 CustomScene()
2919 { startTimer(interval: 10); }
2920
2921 bool gotTimerEvent = false;
2922protected:
2923 void timerEvent(QTimerEvent *) override
2924 {
2925 gotTimerEvent = true;
2926 }
2927};
2928
2929void tst_QGraphicsScene::testEvent()
2930{
2931 // Test that QGraphicsScene properly propagates events to QObject.
2932 CustomScene scene;
2933 QTestEventLoop::instance().enterLoop(secs: 1);
2934 QVERIFY(scene.gotTimerEvent);
2935}
2936
2937class DisabledItemTester : public QGraphicsRectItem
2938{
2939public:
2940 DisabledItemTester(const QRectF &rect, QGraphicsItem *parent = nullptr)
2941 : QGraphicsRectItem(rect, parent)
2942 { }
2943
2944 QList<QEvent::Type> receivedSceneEvents;
2945 QList<QEvent::Type> receivedSceneEventFilters;
2946
2947protected:
2948 bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
2949 {
2950 receivedSceneEventFilters << event->type();
2951 return QGraphicsRectItem::sceneEventFilter(watched, event);
2952 }
2953
2954 bool sceneEvent(QEvent *event) override
2955 {
2956 receivedSceneEvents << event->type();
2957 return QGraphicsRectItem::sceneEvent(event);
2958 }
2959};
2960
2961void tst_QGraphicsScene::eventsToDisabledItems()
2962{
2963 QGraphicsScene scene;
2964
2965 DisabledItemTester *item1 = new DisabledItemTester(QRectF(-50, -50, 100, 100));
2966 DisabledItemTester *item2 = new DisabledItemTester(QRectF(-50, -50, 100, 100));
2967 item1->setZValue(1); // on top
2968
2969 scene.addItem(item: item1);
2970 scene.addItem(item: item2);
2971
2972 item1->installSceneEventFilter(filterItem: item2);
2973
2974 QVERIFY(item1->receivedSceneEvents.isEmpty());
2975 QVERIFY(item2->receivedSceneEvents.isEmpty());
2976 QVERIFY(item1->receivedSceneEventFilters.isEmpty());
2977 QVERIFY(item2->receivedSceneEventFilters.isEmpty());
2978
2979 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
2980 event.setButton(Qt::LeftButton);
2981 QApplication::sendEvent(receiver: &scene, event: &event);
2982
2983 // First item2 receives a scene event filter. Then item1 receives the
2984 // actual event. Finally the event propagates to item2. So both items
2985 // should have received the event, and item1 also got the filter.
2986 QCOMPARE(item1->receivedSceneEvents.size(), 3);
2987 QCOMPARE(item2->receivedSceneEvents.size(), 3);
2988 QCOMPARE(item1->receivedSceneEventFilters.size(), 0);
2989 QCOMPARE(item2->receivedSceneEventFilters.size(), 3);
2990
2991 item1->receivedSceneEvents.clear();
2992 item1->receivedSceneEventFilters.clear();
2993 item2->receivedSceneEvents.clear();
2994 item2->receivedSceneEventFilters.clear();
2995
2996 item1->setEnabled(false); // disable the topmost item, eat mouse events
2997
2998 event.setButton(Qt::LeftButton);
2999 event.setAccepted(false);
3000 QApplication::sendEvent(receiver: &scene, event: &event);
3001
3002 // Check that only item1 received anything - it only got the filter.
3003 QCOMPARE(item1->receivedSceneEvents.size(), 0);
3004 QCOMPARE(item2->receivedSceneEvents.size(), 0);
3005 QCOMPARE(item1->receivedSceneEventFilters.size(), 0);
3006 QCOMPARE(item2->receivedSceneEventFilters.size(), 3);
3007}
3008
3009class ExposedPixmapItem : public QGraphicsPixmapItem
3010{
3011public:
3012 using QGraphicsPixmapItem::QGraphicsPixmapItem;
3013
3014 void paint(QPainter *, const QStyleOptionGraphicsItem *option, QWidget *) override
3015 {
3016 exposed = option->exposedRect;
3017 }
3018
3019 QRectF exposed;
3020};
3021
3022void tst_QGraphicsScene::exposedRect()
3023{
3024 ExposedPixmapItem *item = new ExposedPixmapItem;
3025 item->setPixmap(QPixmap(":/Ash_European.jpg"));
3026 QGraphicsScene scene;
3027 scene.addItem(item);
3028
3029 QCOMPARE(item->exposed, QRectF());
3030
3031 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
3032 QPainter painter(&image);
3033
3034 scene.render(painter: &painter);
3035 QCOMPARE(item->exposed, item->boundingRect());
3036
3037 painter.rotate(a: 180);
3038 painter.translate(dx: 100, dy: 100);
3039
3040 scene.render(painter: &painter);
3041 QCOMPARE(item->exposed, item->boundingRect());
3042}
3043
3044void tst_QGraphicsScene::tabFocus_emptyScene()
3045{
3046 QGraphicsScene scene;
3047 QDial *dial1 = new QDial;
3048 QGraphicsView *view = new QGraphicsView(&scene);
3049 QDial *dial2 = new QDial;
3050
3051 QHBoxLayout *layout = new QHBoxLayout;
3052 layout->addWidget(dial1);
3053 layout->addWidget(view);
3054 layout->addWidget(dial2);
3055
3056 QWidget widget;
3057 widget.setLayout(layout);
3058 widget.setWindowTitle(QTest::currentTestFunction());
3059 widget.show();
3060 QApplication::setActiveWindow(&widget);
3061 widget.activateWindow();
3062 QVERIFY(QTest::qWaitForWindowActive(&widget));
3063
3064 dial1->setFocus();
3065 QVERIFY(dial1->hasFocus());
3066 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3067 QVERIFY(!dial1->hasFocus());
3068 QVERIFY(view->hasFocus());
3069 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3070 QVERIFY(!view->hasFocus());
3071 QVERIFY(dial2->hasFocus());
3072 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3073 QVERIFY(!dial2->hasFocus());
3074 QVERIFY(view->hasFocus());
3075 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3076 QVERIFY(dial1->hasFocus());
3077 QVERIFY(!dial2->hasFocus());
3078}
3079
3080void tst_QGraphicsScene::tabFocus_sceneWithFocusableItems()
3081{
3082 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
3083 QSKIP("Window activation is not supported");
3084
3085 QGraphicsScene scene;
3086 QGraphicsTextItem *item = scene.addText(text: "Qt rocks!");
3087 item->setTabChangesFocus(true);
3088 item->setTextInteractionFlags(Qt::TextEditorInteraction);
3089 QVERIFY(item->flags() & QGraphicsItem::ItemIsFocusable);
3090 item->setFocus();
3091 item->clearFocus();
3092
3093 QGraphicsTextItem *item2 = scene.addText(text: "Qt rocks!");
3094 item2->setTabChangesFocus(true);
3095 item2->setTextInteractionFlags(Qt::TextEditorInteraction);
3096 item2->setPos(ax: 0, ay: item->boundingRect().bottom());
3097 QVERIFY(item2->flags() & QGraphicsItem::ItemIsFocusable);
3098
3099 QDial *dial1 = new QDial;
3100 QGraphicsView *view = new QGraphicsView(&scene);
3101 QDial *dial2 = new QDial;
3102
3103 QHBoxLayout *layout = new QHBoxLayout;
3104 layout->addWidget(dial1);
3105 layout->addWidget(view);
3106 layout->addWidget(dial2);
3107
3108 QWidget widget;
3109 widget.setWindowTitle(QTest::currentTestFunction());
3110 widget.setLayout(layout);
3111 widget.show();
3112 QApplication::setActiveWindow(&widget);
3113 widget.activateWindow();
3114 QVERIFY(QTest::qWaitForWindowActive(&widget));
3115
3116 dial1->setFocus();
3117 QTRY_VERIFY(dial1->hasFocus());
3118 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3119 QApplication::processEvents();
3120 QTRY_VERIFY(view->hasFocus());
3121 QVERIFY(view->viewport()->hasFocus());
3122 QVERIFY(scene.hasFocus());
3123 QVERIFY(item->hasFocus());
3124
3125 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3126 QApplication::processEvents();
3127 QTRY_VERIFY(dial2->hasFocus());
3128
3129 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3130 QApplication::processEvents();
3131 QTRY_VERIFY(view->hasFocus());
3132 QTRY_VERIFY(view->viewport()->hasFocus());
3133 QTRY_VERIFY(scene.hasFocus());
3134 QTRY_VERIFY(item->hasFocus());
3135
3136 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3137 QApplication::processEvents();
3138 QTRY_VERIFY(dial1->hasFocus());
3139
3140 // If the item requests input focus, it can only ensure that the scene
3141 // sets focus on itself, but the scene cannot request focus from any view.
3142 item->setFocus();
3143 QApplication::processEvents();
3144 QTRY_VERIFY(!view->hasFocus());
3145 QVERIFY(!view->viewport()->hasFocus());
3146 QTRY_VERIFY(scene.hasFocus());
3147 QVERIFY(item->hasFocus());
3148
3149 view->setFocus();
3150 QApplication::processEvents();
3151 QTRY_VERIFY(view->hasFocus());
3152 QTRY_VERIFY(view->viewport()->hasFocus());
3153 QTRY_VERIFY(scene.hasFocus());
3154 QTRY_VERIFY(item->hasFocus());
3155
3156 // Check that everyone loses focus when the widget is hidden.
3157 widget.hide();
3158 QTRY_VERIFY(!view->hasFocus());
3159 QVERIFY(!view->viewport()->hasFocus());
3160 QVERIFY(!scene.hasFocus());
3161 QVERIFY(!item->hasFocus());
3162 QCOMPARE(scene.focusItem(), item);
3163
3164 // Check that the correct item regains focus.
3165 widget.show();
3166 QApplication::setActiveWindow(&widget);
3167 widget.activateWindow();
3168 QVERIFY(QTest::qWaitForWindowActive(&widget));
3169 QVERIFY(view->hasFocus());
3170 QTRY_VERIFY(scene.isActive());
3171 QVERIFY(view->viewport()->hasFocus());
3172 QVERIFY(scene.hasFocus());
3173 QCOMPARE(scene.focusItem(), item);
3174 QVERIFY(item->hasFocus());
3175}
3176
3177class FocusWidget : public QGraphicsWidget
3178{
3179 Q_OBJECT
3180public:
3181 FocusWidget(QGraphicsItem *parent = nullptr) : QGraphicsWidget(parent)
3182 {
3183 setFocusPolicy(Qt::StrongFocus);
3184 resize(w: 100, h: 100);
3185 }
3186
3187 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) override
3188 {
3189 if (option->state & QStyle::State_HasFocus) {
3190 painter->fillRect(r: rect(), c: Qt::blue);
3191 }
3192 painter->setBrush(Qt::green);
3193 painter->drawEllipse(r: rect());
3194 if (option->state & QStyle::State_HasFocus) {
3195 painter->setPen(QPen(Qt::black, 1, Qt::DashLine));
3196 painter->setBrush(Qt::NoBrush);
3197 painter->drawEllipse(r: rect().adjusted(xp1: 5, yp1: 5, xp2: -5, yp2: -5));
3198 }
3199 }
3200
3201 int tabs = 0;
3202 int backTabs = 0;
3203
3204protected:
3205 bool sceneEvent(QEvent *event) override
3206 {
3207 if (event->type() == QEvent::KeyPress) {
3208 QKeyEvent *k = static_cast<QKeyEvent *>(event);
3209 if (k->key() == Qt::Key_Tab)
3210 ++tabs;
3211 if (k->key() == Qt::Key_Backtab)
3212 ++backTabs;
3213 }
3214 return QGraphicsWidget::sceneEvent(event);
3215 }
3216
3217 void focusInEvent(QFocusEvent *) override
3218 { update(); }
3219 void focusOutEvent(QFocusEvent *) override
3220 { update(); }
3221};
3222
3223void tst_QGraphicsScene::tabFocus_sceneWithFocusWidgets()
3224{
3225 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
3226 QSKIP("Window activation is not supported");
3227
3228 QGraphicsScene scene;
3229
3230 FocusWidget *widget1 = new FocusWidget;
3231 FocusWidget *widget2 = new FocusWidget;
3232 widget2->setPos(ax: widget1->boundingRect().right(), ay: 0);
3233 scene.addItem(item: widget1);
3234 scene.addItem(item: widget2);
3235
3236 QDial *dial1 = new QDial;
3237 QGraphicsView *view = new QGraphicsView(&scene);
3238 view->setRenderHint(hint: QPainter::Antialiasing);
3239 QDial *dial2 = new QDial;
3240
3241 QHBoxLayout *layout = new QHBoxLayout;
3242 layout->addWidget(dial1);
3243 layout->addWidget(view);
3244 layout->addWidget(dial2);
3245
3246 QWidget widget;
3247 widget.setWindowTitle(QTest::currentTestFunction());
3248 widget.setLayout(layout);
3249 widget.show();
3250 QApplication::setActiveWindow(&widget);
3251 widget.activateWindow();
3252 QVERIFY(QTest::qWaitForWindowActive(&widget));
3253
3254 dial1->setFocus();
3255 QTRY_VERIFY(dial1->hasFocus());
3256
3257 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3258 QApplication::processEvents();
3259 QTRY_VERIFY(view->hasFocus());
3260 QTRY_VERIFY(view->viewport()->hasFocus());
3261 QTRY_VERIFY(scene.hasFocus());
3262 QCOMPARE(widget1->tabs, 0);
3263 QVERIFY(widget1->hasFocus());
3264 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3265 QApplication::processEvents();
3266 QTRY_COMPARE(widget1->tabs, 1);
3267 QTRY_VERIFY(widget2->hasFocus());
3268 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3269 QApplication::processEvents();
3270 QTRY_COMPARE(widget2->tabs, 1);
3271 QTRY_VERIFY(dial2->hasFocus());
3272 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3273 QApplication::processEvents();
3274 QTRY_VERIFY(widget2->hasFocus());
3275 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3276 QApplication::processEvents();
3277 QTRY_COMPARE(widget2->backTabs, 1);
3278 QTRY_VERIFY(widget1->hasFocus());
3279 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3280 QApplication::processEvents();
3281 QTRY_COMPARE(widget1->backTabs, 1);
3282 QTRY_VERIFY(dial1->hasFocus());
3283
3284 widget1->setFocus();
3285 view->viewport()->setFocus();
3286 widget.hide();
3287 QTest::qWait(ms: 15);
3288 widget.show();
3289 QApplication::setActiveWindow(&widget);
3290 widget.activateWindow();
3291 QVERIFY(QTest::qWaitForWindowActive(&widget));
3292 QTRY_VERIFY(widget1->hasFocus());
3293}
3294
3295void tst_QGraphicsScene::tabFocus_sceneWithNestedFocusWidgets()
3296{
3297 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
3298 QSKIP("Window activation is not supported");
3299
3300 QGraphicsScene scene;
3301
3302 FocusWidget *widget1 = new FocusWidget;
3303 FocusWidget *widget1_1 = new FocusWidget;
3304 FocusWidget *widget1_2 = new FocusWidget;
3305 widget1_1->setParentItem(widget1);
3306 const QTransform scale(QTransform::fromScale(dx: 0.5, dy: 0.5));
3307 widget1_1->setTransform(matrix: scale, combine: true);
3308 widget1_1->setPos(ax: 0, ay: widget1->boundingRect().height() / 2);
3309 widget1_2->setParentItem(widget1);
3310 widget1_2->setTransform(matrix: scale, combine: true);
3311 widget1_2->setPos(ax: widget1->boundingRect().width() / 2, ay: widget1->boundingRect().height() / 2);
3312
3313 FocusWidget *widget2 = new FocusWidget;
3314 widget2->setPos(ax: widget1->boundingRect().right(), ay: 0);
3315
3316 widget1->setData(key: 0, value: "widget1");
3317 widget1_1->setData(key: 0, value: "widget1_1");
3318 widget1_2->setData(key: 0, value: "widget1_2");
3319 widget2->setData(key: 0, value: "widget2");
3320
3321 scene.addItem(item: widget1);
3322 scene.addItem(item: widget2);
3323
3324 QDial *dial1 = new QDial;
3325 QGraphicsView *view = new QGraphicsView(&scene);
3326 view->setRenderHint(hint: QPainter::Antialiasing);
3327 QDial *dial2 = new QDial;
3328
3329 QHBoxLayout *layout = new QHBoxLayout;
3330 layout->addWidget(dial1);
3331 layout->addWidget(view);
3332 layout->addWidget(dial2);
3333
3334 QWidget widget;
3335 widget.setWindowTitle(QTest::currentTestFunction());
3336 widget.setLayout(layout);
3337 widget.show();
3338 QApplication::setActiveWindow(&widget);
3339 widget.activateWindow();
3340 QVERIFY(QTest::qWaitForWindowActive(&widget));
3341
3342 dial1->setFocus();
3343 QTRY_VERIFY(dial1->hasFocus());
3344
3345 EventSpy focusInSpy_1(widget1, QEvent::FocusIn);
3346 EventSpy focusOutSpy_1(widget1, QEvent::FocusOut);
3347 EventSpy focusInSpy_1_1(widget1_1, QEvent::FocusIn);
3348 EventSpy focusOutSpy_1_1(widget1_1, QEvent::FocusOut);
3349 EventSpy focusInSpy_1_2(widget1_2, QEvent::FocusIn);
3350 EventSpy focusOutSpy_1_2(widget1_2, QEvent::FocusOut);
3351 EventSpy focusInSpy_2(widget2, QEvent::FocusIn);
3352 EventSpy focusOutSpy_2(widget2, QEvent::FocusOut);
3353
3354 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3355 QApplication::processEvents();
3356 QTRY_VERIFY(widget1->hasFocus());
3357 QCOMPARE(focusInSpy_1.count(), 1);
3358
3359 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3360 QApplication::processEvents();
3361 QTRY_VERIFY(!widget1->hasFocus());
3362 QVERIFY(widget1_1->hasFocus());
3363 QCOMPARE(focusOutSpy_1.count(), 1);
3364 QCOMPARE(focusInSpy_1_1.count(), 1);
3365
3366 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3367 QApplication::processEvents();
3368 QTRY_VERIFY(!widget1_1->hasFocus());
3369 QVERIFY(widget1_2->hasFocus());
3370 QCOMPARE(focusOutSpy_1_1.count(), 1);
3371 QCOMPARE(focusInSpy_1_2.count(), 1);
3372
3373 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3374 QApplication::processEvents();
3375 QTRY_VERIFY(!widget1_2->hasFocus());
3376 QVERIFY(widget2->hasFocus());
3377 QCOMPARE(focusOutSpy_1_2.count(), 1);
3378 QCOMPARE(focusInSpy_2.count(), 1);
3379
3380 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
3381 QApplication::processEvents();
3382 QTRY_VERIFY(!widget2->hasFocus());
3383 QVERIFY(dial2->hasFocus());
3384 QCOMPARE(focusOutSpy_2.count(), 1);
3385
3386 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3387 QApplication::processEvents();
3388 QTRY_VERIFY(widget2->hasFocus());
3389 QCOMPARE(focusInSpy_2.count(), 2);
3390
3391 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3392 QApplication::processEvents();
3393 QTRY_VERIFY(!widget2->hasFocus());
3394 QTRY_VERIFY(widget1_2->hasFocus());
3395 QCOMPARE(focusOutSpy_2.count(), 2);
3396 QCOMPARE(focusInSpy_1_2.count(), 2);
3397
3398 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3399 QApplication::processEvents();
3400 QTRY_VERIFY(!widget1_2->hasFocus());
3401 QTRY_VERIFY(widget1_1->hasFocus());
3402 QCOMPARE(focusOutSpy_1_2.count(), 2);
3403 QCOMPARE(focusInSpy_1_1.count(), 2);
3404
3405 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3406 QApplication::processEvents();
3407 QTRY_VERIFY(!widget1_1->hasFocus());
3408 QTRY_VERIFY(widget1->hasFocus());
3409 QCOMPARE(focusOutSpy_1_1.count(), 2);
3410 QCOMPARE(focusInSpy_1.count(), 2);
3411
3412 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Backtab);
3413 QApplication::processEvents();
3414 QTRY_VERIFY(!widget1->hasFocus());
3415 QTRY_VERIFY(dial1->hasFocus());
3416 QCOMPARE(focusOutSpy_1.count(), 2);
3417
3418 widget1->setFocus();
3419 view->viewport()->setFocus();
3420 widget.hide();
3421 QTest::qWait(ms: 12);
3422 widget.show();
3423 QApplication::setActiveWindow(&widget);
3424 widget.activateWindow();
3425 QVERIFY(QTest::qWaitForWindowActive(&widget));
3426 QTRY_VERIFY(widget1->hasFocus());
3427}
3428
3429void tst_QGraphicsScene::style()
3430{
3431 QPointer<QStyle> windowsStyle = QStyleFactory::create("windows");
3432
3433 QGraphicsScene scene;
3434 QLineEdit *edit = new QLineEdit;
3435 QGraphicsProxyWidget *proxy = scene.addWidget(widget: edit);
3436
3437 EventSpy sceneSpy(&scene, QEvent::StyleChange);
3438 EventSpy proxySpy(proxy, QEvent::StyleChange);
3439 EventSpy editSpy(edit, QEvent::StyleChange);
3440
3441 QCOMPARE(scene.style(), QApplication::style());
3442
3443 scene.setStyle(windowsStyle);
3444 QCOMPARE(sceneSpy.count(), 1);
3445 QCOMPARE(proxySpy.count(), 1);
3446 QCOMPARE(editSpy.count(), 1);
3447 QCOMPARE(scene.style(), windowsStyle.data());
3448 QCOMPARE(proxy->style(), windowsStyle.data());
3449 QCOMPARE(edit->style(), windowsStyle.data());
3450
3451 scene.setStyle(nullptr);
3452 QCOMPARE(sceneSpy.count(), 2);
3453 QCOMPARE(proxySpy.count(), 2);
3454 QCOMPARE(editSpy.count(), 2);
3455 QCOMPARE(scene.style(), QApplication::style());
3456 QCOMPARE(proxy->style(), QApplication::style());
3457 QCOMPARE(edit->style(), QApplication::style());
3458 QVERIFY(!windowsStyle); // deleted
3459}
3460
3461void tst_QGraphicsScene::task139710_bspTreeCrash()
3462{
3463 // create a scene with 2000 items
3464 QGraphicsScene scene(0, 0, 1000, 1000);
3465
3466 for (int i = 0; i < 2; ++i) {
3467 // trigger delayed item indexing
3468 QCoreApplication::processEvents();
3469 scene.setSceneRect(x: 0, y: 0, w: 10000, h: 10000);
3470
3471 // delete all items in the scene - pointers are now likely to be recycled
3472 const auto &items = scene.items();
3473 for (QGraphicsItem *item : items) {
3474 scene.removeItem(item);
3475 delete item;
3476 }
3477
3478 // add 1000 more items - the BSP tree is now resized
3479 for (int i = 0; i < 1000; ++i) {
3480 QGraphicsRectItem *item = scene.addRect(rect: QRectF(0, 0, 200, 200));
3481 item->setPos(ax: QRandomGenerator::global()->bounded(highest: 10000), ay: QRandomGenerator::global()->bounded(highest: 10000));
3482 }
3483
3484 // trigger delayed item indexing for the first 1000 items
3485 QCoreApplication::processEvents();
3486
3487 // add 1000 more items - the BSP tree is now resized
3488 for (int i = 0; i < 1000; ++i) {
3489 QGraphicsRectItem *item = scene.addRect(rect: QRectF(0, 0, 200, 200));
3490 item->setPos(ax: QRandomGenerator::global()->bounded(highest: 10000), ay: QRandomGenerator::global()->bounded(highest: 10000));
3491 }
3492
3493 // get items from the BSP tree and use them. there was junk in the tree
3494 // the second time this happened.
3495 const auto &itemsWithin = scene.items(rect: QRectF(0, 0, 1000, 1000));
3496 for (QGraphicsItem *item : itemsWithin)
3497 item->moveBy(dx: 0, dy: 0);
3498 }
3499}
3500
3501void tst_QGraphicsScene::task139782_containsItemBoundingRect()
3502{
3503 // The item in question has a scene bounding rect of (10, 10, 50, 50)
3504 QGraphicsScene scene(0.0, 0.0, 200.0, 200.0);
3505 QGraphicsRectItem *item = new QGraphicsRectItem(0.0, 0.0, 50.0, 50.0, nullptr);
3506 scene.addItem(item);
3507 item->setPos(ax: 10.0, ay: 10.0);
3508
3509 // The (0, 0, 50, 50) scene rect should not include the item's bounding rect
3510 QVERIFY(!scene.items(QRectF(0.0, 0.0, 50.0, 50.0), Qt::ContainsItemBoundingRect).contains(item));
3511
3512 // The (9, 9, 500, 500) scene rect _should_ include the item's bounding rect
3513 QVERIFY(scene.items(QRectF(9.0, 9.0, 500.0, 500.0), Qt::ContainsItemBoundingRect).contains(item));
3514
3515 // The (25, 25, 5, 5) scene rect should not include the item's bounding rect
3516 QVERIFY(!scene.items(QRectF(25.0, 25.0, 5.0, 5.0), Qt::ContainsItemBoundingRect).contains(item));
3517}
3518
3519void tst_QGraphicsScene::task176178_itemIndexMethodBreaksSceneRect()
3520{
3521 QGraphicsScene scene;
3522 scene.setItemIndexMethod(QGraphicsScene::NoIndex);
3523 QGraphicsRectItem *rect = new QGraphicsRectItem;
3524 rect->setPen(QPen(Qt::black, 0));
3525 rect->setRect(ax: 0,ay: 0,w: 100,h: 100);
3526 scene.addItem(item: rect);
3527 QCOMPARE(scene.sceneRect(), rect->rect());
3528}
3529
3530void tst_QGraphicsScene::task160653_selectionChanged()
3531{
3532 QGraphicsScene scene(0, 0, 100, 100);
3533 scene.addItem(item: new QGraphicsRectItem(0, 0, 20, 20));
3534 scene.addItem(item: new QGraphicsRectItem(30, 30, 20, 20));
3535 const auto &items = scene.items();
3536 for (QGraphicsItem *item : items) {
3537 item->setFlags(
3538 item->flags() | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
3539 item->setSelected(true);
3540 }
3541 QVERIFY(scene.items().size() > 1);
3542 QCOMPARE(scene.items().size(), scene.selectedItems().size());
3543
3544 QSignalSpy spy(&scene, &QGraphicsScene::selectionChanged);
3545 QGraphicsView view(&scene);
3546 view.setWindowTitle(QTest::currentTestFunction());
3547 view.resize(m_testSize);
3548 view.show();
3549 QVERIFY(QTest::qWaitForWindowActive(&view));
3550 QTest::mouseClick(
3551 widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: scene.items().first()->scenePos()));
3552 QCOMPARE(spy.count(), 1);
3553}
3554
3555void tst_QGraphicsScene::task250680_childClip()
3556{
3557 QGraphicsRectItem *clipper = new QGraphicsRectItem;
3558 clipper->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
3559 clipper->setPen(QPen(Qt::green, 0));
3560 clipper->setRect(ax: 200, ay: 200, w: 640, h: 480);
3561
3562 QGraphicsRectItem *rect = new QGraphicsRectItem(clipper);
3563 rect->setPen(QPen(Qt::red, 0));
3564 rect->setBrush(QBrush(QColor(255, 0, 0, 75)));
3565 rect->setPos(ax: 320, ay: 240);
3566 rect->setRect(ax: -25, ay: -25, w: 50, h: 50);
3567
3568 QGraphicsScene scene;
3569 scene.addItem(item: clipper);
3570
3571 QPainterPath path;
3572 path.addRect(x: -25, y: -25, w: 50, h: 50);
3573 QVERIFY(QPathCompare::comparePaths(rect->clipPath().simplified(), path));
3574
3575 QCOMPARE(scene.items(QRectF(320, 240, 5, 5)).size(), 2);
3576 rect->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
3577 QCOMPARE(scene.items(QRectF(320, 240, 5, 5)).size(), 2);
3578}
3579
3580void tst_QGraphicsScene::sorting()
3581{
3582 QGraphicsScene scene;
3583
3584 QGraphicsRectItem *t_1 = new QGraphicsRectItem(0, 0, 50, 50);
3585 QGraphicsRectItem *c_1 = new QGraphicsRectItem(0, 0, 40, 40, t_1);
3586 QGraphicsRectItem *c_1_1 = new QGraphicsRectItem(0, 0, 30, 30, c_1);
3587 QGraphicsRectItem *c_1_1_1 = new QGraphicsRectItem(0, 0, 20, 20, c_1_1);
3588 QGraphicsRectItem *c_1_2 = new QGraphicsRectItem(0, 0, 30, 30, c_1);
3589 QGraphicsRectItem *c_2 = new QGraphicsRectItem(0, 0, 40, 40, t_1);
3590 QGraphicsRectItem *c_2_1 = new QGraphicsRectItem(0, 0, 30, 30, c_2);
3591 QGraphicsRectItem *c_2_1_1 = new QGraphicsRectItem(0, 0, 20, 20, c_2_1);
3592 QGraphicsRectItem *c_2_2 = new QGraphicsRectItem(0, 0, 30, 30, c_2);
3593 t_1->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3594 c_1->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3595 c_1_1->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3596 c_1_1_1->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3597 c_1_2->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3598 c_2->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3599 c_2_1->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3600 c_2_1_1->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3601 c_2_2->setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
3602
3603 c_1->setPos(ax: 23, ay: 18);
3604 c_1_1->setPos(ax: 24, ay: 28);
3605 c_1_1_1->setPos(ax: -16, ay: 16);
3606 c_1_2->setPos(ax: -16, ay: 28);
3607 c_1_2->setZValue(1);
3608 c_2->setPos(ax: -23, ay: 18);
3609 c_2->setZValue(1);
3610 c_2_1->setPos(ax: 24, ay: 28);
3611 c_2_1_1->setPos(ax: -16, ay: 16);
3612 c_2_2->setPos(ax: -16, ay: 28);
3613 c_2_2->setZValue(1);
3614
3615 c_1->setFlag(flag: QGraphicsItem::ItemIsMovable);
3616 c_1_1->setFlag(flag: QGraphicsItem::ItemIsMovable);
3617 c_1_1_1->setFlag(flag: QGraphicsItem::ItemIsMovable);
3618 c_1_2->setFlag(flag: QGraphicsItem::ItemIsMovable);
3619 c_2->setFlag(flag: QGraphicsItem::ItemIsMovable);
3620 c_2_1->setFlag(flag: QGraphicsItem::ItemIsMovable);
3621 c_2_1_1->setFlag(flag: QGraphicsItem::ItemIsMovable);
3622 c_2_2->setFlag(flag: QGraphicsItem::ItemIsMovable);
3623
3624 t_1->setData(key: 0, value: "t_1");
3625 c_1->setData(key: 0, value: "c_1");
3626 c_1_1->setData(key: 0, value: "c_1_1");
3627 c_1_1_1->setData(key: 0, value: "c_1_1_1");
3628 c_1_2->setData(key: 0, value: "c_1_2");
3629 c_2->setData(key: 0, value: "c_2");
3630 c_2_1->setData(key: 0, value: "c_2_1");
3631 c_2_1_1->setData(key: 0, value: "c_2_1_1");
3632 c_2_2->setData(key: 0, value: "c_2_2");
3633
3634 scene.addItem(item: t_1);
3635
3636 const auto &items = scene.items();
3637 for (QGraphicsItem *item : items)
3638 item->setFlag(flag: QGraphicsItem::ItemIsSelectable);
3639
3640 // QGraphicsView view(&scene);
3641 // view.setDragMode(QGraphicsView::RubberBandDrag);
3642 // view.show();
3643
3644 if (lcTests().isDebugEnabled()) {
3645 qCDebug(lcTests) << "items: {";
3646 const auto &itemsWithin = scene.items(rect: QRectF(32, 31, 4, 55));
3647 for (QGraphicsItem *item : itemsWithin)
3648 qCDebug(lcTests).nospace() << '\t' << item->data(key: 0).toString();
3649 qCDebug(lcTests) << '}';
3650 }
3651
3652 QCOMPARE(scene.items(QRectF(32, 31, 4, 55)),
3653 QList<QGraphicsItem *>()
3654 << c_1_2 << c_1_1_1 << c_1 << t_1);
3655 QCOMPARE(scene.items(QRectF(-53, 47, 136, 3)),
3656 QList<QGraphicsItem *>()
3657 << c_2_2 << c_2_1 << c_2 << c_1_2 << c_1_1 << c_1 << t_1);
3658 QCOMPARE(scene.items(QRectF(-23, 79, 104, 3)),
3659 QList<QGraphicsItem *>()
3660 << c_2_1_1 << c_1_1_1);
3661 QCOMPARE(scene.items(QRectF(-26, -3, 92, 79)),
3662 QList<QGraphicsItem *>()
3663 << c_2_2 << c_2_1_1 << c_2_1 << c_2
3664 << c_1_2 << c_1_1_1 << c_1_1 << c_1
3665 << t_1);
3666}
3667
3668void tst_QGraphicsScene::insertionOrder()
3669{
3670 QGraphicsScene scene;
3671 const int numItems = 5;
3672 QList<QGraphicsItem*> items;
3673
3674 for (int i = 0; i < numItems; ++i) {
3675 QGraphicsRectItem* item = new QGraphicsRectItem(i * 20, i * 20, 200, 200);
3676 item->setData(key: 0, value: i);
3677 items.append(t: item);
3678 scene.addItem(item);
3679 }
3680
3681 {
3682 QList<QGraphicsItem*> itemList = scene.items();
3683 QCOMPARE(itemList.count(), numItems);
3684 for (int i = 0; i < itemList.count(); ++i) {
3685 QCOMPARE(numItems-1-i, itemList.at(i)->data(0).toInt());
3686 }
3687 }
3688
3689 for (int i = 0; i < items.size(); ++i)
3690 {
3691 scene.removeItem(item: items.at(i));
3692 scene.addItem(item: items.at(i));
3693 }
3694
3695 {
3696 QList<QGraphicsItem*> itemList = scene.items();
3697 QCOMPARE(itemList.count(), numItems);
3698 for (int i = 0; i < itemList.count(); ++i) {
3699 QCOMPARE(numItems-1-i, itemList.at(i)->data(0).toInt());
3700 }
3701 }
3702}
3703
3704class ChangedListener : public QObject
3705{
3706 Q_OBJECT
3707public:
3708 QList<QList<QRectF> > changes;
3709
3710public slots:
3711 void changed(const QList<QRectF> &dirty)
3712 {
3713 changes << dirty;
3714 }
3715};
3716
3717void tst_QGraphicsScene::changedSignal_data()
3718{
3719 QTest::addColumn<bool>(name: "withView");
3720
3721 QTest::newRow(dataTag: "without view") << false;
3722 QTest::newRow(dataTag: "with view") << true;
3723}
3724
3725void tst_QGraphicsScene::changedSignal()
3726{
3727 QFETCH(bool, withView);
3728 QGraphicsScene scene;
3729 ChangedListener cl;
3730 connect(sender: &scene, signal: &QGraphicsScene::changed, receiver: &cl, slot: &ChangedListener::changed);
3731
3732 QScopedPointer<QGraphicsView> view;
3733 if (withView)
3734 view.reset(other: new QGraphicsView(&scene));
3735
3736 QGraphicsRectItem *rect = new QGraphicsRectItem(0, 0, 10, 10);
3737 rect->setPen(QPen(Qt::black, 0));
3738 scene.addItem(item: rect);
3739
3740 QCOMPARE(cl.changes.size(), 0);
3741 QTRY_COMPARE(cl.changes.size(), 1);
3742 QCOMPARE(cl.changes.at(0).size(), 1);
3743 QCOMPARE(cl.changes.at(0).first(), QRectF(0, 0, 10, 10));
3744
3745 rect->setPos(ax: 20, ay: 0);
3746
3747 QCOMPARE(cl.changes.size(), 1);
3748 QCoreApplication::processEvents();
3749 QCOMPARE(cl.changes.size(), 2);
3750 QCOMPARE(cl.changes.at(1).size(), 2);
3751
3752 QCOMPARE(scene.sceneRect(), QRectF(0, 0, 30, 10));
3753}
3754
3755void tst_QGraphicsScene::stickyFocus_data()
3756{
3757 QTest::addColumn<bool>(name: "sticky");
3758 QTest::newRow(dataTag: "sticky") << true;
3759 QTest::newRow(dataTag: "not sticky") << false;
3760}
3761
3762void tst_QGraphicsScene::stickyFocus()
3763{
3764 QFETCH(bool, sticky);
3765
3766 QGraphicsScene scene;
3767 QEvent activate(QEvent::WindowActivate);
3768 QApplication::sendEvent(receiver: &scene, event: &activate);
3769
3770 QGraphicsTextItem *text = scene.addText(text: "Hei");
3771 text->setTextInteractionFlags(Qt::TextEditorInteraction);
3772 text->setFocus();
3773
3774 scene.setStickyFocus(sticky);
3775
3776 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
3777 event.setScenePos(QPointF(-10, -10)); // outside item
3778 event.setButton(Qt::LeftButton);
3779 QCoreApplication::sendEvent(receiver: &scene, event: &event);
3780
3781 QCOMPARE(text->hasFocus(), sticky);
3782}
3783
3784void tst_QGraphicsScene::sendEvent()
3785{
3786 QGraphicsScene scene;
3787 QGraphicsTextItem *item = scene.addText(text: QString());
3788 EventSpy *spy = new EventSpy(&scene, item, QEvent::User);
3789 QCOMPARE(spy->count(), 0);
3790 QEvent event(QEvent::User);
3791 scene.sendEvent(item, event: &event);
3792 QCOMPARE(spy->count(), 1);
3793}
3794
3795void tst_QGraphicsScene::inputMethod_data()
3796{
3797 QTest::addColumn<QGraphicsItem::GraphicsItemFlags>(name: "flags");
3798 QTest::addColumn<bool>(name: "callFocusItem");
3799 QTest::newRow(dataTag: "0") << QGraphicsItem::GraphicsItemFlags() << false;
3800 QTest::newRow(dataTag: "1") << QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemAcceptsInputMethod) << false;
3801 QTest::newRow(dataTag: "2") << QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemIsFocusable) << false;
3802 QTest::newRow(dataTag: "3") <<
3803 (QGraphicsItem::ItemAcceptsInputMethod|QGraphicsItem::ItemIsFocusable) << true;
3804}
3805
3806class InputMethodTester : public QGraphicsRectItem
3807{
3808 void inputMethodEvent(QInputMethodEvent *) override { ++eventCalls; }
3809 QVariant inputMethodQuery(Qt::InputMethodQuery) const override
3810 {
3811 ++queryCalls;
3812 return QVariant();
3813 }
3814
3815public:
3816 int eventCalls = 0;
3817 mutable int queryCalls = 0;
3818};
3819
3820void tst_QGraphicsScene::inputMethod()
3821{
3822 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3823 QSKIP("Wayland: This fails. Figure out why.");
3824
3825 PlatformInputContext inputContext;
3826 QInputMethodPrivate *inputMethodPrivate =
3827 QInputMethodPrivate::get(inputMethod: QGuiApplication::inputMethod());
3828 inputMethodPrivate->testContext = &inputContext;
3829
3830 QFETCH(QGraphicsItem::GraphicsItemFlags, flags);
3831 QFETCH(bool, callFocusItem);
3832
3833 InputMethodTester *item = new InputMethodTester;
3834 item->setFlags(flags);
3835
3836 QGraphicsScene scene;
3837 QGraphicsView view(&scene);
3838 view.resize(m_testSize);
3839 view.show();
3840 view.setWindowTitle(QTest::currentTestFunction());
3841 QApplication::setActiveWindow(&view);
3842 view.setFocus();
3843 QVERIFY(QTest::qWaitForWindowActive(&view));
3844 QCOMPARE(QApplication::activeWindow(), &view);
3845
3846 inputContext.m_resetCallCount = 0;
3847 inputContext.m_commitCallCount = 0;
3848 scene.addItem(item);
3849 QInputMethodEvent event;
3850
3851 scene.setFocusItem(item);
3852 QCOMPARE(!!(item->flags() & QGraphicsItem::ItemIsFocusable), scene.focusItem() == item);
3853 QCOMPARE(inputContext.m_resetCallCount, 0);
3854
3855 item->eventCalls = 0;
3856 QCoreApplication::sendEvent(receiver: &scene, event: &event);
3857 QCOMPARE(item->eventCalls, callFocusItem ? 1 : 0);
3858
3859 item->queryCalls = 0;
3860 scene.inputMethodQuery(query: Qt::InputMethodQuery(0));
3861 QCOMPARE(item->queryCalls, callFocusItem ? 1 : 0);
3862
3863 scene.setFocusItem(item: nullptr);
3864 // the input context is reset twice, once because an item has lost focus and again because
3865 // the Qt::WA_InputMethodEnabled flag is cleared because no item has focus.
3866 QCOMPARE(inputContext.m_resetCallCount + inputContext.m_commitCallCount, callFocusItem ? 2 : 0);
3867 QCOMPARE(item->queryCalls, callFocusItem ? 1 : 0); // verify that value is unaffected
3868
3869 item->eventCalls = 0;
3870 QCoreApplication::sendEvent(receiver: &scene, event: &event);
3871 QCOMPARE(item->eventCalls, 0);
3872
3873 item->queryCalls = 0;
3874 scene.inputMethodQuery(query: Qt::InputMethodQuery(0));
3875 QCOMPARE(item->queryCalls, 0);
3876}
3877
3878void tst_QGraphicsScene::dispatchHoverOnPress()
3879{
3880 QGraphicsScene scene;
3881 EventTester *tester1 = new EventTester;
3882 tester1->setAcceptHoverEvents(true);
3883 EventTester *tester2 = new EventTester;
3884 tester2->setAcceptHoverEvents(true);
3885 tester2->setPos(ax: 30, ay: 30);
3886 scene.addItem(item: tester1);
3887 scene.addItem(item: tester2);
3888
3889 tester1->eventTypes.clear();
3890 tester2->eventTypes.clear();
3891
3892 {
3893 QGraphicsSceneMouseEvent me(QEvent::GraphicsSceneMousePress);
3894 me.setButton(Qt::LeftButton);
3895 me.setButtons(Qt::LeftButton);
3896 QGraphicsSceneMouseEvent me2(QEvent::GraphicsSceneMouseRelease);
3897 me2.setButton(Qt::LeftButton);
3898 QCoreApplication::sendEvent(receiver: &scene, event: &me);
3899 QCoreApplication::sendEvent(receiver: &scene, event: &me2);
3900 QCOMPARE(tester1->eventTypes, QVector<QEvent::Type>()
3901 << QEvent::GraphicsSceneHoverEnter
3902 << QEvent::GraphicsSceneHoverMove
3903 << QEvent::GrabMouse
3904 << QEvent::GraphicsSceneMousePress
3905 << QEvent::UngrabMouse);
3906 tester1->eventTypes.clear();
3907 QCoreApplication::sendEvent(receiver: &scene, event: &me);
3908 QCoreApplication::sendEvent(receiver: &scene, event: &me2);
3909 QCOMPARE(tester1->eventTypes, QVector<QEvent::Type>()
3910 << QEvent::GraphicsSceneHoverMove
3911 << QEvent::GrabMouse
3912 << QEvent::GraphicsSceneMousePress
3913 << QEvent::UngrabMouse);
3914 }
3915 {
3916 QGraphicsSceneMouseEvent me(QEvent::GraphicsSceneMousePress);
3917 me.setScenePos(QPointF(30, 30));
3918 me.setButton(Qt::LeftButton);
3919 me.setButtons(Qt::LeftButton);
3920 QGraphicsSceneMouseEvent me2(QEvent::GraphicsSceneMouseRelease);
3921 me2.setScenePos(QPointF(30, 30));
3922 me2.setButton(Qt::LeftButton);
3923 tester1->eventTypes.clear();
3924 QCoreApplication::sendEvent(receiver: &scene, event: &me);
3925 QCoreApplication::sendEvent(receiver: &scene, event: &me2);
3926 qCDebug(lcTests) << tester1->eventTypes;
3927 QCOMPARE(tester1->eventTypes, QVector<QEvent::Type>()
3928 << QEvent::GraphicsSceneHoverLeave);
3929 QCOMPARE(tester2->eventTypes, QVector<QEvent::Type>()
3930 << QEvent::GraphicsSceneHoverEnter
3931 << QEvent::GraphicsSceneHoverMove
3932 << QEvent::GrabMouse
3933 << QEvent::GraphicsSceneMousePress
3934 << QEvent::UngrabMouse);
3935 tester2->eventTypes.clear();
3936 QCoreApplication::sendEvent(receiver: &scene, event: &me);
3937 QCoreApplication::sendEvent(receiver: &scene, event: &me2);
3938 QCOMPARE(tester2->eventTypes, QVector<QEvent::Type>()
3939 << QEvent::GraphicsSceneHoverMove
3940 << QEvent::GrabMouse
3941 << QEvent::GraphicsSceneMousePress
3942 << QEvent::UngrabMouse);
3943 }
3944}
3945
3946void tst_QGraphicsScene::initialFocus_data()
3947{
3948 QTest::addColumn<bool>(name: "activeScene");
3949 QTest::addColumn<bool>(name: "explicitSetFocus");
3950 QTest::addColumn<bool>(name: "isPanel");
3951 QTest::addColumn<bool>(name: "shouldHaveFocus");
3952
3953 QTest::newRow(dataTag: "inactive scene, normal item") << false << false << false << false;
3954 QTest::newRow(dataTag: "inactive scene, panel item") << false << false << true << true;
3955 QTest::newRow(dataTag: "inactive scene, normal item, explicit focus") << false << true << false << true;
3956 QTest::newRow(dataTag: "inactive scene, panel, explicit focus") << false << true << true << true;
3957 QTest::newRow(dataTag: "active scene, normal item") << true << false << false << false;
3958 QTest::newRow(dataTag: "active scene, panel item") << true << false << true << true;
3959 QTest::newRow(dataTag: "active scene, normal item, explicit focus") << true << true << false << true;
3960 QTest::newRow(dataTag: "active scene, panel, explicit focus") << true << true << true << true;
3961}
3962
3963void tst_QGraphicsScene::initialFocus()
3964{
3965 QFETCH(bool, activeScene);
3966 QFETCH(bool, explicitSetFocus);
3967 QFETCH(bool, isPanel);
3968 QFETCH(bool, shouldHaveFocus);
3969
3970 QGraphicsRectItem *rect = new QGraphicsRectItem;
3971 rect->setFlag(flag: QGraphicsItem::ItemIsFocusable);
3972 QVERIFY(!rect->hasFocus());
3973
3974 if (isPanel)
3975 rect->setFlag(flag: QGraphicsItem::ItemIsPanel);
3976
3977 // Setting focus on an item before adding to the scene will ensure
3978 // it gets focus when the scene is activated.
3979 if (explicitSetFocus)
3980 rect->setFocus();
3981
3982 QGraphicsScene scene;
3983 QVERIFY(!scene.isActive());
3984
3985 if (activeScene) {
3986 QEvent windowActivate(QEvent::WindowActivate);
3987 QCoreApplication::sendEvent(receiver: &scene, event: &windowActivate);
3988 scene.setFocus();
3989 }
3990
3991 scene.addItem(item: rect);
3992
3993 if (!activeScene) {
3994 QEvent windowActivate(QEvent::WindowActivate);
3995 QCoreApplication::sendEvent(receiver: &scene, event: &windowActivate);
3996 scene.setFocus();
3997 }
3998
3999 QCOMPARE(rect->hasFocus(), shouldHaveFocus);
4000}
4001
4002class PolishItem : public QGraphicsTextItem
4003{
4004public:
4005 using QGraphicsTextItem::QGraphicsTextItem;
4006
4007 bool polished = false;
4008 bool deleteChildrenInPolish = true;
4009 bool addChildrenInPolish = false;
4010protected:
4011 QVariant itemChange(GraphicsItemChange change, const QVariant& value) override
4012 {
4013 if (change == ItemVisibleChange) {
4014 polished = true;
4015 if (deleteChildrenInPolish)
4016 qDeleteAll(c: childItems());
4017 if (addChildrenInPolish) {
4018 for (int i = 0; i < 10; ++i)
4019 new PolishItem(this);
4020 }
4021 }
4022 return QGraphicsItem::itemChange(change, value);
4023 }
4024};
4025
4026void tst_QGraphicsScene::polishItems()
4027{
4028 QGraphicsScene scene;
4029 PolishItem *parent = new PolishItem;
4030 scene.addItem(item: parent);
4031 PolishItem *child = new PolishItem(parent);
4032 Q_UNUSED(child)
4033 // test that QGraphicsScenePrivate::_q_polishItems() doesn't crash
4034 QMetaObject::invokeMethod(obj: &scene,member: "_q_polishItems");
4035}
4036
4037void tst_QGraphicsScene::polishItems2()
4038{
4039 QGraphicsScene scene;
4040 PolishItem *item = new PolishItem;
4041 item->addChildrenInPolish = true;
4042 item->deleteChildrenInPolish = true;
4043 // These children should be deleted in the polish.
4044 for (int i = 0; i < 20; ++i)
4045 new PolishItem(item);
4046 scene.addItem(item);
4047
4048 // Wait for the polish event to be delivered.
4049 QVERIFY(!item->polished);
4050 QCoreApplication::sendPostedEvents(receiver: &scene, event_type: QEvent::MetaCall);
4051 QVERIFY(item->polished);
4052
4053 // We deleted the children we added above, but we also
4054 // added 10 new children. These should be polished in the next
4055 // event loop iteration.
4056 const QList<QGraphicsItem *> children = item->childItems();
4057 QCOMPARE(children.count(), 10);
4058 for (QGraphicsItem *child : children)
4059 QVERIFY(!static_cast<PolishItem *>(child)->polished);
4060
4061 QCoreApplication::sendPostedEvents(receiver: &scene, event_type: QEvent::MetaCall);
4062 for (QGraphicsItem *child : children)
4063 QVERIFY(static_cast<PolishItem *>(child)->polished);
4064}
4065
4066void tst_QGraphicsScene::isActive()
4067{
4068 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
4069 QSKIP("Window activation is not supported");
4070
4071#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
4072 QSKIP("Fails on Android (QTBUG-44430)");
4073#endif
4074
4075 QGraphicsScene scene1;
4076 QVERIFY(!scene1.isActive());
4077 QGraphicsScene scene2;
4078 QVERIFY(!scene2.isActive());
4079
4080 {
4081 QWidget toplevel1;
4082 toplevel1.setWindowTitle(QTest::currentTestFunction());
4083 toplevel1.resize(m_testSize);
4084 QHBoxLayout *layout = new QHBoxLayout;
4085 toplevel1.setLayout(layout);
4086 QGraphicsView *view1 = new QGraphicsView(&scene1);
4087 QGraphicsView *view2 = new QGraphicsView(&scene2);
4088 layout->addWidget(view1);
4089 layout->addWidget(view2);
4090
4091 QVERIFY(!scene1.isActive());
4092 QVERIFY(!scene2.isActive());
4093
4094 view1->setVisible(false);
4095
4096 toplevel1.show();
4097 QApplication::setActiveWindow(&toplevel1);
4098 QVERIFY(QTest::qWaitForWindowActive(&toplevel1));
4099 QCOMPARE(QApplication::activeWindow(), &toplevel1);
4100
4101 QVERIFY(!scene1.isActive()); //it is hidden;
4102 QVERIFY(scene2.isActive());
4103 QVERIFY(!scene1.hasFocus());
4104 QVERIFY(scene2.hasFocus());
4105
4106 view1->show();
4107 QVERIFY(scene1.isActive());
4108 QVERIFY(scene2.isActive());
4109 QVERIFY(!scene1.hasFocus());
4110 QVERIFY(scene2.hasFocus());
4111
4112 view2->hide();
4113
4114 QVERIFY(scene1.isActive());
4115 QVERIFY(!scene2.isActive());
4116 QVERIFY(scene1.hasFocus());
4117 QVERIFY(!scene2.hasFocus());
4118
4119 toplevel1.hide();
4120 QTRY_VERIFY(!scene1.isActive());
4121 QTRY_VERIFY(!scene2.isActive());
4122 QVERIFY(!scene1.hasFocus());
4123 QVERIFY(!scene2.hasFocus());
4124
4125 toplevel1.show();
4126 QApplication::setActiveWindow(&toplevel1);
4127 QVERIFY(QTest::qWaitForWindowActive(&toplevel1));
4128 QCOMPARE(QApplication::activeWindow(), &toplevel1);
4129
4130 QTRY_VERIFY(scene1.isActive());
4131 QTRY_VERIFY(!scene2.isActive());
4132 QVERIFY(scene1.hasFocus());
4133 QVERIFY(!scene2.hasFocus());
4134
4135 view2->show();
4136 QVERIFY(scene1.isActive());
4137 QVERIFY(scene2.isActive());
4138 QVERIFY(scene1.hasFocus());
4139 QVERIFY(!scene2.hasFocus());
4140 }
4141
4142 QVERIFY(!scene1.isActive());
4143 QVERIFY(!scene2.isActive());
4144 QVERIFY(!scene1.hasFocus());
4145 QVERIFY(!scene2.hasFocus());
4146
4147
4148 {
4149 QWidget toplevel2;
4150 toplevel2.setWindowTitle(QTest::currentTestFunction());
4151 toplevel2.resize(m_testSize);
4152 QHBoxLayout *layout = new QHBoxLayout;
4153 toplevel2.setLayout(layout);
4154 QGraphicsView *view1 = new QGraphicsView(&scene1);
4155 QGraphicsView *view2 = new QGraphicsView();
4156 layout->addWidget(view1);
4157 layout->addWidget(view2);
4158
4159 QVERIFY(!scene1.isActive());
4160 QVERIFY(!scene2.isActive());
4161 QVERIFY(!scene1.hasFocus());
4162 QVERIFY(!scene2.hasFocus());
4163
4164 toplevel2.move(m_availableGeometry.topLeft() + QPoint(50, 50));
4165 toplevel2.show();
4166 QApplication::setActiveWindow(&toplevel2);
4167 QVERIFY(QTest::qWaitForWindowActive(&toplevel2));
4168 QCOMPARE(QApplication::activeWindow(), &toplevel2);
4169
4170 QTRY_VERIFY(scene1.isActive());
4171 QVERIFY(!scene2.isActive());
4172 QVERIFY(scene1.hasFocus());
4173 QVERIFY(!scene2.hasFocus());
4174
4175 view2->setScene(&scene2);
4176
4177 QVERIFY(scene1.isActive());
4178 QVERIFY(scene2.isActive());
4179 QVERIFY(scene1.hasFocus());
4180 QVERIFY(!scene2.hasFocus());
4181
4182 view1->setScene(&scene2);
4183 QVERIFY(!scene1.isActive());
4184 QVERIFY(scene2.isActive());
4185 QVERIFY(!scene1.hasFocus());
4186 QVERIFY(scene2.hasFocus());
4187
4188 view1->hide();
4189 QVERIFY(!scene1.isActive());
4190 QVERIFY(scene2.isActive());
4191 QVERIFY(!scene1.hasFocus());
4192 QVERIFY(scene2.hasFocus());
4193
4194 view1->setScene(&scene1);
4195 QVERIFY(!scene1.isActive());
4196 QVERIFY(scene2.isActive());
4197 QVERIFY(!scene1.hasFocus());
4198 QVERIFY(scene2.hasFocus());
4199
4200 view1->show();
4201 QVERIFY(scene1.isActive());
4202 QVERIFY(scene2.isActive());
4203 QVERIFY(!scene1.hasFocus());
4204 QVERIFY(scene2.hasFocus());
4205
4206 view2->hide();
4207 QVERIFY(scene1.isActive());
4208 QVERIFY(!scene2.isActive());
4209 QVERIFY(scene1.hasFocus());
4210 QVERIFY(!scene2.hasFocus());
4211
4212 QGraphicsView topLevelView;
4213 topLevelView.move(toplevel2.geometry().topRight() + QPoint(100, 50));
4214 topLevelView.resize(m_testSize);
4215 topLevelView.show();
4216 QApplication::setActiveWindow(&topLevelView);
4217 topLevelView.setFocus();
4218 QVERIFY(QTest::qWaitForWindowActive(&topLevelView));
4219 QCOMPARE(QApplication::activeWindow(), &topLevelView);
4220
4221 QVERIFY(!scene1.isActive());
4222 QVERIFY(!scene2.isActive());
4223 QVERIFY(!scene1.hasFocus());
4224 QVERIFY(!scene2.hasFocus());
4225
4226 topLevelView.setScene(&scene1);
4227 QVERIFY(scene1.isActive());
4228 QVERIFY(!scene2.isActive());
4229 QVERIFY(scene1.hasFocus());
4230 QVERIFY(!scene2.hasFocus());
4231
4232 view2->show();
4233 QVERIFY(scene1.isActive());
4234 QVERIFY(!scene2.isActive());
4235 QVERIFY(scene1.hasFocus());
4236 QVERIFY(!scene2.hasFocus());
4237
4238 view1->hide();
4239 QVERIFY(scene1.isActive());
4240 QVERIFY(!scene2.isActive());
4241 QVERIFY(scene1.hasFocus());
4242 QVERIFY(!scene2.hasFocus());
4243
4244 QApplication::setActiveWindow(&toplevel2);
4245 QVERIFY(QTest::qWaitForWindowActive(&toplevel2));
4246
4247 QVERIFY(!scene1.isActive());
4248 QVERIFY(scene2.isActive());
4249 QVERIFY(!scene1.hasFocus());
4250 QVERIFY(scene2.hasFocus());
4251 }
4252
4253 QVERIFY(!scene1.isActive());
4254 QVERIFY(!scene2.isActive());
4255 QVERIFY(!scene1.hasFocus());
4256 QVERIFY(!scene2.hasFocus());
4257
4258 {
4259 QWidget toplevel3;
4260 toplevel3.resize(m_testSize);
4261 QHBoxLayout *layout = new QHBoxLayout;
4262 toplevel3.setLayout(layout);
4263 QGraphicsView *view1 = new QGraphicsView(&scene1);
4264 QGraphicsView *view2 = new QGraphicsView(&scene2);
4265 layout->addWidget(view1);
4266
4267 QVERIFY(!scene1.isActive());
4268 QVERIFY(!scene2.isActive());
4269 QVERIFY(!scene1.hasFocus());
4270 QVERIFY(!scene2.hasFocus());
4271
4272
4273 toplevel3.show();
4274 QApplication::setActiveWindow(&toplevel3);
4275 QVERIFY(QTest::qWaitForWindowActive(&toplevel3));
4276 QCOMPARE(QApplication::activeWindow(), &toplevel3);
4277
4278 QVERIFY(scene1.isActive());
4279 QVERIFY(!scene2.isActive());
4280 QVERIFY(scene1.hasFocus());
4281 QVERIFY(!scene2.hasFocus());
4282
4283 layout->addWidget(view2);
4284 QApplication::processEvents();
4285 QVERIFY(scene1.isActive());
4286 QVERIFY(scene2.isActive());
4287 QVERIFY(scene1.hasFocus());
4288 QVERIFY(!scene2.hasFocus());
4289
4290 view1->setParent(nullptr);
4291 QVERIFY(!scene1.isActive());
4292 QVERIFY(scene2.isActive());
4293 QVERIFY(!scene1.hasFocus());
4294 QVERIFY(scene2.hasFocus());
4295 delete view1;
4296 }
4297
4298 QVERIFY(!scene1.isActive());
4299 QVERIFY(!scene2.isActive());
4300 QVERIFY(!scene1.hasFocus());
4301 QVERIFY(!scene2.hasFocus());
4302
4303}
4304
4305void tst_QGraphicsScene::siblingIndexAlwaysValid()
4306{
4307 QGraphicsScene scene;
4308
4309 QGraphicsWidget *parent = new QGraphicsWidget;
4310 parent->setZValue(350);
4311 parent->setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
4312 QGraphicsWidget *parent2 = new QGraphicsWidget;
4313 parent2->setGeometry(ax: 10, ay: 10, aw: 50, ah: 50);
4314 QGraphicsWidget *child = new QGraphicsWidget(parent2);
4315 child->setGeometry(ax: 15, ay: 15, aw: 25, ah: 25);
4316 child->setZValue(150);
4317 //Both are top level
4318 scene.addItem(item: parent);
4319 scene.addItem(item: parent2);
4320
4321 //Then we make the child a top level
4322 child->setParentItem(nullptr);
4323
4324 //This is trigerred by a repaint...
4325 QGraphicsScenePrivate::get(q: &scene)->index->estimateTopLevelItems(QRectF(), order: Qt::AscendingOrder);
4326
4327 delete child;
4328
4329 //If there are in the list that's bad, we crash...
4330 QVERIFY(!QGraphicsScenePrivate::get(&scene)->topLevelItems.contains(static_cast<QGraphicsItem *>(child)));
4331
4332 //Other case
4333 QGraphicsScene scene2;
4334 // works with bsp tree index
4335 scene2.setItemIndexMethod(QGraphicsScene::NoIndex);
4336
4337 QGraphicsView view2(&scene2);
4338 view2.setWindowTitle(QTest::currentTestFunction());
4339 view2.resize(m_testSize);
4340
4341 // first add the blue rect
4342 QGraphicsRectItem* const item1 = new QGraphicsRectItem(QRect( 10, 10, 10, 10 ));
4343 item1->setPen(QPen(Qt::blue, 0));
4344 item1->setBrush(Qt::blue);
4345 scene2.addItem(item: item1);
4346
4347 // then add the red rect
4348 QGraphicsRectItem* const item2 = new QGraphicsRectItem(5, 5, 10, 10);
4349 item2->setPen(QPen(Qt::red, 0));
4350 item2->setBrush(Qt::red);
4351 scene2.addItem(item: item2);
4352
4353 // now the blue one is visible on top of the red one -> swap them (important for the bug)
4354 item1->setZValue(1.0);
4355 item2->setZValue(0.0);
4356
4357 view2.show();
4358
4359 // handle events as a real life app would do
4360 QApplication::processEvents();
4361
4362 // now delete the red rect
4363 delete item2;
4364
4365 // handle events as a real life app would do
4366 QApplication::processEvents();
4367
4368 //We should not crash
4369
4370}
4371
4372void tst_QGraphicsScene::removeFullyTransparentItem()
4373{
4374 QGraphicsScene scene;
4375
4376 QGraphicsItem *parent = scene.addRect(x: 0, y: 0, w: 100, h: 100);
4377 parent->setFlag(flag: QGraphicsItem::ItemHasNoContents);
4378
4379 QGraphicsItem *child = scene.addRect(x: 0, y: 0, w: 100, h: 100);
4380 child->setParentItem(parent);
4381
4382 CustomView view;
4383 view.setWindowTitle(QTest::currentTestFunction());
4384 view.resize(m_testSize);
4385 view.setScene(&scene);
4386 view.show();
4387 QApplication::setActiveWindow(&view);
4388 QVERIFY(QTest::qWaitForWindowActive(&view));
4389 QCoreApplication::processEvents(); // Process all queued paint events
4390
4391 // NB! The parent has the ItemHasNoContents flag set, which means
4392 // the parent itself doesn't generate any update requests, only the
4393 // child can possibly trigger an update. Also note that the child
4394 // is removed before processing events.
4395 view.repaints = 0;
4396 parent->setOpacity(0);
4397 QVERIFY(qFuzzyIsNull(child->effectiveOpacity()));
4398 scene.removeItem(item: child);
4399 QVERIFY(!scene.items().contains(child));
4400 QTRY_VERIFY(view.repaints > 0);
4401
4402 // Re-add child. There's nothing new to display (child is still
4403 // effectively hidden), so it shouldn't trigger an update.
4404 view.repaints = 0;
4405 child->setParentItem(parent);
4406 QVERIFY(scene.items().contains(child));
4407 QVERIFY(qFuzzyIsNull(child->effectiveOpacity()));
4408 QApplication::processEvents();
4409 QCOMPARE(view.repaints, 0);
4410
4411 // Nothing is visible on the screen, removing child item shouldn't trigger an update.
4412 scene.removeItem(item: child);
4413 QApplication::processEvents();
4414 QCOMPARE(view.repaints, 0);
4415 delete child;
4416}
4417
4418void tst_QGraphicsScene::taskQTBUG_5904_crashWithDeviceCoordinateCache()
4419{
4420 QGraphicsScene scene;
4421 QGraphicsRectItem *rectItem = scene.addRect(rect: QRectF(0, 0, 100, 200), pen: QPen(Qt::black), brush: QBrush(Qt::green));
4422
4423 rectItem->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
4424
4425 QPixmap pixmap(100,200);
4426 QPainter painter(&pixmap);
4427 painter.setRenderHint(hint: QPainter::Antialiasing);
4428 scene.render(painter: &painter);
4429 painter.end();
4430 // No crash, then it passed!
4431}
4432
4433void tst_QGraphicsScene::taskQT657_paintIntoCacheWithTransparentParts()
4434{
4435 // Test using DeviceCoordinateCache and opaque item
4436 QScopedPointer<QWidget> w(new QWidget);
4437 w->setPalette(QColor(0, 0, 255));
4438 w->setGeometry(ax: 0, ay: 0, aw: 50, ah: 50);
4439
4440 QGraphicsScene *scene = new QGraphicsScene();
4441 CustomView view;
4442 view.resize(m_testSize);
4443 view.setWindowTitle(QTest::currentTestFunction());
4444 view.setScene(scene);
4445
4446 QGraphicsProxyWidget *proxy = scene->addWidget(widget: w.data());
4447 proxy->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
4448 proxy->setTransform(matrix: QTransform().rotate(a: 15), combine: true);
4449
4450 view.show();
4451 QVERIFY(QTest::qWaitForWindowExposed(&view));
4452 view.repaints = 0;
4453 proxy->update(ax: 10, ay: 10, width: 10, height: 10);
4454 QTRY_VERIFY(view.repaints > 0);
4455
4456 QPixmap pix;
4457 QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(item: proxy);
4458 QTRY_VERIFY(QPixmapCache::find(itemp->extraItemCache()->deviceData.value(view.viewport()).key, &pix));
4459
4460 QTransform t = proxy->sceneTransform();
4461 // Map from scene coordinates to pixmap coordinates.
4462 // X origin in the pixmap is the most-left point
4463 // of the item's boundingRect in the scene.
4464 const int adjust = t.mapRect(proxy->boundingRect().toRect()).left();
4465 QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(xp1: -adjust, yp1: 0, xp2: -adjust + 1, yp2: 1);
4466 QPixmap subpix = pix.copy(rect);
4467
4468 QImage im = subpix.toImage();
4469 for(int i = 0; i < im.width(); i++) {
4470 for(int j = 0; j < im.height(); j++)
4471 QCOMPARE(qAlpha(im.pixel(i, j)), 255);
4472 }
4473}
4474
4475void tst_QGraphicsScene::taskQTBUG_7863_paintIntoCacheWithTransparentParts()
4476{
4477 // Test using DeviceCoordinateCache and semi-transparent item
4478 {
4479 QGraphicsRectItem *backItem = new QGraphicsRectItem(0, 0, 100, 100);
4480 backItem->setBrush(QColor(255, 255, 0));
4481 QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 50, 50);
4482 rectItem->setBrush(QColor(0, 0, 255, 125));
4483 rectItem->setParentItem(backItem);
4484
4485 QGraphicsScene *scene = new QGraphicsScene();
4486 CustomView view;
4487 view.resize(m_testSize);
4488 view.setWindowTitle(QTest::currentTestFunction());
4489 view.setScene(scene);
4490
4491 scene->addItem(item: backItem);
4492 rectItem->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
4493 backItem->setTransform(matrix: QTransform().rotate(a: 15), combine: true);
4494
4495 view.show();
4496 QVERIFY(QTest::qWaitForWindowExposed(&view));
4497 view.repaints = 0;
4498 rectItem->update(ax: 10, ay: 10, width: 10, height: 10);
4499 QTRY_VERIFY(view.repaints > 0);
4500
4501 QPixmap pix;
4502 QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(item: rectItem);
4503 QTRY_VERIFY(QPixmapCache::find(itemp->extraItemCache()->deviceData.value(view.viewport()).key, &pix));
4504
4505 QTransform t = rectItem->sceneTransform();
4506 // Map from scene coordinates to pixmap coordinates.
4507 // X origin in the pixmap is the most-left point
4508 // of the item's boundingRect in the scene.
4509 const int adjust = t.mapRect(rectItem->boundingRect().toRect()).left();
4510 QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(xp1: -adjust, yp1: 0, xp2: -adjust + 1, yp2: 1);
4511 QPixmap subpix = pix.copy(rect);
4512
4513 QImage im = subpix.toImage();
4514 for(int i = 0; i < im.width(); i++) {
4515 for(int j = 0; j < im.height(); j++) {
4516 QCOMPARE(qAlpha(im.pixel(i, j)), 125);
4517 }
4518 }
4519 }
4520
4521 // Test using ItemCoordinateCache and opaque item
4522 {
4523 QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 50, 50);
4524 rectItem->setBrush(QColor(0, 0, 255));
4525
4526 QGraphicsScene *scene = new QGraphicsScene();
4527 CustomView view;
4528 view.setWindowTitle(QTest::currentTestFunction());
4529 view.resize(m_testSize);
4530 view.setScene(scene);
4531
4532 scene->addItem(item: rectItem);
4533 rectItem->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache);
4534 rectItem->setTransform(matrix: QTransform().rotate(a: 15), combine: true);
4535
4536 view.show();
4537 QVERIFY(QTest::qWaitForWindowExposed(&view));
4538 view.repaints = 0;
4539 rectItem->update(ax: 10, ay: 10, width: 10, height: 10);
4540 QTRY_VERIFY(view.repaints > 0);
4541
4542 QPixmap pix;
4543 QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(item: rectItem);
4544 QTRY_VERIFY(QPixmapCache::find(itemp->extraItemCache()->key, &pix));
4545
4546 QTransform t = rectItem->sceneTransform();
4547 // Map from scene coordinates to pixmap coordinates.
4548 // X origin in the pixmap is the most-left point
4549 // of the item's boundingRect in the scene.
4550 const int adjust = t.mapRect(rectItem->boundingRect().toRect()).left();
4551 QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(xp1: -adjust, yp1: 0, xp2: -adjust + 1, yp2: 1);
4552 QPixmap subpix = pix.copy(rect);
4553
4554 QImage im = subpix.toImage();
4555 for(int i = 0; i < im.width(); i++) {
4556 for(int j = 0; j < im.height(); j++)
4557 QCOMPARE(qAlpha(im.pixel(i, j)), 255);
4558 }
4559 }
4560
4561 // Test using ItemCoordinateCache and semi-transparent item
4562 {
4563 QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 50, 50);
4564 rectItem->setBrush(QColor(0, 0, 255, 125));
4565
4566 QGraphicsScene *scene = new QGraphicsScene();
4567 CustomView view;
4568 view.setWindowTitle(QTest::currentTestFunction());
4569 view.resize(m_testSize);
4570 view.setScene(scene);
4571
4572 scene->addItem(item: rectItem);
4573 rectItem->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache);
4574 rectItem->setTransform(matrix: QTransform().rotate(a: 15), combine: true);
4575
4576 view.show();
4577 QVERIFY(QTest::qWaitForWindowExposed(&view));
4578 view.repaints = 0;
4579 rectItem->update(ax: 10, ay: 10, width: 10, height: 10);
4580 QTRY_VERIFY(view.repaints > 0);
4581
4582 QPixmap pix;
4583 QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(item: rectItem);
4584 QTRY_VERIFY(QPixmapCache::find(itemp->extraItemCache()->key, &pix));
4585
4586 QTransform t = rectItem->sceneTransform();
4587 // Map from scene coordinates to pixmap coordinates.
4588 // X origin in the pixmap is the most-left point
4589 // of the item's boundingRect in the scene.
4590 const int adjust = int(t.mapRect(rectItem->boundingRect().toRect()).left());
4591 QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(xp1: -adjust, yp1: 0, xp2: -adjust + 1, yp2: 1);
4592 QPixmap subpix = pix.copy(rect);
4593
4594 QImage im = subpix.toImage();
4595 for(int i = 0; i < im.width(); i++) {
4596 for(int j = 0; j < im.height(); j++)
4597 QCOMPARE(qAlpha(im.pixel(i, j)), 125);
4598 }
4599 }
4600}
4601
4602void tst_QGraphicsScene::taskQT_3674_doNotCrash()
4603{
4604 QGraphicsScene scene;
4605
4606 QGraphicsView view(&scene);
4607 view.resize(w: 200, h: 200);
4608
4609 QPixmap pixmap(view.size());
4610 QPainter painter(&pixmap);
4611 view.render(painter: &painter);
4612 painter.end();
4613
4614 scene.addItem(item: new QGraphicsWidget);
4615 scene.setBackgroundBrush(Qt::green);
4616
4617 QApplication::processEvents();
4618 QApplication::processEvents();
4619}
4620
4621void tst_QGraphicsScene::zeroScale()
4622{
4623 //should not crash
4624 QGraphicsScene scene;
4625 scene.setSceneRect(x: -100, y: -100, w: 100, h: 100);
4626 QGraphicsView view(&scene);
4627
4628 ChangedListener cl;
4629 connect(sender: &scene, signal: &QGraphicsScene::changed, receiver: &cl, slot: &ChangedListener::changed);
4630
4631 QGraphicsRectItem *rect1 = new QGraphicsRectItem(0, 0, 0.0000001, 0.00000001);
4632 scene.addItem(item: rect1);
4633 rect1->setRotation(82);
4634 rect1->setScale(0.00000001);
4635
4636 QApplication::processEvents();
4637 QTRY_COMPARE(cl.changes.count(), 1);
4638 QGraphicsRectItem *rect2 = new QGraphicsRectItem(-0.0000001, -0.0000001, 0.0000001, 0.0000001);
4639 rect2->setScale(0.00000001);
4640 scene.addItem(item: rect2);
4641 rect1->setPos(ax: 20,ay: 20);
4642 QApplication::processEvents();
4643 QTRY_COMPARE(cl.changes.count(), 2);
4644}
4645
4646void tst_QGraphicsScene::focusItemChangedSignal()
4647{
4648 qRegisterMetaType<QGraphicsItem *>(typeName: "QGraphicsItem *");
4649 qRegisterMetaType<Qt::FocusReason>(typeName: "Qt::FocusReason");
4650
4651 QGraphicsScene scene;
4652 QSignalSpy spy(&scene, &QGraphicsScene::focusItemChanged);
4653 QVERIFY(spy.isValid());
4654 QCOMPARE(spy.count(), 0);
4655 scene.setFocus();
4656 QCOMPARE(spy.count(), 0);
4657 QEvent activateEvent(QEvent::WindowActivate);
4658 QCoreApplication::sendEvent(receiver: &scene, event: &activateEvent);
4659 QCOMPARE(spy.count(), 0);
4660
4661 QGraphicsRectItem *topLevelItem1 = new QGraphicsRectItem;
4662 topLevelItem1->setFlag(flag: QGraphicsItem::ItemIsFocusable);
4663 scene.addItem(item: topLevelItem1);
4664 QCOMPARE(spy.count(), 0);
4665 QVERIFY(!topLevelItem1->hasFocus());
4666
4667 QGraphicsRectItem *topLevelItem2 = new QGraphicsRectItem;
4668 topLevelItem2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
4669 topLevelItem2->setFocus();
4670 QVERIFY(!topLevelItem2->hasFocus());
4671 scene.addItem(item: topLevelItem2);
4672 QCOMPARE(spy.count(), 1);
4673 QList<QVariant> arguments = spy.takeFirst();
4674 QCOMPARE(arguments.size(), 3);
4675 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), topLevelItem2);
4676 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(1)), nullptr);
4677 QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::OtherFocusReason);
4678 QVERIFY(topLevelItem2->hasFocus());
4679
4680 scene.clearFocus();
4681 QCOMPARE(spy.count(), 1);
4682 arguments = spy.takeFirst();
4683 QCOMPARE(arguments.size(), 3);
4684 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), nullptr);
4685 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(1)), topLevelItem2);
4686 QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::OtherFocusReason);
4687
4688 scene.setFocus(Qt::MenuBarFocusReason);
4689 QCOMPARE(spy.count(), 1);
4690 arguments = spy.takeFirst();
4691 QCOMPARE(arguments.size(), 3);
4692 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), topLevelItem2);
4693 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(1)), nullptr);
4694 QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::MenuBarFocusReason);
4695
4696 for (int i = 0; i < 3; ++i) {
4697 topLevelItem1->setFocus(Qt::TabFocusReason);
4698 arguments = spy.takeFirst();
4699 QCOMPARE(arguments.size(), 3);
4700 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), topLevelItem1);
4701 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(1)), topLevelItem2);
4702 QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::TabFocusReason);
4703
4704 topLevelItem2->setFocus(Qt::TabFocusReason);
4705 arguments = spy.takeFirst();
4706 QCOMPARE(arguments.size(), 3);
4707 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), topLevelItem2);
4708 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(1)), topLevelItem1);
4709 QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::TabFocusReason);
4710 }
4711
4712 // The following two are unexpected, but fixing this (i.e., losing and gaining focus
4713 // when the scene activation changes) breaks quite a few tests so leave this fix
4714 // for some future release. See QTBUG-28346.
4715 QEvent deactivateEvent(QEvent::WindowDeactivate);
4716 QCoreApplication::sendEvent(receiver: &scene, event: &deactivateEvent);
4717 QEXPECT_FAIL("", "QTBUG-28346", Continue);
4718 QCOMPARE(spy.count(), 1);
4719 QCoreApplication::sendEvent(receiver: &scene, event: &activateEvent);
4720 QEXPECT_FAIL("", "QTBUG-28346", Continue);
4721 QCOMPARE(spy.count(), 1);
4722
4723 QGraphicsRectItem *panel1 = new QGraphicsRectItem;
4724 panel1->setFlags(QGraphicsItem::ItemIsPanel | QGraphicsItem::ItemIsFocusable);
4725 panel1->setFocus();
4726 scene.addItem(item: panel1);
4727 QCOMPARE(spy.count(), 1);
4728 arguments = spy.takeFirst();
4729 QCOMPARE(arguments.size(), 3);
4730 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), panel1);
4731 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(1)), topLevelItem2);
4732 QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::ActiveWindowFocusReason);
4733
4734 QGraphicsRectItem *panel2 = new QGraphicsRectItem;
4735 panel2->setFlags(QGraphicsItem::ItemIsPanel | QGraphicsItem::ItemIsFocusable);
4736 scene.addItem(item: panel2);
4737 QCOMPARE(spy.count(), 0);
4738
4739 for (int i = 0; i < 3; ++i) {
4740 scene.setActivePanel(panel2);
4741 QCOMPARE(spy.count(), 1);
4742 arguments = spy.takeFirst();
4743 QCOMPARE(arguments.size(), 3);
4744 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), panel2);
4745 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(1)), panel1);
4746 QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::ActiveWindowFocusReason);
4747
4748 scene.setActivePanel(panel1);
4749 QCOMPARE(spy.count(), 1);
4750 arguments = spy.takeFirst();
4751 QCOMPARE(arguments.size(), 3);
4752 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), panel1);
4753 QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(1)), panel2);
4754 QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::ActiveWindowFocusReason);
4755 }
4756
4757}
4758
4759class ItemCountsPaintCalls : public QGraphicsRectItem
4760{
4761public:
4762 using QGraphicsRectItem::QGraphicsRectItem;
4763
4764 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
4765 QWidget *widget = nullptr) override
4766 {
4767 QGraphicsRectItem::paint(painter, option, widget);
4768 ++repaints;
4769 }
4770 int repaints = 0;
4771};
4772
4773void tst_QGraphicsScene::minimumRenderSize()
4774{
4775 Q_CHECK_PAINTEVENTS
4776
4777 ItemCountsPaintCalls *bigParent = new ItemCountsPaintCalls(QRectF(0,0,100,100));
4778 ItemCountsPaintCalls *smallChild = new ItemCountsPaintCalls(QRectF(0,0,10,10), bigParent);
4779 ItemCountsPaintCalls *smallerGrandChild = new ItemCountsPaintCalls(QRectF(0,0,1,1), smallChild);
4780 QGraphicsScene scene;
4781 scene.addItem(item: bigParent);
4782
4783 CustomView view;
4784 view.setWindowTitle(QTest::currentTestFunction());
4785 view.resize(m_testSize);
4786 view.setScene(&scene);
4787 view.show();
4788 QVERIFY(QTest::qWaitForWindowExposed(&view));
4789 QCoreApplication::processEvents();
4790
4791 // Initially, everything should be repainted the same number of times
4792 int viewRepaints = 0;
4793 QTRY_VERIFY(view.repaints > viewRepaints);
4794 viewRepaints = view.repaints;
4795
4796 QCOMPARE(viewRepaints, bigParent->repaints);
4797 QCOMPARE(viewRepaints, smallChild->repaints);
4798 QCOMPARE(viewRepaints, smallerGrandChild->repaints);
4799
4800 // Setting a minimum render size should cause a repaint
4801 scene.setMinimumRenderSize(0.5);
4802 QCoreApplication::processEvents();
4803
4804 QTRY_VERIFY(view.repaints > viewRepaints);
4805 viewRepaints = view.repaints;
4806
4807 QCOMPARE(viewRepaints, bigParent->repaints);
4808 QCOMPARE(viewRepaints, smallChild->repaints);
4809 QCOMPARE(viewRepaints, smallerGrandChild->repaints);
4810
4811 // Scaling should cause a repaint of big items only.
4812 view.scale(sx: 0.1, sy: 0.1);
4813 QCoreApplication::processEvents();
4814
4815 QTRY_VERIFY(view.repaints > viewRepaints);
4816 viewRepaints = view.repaints;
4817
4818 QCOMPARE(viewRepaints, bigParent->repaints);
4819 QCOMPARE(viewRepaints, smallChild->repaints);
4820 QVERIFY(smallChild->repaints > smallerGrandChild->repaints);
4821
4822 // Scaling further should cause even fewer items to be repainted
4823 view.scale(sx: 0.1, sy: 0.1); // Stacks with previous scale
4824 QCoreApplication::processEvents();
4825
4826 QTRY_VERIFY(view.repaints > viewRepaints);
4827 viewRepaints = view.repaints;
4828
4829 QCOMPARE(viewRepaints, bigParent->repaints);
4830 QVERIFY(bigParent->repaints > smallChild->repaints);
4831 QVERIFY(smallChild->repaints > smallerGrandChild->repaints);
4832}
4833
4834void tst_QGraphicsScene::focusOnTouch()
4835{
4836 QGraphicsScene scene;
4837 QGraphicsView view(&scene);
4838 view.setWindowTitle(QTest::currentTestFunction());
4839 view.resize(m_testSize);
4840 scene.setSceneRect(x: 0, y: 0, w: 100, h: 100);
4841 QGraphicsRectItem *rect = scene.addRect(x: 0, y: 0, w: 100, h: 100);
4842 rect->setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: true);
4843
4844 view.show();
4845 QApplication::setActiveWindow(&view);
4846 QVERIFY(QTest::qWaitForWindowActive(&view));
4847
4848 QVERIFY(!rect->hasFocus());
4849
4850 scene.setFocusOnTouch(false);
4851
4852 QTouchDevice device;
4853 device.setType(QTouchDevice::TouchPad);
4854 QList<QTouchEvent::TouchPoint> touchPoints;
4855 QTouchEvent::TouchPoint point;
4856 point.setScenePos(QPointF(10, 10));
4857 point.setState(Qt::TouchPointPressed);
4858 touchPoints.append(t: point);
4859 QTouchEvent event(QEvent::TouchBegin, &device, Qt::NoModifier, Qt::TouchPointStates(),
4860 touchPoints);
4861
4862 QApplication::sendEvent(receiver: &scene, event: &event);
4863
4864 QVERIFY(!rect->hasFocus());
4865 scene.setFocusOnTouch(true);
4866
4867 QApplication::sendEvent(receiver: &scene, event: &event);
4868 QVERIFY(rect->hasFocus());
4869}
4870
4871void tst_QGraphicsScene::taskQTBUG_15977_renderWithDeviceCoordinateCache()
4872{
4873 QGraphicsScene scene;
4874 scene.setSceneRect(x: 0, y: 0, w: 100, h: 100);
4875 QGraphicsRectItem *rect = scene.addRect(x: 0, y: 0, w: 100, h: 100);
4876 rect->setPen(Qt::NoPen);
4877 rect->setBrush(Qt::red);
4878 rect->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
4879
4880 QImage image(100, 100, QImage::Format_RGB32);
4881 QPainter p(&image);
4882 scene.render(painter: &p);
4883 p.end();
4884
4885 QImage expected(100, 100, QImage::Format_RGB32);
4886 p.begin(&expected);
4887 p.fillRect(r: expected.rect(), c: Qt::red);
4888 p.end();
4889
4890 QCOMPARE(image, expected);
4891}
4892
4893void tst_QGraphicsScene::taskQTBUG_16401_focusItem()
4894{
4895 QGraphicsScene scene;
4896 QGraphicsView view(&scene);
4897 view.resize(m_testSize);
4898 QGraphicsRectItem *rect = scene.addRect(x: 0, y: 0, w: 100, h: 100);
4899 rect->setFlag(flag: QGraphicsItem::ItemIsFocusable);
4900
4901 view.show();
4902 QApplication::setActiveWindow(&view);
4903 QVERIFY(QTest::qWaitForWindowActive(&view));
4904
4905 QVERIFY(!scene.focusItem());
4906
4907 rect->setFocus();
4908 QCOMPARE(scene.focusItem(), rect);
4909 QFocusEvent focusOut(QEvent::FocusOut);
4910 QApplication::sendEvent(receiver: &view, event: &focusOut);
4911 QVERIFY(!scene.focusItem());
4912 QFocusEvent focusIn(QEvent::FocusIn);
4913 QApplication::sendEvent(receiver: &view, event: &focusIn);
4914 QCOMPARE(scene.focusItem(), rect);
4915
4916 rect->clearFocus();
4917 QVERIFY(!scene.focusItem());
4918 QApplication::sendEvent(receiver: &view, event: &focusOut);
4919 QVERIFY(!scene.focusItem());
4920 QApplication::sendEvent(receiver: &view, event: &focusIn);
4921 QVERIFY(!scene.focusItem());
4922}
4923
4924void tst_QGraphicsScene::taskQTBUG_42915_focusNextPrevChild()
4925{
4926 QGraphicsScene scene;
4927 QGraphicsView view(&scene);
4928 view.setWindowTitle(QTest::currentTestFunction());
4929 view.resize(m_testSize);
4930 scene.setSceneRect(x: 1, y: 1, w: 198, h: 198);
4931 view.setFocus();
4932
4933 QGraphicsWidget *widget1 = new QGraphicsWidget();
4934 QGraphicsRectItem *rect1 = new QGraphicsRectItem(-50, -50, 100, 100, widget1);
4935 rect1->setBrush(Qt::blue);
4936 scene.addItem(item: widget1);
4937 widget1->setPos(ax: 100, ay: 100);
4938 widget1->setFlags(QGraphicsItem::ItemIsPanel);
4939
4940 QGraphicsWidget *widget2 = new QGraphicsWidget(widget1);
4941 widget2->setFocusPolicy(Qt::NoFocus);
4942
4943 view.show();
4944 QApplication::setActiveWindow(&view);
4945 QVERIFY(QTest::qWaitForWindowActive(&view));
4946
4947 QTest::keyEvent(action: QTest::Click, widget: &view, key: Qt::Key_Tab);
4948}
4949
4950QTEST_MAIN(tst_QGraphicsScene)
4951#include "tst_qgraphicsscene.moc"
4952

source code of qtbase/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp