1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <QtTest/private/qtesthelpers_p.h>
32
33#include <private/qgraphicsitem_p.h>
34#include <private/qgraphicsview_p.h>
35#include <private/qgraphicsscene_p.h>
36#include <QRandomGenerator>
37#include <QStyleOptionGraphicsItem>
38#include <QAbstractTextDocumentLayout>
39#include <QBitmap>
40#include <QCursor>
41#include <QDesktopWidget>
42#include <QScreen>
43#include <QLabel>
44#include <QDial>
45#include <QGraphicsItem>
46#include <QGraphicsScene>
47#include <QGraphicsSceneEvent>
48#include <QGraphicsView>
49#include <QGraphicsWidget>
50#include <QGraphicsProxyWidget>
51#include <QPainter>
52#include <QScrollBar>
53#include <QVBoxLayout>
54#include <QGraphicsEffect>
55#include <QPushButton>
56#include <QLineEdit>
57#include <QGraphicsLinearLayout>
58#include <QTransform>
59#include <QSharedPointer>
60#include <float.h>
61#include <QStyleHints>
62#include <QPainterPath>
63
64using AbstractGraphicsShapeItemPtr = QSharedPointer<QAbstractGraphicsShapeItem>;
65using GraphicsItems = QVector<QGraphicsItem *>;
66using GraphicsItemsList = QList<QGraphicsItem *>;
67
68Q_DECLARE_METATYPE(AbstractGraphicsShapeItemPtr)
69Q_DECLARE_METATYPE(QGraphicsItem::GraphicsItemFlags)
70Q_DECLARE_METATYPE(QPainterPath)
71Q_DECLARE_METATYPE(QSizeF)
72Q_DECLARE_METATYPE(QTransform)
73
74#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
75#include <windows.h>
76#define Q_CHECK_PAINTEVENTS \
77 if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
78 QSKIP("The Graphics View doesn't get the paint events");
79#else
80#define Q_CHECK_PAINTEVENTS
81#endif
82
83#if defined(Q_OS_MAC)
84// On mac (cocoa) we always get full update.
85// So check that the expected region is contained inside the actual
86#define COMPARE_REGIONS(ACTUAL, EXPECTED) QVERIFY((EXPECTED).subtracted(ACTUAL).isEmpty())
87#else
88#define COMPARE_REGIONS QTRY_COMPARE
89#endif
90
91static QGraphicsRectItem staticItem; //QTBUG-7629, we should not crash at exit.
92
93static void sendMousePress(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton)
94{
95 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
96 event.setScenePos(point);
97 event.setButton(button);
98 event.setButtons(button);
99 QCoreApplication::sendEvent(receiver: scene, event: &event);
100}
101
102static void sendMouseMove(QGraphicsScene *scene, const QPointF &point,
103 Qt::MouseButton button = Qt::NoButton, Qt::MouseButtons /* buttons */ = {})
104{
105 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
106 event.setScenePos(point);
107 event.setButton(button);
108 event.setButtons(button);
109 QCoreApplication::sendEvent(receiver: scene, event: &event);
110}
111
112static void sendMouseRelease(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton)
113{
114 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
115 event.setScenePos(point);
116 event.setButton(button);
117 QCoreApplication::sendEvent(receiver: scene, event: &event);
118}
119
120static void sendMouseClick(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton)
121{
122 sendMousePress(scene, point, button);
123 sendMouseRelease(scene, point, button);
124}
125
126static void sendKeyPress(QGraphicsScene *scene, Qt::Key key)
127{
128 QKeyEvent keyEvent(QEvent::KeyPress, key, Qt::NoModifier);
129 QCoreApplication::sendEvent(receiver: scene, event: &keyEvent);
130}
131
132static void sendKeyRelease(QGraphicsScene *scene, Qt::Key key)
133{
134 QKeyEvent keyEvent(QEvent::KeyRelease, key, Qt::NoModifier);
135 QCoreApplication::sendEvent(receiver: scene, event: &keyEvent);
136}
137
138static void sendKeyClick(QGraphicsScene *scene, Qt::Key key)
139{
140 sendKeyPress(scene, key);
141 sendKeyRelease(scene, key);
142}
143
144class EventSpy : public QGraphicsWidget
145{
146 Q_OBJECT
147public:
148 EventSpy(QObject *watched, QEvent::Type type)
149 : spied(type)
150 {
151 watched->installEventFilter(filterObj: this);
152 }
153
154 EventSpy(QGraphicsScene *scene, QGraphicsItem *watched, QEvent::Type type)
155 : spied(type)
156 {
157 scene->addItem(item: this);
158 watched->installSceneEventFilter(filterItem: this);
159 }
160
161 int count() const { return _count; }
162
163protected:
164 bool eventFilter(QObject *watched, QEvent *event) override
165 {
166 Q_UNUSED(watched);
167 if (event->type() == spied)
168 ++_count;
169 return false;
170 }
171
172 bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
173 {
174 Q_UNUSED(watched);
175 if (event->type() == spied)
176 ++_count;
177 return false;
178 }
179
180 int _count = 0;
181 const QEvent::Type spied;
182};
183
184class EventSpy2 : public QGraphicsWidget
185{
186 Q_OBJECT
187public:
188 EventSpy2(QObject *watched)
189 {
190 watched->installEventFilter(filterObj: this);
191 }
192
193 EventSpy2(QGraphicsScene *scene, QGraphicsItem *watched)
194 {
195 scene->addItem(item: this);
196 watched->installSceneEventFilter(filterItem: this);
197 }
198
199 QMap<QEvent::Type, int> counts;
200
201protected:
202 bool eventFilter(QObject *watched, QEvent *event) override
203 {
204 Q_UNUSED(watched);
205 ++counts[event->type()];
206 return false;
207 }
208
209 bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
210 {
211 Q_UNUSED(watched);
212 ++counts[event->type()];
213 return false;
214 }
215};
216
217class EventTester : public QGraphicsItem
218{
219public:
220 using QGraphicsItem::QGraphicsItem;
221
222 void setGeometry(const QRectF &rect)
223 {
224 prepareGeometryChange();
225 br = rect;
226 update();
227 }
228
229 QRectF boundingRect() const override
230 { return br; }
231
232 void paint(QPainter *painter, const QStyleOptionGraphicsItem *o, QWidget *) override
233 {
234 hints = painter->renderHints();
235 painter->setBrush(brush);
236 painter->drawRect(rect: boundingRect());
237 lastExposedRect = o->exposedRect;
238 ++repaints;
239 }
240
241 bool sceneEvent(QEvent *event) override
242 {
243 events << event->type();
244 return QGraphicsItem::sceneEvent(event);
245 }
246
247 void reset()
248 {
249 events.clear();
250 hints = QPainter::RenderHints{};
251 repaints = 0;
252 lastExposedRect = QRectF();
253 }
254
255 QVector<QEvent::Type> events;
256 QPainter::RenderHints hints;
257 int repaints = 0;
258 QRectF br = QRectF(-10, -10, 20, 20);
259 QRectF lastExposedRect;
260 QBrush brush;
261};
262
263class MyGraphicsView : public QGraphicsView
264{
265 Q_OBJECT
266public:
267 QRegion paintedRegion;
268 int repaints = 0;
269
270 using QGraphicsView::QGraphicsView;
271
272 void paintEvent(QPaintEvent *e) override
273 {
274 paintedRegion += e->region();
275 ++repaints;
276 QGraphicsView::paintEvent(event: e);
277 }
278 void reset()
279 {
280 repaints = 0;
281 paintedRegion = QRegion();
282 }
283};
284
285class tst_QGraphicsItem : public QObject
286{
287 Q_OBJECT
288
289public:
290 static void initMain();
291
292private slots:
293 void cleanup();
294 void construction();
295 void constructionWithParent();
296 void destruction();
297 void deleteChildItem();
298 void scene();
299 void parentItem();
300 void setParentItem();
301 void children();
302 void flags();
303 void inputMethodHints();
304 void toolTip();
305 void visible();
306 void isVisibleTo();
307 void explicitlyVisible();
308 void enabled();
309 void explicitlyEnabled();
310 void selected();
311 void selected2();
312 void selected_group();
313 void selected_textItem();
314 void selected_multi();
315 void acceptedMouseButtons();
316 void acceptHoverEvents();
317 void childAcceptsHoverEvents();
318 void hasFocus();
319 void pos();
320 void scenePos();
321 void matrix();
322 void sceneTransform();
323 void setTransform();
324 void zValue();
325 void shape();
326 void contains();
327 void collidesWith_item();
328 void collidesWith_path_data();
329 void collidesWith_path();
330 void collidesWithItemWithClip();
331 void isObscuredBy();
332 void isObscured();
333 void mapFromToParent();
334 void mapFromToScene();
335 void mapFromToItem();
336 void mapRectFromToParent_data();
337 void mapRectFromToParent();
338 void isAncestorOf();
339 void commonAncestorItem();
340 void data();
341 void type();
342 void graphicsitem_cast();
343 void hoverEventsGenerateRepaints();
344 void boundingRects_data();
345 void boundingRects();
346 void boundingRects2();
347 void sceneBoundingRect();
348 void childrenBoundingRect();
349 void childrenBoundingRectTransformed();
350 void childrenBoundingRect2();
351 void childrenBoundingRect3();
352 void childrenBoundingRect4();
353 void childrenBoundingRect5();
354 void group();
355 void setGroup();
356 void setGroup2();
357 void nestedGroups();
358 void warpChildrenIntoGroup();
359 void removeFromGroup();
360 void handlesChildEvents();
361 void handlesChildEvents2();
362 void handlesChildEvents3();
363 void filtersChildEvents();
364 void filtersChildEvents2();
365 void ensureVisible();
366#ifndef QT_NO_CURSOR
367 void cursor();
368#endif
369 //void textControlGetterSetter();
370 void defaultItemTest_QGraphicsLineItem();
371 void defaultItemTest_QGraphicsPixmapItem();
372 void defaultItemTest_QGraphicsTextItem();
373 void defaultItemTest_QGraphicsEllipseItem();
374 void itemChange();
375 void sceneEventFilter();
376 void prepareGeometryChange();
377 void paint();
378 void deleteItemInEventHandlers();
379 void itemClipsToShape();
380 void itemClipsChildrenToShape();
381 void itemClipsChildrenToShape2();
382 void itemClipsChildrenToShape3();
383 void itemClipsChildrenToShape4();
384 void itemClipsChildrenToShape5();
385 void itemClipsTextChildToShape();
386 void itemClippingDiscovery();
387 void itemContainsChildrenInShape();
388 void itemContainsChildrenInShape2();
389 void ancestorFlags();
390 void untransformable();
391#ifndef QT_NO_CONTEXTMENU
392 void contextMenuEventPropagation();
393#endif // QT_NO_CONTEXTMENU
394 void itemIsMovable();
395 void boundingRegion_data();
396 void boundingRegion();
397 void itemTransform_parentChild();
398 void itemTransform_siblings();
399 void itemTransform_unrelated();
400 void opacity_data();
401 void opacity();
402 void opacity2();
403 void opacityZeroUpdates();
404 void itemStacksBehindParent();
405 void nestedClipping();
406 void nestedClippingTransforms();
407 void sceneTransformCache();
408 void tabChangesFocus();
409 void tabChangesFocus_data();
410 void cacheMode();
411 void cacheMode2();
412 void updateCachedItemAfterMove();
413 void deviceTransform_data();
414 void deviceTransform();
415 void update();
416 void setTransformProperties_data();
417 void setTransformProperties();
418 void itemUsesExtendedStyleOption();
419 void itemSendsGeometryChanges();
420 void moveItem();
421 void moveLineItem();
422 void sorting_data();
423 void sorting();
424 void itemHasNoContents();
425 void hitTestUntransformableItem();
426 void hitTestGraphicsEffectItem();
427 void focusProxy();
428 void subFocus();
429 void focusProxyDeletion();
430 void negativeZStacksBehindParent();
431 void setGraphicsEffect();
432 void panel();
433 void addPanelToActiveScene();
434 void panelWithFocusItems();
435 void activate();
436 void setActivePanelOnInactiveScene();
437 void activationOnShowHide();
438 void deactivateInactivePanel();
439 void moveWhileDeleting();
440 void ensureDirtySceneTransform();
441 void focusScope();
442 void focusScope2();
443 void focusScopeItemChangedWhileScopeDoesntHaveFocus();
444 void stackBefore();
445 void sceneModality();
446 void panelModality();
447 void mixedModality();
448 void modality_hover();
449 void modality_mouseGrabber();
450 void modality_clickFocus();
451 void modality_keyEvents();
452 void itemIsInFront();
453 void scenePosChange();
454 void textItem_shortcuts();
455 void scroll();
456 void focusHandling_data();
457 void focusHandling();
458 void touchEventPropagation_data();
459 void touchEventPropagation();
460 void touchEventTransformation_data();
461 void touchEventTransformation();
462 void deviceCoordinateCache_simpleRotations();
463 void resolvePaletteForItemChildren();
464
465 // task specific tests below me
466 void task141694_textItemEnsureVisible();
467 void task128696_textItemEnsureMovable();
468 void ensureUpdateOnTextItem();
469 void task177918_lineItemUndetected();
470 void task240400_clickOnTextItem_data();
471 void task240400_clickOnTextItem();
472 void task243707_addChildBeforeParent();
473 void task197802_childrenVisibility();
474 void QTBUG_4233_updateCachedWithSceneRect();
475 void QTBUG_5418_textItemSetDefaultColor();
476 void QTBUG_6738_missingUpdateWithSetParent();
477 void QTBUG_7714_fullUpdateDiscardingOpacityUpdate2();
478 void QT_2653_fullUpdateDiscardingOpacityUpdate();
479 void QT_2649_focusScope();
480 void sortItemsWhileAdding();
481 void doNotMarkFullUpdateIfNotInScene();
482 void itemDiesDuringDraggingOperation();
483 void QTBUG_12112_focusItem();
484 void QTBUG_13473_sceneposchange();
485 void QTBUG_16374_crashInDestructor();
486 void QTBUG_20699_focusScopeCrash();
487 void QTBUG_30990_rightClickSelection();
488 void QTBUG_21618_untransformable_sceneTransform();
489
490private:
491 GraphicsItems paintedItems;
492 QTouchDevice *m_touchDevice = nullptr;
493};
494
495void tst_QGraphicsItem::initMain()
496{
497#ifdef Q_OS_WIN
498 // Ensure minimum size constraints of framed windows on High DPI screens
499 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
500#endif
501}
502
503void tst_QGraphicsItem::cleanup()
504{
505 QVERIFY(QApplication::topLevelWidgets().isEmpty());
506}
507
508template <class I>
509static inline I *createBlackShapeItem()
510{
511 auto result = new I;
512 result->setPen(QPen(Qt::black, 0));
513 return result;
514}
515
516void tst_QGraphicsItem::construction()
517{
518 for (int i = 0; i < 7; ++i) {
519 QGraphicsItem *item = nullptr;
520 switch (i) {
521 case 0:
522 item = createBlackShapeItem<QGraphicsEllipseItem>();
523 QCOMPARE(item->type(), int(QGraphicsEllipseItem::Type));
524 QCOMPARE(qgraphicsitem_cast<QGraphicsEllipseItem *>(item), item);
525 QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
526 QCOMPARE(item->flags(), 0);
527 break;
528 case 1:
529 item = createBlackShapeItem<QGraphicsLineItem>();
530 QCOMPARE(int(item->type()), int(QGraphicsLineItem::Type));
531 QCOMPARE(qgraphicsitem_cast<QGraphicsLineItem *>(item), item);
532 QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
533 QCOMPARE(item->flags(), 0);
534 break;
535 case 2:
536 item = createBlackShapeItem<QGraphicsPathItem>();
537 QCOMPARE(int(item->type()), int(QGraphicsPathItem::Type));
538 QCOMPARE(qgraphicsitem_cast<QGraphicsPathItem *>(item), item);
539 QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
540 QCOMPARE(item->flags(), 0);
541 break;
542 case 3:
543 item = new QGraphicsPixmapItem;
544 QCOMPARE(int(item->type()), int(QGraphicsPixmapItem::Type));
545 QCOMPARE(qgraphicsitem_cast<QGraphicsPixmapItem *>(item), item);
546 QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
547 QCOMPARE(item->flags(), 0);
548 break;
549 case 4:
550 item = createBlackShapeItem<QGraphicsPolygonItem>();
551 QCOMPARE(int(item->type()), int(QGraphicsPolygonItem::Type));
552 QCOMPARE(qgraphicsitem_cast<QGraphicsPolygonItem *>(item), item);
553 QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
554 QCOMPARE(item->flags(), 0);
555 break;
556 case 5:
557 item = createBlackShapeItem<QGraphicsRectItem>();
558 QCOMPARE(int(item->type()), int(QGraphicsRectItem::Type));
559 QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), item);
560 QCOMPARE(qgraphicsitem_cast<QGraphicsLineItem *>(item), nullptr);
561 QCOMPARE(item->flags(), 0);
562 break;
563 case 6:
564 item = new QGraphicsTextItem;
565 QCOMPARE(int(item->type()), int(QGraphicsTextItem::Type));
566 QCOMPARE(qgraphicsitem_cast<QGraphicsTextItem *>(item), item);
567 QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
568 // This is the only item that uses an extended style option.
569 QCOMPARE(item->flags(), QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemUsesExtendedStyleOption));
570 break;
571 default:
572 qFatal(msg: "You broke the logic, please fix!");
573 }
574
575 QCOMPARE(item->scene(), nullptr);
576 QCOMPARE(item->parentItem(), nullptr);
577 QVERIFY(item->childItems().isEmpty());
578 QVERIFY(item->isVisible());
579 QVERIFY(item->isEnabled());
580 QVERIFY(!item->isSelected());
581 QCOMPARE(item->acceptedMouseButtons(), Qt::MouseButtons(0x1f));
582 if (item->type() == QGraphicsTextItem::Type)
583 QVERIFY(item->acceptHoverEvents());
584 else
585 QVERIFY(!item->acceptHoverEvents());
586 QVERIFY(!item->hasFocus());
587 QCOMPARE(item->pos(), QPointF());
588 QCOMPARE(item->transform(), QTransform());
589 QCOMPARE(item->sceneTransform(), QTransform());
590 QCOMPARE(item->zValue(), qreal(0));
591 QCOMPARE(item->sceneBoundingRect(), QRectF());
592 QCOMPARE(item->shape(), QPainterPath());
593 QVERIFY(!item->contains(QPointF(0, 0)));
594 QVERIFY(!item->collidesWithItem(nullptr));
595 QVERIFY(item->collidesWithItem(item));
596 QVERIFY(!item->collidesWithPath(QPainterPath()));
597 QVERIFY(!item->isAncestorOf(nullptr));
598 QVERIFY(!item->isAncestorOf(item));
599 QCOMPARE(item->data(0), QVariant());
600 delete item;
601 }
602}
603
604class BoundingRectItem : public QGraphicsRectItem
605{
606public:
607 BoundingRectItem(QGraphicsItem *parent = nullptr)
608 : QGraphicsRectItem(0, 0, parent ? 200 : 100, parent ? 200 : 100,
609 parent)
610 {
611 setPen(QPen(Qt::black, 0));
612 }
613
614 QRectF boundingRect() const override
615 {
616 QRectF tmp = QGraphicsRectItem::boundingRect();
617 const auto children = childItems();
618 for (QGraphicsItem *child : children)
619 tmp |= child->boundingRect(); // <- might be pure virtual
620 return tmp;
621 }
622};
623
624void tst_QGraphicsItem::constructionWithParent()
625{
626 // This test causes a crash if item1 calls item2's pure virtuals before the
627 // object has been constructed.
628 QGraphicsItem *item0 = new BoundingRectItem;
629 QGraphicsItem *item1 = new BoundingRectItem;
630 QGraphicsScene scene;
631 scene.addItem(item: item0);
632 scene.addItem(item: item1);
633 QGraphicsItem *item2 = new BoundingRectItem(item1);
634 QCOMPARE(item1->childItems(), GraphicsItemsList{item2});
635 QCOMPARE(item1->boundingRect(), QRectF(0, 0, 200, 200));
636
637 item2->setParentItem(item0);
638 QCOMPARE(item0->childItems(), GraphicsItemsList{item2});
639 QCOMPARE(item0->boundingRect(), QRectF(0, 0, 200, 200));
640}
641
642static int itemDeleted = 0;
643class Item : public QGraphicsRectItem
644{
645public:
646 ~Item()
647 { ++itemDeleted; }
648};
649
650void tst_QGraphicsItem::destruction()
651{
652 QCOMPARE(itemDeleted, 0);
653 {
654 QGraphicsItem *parent = new QGraphicsRectItem;
655 Item *child = new Item;
656 child->setParentItem(parent);
657 QCOMPARE(child->parentItem(), parent);
658 delete parent;
659 QCOMPARE(itemDeleted, 1);
660 }
661 {
662 QGraphicsItem *parent = new QGraphicsRectItem;
663 Item *child = new Item;
664 child->setParentItem(parent);
665 QCOMPARE(parent->childItems().size(), 1);
666 delete child;
667 QCOMPARE(parent->childItems().size(), 0);
668 delete parent;
669 QCOMPARE(itemDeleted, 2);
670 }
671 {
672 QGraphicsScene scene;
673 QGraphicsItem *parent = new QGraphicsRectItem;
674 Item *child = new Item;
675 QCOMPARE(child->parentItem(), nullptr);
676 child->setParentItem(parent);
677 QCOMPARE(child->parentItem(), parent);
678 scene.addItem(item: parent);
679 QCOMPARE(child->parentItem(), parent);
680 delete parent;
681 QCOMPARE(itemDeleted, 3);
682 }
683 {
684 QGraphicsScene scene;
685 QGraphicsItem *parent = new QGraphicsRectItem;
686 Item *child = new Item;
687 child->setParentItem(parent);
688 scene.addItem(item: parent);
689 QCOMPARE(child->scene(), &scene);
690 QCOMPARE(parent->childItems().size(), 1);
691 delete child;
692 QCOMPARE(parent->childItems().size(), 0);
693 delete parent;
694 QCOMPARE(itemDeleted, 4);
695 }
696 {
697 QGraphicsScene scene;
698 QGraphicsItem *parent = new QGraphicsRectItem;
699 Item *child = new Item;
700 child->setParentItem(parent);
701 scene.addItem(item: parent);
702 QCOMPARE(child->scene(), &scene);
703 scene.removeItem(item: parent);
704 QCOMPARE(child->scene(), nullptr);
705 delete parent;
706 QCOMPARE(itemDeleted, 5);
707 }
708 {
709 QGraphicsScene scene;
710 QGraphicsItem *parent = new QGraphicsRectItem;
711 Item *child = new Item;
712 child->setParentItem(parent);
713 QCOMPARE(child->scene(), nullptr);
714 QCOMPARE(parent->scene(), nullptr);
715 scene.addItem(item: parent);
716 QCOMPARE(child->scene(), &scene);
717 scene.removeItem(item: child);
718 QCOMPARE(child->scene(), nullptr);
719 QCOMPARE(parent->scene(), &scene);
720 QCOMPARE(child->parentItem(), nullptr);
721 QVERIFY(parent->childItems().isEmpty());
722 delete parent;
723 QCOMPARE(itemDeleted, 5);
724 delete child;
725 QCOMPARE(itemDeleted, 6);
726 }
727 {
728 QGraphicsScene scene;
729 QGraphicsItem *parent = new QGraphicsRectItem;
730 Item *child = new Item;
731 child->setParentItem(parent);
732 scene.addItem(item: parent);
733 scene.removeItem(item: child);
734 scene.removeItem(item: parent);
735 delete child;
736 delete parent;
737 QCOMPARE(itemDeleted, 7);
738 }
739 {
740 QGraphicsScene scene;
741 QGraphicsItem *parent = new QGraphicsRectItem;
742 Item *child = new Item;
743 child->setParentItem(parent);
744 scene.addItem(item: parent);
745 QGraphicsScene scene2;
746 scene2.addItem(item: parent);
747 delete parent;
748 QCOMPARE(itemDeleted, 8);
749 }
750 {
751 QGraphicsScene scene;
752 QGraphicsItem *parent = new QGraphicsRectItem;
753 Item *child = new Item;
754 child->setParentItem(parent);
755 scene.addItem(item: parent);
756 QCOMPARE(child->scene(), &scene);
757 QGraphicsScene scene2;
758 scene2.addItem(item: parent);
759 QCOMPARE(child->scene(), &scene2);
760 scene.addItem(item: parent);
761 QCOMPARE(child->scene(), &scene);
762 scene2.addItem(item: parent);
763 QCOMPARE(child->scene(), &scene2);
764 delete parent;
765 QCOMPARE(itemDeleted, 9);
766 }
767 {
768 QGraphicsScene scene;
769 QGraphicsItem *parent = new QGraphicsRectItem;
770 Item *child = new Item;
771 child->setParentItem(parent);
772 scene.addItem(item: parent);
773 QCOMPARE(child->scene(), &scene);
774 QGraphicsScene scene2;
775 scene2.addItem(item: child);
776 QCOMPARE(child->scene(), &scene2);
777 delete parent;
778 QCOMPARE(itemDeleted, 9);
779 delete child;
780 QCOMPARE(itemDeleted, 10);
781 }
782 {
783 QGraphicsScene scene;
784 QGraphicsItem *root = new QGraphicsRectItem;
785 QGraphicsItem *parent = root;
786 QGraphicsItem *middleItem = nullptr;
787 for (int i = 0; i < 99; ++i) {
788 Item *child = new Item;
789 child->setParentItem(parent);
790 parent = child;
791 if (i == 50)
792 middleItem = parent;
793 }
794 scene.addItem(item: root);
795
796 QCOMPARE(scene.items().size(), 100);
797
798 QGraphicsScene scene2;
799 scene2.addItem(item: middleItem);
800
801 delete middleItem;
802 QCOMPARE(itemDeleted, 59);
803 }
804 QCOMPARE(itemDeleted, 109);
805 {
806 QGraphicsScene *scene = new QGraphicsScene;
807 QGraphicsRectItem *parent = new QGraphicsRectItem;
808 Item *child = new Item;
809 child->setParentItem(parent);
810 parent->setVisible(false);
811 scene->addItem(item: parent);
812 QCOMPARE(child->parentItem(), static_cast<QGraphicsItem*>(parent));
813 delete scene;
814 QCOMPARE(itemDeleted, 110);
815 }
816}
817
818void tst_QGraphicsItem::deleteChildItem()
819{
820 QGraphicsScene scene;
821 QGraphicsItem *rect = scene.addRect(rect: QRectF());
822 QGraphicsItem *child1 = new QGraphicsRectItem(rect);
823 QGraphicsItem *child2 = new QGraphicsRectItem(rect);
824 QGraphicsItem *child3 = new QGraphicsRectItem(rect);
825 Q_UNUSED(child3);
826 delete child1;
827 child2->setParentItem(nullptr);
828 delete child2;
829}
830
831void tst_QGraphicsItem::scene()
832{
833 QGraphicsRectItem *item = new QGraphicsRectItem;
834 QCOMPARE(item->scene(), nullptr);
835
836 QGraphicsScene scene;
837 scene.addItem(item);
838 QCOMPARE(item->scene(), &scene);
839
840 QGraphicsScene scene2;
841 scene2.addItem(item);
842 QCOMPARE(item->scene(), &scene2);
843
844 scene2.removeItem(item);
845 QCOMPARE(item->scene(), nullptr);
846
847 delete item;
848}
849
850void tst_QGraphicsItem::parentItem()
851{
852 QGraphicsRectItem item;
853 QCOMPARE(item.parentItem(), nullptr);
854
855 QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(), &item);
856 QCOMPARE(item2->parentItem(), &item);
857 item2->setParentItem(&item);
858 QCOMPARE(item2->parentItem(), &item);
859 item2->setParentItem(nullptr);
860 QCOMPARE(item2->parentItem(), nullptr);
861
862 delete item2;
863}
864
865void tst_QGraphicsItem::setParentItem()
866{
867 QGraphicsScene scene;
868 const QScopedPointer<QGraphicsItem> item(scene.addRect(rect: QRectF(0, 0, 10, 10)));
869 QCOMPARE(item->scene(), &scene);
870
871 const QScopedPointer<QGraphicsRectItem> child(new QGraphicsRectItem);
872 QCOMPARE(child->scene(), nullptr);
873
874 // This implicitly adds the item to the parent's scene
875 child->setParentItem(item.data());
876 QCOMPARE(child->scene(), &scene);
877
878 // This just makes it a toplevel
879 child->setParentItem(nullptr);
880 QCOMPARE(child->scene(), &scene);
881
882 // Add the child back to the parent, then remove the parent from the scene
883 child->setParentItem(item.data());
884 scene.removeItem(item: item.data());
885 QCOMPARE(child->scene(), nullptr);
886}
887
888void tst_QGraphicsItem::children()
889{
890 QGraphicsRectItem item;
891 QVERIFY(item.childItems().isEmpty());
892
893 QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(), &item);
894 QCOMPARE(item.childItems().size(), 1);
895 QCOMPARE(item.childItems().constFirst(), item2);
896 QVERIFY(item2->childItems().isEmpty());
897
898 delete item2;
899 QVERIFY(item.childItems().isEmpty());
900}
901
902void tst_QGraphicsItem::flags()
903{
904 QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20));
905 QCOMPARE(item->flags(), 0);
906
907 QGraphicsScene scene;
908 QEvent activate(QEvent::WindowActivate);
909 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
910
911 scene.addItem(item);
912
913 {
914 // Focus
915 item->setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: false);
916 QVERIFY(!item->hasFocus());
917 item->setFocus();
918 QVERIFY(!item->hasFocus());
919
920 item->setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: true);
921 QVERIFY(!item->hasFocus());
922 item->setFocus();
923 QVERIFY(item->hasFocus());
924 QVERIFY(scene.hasFocus());
925
926 item->setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: false);
927 QVERIFY(!item->hasFocus());
928 QVERIFY(scene.hasFocus());
929 }
930 {
931 // Selectable
932 item->setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: false);
933 QVERIFY(!item->isSelected());
934 item->setSelected(true);
935 QVERIFY(!item->isSelected());
936
937 item->setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: true);
938 QVERIFY(!item->isSelected());
939 item->setSelected(true);
940 QVERIFY(item->isSelected());
941 item->setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: false);
942 QVERIFY(!item->isSelected());
943 }
944 {
945 // Movable
946 item->setFlag(flag: QGraphicsItem::ItemIsMovable, enabled: false);
947 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
948 event.setScenePos(QPointF(0, 0));
949 event.setButton(Qt::LeftButton);
950 event.setButtons(Qt::LeftButton);
951 QCoreApplication::sendEvent(receiver: &scene, event: &event);
952 QCOMPARE(scene.mouseGrabberItem(), nullptr); // mouse grabber is reset
953
954 QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove);
955 event2.setScenePos(QPointF(10, 10));
956 event2.setButton(Qt::LeftButton);
957 event2.setButtons(Qt::LeftButton);
958 QCoreApplication::sendEvent(receiver: &scene, event: &event2);
959 QCOMPARE(item->pos(), QPointF());
960
961 QGraphicsSceneMouseEvent event3(QEvent::GraphicsSceneMouseRelease);
962 event3.setScenePos(QPointF(10, 10));
963 event3.setButtons({});
964 QCoreApplication::sendEvent(receiver: &scene, event: &event3);
965 QCOMPARE(scene.mouseGrabberItem(), nullptr);
966
967 item->setFlag(flag: QGraphicsItem::ItemIsMovable, enabled: true);
968 QGraphicsSceneMouseEvent event4(QEvent::GraphicsSceneMousePress);
969 event4.setScenePos(QPointF(0, 0));
970 event4.setButton(Qt::LeftButton);
971 event4.setButtons(Qt::LeftButton);
972 QCoreApplication::sendEvent(receiver: &scene, event: &event4);
973 QCOMPARE(scene.mouseGrabberItem(), item);
974 QGraphicsSceneMouseEvent event5(QEvent::GraphicsSceneMouseMove);
975 event5.setScenePos(QPointF(10, 10));
976 event5.setButton(Qt::LeftButton);
977 event5.setButtons(Qt::LeftButton);
978 QCoreApplication::sendEvent(receiver: &scene, event: &event5);
979 QCOMPARE(item->pos(), QPointF(10, 10));
980 }
981 {
982 const QScopedPointer<QGraphicsItem> clippingParent(new QGraphicsRectItem);
983 clippingParent->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape, enabled: true);
984
985 const QScopedPointer<QGraphicsItem> nonClippingParent(new QGraphicsRectItem);
986 nonClippingParent->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape, enabled: false);
987
988 QGraphicsItem* child = new QGraphicsRectItem(nonClippingParent.data());
989 QVERIFY(!child->isClipped());
990
991 child->setParentItem(clippingParent.data());
992 QVERIFY(child->isClipped());
993
994 child->setParentItem(nonClippingParent.data());
995 QVERIFY(!child->isClipped());
996 }
997}
998
999class ImhTester : public QGraphicsItem
1000{
1001 QRectF boundingRect() const override { return QRectF(); }
1002 void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
1003};
1004
1005void tst_QGraphicsItem::inputMethodHints()
1006{
1007 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
1008 QSKIP("Window activation is not supported");
1009
1010 ImhTester *item = new ImhTester;
1011 item->setFlag(flag: QGraphicsItem::ItemAcceptsInputMethod, enabled: true);
1012 item->setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: true);
1013 QCOMPARE(item->inputMethodHints(), Qt::ImhNone);
1014 ImhTester *item2 = new ImhTester;
1015 item2->setFlag(flag: QGraphicsItem::ItemAcceptsInputMethod, enabled: true);
1016 item2->setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: true);
1017 Qt::InputMethodHints imHints = item2->inputMethodHints();
1018 imHints |= Qt::ImhHiddenText;
1019 item2->setInputMethodHints(imHints);
1020 QGraphicsScene scene;
1021 scene.addItem(item);
1022 scene.addItem(item: item2);
1023 QGraphicsView view(&scene);
1024 QApplication::setActiveWindow(&view);
1025 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1026 view.show();
1027 QVERIFY(QTest::qWaitForWindowExposed(&view));
1028 QVERIFY(QTest::qWaitForWindowActive(&view));
1029 item->setFocus();
1030 QTRY_VERIFY(item->hasFocus());
1031 QCOMPARE(view.inputMethodHints(), item->inputMethodHints());
1032 item2->setFocus();
1033 QTRY_VERIFY(item2->hasFocus());
1034 QCOMPARE(view.inputMethodHints(), item2->inputMethodHints());
1035 item->setFlag(flag: QGraphicsItem::ItemAcceptsInputMethod, enabled: false);
1036 item->setFocus();
1037 QTRY_VERIFY(item->hasFocus());
1038 //Focus has changed but the new item doesn't accept input method, no hints.
1039 QCOMPARE(view.inputMethodHints(), 0);
1040 item2->setFocus();
1041 QTRY_VERIFY(item2->hasFocus());
1042 QCOMPARE(view.inputMethodHints(), item2->inputMethodHints());
1043 imHints = item2->inputMethodHints();
1044 imHints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1045 item2->setInputMethodHints(imHints);
1046 QCOMPARE(view.inputMethodHints(), item2->inputMethodHints());
1047 QGraphicsProxyWidget *widget = new QGraphicsProxyWidget;
1048 QLineEdit *edit = new QLineEdit;
1049 edit->setEchoMode(QLineEdit::Password);
1050 scene.addItem(item: widget);
1051 widget->setFocus();
1052 QTRY_VERIFY(widget->hasFocus());
1053 //No widget on the proxy, so no hints
1054 QCOMPARE(view.inputMethodHints(), 0);
1055 widget->setWidget(edit);
1056 //View should match with the line edit
1057 QCOMPARE(view.inputMethodHints(), edit->inputMethodHints());
1058}
1059
1060void tst_QGraphicsItem::toolTip()
1061{
1062 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
1063 QSKIP("Window activation is not supported");
1064
1065 QString toolTip = "Qt rocks!";
1066
1067 QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
1068 item->setPen(QPen(Qt::red, 1));
1069 item->setBrush(QBrush(Qt::blue));
1070 QVERIFY(item->toolTip().isEmpty());
1071 item->setToolTip(toolTip);
1072 QCOMPARE(item->toolTip(), toolTip);
1073
1074 QGraphicsScene scene;
1075 scene.addItem(item);
1076
1077 QGraphicsView view(&scene);
1078 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1079 view.setFixedSize(w: 200, h: 200);
1080 view.show();
1081 QApplication::setActiveWindow(&view);
1082 QVERIFY(QTest::qWaitForWindowExposed(&view));
1083 QVERIFY(QTest::qWaitForWindowActive(&view));
1084 {
1085 QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(),
1086 view.viewport()->mapToGlobal(view.viewport()->rect().topLeft()));
1087 QCoreApplication::sendEvent(receiver: view.viewport(), event: &helpEvent);
1088 QTest::qWait(ms: 250);
1089
1090 bool foundView = false;
1091 bool foundTipLabel = false;
1092 const auto topLevels = QApplication::topLevelWidgets();
1093 for (auto widget : topLevels) {
1094 if (widget == &view)
1095 foundView = true;
1096 if (widget->inherits(classname: "QTipLabel"))
1097 foundTipLabel = true;
1098 }
1099 QVERIFY(foundView);
1100 QVERIFY(!foundTipLabel);
1101 }
1102
1103 {
1104 QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().center(),
1105 view.viewport()->mapToGlobal(view.viewport()->rect().center()));
1106 QCoreApplication::sendEvent(receiver: view.viewport(), event: &helpEvent);
1107 QTest::qWait(ms: 250);
1108
1109 bool foundView = false;
1110 bool foundTipLabel = false;
1111 const auto topLevels = QApplication::topLevelWidgets();
1112 for (auto widget : topLevels) {
1113 if (widget == &view)
1114 foundView = true;
1115 if (widget->inherits(classname: "QTipLabel"))
1116 foundTipLabel = true;
1117 }
1118 QVERIFY(foundView);
1119 QVERIFY(foundTipLabel);
1120 }
1121
1122 {
1123 QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(),
1124 view.viewport()->mapToGlobal(view.viewport()->rect().topLeft()));
1125 QCoreApplication::sendEvent(receiver: view.viewport(), event: &helpEvent);
1126 QTest::qWait(ms: 1000);
1127
1128 bool foundView = false;
1129 bool foundTipLabel = false;
1130 const auto topLevels = QApplication::topLevelWidgets();
1131 for (auto widget : topLevels) {
1132 if (widget == &view)
1133 foundView = true;
1134 if (widget->inherits(classname: "QTipLabel") && widget->isVisible())
1135 foundTipLabel = true;
1136 }
1137 QVERIFY(foundView);
1138 QVERIFY(!foundTipLabel);
1139 }
1140}
1141
1142void tst_QGraphicsItem::visible()
1143{
1144 QGraphicsItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20));
1145 item->setFlag(flag: QGraphicsItem::ItemIsMovable);
1146 QVERIFY(item->isVisible());
1147 item->setVisible(false);
1148 QVERIFY(!item->isVisible());
1149 item->setVisible(true);
1150 QVERIFY(item->isVisible());
1151
1152 QGraphicsScene scene;
1153 QEvent activate(QEvent::WindowActivate);
1154 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
1155
1156 scene.addItem(item);
1157 QVERIFY(item->isVisible());
1158 QCOMPARE(scene.items(QPointF(0, 0)).value(0, nullptr), item);
1159 item->setVisible(false);
1160 QVERIFY(scene.items(QPointF(0, 0)).isEmpty());
1161 item->setVisible(true);
1162 QCOMPARE(scene.items(QPointF(0, 0)).value(0, nullptr), item);
1163
1164 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
1165 event.setButton(Qt::LeftButton);
1166 event.setScenePos(QPointF(0, 0));
1167 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1168 QCOMPARE(scene.mouseGrabberItem(), item);
1169 item->setVisible(false);
1170 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1171 item->setVisible(true);
1172 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1173
1174 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
1175 item->setFocus();
1176 QVERIFY(item->hasFocus());
1177 item->setVisible(false);
1178 QVERIFY(!item->hasFocus());
1179 item->setVisible(true);
1180 QVERIFY(!item->hasFocus());
1181}
1182
1183void tst_QGraphicsItem::isVisibleTo()
1184{
1185 QGraphicsScene scene;
1186 QGraphicsItem *parent = scene.addRect(rect: QRectF(0, 0, 100, 100));
1187 QGraphicsItem *child = scene.addRect(rect: QRectF(25, 25, 50, 50));
1188 QGraphicsItem *grandChild = scene.addRect(rect: QRectF(50, 50, 50, 50));
1189 QGraphicsItem *stranger = scene.addRect(x: 100, y: 100, w: 100, h: 100);
1190
1191 child->setParentItem(parent);
1192 grandChild->setParentItem(child);
1193
1194 QVERIFY(grandChild->isVisible());
1195 QVERIFY(grandChild->isVisibleTo(grandChild));
1196 QVERIFY(grandChild->isVisibleTo(child));
1197 QVERIFY(grandChild->isVisibleTo(parent));
1198 QVERIFY(grandChild->isVisibleTo(nullptr));
1199 QVERIFY(child->isVisible());
1200 QVERIFY(child->isVisibleTo(child));
1201 QVERIFY(child->isVisibleTo(parent));
1202 QVERIFY(child->isVisibleTo(nullptr));
1203 QVERIFY(parent->isVisible());
1204 QVERIFY(parent->isVisibleTo(parent));
1205 QVERIFY(parent->isVisibleTo(nullptr));
1206 QVERIFY(!parent->isVisibleTo(child));
1207 QVERIFY(!child->isVisibleTo(grandChild));
1208 QVERIFY(!grandChild->isVisibleTo(stranger));
1209 QVERIFY(!child->isVisibleTo(stranger));
1210 QVERIFY(!parent->isVisibleTo(stranger));
1211 QVERIFY(!stranger->isVisibleTo(grandChild));
1212 QVERIFY(!stranger->isVisibleTo(child));
1213 QVERIFY(!stranger->isVisibleTo(parent));
1214
1215 // Case 1: only parent is explicitly hidden
1216 parent->hide();
1217
1218 QVERIFY(!grandChild->isVisible());
1219 QVERIFY(grandChild->isVisibleTo(grandChild));
1220 QVERIFY(grandChild->isVisibleTo(child));
1221 QVERIFY(grandChild->isVisibleTo(parent));
1222 QVERIFY(!grandChild->isVisibleTo(nullptr));
1223 QVERIFY(!child->isVisible());
1224 QVERIFY(child->isVisibleTo(child));
1225 QVERIFY(child->isVisibleTo(parent));
1226 QVERIFY(!child->isVisibleTo(nullptr));
1227 QVERIFY(!parent->isVisible());
1228 QVERIFY(!parent->isVisibleTo(parent));
1229 QVERIFY(!parent->isVisibleTo(nullptr));
1230 QVERIFY(!parent->isVisibleTo(child));
1231 QVERIFY(!child->isVisibleTo(grandChild));
1232 QVERIFY(!grandChild->isVisibleTo(stranger));
1233 QVERIFY(!child->isVisibleTo(stranger));
1234 QVERIFY(!parent->isVisibleTo(stranger));
1235 QVERIFY(!stranger->isVisibleTo(grandChild));
1236 QVERIFY(!stranger->isVisibleTo(child));
1237 QVERIFY(!stranger->isVisibleTo(parent));
1238
1239 // Case 2: only child is hidden
1240 parent->show();
1241 child->hide();
1242
1243 QVERIFY(!grandChild->isVisible());
1244 QVERIFY(grandChild->isVisibleTo(grandChild));
1245 QVERIFY(grandChild->isVisibleTo(child));
1246 QVERIFY(!grandChild->isVisibleTo(parent));
1247 QVERIFY(!grandChild->isVisibleTo(nullptr));
1248 QVERIFY(!child->isVisible());
1249 QVERIFY(!child->isVisibleTo(child));
1250 QVERIFY(!child->isVisibleTo(parent));
1251 QVERIFY(!child->isVisibleTo(nullptr));
1252 QVERIFY(parent->isVisible());
1253 QVERIFY(parent->isVisibleTo(parent));
1254 QVERIFY(parent->isVisibleTo(nullptr));
1255 QVERIFY(!parent->isVisibleTo(child));
1256 QVERIFY(!child->isVisibleTo(grandChild));
1257 QVERIFY(!grandChild->isVisibleTo(stranger));
1258 QVERIFY(!child->isVisibleTo(stranger));
1259 QVERIFY(!parent->isVisibleTo(stranger));
1260 QVERIFY(!stranger->isVisibleTo(grandChild));
1261 QVERIFY(!stranger->isVisibleTo(child));
1262 QVERIFY(!stranger->isVisibleTo(parent));
1263
1264 // Case 3: only grand child is hidden
1265 child->show();
1266 grandChild->hide();
1267
1268 QVERIFY(!grandChild->isVisible());
1269 QVERIFY(!grandChild->isVisibleTo(grandChild));
1270 QVERIFY(!grandChild->isVisibleTo(child));
1271 QVERIFY(!grandChild->isVisibleTo(parent));
1272 QVERIFY(!grandChild->isVisibleTo(nullptr));
1273 QVERIFY(child->isVisible());
1274 QVERIFY(child->isVisibleTo(child));
1275 QVERIFY(child->isVisibleTo(parent));
1276 QVERIFY(child->isVisibleTo(nullptr));
1277 QVERIFY(parent->isVisible());
1278 QVERIFY(parent->isVisibleTo(parent));
1279 QVERIFY(parent->isVisibleTo(nullptr));
1280 QVERIFY(!parent->isVisibleTo(child));
1281 QVERIFY(!child->isVisibleTo(grandChild));
1282 QVERIFY(!grandChild->isVisibleTo(stranger));
1283 QVERIFY(!child->isVisibleTo(stranger));
1284 QVERIFY(!parent->isVisibleTo(stranger));
1285 QVERIFY(!stranger->isVisibleTo(grandChild));
1286 QVERIFY(!stranger->isVisibleTo(child));
1287 QVERIFY(!stranger->isVisibleTo(parent));
1288}
1289
1290void tst_QGraphicsItem::explicitlyVisible()
1291{
1292 QGraphicsScene scene;
1293 QGraphicsItem *parent = scene.addRect(rect: QRectF(0, 0, 100, 100));
1294 QGraphicsItem *child = scene.addRect(rect: QRectF(25, 25, 50, 50));
1295 child->setParentItem(parent);
1296
1297 QVERIFY(parent->isVisible());
1298 QVERIFY(child->isVisible());
1299
1300 parent->hide();
1301
1302 QVERIFY(!parent->isVisible());
1303 QVERIFY(!child->isVisible());
1304
1305 parent->show();
1306 child->hide();
1307
1308 QVERIFY(parent->isVisible());
1309 QVERIFY(!child->isVisible());
1310
1311 parent->hide();
1312
1313 QVERIFY(!parent->isVisible());
1314 QVERIFY(!child->isVisible());
1315
1316 parent->show();
1317
1318 QVERIFY(parent->isVisible());
1319 QVERIFY(!child->isVisible()); // <- explicitly hidden
1320
1321 child->show();
1322
1323 QVERIFY(child->isVisible());
1324
1325 parent->hide();
1326
1327 QVERIFY(!parent->isVisible());
1328 QVERIFY(!child->isVisible()); // <- explicit show doesn't work
1329
1330 parent->show();
1331
1332 QVERIFY(parent->isVisible());
1333 QVERIFY(child->isVisible()); // <- no longer explicitly hidden
1334
1335 // ------------------- Reparenting ------------------------------
1336
1337 QGraphicsItem *parent2 = scene.addRect(x: -50, y: -50, w: 200, h: 200);
1338 QVERIFY(parent2->isVisible());
1339
1340 // Reparent implicitly hidden item to a visible parent.
1341 parent->hide();
1342 QVERIFY(!parent->isVisible());
1343 QVERIFY(!child->isVisible());
1344 child->setParentItem(parent2);
1345 QVERIFY(parent2->isVisible());
1346 QVERIFY(child->isVisible());
1347
1348 // Reparent implicitly hidden item to a hidden parent.
1349 child->setParentItem(parent);
1350 parent2->hide();
1351 child->setParentItem(parent2);
1352 QVERIFY(!parent2->isVisible());
1353 QVERIFY(!child->isVisible());
1354
1355 // Reparent explicitly hidden item to a visible parent.
1356 child->hide();
1357 parent->show();
1358 child->setParentItem(parent);
1359 QVERIFY(parent->isVisible());
1360 QVERIFY(!child->isVisible());
1361
1362 // Reparent explicitly hidden item to a hidden parent.
1363 child->setParentItem(parent2);
1364 QVERIFY(!parent2->isVisible());
1365 QVERIFY(!child->isVisible());
1366
1367 // Reparent explicitly hidden item to a visible parent.
1368 parent->show();
1369 child->setParentItem(parent);
1370 QVERIFY(parent->isVisible());
1371 QVERIFY(!child->isVisible());
1372
1373 // Reparent visible item to a hidden parent.
1374 child->show();
1375 parent2->hide();
1376 child->setParentItem(parent2);
1377 QVERIFY(!parent2->isVisible());
1378 QVERIFY(!child->isVisible());
1379 parent2->show();
1380 QVERIFY(parent2->isVisible());
1381 QVERIFY(child->isVisible());
1382
1383 // Reparent implicitly hidden child to root.
1384 parent2->hide();
1385 QVERIFY(!child->isVisible());
1386 child->setParentItem(nullptr);
1387 QVERIFY(child->isVisible());
1388
1389 // Reparent an explicitly hidden child to root.
1390 child->hide();
1391 child->setParentItem(parent2);
1392 parent2->show();
1393 QVERIFY(!child->isVisible());
1394 child->setParentItem(nullptr);
1395 QVERIFY(!child->isVisible());
1396}
1397
1398void tst_QGraphicsItem::enabled()
1399{
1400 QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20));
1401 item->setFlag(flag: QGraphicsItem::ItemIsMovable);
1402 QVERIFY(item->isEnabled());
1403 item->setEnabled(false);
1404 QVERIFY(!item->isEnabled());
1405 item->setEnabled(true);
1406 QVERIFY(item->isEnabled());
1407 item->setEnabled(false);
1408 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
1409 QGraphicsScene scene;
1410 QEvent activate(QEvent::WindowActivate);
1411 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
1412
1413 scene.addItem(item);
1414 item->setFocus();
1415 QVERIFY(!item->hasFocus());
1416 item->setEnabled(true);
1417 item->setFocus();
1418 QVERIFY(item->hasFocus());
1419 item->setEnabled(false);
1420 QVERIFY(!item->hasFocus());
1421
1422 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
1423 event.setButton(Qt::LeftButton);
1424 event.setScenePos(QPointF(0, 0));
1425 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1426 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1427 item->setEnabled(true);
1428 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1429 QCOMPARE(scene.mouseGrabberItem(), item);
1430 item->setEnabled(false);
1431 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1432}
1433
1434void tst_QGraphicsItem::explicitlyEnabled()
1435{
1436 QGraphicsScene scene;
1437 QGraphicsItem *parent = scene.addRect(rect: QRectF(0, 0, 100, 100));
1438 QGraphicsItem *child = scene.addRect(rect: QRectF(25, 25, 50, 50));
1439 child->setParentItem(parent);
1440
1441 QVERIFY(parent->isEnabled());
1442 QVERIFY(child->isEnabled());
1443
1444 parent->setEnabled(false);
1445
1446 QVERIFY(!parent->isEnabled());
1447 QVERIFY(!child->isEnabled());
1448
1449 parent->setEnabled(true);
1450 child->setEnabled(false);
1451
1452 QVERIFY(parent->isEnabled());
1453 QVERIFY(!child->isEnabled());
1454
1455 parent->setEnabled(false);
1456
1457 QVERIFY(!parent->isEnabled());
1458 QVERIFY(!child->isEnabled());
1459
1460 parent->setEnabled(true);
1461
1462 QVERIFY(parent->isEnabled());
1463 QVERIFY(!child->isEnabled()); // <- explicitly disabled
1464
1465 child->setEnabled(true);
1466
1467 QVERIFY(child->isEnabled());
1468
1469 parent->setEnabled(false);
1470
1471 QVERIFY(!parent->isEnabled());
1472 QVERIFY(!child->isEnabled()); // <- explicit enabled doesn't work
1473
1474 parent->setEnabled(true);
1475
1476 QVERIFY(parent->isEnabled());
1477 QVERIFY(child->isEnabled()); // <- no longer explicitly disabled
1478
1479 // ------------------- Reparenting ------------------------------
1480
1481 QGraphicsItem *parent2 = scene.addRect(x: -50, y: -50, w: 200, h: 200);
1482 QVERIFY(parent2->isEnabled());
1483
1484 // Reparent implicitly hidden item to a enabled parent.
1485 parent->setEnabled(false);
1486 QVERIFY(!parent->isEnabled());
1487 QVERIFY(!child->isEnabled());
1488 child->setParentItem(parent2);
1489 QVERIFY(parent2->isEnabled());
1490 QVERIFY(child->isEnabled());
1491
1492 // Reparent implicitly hidden item to a hidden parent.
1493 child->setParentItem(parent);
1494 parent2->setEnabled(false);
1495 child->setParentItem(parent2);
1496 QVERIFY(!parent2->isEnabled());
1497 QVERIFY(!child->isEnabled());
1498
1499 // Reparent explicitly hidden item to a enabled parent.
1500 child->setEnabled(false);
1501 parent->setEnabled(true);
1502 child->setParentItem(parent);
1503 QVERIFY(parent->isEnabled());
1504 QVERIFY(!child->isEnabled());
1505
1506 // Reparent explicitly hidden item to a hidden parent.
1507 child->setParentItem(parent2);
1508 QVERIFY(!parent2->isEnabled());
1509 QVERIFY(!child->isEnabled());
1510
1511 // Reparent explicitly hidden item to a enabled parent.
1512 parent->setEnabled(true);
1513 child->setParentItem(parent);
1514 QVERIFY(parent->isEnabled());
1515 QVERIFY(!child->isEnabled());
1516
1517 // Reparent enabled item to a hidden parent.
1518 child->setEnabled(true);
1519 parent2->setEnabled(false);
1520 child->setParentItem(parent2);
1521 QVERIFY(!parent2->isEnabled());
1522 QVERIFY(!child->isEnabled());
1523 parent2->setEnabled(true);
1524 QVERIFY(parent2->isEnabled());
1525 QVERIFY(child->isEnabled());
1526
1527 // Reparent implicitly hidden child to root.
1528 parent2->setEnabled(false);
1529 QVERIFY(!child->isEnabled());
1530 child->setParentItem(nullptr);
1531 QVERIFY(child->isEnabled());
1532
1533 // Reparent an explicitly hidden child to root.
1534 child->setEnabled(false);
1535 child->setParentItem(parent2);
1536 parent2->setEnabled(true);
1537 QVERIFY(!child->isEnabled());
1538 child->setParentItem(nullptr);
1539 QVERIFY(!child->isEnabled());
1540}
1541
1542class SelectChangeItem : public QGraphicsRectItem
1543{
1544public:
1545 SelectChangeItem() : QGraphicsRectItem(-50, -50, 100, 100) { setBrush(Qt::blue); }
1546 QVector<bool> values;
1547
1548protected:
1549 QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
1550 {
1551 if (change == ItemSelectedChange)
1552 values << value.toBool();
1553 return QGraphicsRectItem::itemChange(change, value);
1554 }
1555};
1556
1557void tst_QGraphicsItem::selected()
1558{
1559 SelectChangeItem *item = new SelectChangeItem;
1560 item->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1561 QVERIFY(!item->isSelected());
1562 QVERIFY(item->values.isEmpty());
1563 item->setSelected(true);
1564 QCOMPARE(item->values.size(), 1);
1565 QCOMPARE(item->values.constLast(), true);
1566 QVERIFY(item->isSelected());
1567 item->setSelected(false);
1568 QCOMPARE(item->values.size(), 2);
1569 QCOMPARE(item->values.constLast(), false);
1570 QVERIFY(!item->isSelected());
1571 item->setSelected(true);
1572 QCOMPARE(item->values.size(), 3);
1573 item->setEnabled(false);
1574 QCOMPARE(item->values.size(), 4);
1575 QCOMPARE(item->values.constLast(), false);
1576 QVERIFY(!item->isSelected());
1577 item->setEnabled(true);
1578 QCOMPARE(item->values.size(), 4);
1579 item->setSelected(true);
1580 QCOMPARE(item->values.size(), 5);
1581 QCOMPARE(item->values.constLast(), true);
1582 QVERIFY(item->isSelected());
1583 item->setVisible(false);
1584 QCOMPARE(item->values.size(), 6);
1585 QCOMPARE(item->values.constLast(), false);
1586 QVERIFY(!item->isSelected());
1587 item->setVisible(true);
1588 QCOMPARE(item->values.size(), 6);
1589 item->setSelected(true);
1590 QCOMPARE(item->values.size(), 7);
1591 QCOMPARE(item->values.constLast(), true);
1592 QVERIFY(item->isSelected());
1593
1594 QGraphicsScene scene(-100, -100, 200, 200);
1595 scene.addItem(item);
1596 QCOMPARE(scene.selectedItems(), GraphicsItemsList{item});
1597 item->setSelected(false);
1598 QVERIFY(scene.selectedItems().isEmpty());
1599 item->setSelected(true);
1600 QCOMPARE(scene.selectedItems(), GraphicsItemsList{item});
1601 item->setSelected(false);
1602 QVERIFY(scene.selectedItems().isEmpty());
1603
1604 // Interactive selection
1605 QGraphicsView view(&scene);
1606 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1607 view.setFixedSize(w: 250, h: 250);
1608 view.show();
1609
1610 QVERIFY(QTest::qWaitForWindowExposed(&view));
1611 QCoreApplication::processEvents();
1612 QCoreApplication::processEvents();
1613
1614 scene.clearSelection();
1615 QCOMPARE(item->values.size(), 10);
1616 QCOMPARE(item->values.constLast(), false);
1617 QVERIFY(!item->isSelected());
1618
1619 // Click inside and check that it's selected
1620 QTest::mouseMove(widget: view.viewport());
1621 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item->scenePos()));
1622 QCOMPARE(item->values.size(), 11);
1623 QCOMPARE(item->values.constLast(), true);
1624 QVERIFY(item->isSelected());
1625
1626 // Click outside and check that it's not selected
1627 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item->scenePos() + QPointF(item->boundingRect().width(), item->boundingRect().height())));
1628 QCOMPARE(item->values.size(), 12);
1629 QCOMPARE(item->values.constLast(), false);
1630 QVERIFY(!item->isSelected());
1631
1632 SelectChangeItem *item2 = new SelectChangeItem;
1633 item2->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1634 item2->setPos(ax: 100, ay: 0);
1635 scene.addItem(item: item2);
1636
1637 // Click inside and check that it's selected
1638 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item->scenePos()));
1639 QCOMPARE(item->values.size(), 13);
1640 QCOMPARE(item->values.constLast(), true);
1641 QVERIFY(item->isSelected());
1642
1643 // Click inside item2 and check that it's selected, and item is not
1644 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item2->scenePos()));
1645 QCOMPARE(item->values.size(), 14);
1646 QCOMPARE(item->values.constLast(), false);
1647 QVERIFY(!item->isSelected());
1648 QCOMPARE(item2->values.size(), 1);
1649 QCOMPARE(item2->values.constLast(), true);
1650 QVERIFY(item2->isSelected());
1651}
1652
1653void tst_QGraphicsItem::selected2()
1654{
1655 // Selecting an item, then moving another previously caused a crash.
1656 QGraphicsScene scene;
1657 QGraphicsItem *line1 = scene.addRect(rect: QRectF(0, 0, 100, 100));
1658 line1->setPos(ax: -105, ay: 0);
1659 line1->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1660
1661 QGraphicsItem *line2 = scene.addRect(rect: QRectF(0, 0, 100, 100));
1662 line2->setFlag(flag: QGraphicsItem::ItemIsMovable);
1663
1664 line1->setSelected(true);
1665
1666 {
1667 QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress);
1668 mousePress.setScenePos(QPointF(50, 50));
1669 mousePress.setButton(Qt::LeftButton);
1670 QCoreApplication::sendEvent(receiver: &scene, event: &mousePress);
1671 QVERIFY(mousePress.isAccepted());
1672 }
1673 {
1674 QGraphicsSceneMouseEvent mouseMove(QEvent::GraphicsSceneMouseMove);
1675 mouseMove.setScenePos(QPointF(60, 60));
1676 mouseMove.setButton(Qt::LeftButton);
1677 mouseMove.setButtons(Qt::LeftButton);
1678 QCoreApplication::sendEvent(receiver: &scene, event: &mouseMove);
1679 QVERIFY(mouseMove.isAccepted());
1680 }
1681}
1682
1683void tst_QGraphicsItem::selected_group()
1684{
1685 QGraphicsScene scene;
1686 QGraphicsItem *item1 = scene.addRect(rect: QRectF());
1687 QGraphicsItem *item2 = scene.addRect(rect: QRectF());
1688 item1->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1689 item2->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1690 scene.addRect(rect: QRectF())->setParentItem(item1);
1691 QGraphicsItem *leaf = scene.addRect(rect: QRectF());
1692 leaf->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1693 leaf->setParentItem(item2);
1694
1695 QGraphicsItemGroup *group = scene.createItemGroup(items: GraphicsItemsList{item1, item2});
1696 QCOMPARE(group->scene(), &scene);
1697 group->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1698 const auto items = scene.items();
1699 for (QGraphicsItem *item : items) {
1700 if (item == group)
1701 QVERIFY(!item->group());
1702 else
1703 QCOMPARE(item->group(), group);
1704 }
1705
1706 QVERIFY(group->handlesChildEvents());
1707 QVERIFY(!group->isSelected());
1708 group->setSelected(false);
1709 QVERIFY(!group->isSelected());
1710 group->setSelected(true);
1711 QVERIFY(group->isSelected());
1712
1713 const auto itemIsSelected = [](const QGraphicsItem *item) { return item->isSelected(); };
1714 QVERIFY(std::all_of(items.cbegin(), items.cend(), itemIsSelected));
1715 group->setSelected(false);
1716 QVERIFY(!group->isSelected());
1717 QVERIFY(std::none_of(items.cbegin(), items.cend(), itemIsSelected));
1718 leaf->setSelected(true);
1719 QVERIFY(std::all_of(items.cbegin(), items.cend(), itemIsSelected));
1720 leaf->setSelected(false);
1721 QVERIFY(std::none_of(items.cbegin(), items.cend(), itemIsSelected));
1722
1723 leaf->setSelected(true);
1724 QGraphicsScene scene2;
1725 scene2.addItem(item: item1);
1726 QVERIFY(!item1->isSelected());
1727 QVERIFY(item2->isSelected());
1728}
1729
1730void tst_QGraphicsItem::selected_textItem()
1731{
1732 QGraphicsScene scene;
1733 QGraphicsTextItem *text = scene.addText(text: QLatin1String("Text"));
1734 text->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1735
1736 QGraphicsView view(&scene);
1737 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1738 view.show();
1739 QVERIFY(QTest::qWaitForWindowExposed(&view));
1740
1741 QTRY_VERIFY(!text->isSelected());
1742 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {},
1743 pos: view.mapFromScene(point: text->mapToScene(ax: 0, ay: 0)));
1744 QTRY_VERIFY(text->isSelected());
1745
1746 text->setSelected(false);
1747 text->setTextInteractionFlags(Qt::TextEditorInteraction);
1748
1749 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {},
1750 pos: view.mapFromScene(point: text->mapToScene(ax: 0, ay: 0)));
1751 QTRY_VERIFY(text->isSelected());
1752}
1753
1754void tst_QGraphicsItem::selected_multi()
1755{
1756 // Test multiselection behavior
1757 QGraphicsScene scene;
1758
1759 // Create two disjoint items
1760 QGraphicsItem *item1 = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1761 QGraphicsItem *item2 = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1762 item1->setPos(ax: -15, ay: 0);
1763 item2->setPos(ax: 15, ay: 20);
1764
1765 // Make both items selectable
1766 item1->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1767 item2->setFlag(flag: QGraphicsItem::ItemIsSelectable);
1768
1769 // Create and show a view
1770 QGraphicsView view(&scene);
1771 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1772 view.show();
1773 view.fitInView(rect: scene.sceneRect());
1774 QVERIFY(QTest::qWaitForWindowExposed(&view));
1775 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
1776 QVERIFY(QTest::qWaitForWindowActive(&view));
1777
1778 QVERIFY(!item1->isSelected());
1779 QVERIFY(!item2->isSelected());
1780
1781 // Click on item1
1782 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item1->scenePos()));
1783 QVERIFY(item1->isSelected());
1784 QVERIFY(!item2->isSelected());
1785
1786 // Click on item2
1787 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item2->scenePos()));
1788 QVERIFY(item2->isSelected());
1789 QVERIFY(!item1->isSelected());
1790
1791 // Ctrl-click on item1
1792 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(point: item1->scenePos()));
1793 QVERIFY(item2->isSelected());
1794 QVERIFY(item1->isSelected());
1795
1796 // Ctrl-click on item1 again
1797 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(point: item1->scenePos()));
1798 QVERIFY(item2->isSelected());
1799 QVERIFY(!item1->isSelected());
1800
1801 // Ctrl-click on item2
1802 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(point: item2->scenePos()));
1803 QVERIFY(!item2->isSelected());
1804 QVERIFY(!item1->isSelected());
1805
1806 // Click on item1
1807 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item1->scenePos()));
1808 QVERIFY(item1->isSelected());
1809 QVERIFY(!item2->isSelected());
1810
1811 // Click on scene
1812 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(ax: 0, ay: 0));
1813 QVERIFY(!item1->isSelected());
1814 QVERIFY(!item2->isSelected());
1815
1816 // Click on item1
1817 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item1->scenePos()));
1818 QVERIFY(item1->isSelected());
1819 QVERIFY(!item2->isSelected());
1820
1821 // Ctrl-click on scene
1822 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(ax: 0, ay: 0));
1823 QVERIFY(item1->isSelected());
1824 QVERIFY(!item2->isSelected());
1825
1826 // Click on scene
1827 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(ax: 0, ay: 0));
1828 QVERIFY(!item1->isSelected());
1829 QVERIFY(!item2->isSelected());
1830
1831 // Click on item1
1832 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item1->scenePos()));
1833 QVERIFY(item1->isSelected());
1834 QVERIFY(!item2->isSelected());
1835
1836 // Press on item2
1837 QTest::mousePress(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item2->scenePos()));
1838 QVERIFY(!item1->isSelected());
1839 QVERIFY(item2->isSelected());
1840
1841 // Release on item2
1842 QTest::mouseRelease(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item2->scenePos()));
1843 QVERIFY(!item1->isSelected());
1844 QVERIFY(item2->isSelected());
1845
1846 // Click on item1
1847 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item1->scenePos()));
1848 QVERIFY(item1->isSelected());
1849 QVERIFY(!item2->isSelected());
1850
1851 // Ctrl-click on item1
1852 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(point: item1->scenePos()));
1853 QVERIFY(!item1->isSelected());
1854 QVERIFY(!item2->isSelected());
1855
1856 // Ctrl-press on item1
1857 QTest::mousePress(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(point: item1->scenePos()));
1858 QVERIFY(!item1->isSelected());
1859 QVERIFY(!item2->isSelected());
1860
1861 {
1862 // Ctrl-move on item1
1863 const QPoint item1Point = view.mapFromScene(point: item1->scenePos()) + QPoint(1, 0);
1864 QMouseEvent event(QEvent::MouseMove, item1Point, view.viewport()->mapToGlobal(item1Point), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier);
1865 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
1866 QVERIFY(!item1->isSelected());
1867 QVERIFY(!item2->isSelected());
1868 }
1869
1870 // Release on item1
1871 QTest::mouseRelease(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(point: item1->scenePos()));
1872 QVERIFY(item1->isSelected());
1873 QVERIFY(!item2->isSelected());
1874
1875 item1->setFlag(flag: QGraphicsItem::ItemIsMovable);
1876 item1->setSelected(false);
1877
1878 // Ctrl-press on item1
1879 QTest::mousePress(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(point: item1->scenePos()));
1880 QVERIFY(!item1->isSelected());
1881 QVERIFY(!item2->isSelected());
1882
1883 {
1884 // Ctrl-move on item1
1885 const QPoint item1Point = view.mapFromScene(point: item1->scenePos()) + QPoint(1, 0);
1886 QMouseEvent event(QEvent::MouseMove, item1Point, view.viewport()->mapToGlobal(item1Point), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier);
1887 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
1888 QVERIFY(item1->isSelected());
1889 QVERIFY(!item2->isSelected());
1890 }
1891
1892 // Release on item1
1893 QTest::mouseRelease(widget: view.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: view.mapFromScene(point: item1->scenePos()));
1894 QVERIFY(item1->isSelected());
1895 QVERIFY(!item2->isSelected());
1896}
1897
1898void tst_QGraphicsItem::acceptedMouseButtons()
1899{
1900 QGraphicsScene scene;
1901 QGraphicsRectItem *item1 = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1902 QGraphicsRectItem *item2 = scene.addRect(rect: QRectF(-10, -10, 20, 20));
1903 item2->setZValue(1);
1904
1905 item1->setFlag(flag: QGraphicsItem::ItemIsMovable);
1906 item2->setFlag(flag: QGraphicsItem::ItemIsMovable);
1907
1908 QCOMPARE(item1->acceptedMouseButtons(), Qt::MouseButtons(0x1f));
1909 QCOMPARE(item2->acceptedMouseButtons(), Qt::MouseButtons(0x1f));
1910
1911 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
1912 event.setButton(Qt::LeftButton);
1913 event.setScenePos(QPointF(0, 0));
1914 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1915 QCOMPARE(scene.mouseGrabberItem(), item2);
1916 item2->setAcceptedMouseButtons({ });
1917 QCOMPARE(scene.mouseGrabberItem(), nullptr);
1918 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1919 QCOMPARE(scene.mouseGrabberItem(), item1);
1920}
1921
1922class HoverItem : public QGraphicsRectItem
1923{
1924public:
1925 HoverItem(const QRectF &rect) : QGraphicsRectItem(rect) { }
1926
1927 int hoverInCount = 0;
1928 int hoverMoveCount = 0;
1929 int hoverOutCount = 0;
1930protected:
1931 void hoverEnterEvent(QGraphicsSceneHoverEvent *) override
1932 { ++hoverInCount; }
1933
1934 void hoverMoveEvent(QGraphicsSceneHoverEvent *) override
1935 { ++hoverMoveCount; }
1936
1937 void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override
1938 { ++hoverOutCount; }
1939};
1940
1941void tst_QGraphicsItem::acceptHoverEvents()
1942{
1943 QGraphicsScene scene;
1944 HoverItem *item1 = new HoverItem(QRectF(-10, -10, 20, 20));
1945 HoverItem *item2 = new HoverItem(QRectF(-5, -5, 10, 10));
1946 scene.addItem(item: item1);
1947 scene.addItem(item: item2);
1948 item2->setZValue(1);
1949
1950 QVERIFY(!item1->acceptHoverEvents());
1951 QVERIFY(!item2->acceptHoverEvents());
1952 item1->setAcceptHoverEvents(true);
1953 item2->setAcceptHoverEvents(true);
1954
1955 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
1956 event.setScenePos(QPointF(-100, -100));
1957 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1958 event.setScenePos(QPointF(-2.5, -2.5));
1959 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1960
1961 QCOMPARE(item1->hoverInCount, 0);
1962 QCOMPARE(item2->hoverInCount, 1);
1963
1964 item1->setAcceptHoverEvents(false);
1965 item2->setAcceptHoverEvents(false);
1966
1967 event.setScenePos(QPointF(-100, -100));
1968 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1969 event.setScenePos(QPointF(-2.5, -2.5));
1970 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1971
1972 QCOMPARE(item1->hoverInCount, 0);
1973 QCOMPARE(item2->hoverInCount, 1);
1974
1975 item1->setAcceptHoverEvents(true);
1976 item2->setAcceptHoverEvents(false);
1977
1978 event.setScenePos(QPointF(-100, -100));
1979 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1980 event.setScenePos(QPointF(-2.5, -2.5));
1981 QCoreApplication::sendEvent(receiver: &scene, event: &event);
1982
1983 QCOMPARE(item1->hoverInCount, 1);
1984 QCOMPARE(item2->hoverInCount, 1);
1985}
1986
1987void tst_QGraphicsItem::childAcceptsHoverEvents()
1988{
1989 QGraphicsScene scene;
1990 HoverItem *item1 = new HoverItem(QRectF(-10, -10, 20, 20));
1991 HoverItem *item2 = new HoverItem(QRectF(-5, -5, 10, 10));
1992
1993 scene.addItem(item: item1);
1994 scene.addItem(item: item2);
1995 item2->setParentItem(item1);
1996 item2->setAcceptHoverEvents(true);
1997
1998 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
1999 event.setScenePos(QPointF(-100, -100));
2000 QCoreApplication::sendEvent(receiver: &scene, event: &event);
2001 QCOMPARE(item2->hoverInCount, 0);
2002 QCOMPARE(item2->hoverMoveCount, 0);
2003 QCOMPARE(item2->hoverOutCount, 0);
2004 QCOMPARE(item1->hoverInCount, 0);
2005 QCOMPARE(item1->hoverMoveCount, 0);
2006 QCOMPARE(item1->hoverOutCount, 0);
2007
2008 event.setScenePos(QPointF(-2.5, -2.5));
2009 QCoreApplication::sendEvent(receiver: &scene, event: &event);
2010
2011 QCOMPARE(item2->hoverInCount, 1);
2012 QCOMPARE(item2->hoverMoveCount, 1);
2013 QCOMPARE(item2->hoverOutCount, 0);
2014 QCOMPARE(item1->hoverInCount, 0);
2015 QCOMPARE(item1->hoverMoveCount, 0);
2016 QCOMPARE(item1->hoverOutCount, 0);
2017
2018 event.setScenePos(QPointF(0, 0));
2019 QCoreApplication::sendEvent(receiver: &scene, event: &event);
2020
2021 QCOMPARE(item2->hoverInCount, 1);
2022 QCOMPARE(item2->hoverMoveCount, 2);
2023 QCOMPARE(item2->hoverOutCount, 0);
2024 QCOMPARE(item1->hoverInCount, 0);
2025 QCOMPARE(item1->hoverMoveCount, 0);
2026 QCOMPARE(item1->hoverOutCount, 0);
2027
2028 event.setScenePos(QPointF(-7, -7));
2029 QCoreApplication::sendEvent(receiver: &scene, event: &event);
2030
2031 QCOMPARE(item2->hoverInCount, 1);
2032 QCOMPARE(item2->hoverMoveCount, 2);
2033 QCOMPARE(item2->hoverOutCount, 1);
2034 QCOMPARE(item1->hoverInCount, 0);
2035 QCOMPARE(item1->hoverMoveCount, 0);
2036 QCOMPARE(item1->hoverOutCount, 0);
2037
2038 event.setScenePos(QPointF(0, 0));
2039 QCoreApplication::sendEvent(receiver: &scene, event: &event);
2040
2041 QCOMPARE(item2->hoverInCount, 2);
2042 QCOMPARE(item2->hoverMoveCount, 3);
2043 QCOMPARE(item2->hoverOutCount, 1);
2044 QCOMPARE(item1->hoverInCount, 0);
2045 QCOMPARE(item1->hoverMoveCount, 0);
2046 QCOMPARE(item1->hoverOutCount, 0);
2047
2048 HoverItem *item0 = new HoverItem(QRectF(-20, -20, 20, 20));
2049 scene.addItem(item: item0);
2050 item1->setParentItem(item0);
2051 item0->setAcceptHoverEvents(true);
2052
2053 event.setScenePos(QPointF(-100, -100));
2054 QCoreApplication::sendEvent(receiver: &scene, event: &event);
2055
2056 event.setScenePos(QPointF(-15, -15));
2057 QCoreApplication::sendEvent(receiver: &scene, event: &event);
2058
2059 QCOMPARE(item2->hoverInCount, 2);
2060 QCOMPARE(item2->hoverMoveCount, 3);
2061 QCOMPARE(item2->hoverOutCount, 2);
2062 QCOMPARE(item1->hoverInCount, 0);
2063 QCOMPARE(item1->hoverMoveCount, 0);
2064 QCOMPARE(item1->hoverOutCount, 0);
2065 QCOMPARE(item0->hoverInCount, 1);
2066 QCOMPARE(item0->hoverMoveCount, 1);
2067 QCOMPARE(item0->hoverOutCount, 0);
2068}
2069
2070void tst_QGraphicsItem::hasFocus()
2071{
2072 QGraphicsLineItem *line = new QGraphicsLineItem;
2073 QVERIFY(!line->hasFocus());
2074 line->setFocus();
2075 QVERIFY(!line->hasFocus());
2076
2077 QGraphicsScene scene;
2078 QEvent activate(QEvent::WindowActivate);
2079 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
2080
2081 scene.addItem(item: line);
2082
2083 line->setFocus();
2084 QVERIFY(!line->hasFocus());
2085 line->setFlag(flag: QGraphicsItem::ItemIsFocusable);
2086 line->setFocus();
2087 QVERIFY(line->hasFocus());
2088
2089 QGraphicsScene scene2;
2090 QCoreApplication::sendEvent(receiver: &scene2, event: &activate);
2091
2092 scene2.addItem(item: line);
2093 QVERIFY(!line->hasFocus());
2094
2095 QCOMPARE(scene.focusItem(), nullptr);
2096 QCOMPARE(scene2.focusItem(), nullptr);
2097
2098 line->setFocus();
2099 QVERIFY(line->hasFocus());
2100 line->clearFocus();
2101 QVERIFY(!line->hasFocus());
2102
2103 QGraphicsLineItem *line2 = new QGraphicsLineItem;
2104 line2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
2105 scene2.addItem(item: line2);
2106
2107 line2->setFocus();
2108 QVERIFY(!line->hasFocus());
2109 QVERIFY(line2->hasFocus());
2110 line->setFocus();
2111 QVERIFY(line->hasFocus());
2112 QVERIFY(!line2->hasFocus());
2113}
2114
2115void tst_QGraphicsItem::pos()
2116{
2117 QGraphicsItem *child = new QGraphicsLineItem;
2118 QGraphicsItem *parent = new QGraphicsLineItem;
2119
2120 QCOMPARE(child->pos(), QPointF());
2121 QCOMPARE(parent->pos(), QPointF());
2122
2123 child->setParentItem(parent);
2124 child->setPos(ax: 10, ay: 10);
2125
2126 QCOMPARE(child->pos(), QPointF(10, 10));
2127
2128 parent->setPos(ax: 10, ay: 10);
2129
2130 QCOMPARE(parent->pos(), QPointF(10, 10));
2131 QCOMPARE(child->pos(), QPointF(10, 10));
2132
2133 delete child;
2134 delete parent;
2135}
2136
2137void tst_QGraphicsItem::scenePos()
2138{
2139 QGraphicsItem *child = new QGraphicsLineItem;
2140 QGraphicsItem *parent = new QGraphicsLineItem;
2141
2142 QCOMPARE(child->scenePos(), QPointF());
2143 QCOMPARE(parent->scenePos(), QPointF());
2144
2145 child->setParentItem(parent);
2146 child->setPos(ax: 10, ay: 10);
2147
2148 QCOMPARE(child->scenePos(), QPointF(10, 10));
2149
2150 parent->setPos(ax: 10, ay: 10);
2151
2152 QCOMPARE(parent->scenePos(), QPointF(10, 10));
2153 QCOMPARE(child->scenePos(), QPointF(20, 20));
2154
2155 parent->setPos(ax: 20, ay: 20);
2156
2157 QCOMPARE(parent->scenePos(), QPointF(20, 20));
2158 QCOMPARE(child->scenePos(), QPointF(30, 30));
2159
2160 delete child;
2161 delete parent;
2162}
2163
2164void tst_QGraphicsItem::matrix()
2165{
2166 QGraphicsLineItem line;
2167 QCOMPARE(line.transform(), QTransform());
2168 line.setTransform(matrix: QTransform().rotate(a: 90));
2169 QCOMPARE(line.transform(), QTransform().rotate(90));
2170 line.setTransform(matrix: QTransform().rotate(a: 90));
2171 QCOMPARE(line.transform(), QTransform().rotate(90));
2172 line.setTransform(matrix: QTransform().rotate(a: 90), combine: true);
2173 QCOMPARE(line.transform(), QTransform().rotate(180));
2174 line.setTransform(matrix: QTransform().rotate(a: -90), combine: true);
2175 QCOMPARE(line.transform(), QTransform().rotate(90));
2176 line.resetTransform();
2177 QCOMPARE(line.transform(), QTransform());
2178
2179 line.setTransform(matrix: QTransform().rotate(a: 90), combine: true);
2180 QCOMPARE(line.transform(), QTransform().rotate(90));
2181 line.setTransform(matrix: QTransform().rotate(a: 90), combine: true);
2182 QCOMPARE(line.transform(), QTransform().rotate(90).rotate(90));
2183 line.resetTransform();
2184
2185 line.setTransform(matrix: QTransform::fromScale(dx: 2, dy: 4), combine: true);
2186 QCOMPARE(line.transform(), QTransform::fromScale(2, 4));
2187 line.setTransform(matrix: QTransform::fromScale(dx: 2, dy: 4), combine: true);
2188 QCOMPARE(line.transform(), QTransform::fromScale(2, 4).scale(2, 4));
2189 line.resetTransform();
2190
2191 line.setTransform(matrix: QTransform().shear(sh: 2, sv: 4), combine: true);
2192 QCOMPARE(line.transform(), QTransform().shear(2, 4));
2193 line.setTransform(matrix: QTransform().shear(sh: 2, sv: 4), combine: true);
2194 QCOMPARE(line.transform(), QTransform().shear(2, 4).shear(2, 4));
2195 line.resetTransform();
2196
2197 line.setTransform(matrix: QTransform::fromTranslate(dx: 10, dy: 10), combine: true);
2198 QCOMPARE(line.transform(), QTransform::fromTranslate(10, 10));
2199 line.setTransform(matrix: QTransform::fromTranslate(dx: 10, dy: 10), combine: true);
2200 QCOMPARE(line.transform(), QTransform::fromTranslate(10, 10).translate(10, 10));
2201 line.resetTransform();
2202}
2203
2204void tst_QGraphicsItem::sceneTransform()
2205{
2206 QGraphicsLineItem *parent = new QGraphicsLineItem;
2207 QGraphicsLineItem *child = new QGraphicsLineItem(QLineF(), parent);
2208
2209 QCOMPARE(parent->sceneTransform(), QTransform());
2210 QCOMPARE(child->sceneTransform(), QTransform());
2211
2212 parent->setTransform(matrix: QTransform::fromTranslate(dx: 10, dy: 10), combine: true);
2213 QCOMPARE(parent->sceneTransform(), QTransform().translate(10, 10));
2214 QCOMPARE(child->sceneTransform(), QTransform().translate(10, 10));
2215
2216 child->setTransform(matrix: QTransform::fromTranslate(dx: 10, dy: 10), combine: true);
2217 QCOMPARE(parent->sceneTransform(), QTransform().translate(10, 10));
2218 QCOMPARE(child->sceneTransform(), QTransform().translate(20, 20));
2219
2220 parent->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
2221 QCOMPARE(parent->sceneTransform(), QTransform().translate(10, 10).rotate(90));
2222 QCOMPARE(child->sceneTransform(), QTransform().translate(10, 10).rotate(90).translate(10, 10));
2223
2224 delete child;
2225 delete parent;
2226}
2227
2228void tst_QGraphicsItem::setTransform()
2229{
2230 QGraphicsScene scene;
2231 QSignalSpy spy(&scene, &QGraphicsScene::changed);
2232 QRectF unrotatedRect(-12, -34, 56, 78);
2233 QGraphicsRectItem item(unrotatedRect, nullptr);
2234 item.setPen(QPen(Qt::black, 0));
2235 scene.addItem(item: &item);
2236 scene.update(rect: scene.sceneRect());
2237 QCoreApplication::processEvents();
2238
2239 QCOMPARE(spy.count(), 1);
2240
2241 item.setTransform(matrix: QTransform().rotate(a: qreal(12.34)));
2242 QRectF rotatedRect = scene.sceneRect();
2243 QVERIFY(unrotatedRect != rotatedRect);
2244 scene.update(rect: scene.sceneRect());
2245 QCoreApplication::processEvents();
2246
2247 QCOMPARE(spy.count(), 2);
2248
2249 item.setTransform(matrix: QTransform());
2250
2251 scene.update(rect: scene.sceneRect());
2252 QCoreApplication::processEvents();
2253
2254 QCOMPARE(spy.count(), 3);
2255 QList<QRectF> rlist = qvariant_cast<QList<QRectF> >(v: spy.last().at(i: 0));
2256
2257 QCOMPARE(rlist.size(), 2);
2258 // From item.setMatrix() (clearing rotated rect), from scene.update() (updating scene rect),
2259 // squashed into one
2260 QCOMPARE(rlist.at(0), rotatedRect);
2261 QCOMPARE(rlist.at(1), unrotatedRect); // From post-update (update current state)
2262}
2263
2264static GraphicsItems _paintedItems;
2265class PainterItem : public QGraphicsItem
2266{
2267protected:
2268 QRectF boundingRect() const override
2269 { return QRectF(-10, -10, 20, 20); }
2270
2271 void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
2272 {
2273 _paintedItems << this;
2274 painter->fillRect(r: boundingRect(), c: Qt::red);
2275 }
2276};
2277
2278void tst_QGraphicsItem::zValue()
2279{
2280 Q_CHECK_PAINTEVENTS
2281
2282 QGraphicsScene scene;
2283
2284 QGraphicsItem *item1 = new PainterItem;
2285 QGraphicsItem *item2 = new PainterItem;
2286 QGraphicsItem *item3 = new PainterItem;
2287 QGraphicsItem *item4 = new PainterItem;
2288 scene.addItem(item: item1);
2289 scene.addItem(item: item2);
2290 scene.addItem(item: item3);
2291 scene.addItem(item: item4);
2292 item2->setZValue(-3);
2293 item4->setZValue(-2);
2294 item1->setZValue(-1);
2295 item3->setZValue(0);
2296
2297 QGraphicsView view(&scene);
2298 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2299 view.show();
2300 QVERIFY(QTest::qWaitForWindowExposed(&view));
2301
2302 QApplication::processEvents();
2303
2304 QTRY_VERIFY(!_paintedItems.isEmpty());
2305 QVERIFY((_paintedItems.size() % 4) == 0);
2306 for (int i = 0; i < 3; ++i)
2307 QVERIFY(_paintedItems.at(i)->zValue() < _paintedItems.at(i + 1)->zValue());
2308}
2309
2310void tst_QGraphicsItem::shape()
2311{
2312 QGraphicsLineItem line(QLineF(-10, -10, 20, 20));
2313 line.setPen(QPen(Qt::black, 0));
2314
2315 // We unfortunately need this hack as QPainterPathStroker will set a width of 1.0
2316 // if we pass a value of 0.0 to QPainterPathStroker::setWidth()
2317 const qreal penWidthZero = qreal(0.00000001);
2318
2319 QPainterPathStroker ps;
2320 ps.setWidth(penWidthZero);
2321
2322 QPainterPath path(line.line().p1());
2323 path.lineTo(p: line.line().p2());
2324 QPainterPath p = ps.createStroke(path);
2325 p.addPath(path);
2326 QCOMPARE(line.shape(), p);
2327
2328 QPen linePen;
2329 linePen.setWidthF(5.0);
2330 linePen.setCapStyle(Qt::RoundCap);
2331 line.setPen(linePen);
2332
2333 ps.setCapStyle(line.pen().capStyle());
2334 ps.setWidth(line.pen().widthF());
2335 p = ps.createStroke(path);
2336 p.addPath(path);
2337 QCOMPARE(line.shape(), p);
2338
2339 linePen.setCapStyle(Qt::FlatCap);
2340 line.setPen(linePen);
2341 ps.setCapStyle(line.pen().capStyle());
2342 p = ps.createStroke(path);
2343 p.addPath(path);
2344 QCOMPARE(line.shape(), p);
2345
2346 linePen.setCapStyle(Qt::SquareCap);
2347 line.setPen(linePen);
2348 ps.setCapStyle(line.pen().capStyle());
2349 p = ps.createStroke(path);
2350 p.addPath(path);
2351 QCOMPARE(line.shape(), p);
2352
2353 QGraphicsRectItem rect(QRectF(-10, -10, 20, 20));
2354 rect.setPen(QPen(Qt::black, 0));
2355 QPainterPathStroker ps1;
2356 ps1.setWidth(penWidthZero);
2357 path = QPainterPath();
2358 path.addRect(rect: rect.rect());
2359 p = ps1.createStroke(path);
2360 p.addPath(path);
2361 QCOMPARE(rect.shape(), p);
2362
2363 QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20));
2364 ellipse.setPen(QPen(Qt::black, 0));
2365 QPainterPathStroker ps2;
2366 ps2.setWidth(ellipse.pen().widthF() <= 0.0 ? penWidthZero : ellipse.pen().widthF());
2367 path = QPainterPath();
2368 path.addEllipse(rect: ellipse.rect());
2369 p = ps2.createStroke(path);
2370 p.addPath(path);
2371 QCOMPARE(ellipse.shape(), p);
2372
2373 QPainterPathStroker ps3;
2374 ps3.setWidth(penWidthZero);
2375 p = ps3.createStroke(path);
2376 p.addPath(path);
2377 QGraphicsPathItem pathItem(path);
2378 pathItem.setPen(QPen(Qt::black, 0));
2379 QCOMPARE(pathItem.shape(), p);
2380
2381 QRegion region(QRect(0, 0, 300, 200));
2382 region = region.subtracted(r: QRect(50, 50, 200, 100));
2383
2384 QImage image(300, 200, QImage::Format_ARGB32_Premultiplied);
2385 image.fill(pixel: 0);
2386 QPainter painter(&image);
2387 painter.setClipRegion(region);
2388 painter.fillRect(x: 0, y: 0, w: 300, h: 200, c: Qt::green);
2389 painter.end();
2390 QPixmap pixmap = QPixmap::fromImage(image);
2391
2392 QGraphicsPixmapItem pixmapItem(pixmap);
2393 path = QPainterPath();
2394 path.addRegion(region);
2395
2396 {
2397 QBitmap bitmap(300, 200);
2398 bitmap.clear();
2399 QPainter painter(&bitmap);
2400 painter.setClipRegion(region);
2401 painter.fillRect(x: 0, y: 0, w: 300, h: 200, c: Qt::color1);
2402 painter.end();
2403
2404 QBitmap bitmap2(300, 200);
2405 bitmap2.clear();
2406 painter.begin(&bitmap2);
2407 painter.setClipPath(path: pixmapItem.shape());
2408 painter.fillRect(x: 0, y: 0, w: 300, h: 200, c: Qt::color1);
2409 painter.end();
2410
2411 QCOMPARE(bitmap.toImage(), bitmap2.toImage());
2412 }
2413
2414 QPolygonF poly;
2415 poly << QPointF(0, 0) << QPointF(10, 0) << QPointF(0, 10);
2416 QGraphicsPolygonItem polygon(poly);
2417 polygon.setPen(QPen(Qt::black, 0));
2418 path = QPainterPath();
2419 path.addPolygon(polygon: poly);
2420
2421 QPainterPathStroker ps4;
2422 ps4.setWidth(penWidthZero);
2423 p = ps4.createStroke(path);
2424 p.addPath(path);
2425 QCOMPARE(polygon.shape(), p);
2426}
2427
2428void tst_QGraphicsItem::contains()
2429{
2430 if (sizeof(qreal) != sizeof(double))
2431 QSKIP("Skipped due to rounding errors");
2432
2433 // Rect
2434 QGraphicsRectItem rect(QRectF(-10, -10, 20, 20));
2435 QVERIFY(!rect.contains(QPointF(-11, -10)));
2436 QVERIFY(rect.contains(QPointF(-10, -10)));
2437 QVERIFY(!rect.contains(QPointF(-11, 0)));
2438 QVERIFY(rect.contains(QPointF(-10, 0)));
2439 QVERIFY(rect.contains(QPointF(0, -10)));
2440 QVERIFY(rect.contains(QPointF(0, 0)));
2441 QVERIFY(rect.contains(QPointF(9, 9)));
2442
2443 // Ellipse
2444 QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20));
2445 QVERIFY(!ellipse.contains(QPointF(-10, -10)));
2446 QVERIFY(ellipse.contains(QPointF(-9, 0)));
2447 QVERIFY(ellipse.contains(QPointF(0, -9)));
2448 QVERIFY(ellipse.contains(QPointF(0, 0)));
2449 QVERIFY(!ellipse.contains(QPointF(9, 9)));
2450
2451 // Line
2452 QGraphicsLineItem line(QLineF(-10, -10, 20, 20));
2453 QVERIFY(!line.contains(QPointF(-10, 0)));
2454 QVERIFY(!line.contains(QPointF(0, -10)));
2455 QVERIFY(!line.contains(QPointF(10, 0)));
2456 QVERIFY(!line.contains(QPointF(0, 10)));
2457 QVERIFY(line.contains(QPointF(0, 0)));
2458 QVERIFY(line.contains(QPointF(-9, -9)));
2459 QVERIFY(line.contains(QPointF(9, 9)));
2460
2461 // Polygon
2462 QGraphicsPolygonItem polygon(QPolygonF()
2463 << QPointF(0, 0)
2464 << QPointF(10, 0)
2465 << QPointF(0, 10));
2466 QVERIFY(polygon.contains(QPointF(1, 1)));
2467 QVERIFY(polygon.contains(QPointF(4, 4)));
2468 QVERIFY(polygon.contains(QPointF(1, 4)));
2469 QVERIFY(polygon.contains(QPointF(4, 1)));
2470 QVERIFY(!polygon.contains(QPointF(8, 8)));
2471 QVERIFY(polygon.contains(QPointF(1, 8)));
2472 QVERIFY(polygon.contains(QPointF(8, 1)));
2473}
2474
2475void tst_QGraphicsItem::collidesWith_item()
2476{
2477 // Rectangle
2478 QGraphicsRectItem rect(QRectF(-10, -10, 20, 20));
2479 QGraphicsRectItem rect2(QRectF(-10, -10, 20, 20));
2480 QVERIFY(rect.collidesWithItem(&rect2));
2481 QVERIFY(rect2.collidesWithItem(&rect));
2482 rect2.setPos(ax: 21, ay: 21);
2483 QVERIFY(!rect.collidesWithItem(&rect2));
2484 QVERIFY(!rect2.collidesWithItem(&rect));
2485 rect2.setPos(ax: -21, ay: -21);
2486 QVERIFY(!rect.collidesWithItem(&rect2));
2487 QVERIFY(!rect2.collidesWithItem(&rect));
2488 rect2.setPos(ax: -17, ay: -17);
2489 QVERIFY(rect.collidesWithItem(&rect2));
2490 QVERIFY(rect2.collidesWithItem(&rect));
2491
2492 QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20));
2493 QGraphicsEllipseItem ellipse2(QRectF(-10, -10, 20, 20));
2494 QVERIFY(ellipse.collidesWithItem(&ellipse2));
2495 QVERIFY(ellipse2.collidesWithItem(&ellipse));
2496 ellipse2.setPos(ax: 21, ay: 21);
2497 QVERIFY(!ellipse.collidesWithItem(&ellipse2));
2498 QVERIFY(!ellipse2.collidesWithItem(&ellipse));
2499 ellipse2.setPos(ax: -21, ay: -21);
2500 QVERIFY(!ellipse.collidesWithItem(&ellipse2));
2501 QVERIFY(!ellipse2.collidesWithItem(&ellipse));
2502
2503 ellipse2.setPos(ax: -17, ay: -17);
2504 QVERIFY(!ellipse.collidesWithItem(&ellipse2));
2505 QVERIFY(!ellipse2.collidesWithItem(&ellipse));
2506
2507 {
2508 QGraphicsScene scene;
2509 QGraphicsRectItem rect(20, 20, 100, 100, nullptr);
2510 scene.addItem(item: &rect);
2511 QGraphicsRectItem rect2(40, 40, 50, 50, nullptr);
2512 scene.addItem(item: &rect2);
2513 rect2.setZValue(1);
2514 QGraphicsLineItem line(0, 0, 200, 200, nullptr);
2515 scene.addItem(item: &line);
2516 line.setZValue(2);
2517
2518 QCOMPARE(scene.items().size(), 3);
2519
2520 QList<QGraphicsItem *> col1 = rect.collidingItems();
2521 QCOMPARE(col1.size(), 2);
2522 QCOMPARE(col1.constFirst(), &line);
2523 QCOMPARE(col1.constLast(), &rect2);
2524
2525 QList<QGraphicsItem *> col2 = rect2.collidingItems();
2526 QCOMPARE(col2.size(), 2);
2527 QCOMPARE(col2.constFirst(), &line);
2528 QCOMPARE(col2.constLast(), &rect);
2529
2530 QList<QGraphicsItem *> col3 = line.collidingItems();
2531 QCOMPARE(col3.size(), 2);
2532 QCOMPARE(col3.constFirst(), &rect2);
2533 QCOMPARE(col3.constLast(), &rect);
2534 }
2535}
2536
2537void tst_QGraphicsItem::collidesWith_path_data()
2538{
2539 QTest::addColumn<QPointF>(name: "pos");
2540 QTest::addColumn<QTransform>(name: "transform");
2541 QTest::addColumn<QPainterPath>(name: "shape");
2542 QTest::addColumn<bool>(name: "rectCollides");
2543 QTest::addColumn<bool>(name: "ellipseCollides");
2544
2545 QTest::newRow(dataTag: "nothing") << QPointF(0, 0) << QTransform() << QPainterPath() << false << false;
2546
2547 QPainterPath rect;
2548 rect.addRect(x: 0, y: 0, w: 20, h: 20);
2549
2550 QTest::newRow(dataTag: "rect1") << QPointF(0, 0) << QTransform() << rect << true << true;
2551 QTest::newRow(dataTag: "rect2") << QPointF(0, 0) << QTransform::fromTranslate(dx: 21, dy: 21) << rect << false << false;
2552 QTest::newRow(dataTag: "rect3") << QPointF(21, 21) << QTransform() << rect << false << false;
2553}
2554
2555void tst_QGraphicsItem::collidesWith_path()
2556{
2557 QFETCH(QPointF, pos);
2558 QFETCH(QTransform, transform);
2559 QFETCH(QPainterPath, shape);
2560 QFETCH(bool, rectCollides);
2561 QFETCH(bool, ellipseCollides);
2562
2563 QGraphicsRectItem rect(QRectF(0, 0, 20, 20));
2564 QGraphicsEllipseItem ellipse(QRectF(0, 0, 20, 20));
2565
2566 rect.setPos(pos);
2567 rect.setTransform(matrix: transform);
2568
2569 ellipse.setPos(pos);
2570 ellipse.setTransform(matrix: transform);
2571
2572 QPainterPath mappedShape = rect.sceneTransform().inverted().map(p: shape);
2573
2574 if (rectCollides)
2575 QVERIFY(rect.collidesWithPath(mappedShape));
2576 else
2577 QVERIFY(!rect.collidesWithPath(mappedShape));
2578
2579 if (ellipseCollides)
2580 QVERIFY(ellipse.collidesWithPath(mappedShape));
2581 else
2582 QVERIFY(!ellipse.collidesWithPath(mappedShape));
2583}
2584
2585void tst_QGraphicsItem::collidesWithItemWithClip()
2586{
2587 QGraphicsScene scene;
2588
2589 QGraphicsEllipseItem *ellipse = scene.addEllipse(x: 0, y: 0, w: 100, h: 100);
2590 ellipse->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
2591 QGraphicsEllipseItem *ellipse2 = scene.addEllipse(x: 0, y: 0, w: 10, h: 10);
2592 ellipse2->setParentItem(ellipse);
2593 QGraphicsEllipseItem *ellipse3 = scene.addEllipse(x: 0, y: 0, w: 10, h: 10);
2594 ellipse3->setParentItem(ellipse);
2595 QGraphicsEllipseItem *ellipse5 = scene.addEllipse(x: 50, y: 50, w: 10, h: 10);
2596 ellipse5->setParentItem(ellipse);
2597 QGraphicsEllipseItem *ellipse4 = scene.addEllipse(x: 0, y: 0, w: 10, h: 10);
2598
2599 QVERIFY(ellipse2->collidesWithItem(ellipse3));
2600 QVERIFY(ellipse3->collidesWithItem(ellipse2));
2601 QVERIFY(!ellipse2->collidesWithItem(ellipse));
2602 QVERIFY(!ellipse->collidesWithItem(ellipse2));
2603 QVERIFY(!ellipse4->collidesWithItem(ellipse));
2604 QVERIFY(!ellipse4->collidesWithItem(ellipse2));
2605 QVERIFY(!ellipse4->collidesWithItem(ellipse3));
2606 QVERIFY(!ellipse->collidesWithItem(ellipse4));
2607 QVERIFY(!ellipse2->collidesWithItem(ellipse4));
2608 QVERIFY(!ellipse3->collidesWithItem(ellipse4));
2609 QVERIFY(ellipse->collidesWithItem(ellipse5));
2610 QVERIFY(ellipse5->collidesWithItem(ellipse));
2611}
2612
2613class MyItem : public QGraphicsEllipseItem
2614{
2615public:
2616 bool isObscuredBy(const QGraphicsItem *item) const override
2617 {
2618 auto myItem = qgraphicsitem_cast<const MyItem *>(item);
2619 if (!myItem)
2620 return QGraphicsEllipseItem::isObscuredBy(item);
2621
2622 if (item->zValue() <= zValue())
2623 return false;
2624
2625 QRectF r = rect();
2626 QPointF topMid = (r.topRight() + r.topLeft()) / 2;
2627 QPointF botMid = (r.bottomRight() + r.bottomLeft()) / 2;
2628 QPointF leftMid = (r.topLeft() + r.bottomLeft()) / 2;
2629 QPointF rightMid = (r.topRight() + r.bottomRight()) / 2;
2630
2631 QPainterPath mappedShape = item->mapToItem(item: this, path: item->opaqueArea());
2632
2633 return mappedShape.contains(pt: topMid) && mappedShape.contains(pt: botMid)
2634 && mappedShape.contains(pt: leftMid) && mappedShape.contains(pt: rightMid);
2635 }
2636
2637 QPainterPath opaqueArea() const override
2638 {
2639 return shape();
2640 }
2641
2642 enum { Type = UserType + 1 };
2643
2644 int type() const override { return Type; }
2645};
2646
2647void tst_QGraphicsItem::isObscuredBy()
2648{
2649 QGraphicsScene scene;
2650
2651 MyItem myitem1, myitem2;
2652
2653 myitem1.setRect(QRectF(50, 50, 40, 200));
2654 myitem1.setTransform(matrix: QTransform().rotate(a: 67), combine: true);
2655
2656 myitem2.setRect(QRectF(25, 25, 20, 20));
2657 myitem2.setZValue(-1.0);
2658 scene.addItem(item: &myitem1);
2659 scene.addItem(item: &myitem2);
2660
2661 QVERIFY(!myitem2.isObscuredBy(&myitem1));
2662 QVERIFY(!myitem1.isObscuredBy(&myitem2));
2663
2664 myitem2.setRect(QRectF(-50, 85, 20, 20));
2665 QVERIFY(myitem2.isObscuredBy(&myitem1));
2666 QVERIFY(!myitem1.isObscuredBy(&myitem2));
2667
2668 myitem2.setRect(QRectF(-30, 70, 20, 20));
2669 QVERIFY(!myitem2.isObscuredBy(&myitem1));
2670 QVERIFY(!myitem1.isObscuredBy(&myitem2));
2671
2672 QGraphicsRectItem rect1, rect2;
2673
2674 rect1.setRect(QRectF(-40, -40, 50, 50));
2675 rect1.setBrush(QBrush(Qt::red));
2676 rect2.setRect(QRectF(-30, -20, 20, 20));
2677 rect2.setZValue(-1.0);
2678 rect2.setBrush(QBrush(Qt::blue));
2679
2680 QVERIFY(rect2.isObscuredBy(&rect1));
2681 QVERIFY(!rect1.isObscuredBy(&rect2));
2682
2683 rect2.setPos(QPointF(-20, -25));
2684
2685 QVERIFY(!rect2.isObscuredBy(&rect1));
2686 QVERIFY(!rect1.isObscuredBy(&rect2));
2687
2688 rect2.setPos(QPointF(-100, -100));
2689
2690 QVERIFY(!rect2.isObscuredBy(&rect1));
2691 QVERIFY(!rect1.isObscuredBy(&rect2));
2692}
2693
2694class OpaqueItem : public QGraphicsRectItem
2695{
2696protected:
2697 QPainterPath opaqueArea() const override
2698 {
2699 return shape();
2700 }
2701};
2702
2703void tst_QGraphicsItem::isObscured()
2704{
2705 if (sizeof(qreal) != sizeof(double))
2706 QSKIP("Skipped due to rounding errors");
2707
2708 OpaqueItem *item1 = new OpaqueItem;
2709 item1->setRect(ax: 0, ay: 0, w: 100, h: 100);
2710 item1->setZValue(0);
2711
2712 OpaqueItem *item2 = new OpaqueItem;
2713 item2->setZValue(1);
2714 item2->setRect(ax: 0, ay: 0, w: 100, h: 100);
2715
2716 QGraphicsScene scene;
2717 scene.addItem(item: item1);
2718 scene.addItem(item: item2);
2719
2720 QVERIFY(item1->isObscured());
2721 QVERIFY(item1->isObscuredBy(item2));
2722 QVERIFY(item1->isObscured(QRectF(0, 0, 50, 50)));
2723 QVERIFY(item1->isObscured(QRectF(50, 0, 50, 50)));
2724 QVERIFY(item1->isObscured(QRectF(50, 50, 50, 50)));
2725 QVERIFY(item1->isObscured(QRectF(0, 50, 50, 50)));
2726 QVERIFY(item1->isObscured(0, 0, 50, 50));
2727 QVERIFY(item1->isObscured(50, 0, 50, 50));
2728 QVERIFY(item1->isObscured(50, 50, 50, 50));
2729 QVERIFY(item1->isObscured(0, 50, 50, 50));
2730 QVERIFY(!item2->isObscured());
2731 QVERIFY(!item2->isObscuredBy(item1));
2732 QVERIFY(!item2->isObscured(QRectF(0, 0, 50, 50)));
2733 QVERIFY(!item2->isObscured(QRectF(50, 0, 50, 50)));
2734 QVERIFY(!item2->isObscured(QRectF(50, 50, 50, 50)));
2735 QVERIFY(!item2->isObscured(QRectF(0, 50, 50, 50)));
2736 QVERIFY(!item2->isObscured(0, 0, 50, 50));
2737 QVERIFY(!item2->isObscured(50, 0, 50, 50));
2738 QVERIFY(!item2->isObscured(50, 50, 50, 50));
2739 QVERIFY(!item2->isObscured(0, 50, 50, 50));
2740
2741 item2->moveBy(dx: 50, dy: 0);
2742
2743 QVERIFY(!item1->isObscured());
2744 QVERIFY(!item1->isObscuredBy(item2));
2745 QVERIFY(!item1->isObscured(QRectF(0, 0, 50, 50)));
2746 QVERIFY(item1->isObscured(QRectF(50, 0, 50, 50)));
2747 QVERIFY(item1->isObscured(QRectF(50, 50, 50, 50)));
2748 QVERIFY(!item1->isObscured(QRectF(0, 50, 50, 50)));
2749 QVERIFY(!item1->isObscured(0, 0, 50, 50));
2750 QVERIFY(item1->isObscured(50, 0, 50, 50));
2751 QVERIFY(item1->isObscured(50, 50, 50, 50));
2752 QVERIFY(!item1->isObscured(0, 50, 50, 50));
2753 QVERIFY(!item2->isObscured());
2754 QVERIFY(!item2->isObscuredBy(item1));
2755 QVERIFY(!item2->isObscured(QRectF(0, 0, 50, 50)));
2756 QVERIFY(!item2->isObscured(QRectF(50, 0, 50, 50)));
2757 QVERIFY(!item2->isObscured(QRectF(50, 50, 50, 50)));
2758 QVERIFY(!item2->isObscured(QRectF(0, 50, 50, 50)));
2759 QVERIFY(!item2->isObscured(0, 0, 50, 50));
2760 QVERIFY(!item2->isObscured(50, 0, 50, 50));
2761 QVERIFY(!item2->isObscured(50, 50, 50, 50));
2762 QVERIFY(!item2->isObscured(0, 50, 50, 50));
2763}
2764
2765void tst_QGraphicsItem::mapFromToParent()
2766{
2767 QPainterPath path1;
2768 path1.addRect(x: 0, y: 0, w: 200, h: 200);
2769
2770 QPainterPath path2;
2771 path2.addRect(x: 0, y: 0, w: 100, h: 100);
2772
2773 QPainterPath path3;
2774 path3.addRect(x: 0, y: 0, w: 50, h: 50);
2775
2776 QPainterPath path4;
2777 path4.addRect(x: 0, y: 0, w: 25, h: 25);
2778
2779 QGraphicsItem *item1 = new QGraphicsPathItem(path1);
2780 QGraphicsItem *item2 = new QGraphicsPathItem(path2, item1);
2781 QGraphicsItem *item3 = new QGraphicsPathItem(path3, item2);
2782 QGraphicsItem *item4 = new QGraphicsPathItem(path4, item3);
2783
2784 item1->setPos(ax: 10, ay: 10);
2785 item2->setPos(ax: 10, ay: 10);
2786 item3->setPos(ax: 10, ay: 10);
2787 item4->setPos(ax: 10, ay: 10);
2788
2789 for (int i = 0; i < 4; ++i) {
2790 QTransform transform;
2791 transform.rotate(a: i * 90);
2792 transform.translate(dx: i * 100, dy: -i * 100);
2793 transform.scale(sx: 2, sy: 4);
2794 item1->setTransform(matrix: transform);
2795
2796 QCOMPARE(item1->mapToParent(QPointF(0, 0)), item1->pos() + transform.map(QPointF(0, 0)));
2797 QCOMPARE(item2->mapToParent(QPointF(0, 0)), item2->pos());
2798 QCOMPARE(item3->mapToParent(QPointF(0, 0)), item3->pos());
2799 QCOMPARE(item4->mapToParent(QPointF(0, 0)), item4->pos());
2800 QCOMPARE(item1->mapToParent(QPointF(10, -10)), item1->pos() + transform.map(QPointF(10, -10)));
2801 QCOMPARE(item2->mapToParent(QPointF(10, -10)), item2->pos() + QPointF(10, -10));
2802 QCOMPARE(item3->mapToParent(QPointF(10, -10)), item3->pos() + QPointF(10, -10));
2803 QCOMPARE(item4->mapToParent(QPointF(10, -10)), item4->pos() + QPointF(10, -10));
2804 QCOMPARE(item1->mapToParent(QPointF(-10, 10)), item1->pos() + transform.map(QPointF(-10, 10)));
2805 QCOMPARE(item2->mapToParent(QPointF(-10, 10)), item2->pos() + QPointF(-10, 10));
2806 QCOMPARE(item3->mapToParent(QPointF(-10, 10)), item3->pos() + QPointF(-10, 10));
2807 QCOMPARE(item4->mapToParent(QPointF(-10, 10)), item4->pos() + QPointF(-10, 10));
2808 QCOMPARE(item1->mapFromParent(item1->pos()), transform.inverted().map(QPointF(0, 0)));
2809 QCOMPARE(item2->mapFromParent(item2->pos()), QPointF(0, 0));
2810 QCOMPARE(item3->mapFromParent(item3->pos()), QPointF(0, 0));
2811 QCOMPARE(item4->mapFromParent(item4->pos()), QPointF(0, 0));
2812 QCOMPARE(item1->mapFromParent(item1->pos() + QPointF(10, -10)),
2813 transform.inverted().map(QPointF(10, -10)));
2814 QCOMPARE(item2->mapFromParent(item2->pos() + QPointF(10, -10)), QPointF(10, -10));
2815 QCOMPARE(item3->mapFromParent(item3->pos() + QPointF(10, -10)), QPointF(10, -10));
2816 QCOMPARE(item4->mapFromParent(item4->pos() + QPointF(10, -10)), QPointF(10, -10));
2817 QCOMPARE(item1->mapFromParent(item1->pos() + QPointF(-10, 10)),
2818 transform.inverted().map(QPointF(-10, 10)));
2819 QCOMPARE(item2->mapFromParent(item2->pos() + QPointF(-10, 10)), QPointF(-10, 10));
2820 QCOMPARE(item3->mapFromParent(item3->pos() + QPointF(-10, 10)), QPointF(-10, 10));
2821 QCOMPARE(item4->mapFromParent(item4->pos() + QPointF(-10, 10)), QPointF(-10, 10));
2822 }
2823
2824 delete item1;
2825}
2826
2827void tst_QGraphicsItem::mapFromToScene()
2828{
2829 QGraphicsItem *item1 = new QGraphicsPathItem(QPainterPath());
2830 QGraphicsItem *item2 = new QGraphicsPathItem(QPainterPath(), item1);
2831 QGraphicsItem *item3 = new QGraphicsPathItem(QPainterPath(), item2);
2832 QGraphicsItem *item4 = new QGraphicsPathItem(QPainterPath(), item3);
2833
2834 item1->setPos(ax: 100, ay: 100);
2835 item2->setPos(ax: 100, ay: 100);
2836 item3->setPos(ax: 100, ay: 100);
2837 item4->setPos(ax: 100, ay: 100);
2838 QCOMPARE(item1->pos(), QPointF(100, 100));
2839 QCOMPARE(item2->pos(), QPointF(100, 100));
2840 QCOMPARE(item3->pos(), QPointF(100, 100));
2841 QCOMPARE(item4->pos(), QPointF(100, 100));
2842 QCOMPARE(item1->pos(), item1->mapToParent(0, 0));
2843 QCOMPARE(item2->pos(), item2->mapToParent(0, 0));
2844 QCOMPARE(item3->pos(), item3->mapToParent(0, 0));
2845 QCOMPARE(item4->pos(), item4->mapToParent(0, 0));
2846 QCOMPARE(item1->mapToParent(10, 10), QPointF(110, 110));
2847 QCOMPARE(item2->mapToParent(10, 10), QPointF(110, 110));
2848 QCOMPARE(item3->mapToParent(10, 10), QPointF(110, 110));
2849 QCOMPARE(item4->mapToParent(10, 10), QPointF(110, 110));
2850 QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100));
2851 QCOMPARE(item2->mapToScene(0, 0), QPointF(200, 200));
2852 QCOMPARE(item3->mapToScene(0, 0), QPointF(300, 300));
2853 QCOMPARE(item4->mapToScene(0, 0), QPointF(400, 400));
2854 QCOMPARE(item1->mapToScene(10, 0), QPointF(110, 100));
2855 QCOMPARE(item2->mapToScene(10, 0), QPointF(210, 200));
2856 QCOMPARE(item3->mapToScene(10, 0), QPointF(310, 300));
2857 QCOMPARE(item4->mapToScene(10, 0), QPointF(410, 400));
2858 QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0));
2859 QCOMPARE(item2->mapFromScene(200, 200), QPointF(0, 0));
2860 QCOMPARE(item3->mapFromScene(300, 300), QPointF(0, 0));
2861 QCOMPARE(item4->mapFromScene(400, 400), QPointF(0, 0));
2862 QCOMPARE(item1->mapFromScene(110, 100), QPointF(10, 0));
2863 QCOMPARE(item2->mapFromScene(210, 200), QPointF(10, 0));
2864 QCOMPARE(item3->mapFromScene(310, 300), QPointF(10, 0));
2865 QCOMPARE(item4->mapFromScene(410, 400), QPointF(10, 0));
2866
2867 // Rotate item1 90 degrees clockwise
2868 QTransform transform; transform.rotate(a: 90);
2869 item1->setTransform(matrix: transform);
2870 QCOMPARE(item1->pos(), item1->mapToParent(0, 0));
2871 QCOMPARE(item2->pos(), item2->mapToParent(0, 0));
2872 QCOMPARE(item3->pos(), item3->mapToParent(0, 0));
2873 QCOMPARE(item4->pos(), item4->mapToParent(0, 0));
2874 QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110));
2875 QCOMPARE(item2->mapToParent(10, 0), QPointF(110, 100));
2876 QCOMPARE(item3->mapToParent(10, 0), QPointF(110, 100));
2877 QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100));
2878 QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100));
2879 QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200));
2880 QCOMPARE(item3->mapToScene(0, 0), QPointF(-100, 300));
2881 QCOMPARE(item4->mapToScene(0, 0), QPointF(-200, 400));
2882 QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110));
2883 QCOMPARE(item2->mapToScene(10, 0), QPointF(0, 210));
2884 QCOMPARE(item3->mapToScene(10, 0), QPointF(-100, 310));
2885 QCOMPARE(item4->mapToScene(10, 0), QPointF(-200, 410));
2886 QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0));
2887 QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0));
2888 QCOMPARE(item3->mapFromScene(-100, 300), QPointF(0, 0));
2889 QCOMPARE(item4->mapFromScene(-200, 400), QPointF(0, 0));
2890 QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0));
2891 QCOMPARE(item2->mapFromScene(0, 210), QPointF(10, 0));
2892 QCOMPARE(item3->mapFromScene(-100, 310), QPointF(10, 0));
2893 QCOMPARE(item4->mapFromScene(-200, 410), QPointF(10, 0));
2894
2895 // Rotate item2 90 degrees clockwise
2896 item2->setTransform(matrix: transform);
2897 QCOMPARE(item1->pos(), item1->mapToParent(0, 0));
2898 QCOMPARE(item2->pos(), item2->mapToParent(0, 0));
2899 QCOMPARE(item3->pos(), item3->mapToParent(0, 0));
2900 QCOMPARE(item4->pos(), item4->mapToParent(0, 0));
2901 QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110));
2902 QCOMPARE(item2->mapToParent(10, 0), QPointF(100, 110));
2903 QCOMPARE(item3->mapToParent(10, 0), QPointF(110, 100));
2904 QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100));
2905 QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100));
2906 QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200));
2907 QCOMPARE(item3->mapToScene(0, 0), QPointF(-100, 100));
2908 QCOMPARE(item4->mapToScene(0, 0), QPointF(-200, 0));
2909 QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110));
2910 QCOMPARE(item2->mapToScene(10, 0), QPointF(-10, 200));
2911 QCOMPARE(item3->mapToScene(10, 0), QPointF(-110, 100));
2912 QCOMPARE(item4->mapToScene(10, 0), QPointF(-210, 0));
2913 QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0));
2914 QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0));
2915 QCOMPARE(item3->mapFromScene(-100, 100), QPointF(0, 0));
2916 QCOMPARE(item4->mapFromScene(-200, 0), QPointF(0, 0));
2917 QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0));
2918 QCOMPARE(item2->mapFromScene(-10, 200), QPointF(10, 0));
2919 QCOMPARE(item3->mapFromScene(-110, 100), QPointF(10, 0));
2920 QCOMPARE(item4->mapFromScene(-210, 0), QPointF(10, 0));
2921
2922 // Translate item3 50 points, then rotate 90 degrees counterclockwise
2923 QTransform transform2;
2924 transform2.translate(dx: 50, dy: 0);
2925 transform2.rotate(a: -90);
2926 item3->setTransform(matrix: transform2);
2927 QCOMPARE(item1->pos(), item1->mapToParent(0, 0));
2928 QCOMPARE(item2->pos(), item2->mapToParent(0, 0));
2929 QCOMPARE(item3->pos(), item3->mapToParent(0, 0) - QPointF(50, 0));
2930 QCOMPARE(item4->pos(), item4->mapToParent(0, 0));
2931 QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110));
2932 QCOMPARE(item2->mapToParent(10, 0), QPointF(100, 110));
2933 QCOMPARE(item3->mapToParent(10, 0), QPointF(150, 90));
2934 QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100));
2935 QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100));
2936 QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200));
2937 QCOMPARE(item3->mapToScene(0, 0), QPointF(-150, 100));
2938 QCOMPARE(item4->mapToScene(0, 0), QPointF(-250, 200));
2939 QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110));
2940 QCOMPARE(item2->mapToScene(10, 0), QPointF(-10, 200));
2941 QCOMPARE(item3->mapToScene(10, 0), QPointF(-150, 110));
2942 QCOMPARE(item4->mapToScene(10, 0), QPointF(-250, 210));
2943 QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0));
2944 QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0));
2945 QCOMPARE(item3->mapFromScene(-150, 100), QPointF(0, 0));
2946 QCOMPARE(item4->mapFromScene(-250, 200), QPointF(0, 0));
2947 QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0));
2948 QCOMPARE(item2->mapFromScene(-10, 200), QPointF(10, 0));
2949 QCOMPARE(item3->mapFromScene(-150, 110), QPointF(10, 0));
2950 QCOMPARE(item4->mapFromScene(-250, 210), QPointF(10, 0));
2951
2952 delete item1;
2953}
2954
2955void tst_QGraphicsItem::mapFromToItem()
2956{
2957 QGraphicsItem *item1 = new QGraphicsPathItem;
2958 QGraphicsItem *item2 = new QGraphicsPathItem;
2959 QGraphicsItem *item3 = new QGraphicsPathItem;
2960 QGraphicsItem *item4 = new QGraphicsPathItem;
2961
2962 item1->setPos(ax: -100, ay: -100);
2963 item2->setPos(ax: 100, ay: -100);
2964 item3->setPos(ax: 100, ay: 100);
2965 item4->setPos(ax: -100, ay: 100);
2966
2967 QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(200, 0));
2968 QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(0, 200));
2969 QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(-200, 0));
2970 QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(0, -200));
2971 QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(0, 200));
2972 QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(-200, 0));
2973 QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(0, -200));
2974 QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(200, 0));
2975
2976 QTransform transform;
2977 transform.translate(dx: 100, dy: 100);
2978 item1->setTransform(matrix: transform);
2979
2980 QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(100, -100));
2981 QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(0, 200));
2982 QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(-200, 0));
2983 QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(100, -100));
2984 QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(-100, 100));
2985 QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(-100, 100));
2986 QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(0, -200));
2987 QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(200, 0));
2988
2989 transform.rotate(a: 90);
2990 item1->setTransform(matrix: transform);
2991 item2->setTransform(matrix: transform);
2992 item3->setTransform(matrix: transform);
2993 item4->setTransform(matrix: transform);
2994
2995 QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(0, -200));
2996 QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(200, 0));
2997 QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(0, 200));
2998 QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(-200, 0));
2999 QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(200, 0));
3000 QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(0, 200));
3001 QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(-200, 0));
3002 QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(0, -200));
3003 QCOMPARE(item1->mapFromItem(item2, 10, -5), QPointF(10, -205));
3004 QCOMPARE(item2->mapFromItem(item3, 10, -5), QPointF(210, -5));
3005 QCOMPARE(item3->mapFromItem(item4, 10, -5), QPointF(10, 195));
3006 QCOMPARE(item4->mapFromItem(item1, 10, -5), QPointF(-190, -5));
3007 QCOMPARE(item1->mapFromItem(item4, 10, -5), QPointF(210, -5));
3008 QCOMPARE(item2->mapFromItem(item1, 10, -5), QPointF(10, 195));
3009 QCOMPARE(item3->mapFromItem(item2, 10, -5), QPointF(-190, -5));
3010 QCOMPARE(item4->mapFromItem(item3, 10, -5), QPointF(10, -205));
3011
3012 QCOMPARE(item1->mapFromItem(nullptr, 10, -5), item1->mapFromScene(10, -5));
3013 QCOMPARE(item2->mapFromItem(nullptr, 10, -5), item2->mapFromScene(10, -5));
3014 QCOMPARE(item3->mapFromItem(nullptr, 10, -5), item3->mapFromScene(10, -5));
3015 QCOMPARE(item4->mapFromItem(nullptr, 10, -5), item4->mapFromScene(10, -5));
3016 QCOMPARE(item1->mapToItem(nullptr, 10, -5), item1->mapToScene(10, -5));
3017 QCOMPARE(item2->mapToItem(nullptr, 10, -5), item2->mapToScene(10, -5));
3018 QCOMPARE(item3->mapToItem(nullptr, 10, -5), item3->mapToScene(10, -5));
3019 QCOMPARE(item4->mapToItem(nullptr, 10, -5), item4->mapToScene(10, -5));
3020
3021 delete item1;
3022 delete item2;
3023 delete item3;
3024 delete item4;
3025}
3026
3027void tst_QGraphicsItem::mapRectFromToParent_data()
3028{
3029 QTest::addColumn<bool>(name: "parent");
3030 QTest::addColumn<QPointF>(name: "parentPos");
3031 QTest::addColumn<QTransform>(name: "parentTransform");
3032 QTest::addColumn<QPointF>(name: "pos");
3033 QTest::addColumn<QTransform>(name: "transform");
3034 QTest::addColumn<QRectF>(name: "inputRect");
3035 QTest::addColumn<QRectF>(name: "outputRect");
3036
3037 QTest::newRow(dataTag: "nil") << false << QPointF() << QTransform() << QPointF() << QTransform() << QRectF() << QRectF();
3038 QTest::newRow(dataTag: "simple") << false << QPointF() << QTransform() << QPointF() << QTransform()
3039 << QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10);
3040 QTest::newRow(dataTag: "simple w/parent") << true
3041 << QPointF() << QTransform()
3042 << QPointF() << QTransform()
3043 << QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10);
3044 QTest::newRow(dataTag: "simple w/parent parentPos") << true
3045 << QPointF(50, 50) << QTransform()
3046 << QPointF() << QTransform()
3047 << QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10);
3048 QTest::newRow(dataTag: "simple w/parent parentPos parentRotation") << true
3049 << QPointF(50, 50) << QTransform().rotate(a: 45)
3050 << QPointF() << QTransform()
3051 << QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10);
3052 QTest::newRow(dataTag: "pos w/parent") << true
3053 << QPointF() << QTransform()
3054 << QPointF(50, 50) << QTransform()
3055 << QRectF(0, 0, 10, 10) << QRectF(50, 50, 10, 10);
3056 QTest::newRow(dataTag: "rotation w/parent") << true
3057 << QPointF() << QTransform()
3058 << QPointF() << QTransform().rotate(a: 90)
3059 << QRectF(0, 0, 10, 10) << QRectF(-10, 0, 10, 10);
3060 QTest::newRow(dataTag: "pos rotation w/parent") << true
3061 << QPointF() << QTransform()
3062 << QPointF(50, 50) << QTransform().rotate(a: 90)
3063 << QRectF(0, 0, 10, 10) << QRectF(40, 50, 10, 10);
3064 QTest::newRow(dataTag: "pos rotation w/parent parentPos parentRotation") << true
3065 << QPointF(-170, -190) << QTransform().rotate(a: 90)
3066 << QPointF(50, 50) << QTransform().rotate(a: 90)
3067 << QRectF(0, 0, 10, 10) << QRectF(40, 50, 10, 10);
3068}
3069
3070void tst_QGraphicsItem::mapRectFromToParent()
3071{
3072 QFETCH(bool, parent);
3073 QFETCH(QPointF, parentPos);
3074 QFETCH(QTransform, parentTransform);
3075 QFETCH(QPointF, pos);
3076 QFETCH(QTransform, transform);
3077 QFETCH(QRectF, inputRect);
3078 QFETCH(QRectF, outputRect);
3079
3080 QGraphicsRectItem *rect = new QGraphicsRectItem;
3081 rect->setPos(pos);
3082 rect->setTransform(matrix: transform);
3083
3084 if (parent) {
3085 QGraphicsRectItem *rectParent = new QGraphicsRectItem;
3086 rect->setParentItem(rectParent);
3087 rectParent->setPos(parentPos);
3088 rectParent->setTransform(matrix: parentTransform);
3089 }
3090
3091 // Make sure we use non-destructive transform operations (e.g., 90 degree
3092 // rotations).
3093 QCOMPARE(rect->mapRectToParent(inputRect), outputRect);
3094 QCOMPARE(rect->mapRectFromParent(outputRect), inputRect);
3095 QCOMPARE(rect->itemTransform(rect->parentItem()).mapRect(inputRect), outputRect);
3096 QCOMPARE(rect->mapToParent(inputRect).boundingRect(), outputRect);
3097 QCOMPARE(rect->mapToParent(QPolygonF(inputRect)).boundingRect(), outputRect);
3098 QCOMPARE(rect->mapFromParent(outputRect).boundingRect(), inputRect);
3099 QCOMPARE(rect->mapFromParent(QPolygonF(outputRect)).boundingRect(), inputRect);
3100 QPainterPath inputPath;
3101 inputPath.addRect(rect: inputRect);
3102 QPainterPath outputPath;
3103 outputPath.addRect(rect: outputRect);
3104 QCOMPARE(rect->mapToParent(inputPath).boundingRect(), outputPath.boundingRect());
3105 QCOMPARE(rect->mapFromParent(outputPath).boundingRect(), inputPath.boundingRect());
3106}
3107
3108void tst_QGraphicsItem::isAncestorOf()
3109{
3110 QGraphicsItem *grandPa = new QGraphicsRectItem;
3111 QGraphicsItem *parent = new QGraphicsRectItem;
3112 QGraphicsItem *child = new QGraphicsRectItem;
3113
3114 QVERIFY(!parent->isAncestorOf(nullptr));
3115 QVERIFY(!child->isAncestorOf(nullptr));
3116 QVERIFY(!parent->isAncestorOf(child));
3117 QVERIFY(!child->isAncestorOf(parent));
3118 QVERIFY(!parent->isAncestorOf(parent));
3119
3120 child->setParentItem(parent);
3121 parent->setParentItem(grandPa);
3122
3123 QVERIFY(parent->isAncestorOf(child));
3124 QVERIFY(grandPa->isAncestorOf(parent));
3125 QVERIFY(grandPa->isAncestorOf(child));
3126 QVERIFY(!child->isAncestorOf(parent));
3127 QVERIFY(!parent->isAncestorOf(grandPa));
3128 QVERIFY(!child->isAncestorOf(grandPa));
3129 QVERIFY(!child->isAncestorOf(child));
3130 QVERIFY(!parent->isAncestorOf(parent));
3131 QVERIFY(!grandPa->isAncestorOf(grandPa));
3132
3133 parent->setParentItem(nullptr);
3134
3135 delete child;
3136 delete parent;
3137 delete grandPa;
3138}
3139
3140void tst_QGraphicsItem::commonAncestorItem()
3141{
3142 QGraphicsRectItem ancestorItem;
3143 QGraphicsItem *ancestor = &ancestorItem;
3144 QGraphicsItem *grandMa = new QGraphicsRectItem;
3145 QGraphicsItem *grandPa = new QGraphicsRectItem;
3146 QGraphicsItem *brotherInLaw = new QGraphicsRectItem;
3147 QGraphicsItem *cousin = new QGraphicsRectItem;
3148 QGraphicsItem *husband = new QGraphicsRectItem;
3149 QGraphicsItem *child = new QGraphicsRectItem;
3150 QGraphicsItem *wife = new QGraphicsRectItem;
3151
3152 child->setParentItem(husband);
3153 husband->setParentItem(grandPa);
3154 brotherInLaw->setParentItem(grandPa);
3155 cousin->setParentItem(brotherInLaw);
3156 wife->setParentItem(grandMa);
3157 grandMa->setParentItem(ancestor);
3158 grandPa->setParentItem(ancestor);
3159
3160 QCOMPARE(grandMa->commonAncestorItem(grandMa), grandMa);
3161 QCOMPARE(grandMa->commonAncestorItem(nullptr), nullptr);
3162 QCOMPARE(grandMa->commonAncestorItem(grandPa), ancestor);
3163 QCOMPARE(grandPa->commonAncestorItem(grandMa), ancestor);
3164 QCOMPARE(grandPa->commonAncestorItem(husband), grandPa);
3165 QCOMPARE(grandPa->commonAncestorItem(wife), ancestor);
3166 QCOMPARE(grandMa->commonAncestorItem(husband), ancestor);
3167 QCOMPARE(grandMa->commonAncestorItem(wife), grandMa);
3168 QCOMPARE(wife->commonAncestorItem(grandMa), grandMa);
3169 QCOMPARE(child->commonAncestorItem(cousin), grandPa);
3170 QCOMPARE(cousin->commonAncestorItem(child), grandPa);
3171 QCOMPARE(wife->commonAncestorItem(child), ancestor);
3172 QCOMPARE(child->commonAncestorItem(wife), ancestor);
3173}
3174
3175void tst_QGraphicsItem::data()
3176{
3177 QGraphicsTextItem text;
3178
3179 QCOMPARE(text.data(0), QVariant());
3180 text.setData(key: 0, value: "TextItem");
3181 QCOMPARE(text.data(0), QVariant(QString("TextItem")));
3182 text.setData(key: 0, value: QVariant());
3183 QCOMPARE(text.data(0), QVariant());
3184}
3185
3186void tst_QGraphicsItem::type()
3187{
3188 QCOMPARE(int(QGraphicsItem::Type), 1);
3189 QCOMPARE(int(QGraphicsPathItem::Type), 2);
3190 QCOMPARE(int(QGraphicsRectItem::Type), 3);
3191 QCOMPARE(int(QGraphicsEllipseItem::Type), 4);
3192 QCOMPARE(int(QGraphicsPolygonItem::Type), 5);
3193 QCOMPARE(int(QGraphicsLineItem::Type), 6);
3194 QCOMPARE(int(QGraphicsPixmapItem::Type), 7);
3195 QCOMPARE(int(QGraphicsTextItem::Type), 8);
3196
3197 QCOMPARE(QGraphicsPathItem().type(), 2);
3198 QCOMPARE(QGraphicsRectItem().type(), 3);
3199 QCOMPARE(QGraphicsEllipseItem().type(), 4);
3200 QCOMPARE(QGraphicsPolygonItem().type(), 5);
3201 QCOMPARE(QGraphicsLineItem().type(), 6);
3202 QCOMPARE(QGraphicsPixmapItem().type(), 7);
3203 QCOMPARE(QGraphicsTextItem().type(), 8);
3204}
3205
3206void tst_QGraphicsItem::graphicsitem_cast()
3207{
3208 QGraphicsPathItem pathItem;
3209 const QGraphicsPathItem *pPathItem = &pathItem;
3210 QGraphicsRectItem rectItem;
3211 const QGraphicsRectItem *pRectItem = &rectItem;
3212 QGraphicsEllipseItem ellipseItem;
3213 const QGraphicsEllipseItem *pEllipseItem = &ellipseItem;
3214 QGraphicsPolygonItem polygonItem;
3215 const QGraphicsPolygonItem *pPolygonItem = &polygonItem;
3216 QGraphicsLineItem lineItem;
3217 const QGraphicsLineItem *pLineItem = &lineItem;
3218 QGraphicsPixmapItem pixmapItem;
3219 const QGraphicsPixmapItem *pPixmapItem = &pixmapItem;
3220 QGraphicsTextItem textItem;
3221 const QGraphicsTextItem *pTextItem = &textItem;
3222
3223 QVERIFY(qgraphicsitem_cast<QGraphicsPathItem *>(&pathItem));
3224 //QVERIFY(qgraphicsitem_cast<QAbstractGraphicsPathItem *>(&pathItem));
3225 QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&pathItem));
3226 QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pPathItem));
3227 QVERIFY(qgraphicsitem_cast<const QGraphicsPathItem *>(pPathItem));
3228
3229 QVERIFY(qgraphicsitem_cast<QGraphicsRectItem *>(&rectItem));
3230 QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&rectItem));
3231 QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pRectItem));
3232 QVERIFY(qgraphicsitem_cast<const QGraphicsRectItem *>(pRectItem));
3233
3234 QVERIFY(qgraphicsitem_cast<QGraphicsEllipseItem *>(&ellipseItem));
3235 QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&ellipseItem));
3236 QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pEllipseItem));
3237 QVERIFY(qgraphicsitem_cast<const QGraphicsEllipseItem *>(pEllipseItem));
3238
3239 QVERIFY(qgraphicsitem_cast<QGraphicsPolygonItem *>(&polygonItem));
3240 //QVERIFY(qgraphicsitem_cast<QAbstractGraphicsPathItem *>(&polygonItem));
3241 QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&polygonItem));
3242 QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pPolygonItem));
3243 QVERIFY(qgraphicsitem_cast<const QGraphicsPolygonItem *>(pPolygonItem));
3244
3245 QVERIFY(qgraphicsitem_cast<QGraphicsLineItem *>(&lineItem));
3246 QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&lineItem));
3247 QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pLineItem));
3248 QVERIFY(qgraphicsitem_cast<const QGraphicsLineItem *>(pLineItem));
3249
3250 QVERIFY(qgraphicsitem_cast<QGraphicsPixmapItem *>(&pixmapItem));
3251 QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&pixmapItem));
3252 QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pPixmapItem));
3253 QVERIFY(qgraphicsitem_cast<const QGraphicsPixmapItem *>(pPixmapItem));
3254
3255 QVERIFY(qgraphicsitem_cast<QGraphicsTextItem *>(&textItem));
3256 QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&textItem));
3257 QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pTextItem));
3258 QVERIFY(qgraphicsitem_cast<const QGraphicsTextItem *>(pTextItem));
3259
3260 // and some casts that _should_ fail:
3261 QVERIFY(!qgraphicsitem_cast<QGraphicsEllipseItem *>(&pathItem));
3262 QVERIFY(!qgraphicsitem_cast<const QGraphicsTextItem *>(pPolygonItem));
3263
3264 // and this shouldn't crash
3265 QGraphicsItem *ptr = nullptr;
3266 QVERIFY(!qgraphicsitem_cast<QGraphicsTextItem *>(ptr));
3267 QVERIFY(!qgraphicsitem_cast<QGraphicsItem *>(ptr));
3268}
3269
3270void tst_QGraphicsItem::hoverEventsGenerateRepaints()
3271{
3272 Q_CHECK_PAINTEVENTS
3273
3274 QGraphicsScene scene;
3275 QGraphicsView view(&scene);
3276 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3277 view.show();
3278 QVERIFY(QTest::qWaitForWindowExposed(&view));
3279 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
3280 QVERIFY(QTest::qWaitForWindowActive(&view));
3281 QCoreApplication::processEvents(); // Process all queued paint events
3282
3283 EventTester *tester = new EventTester;
3284 scene.addItem(item: tester);
3285 tester->setAcceptHoverEvents(true);
3286
3287 QTRY_COMPARE(tester->repaints, 1);
3288
3289 // Send a hover enter event
3290 QGraphicsSceneHoverEvent hoverEnterEvent(QEvent::GraphicsSceneHoverEnter);
3291 hoverEnterEvent.setScenePos(QPointF(0, 0));
3292 hoverEnterEvent.setPos(QPointF(0, 0));
3293 QCoreApplication::sendEvent(receiver: &scene, event: &hoverEnterEvent);
3294
3295 // Check that we get a repaint
3296 int npaints = tester->repaints;
3297 QCoreApplication::processEvents();
3298 QCoreApplication::processEvents();
3299 QCOMPARE(tester->events.size(), 2); // enter + move
3300 QCOMPARE(tester->repaints, npaints + 1);
3301 QCOMPARE(tester->events.constLast(), QEvent::GraphicsSceneHoverMove);
3302
3303 // Send a hover move event
3304 QGraphicsSceneHoverEvent hoverMoveEvent(QEvent::GraphicsSceneHoverMove);
3305 hoverMoveEvent.setScenePos(QPointF(0, 0));
3306 hoverMoveEvent.setPos(QPointF(0, 0));
3307 QCoreApplication::sendEvent(receiver: &scene, event: &hoverMoveEvent);
3308
3309 // Check that we don't get a repaint
3310 QCoreApplication::processEvents();
3311 QCoreApplication::processEvents();
3312
3313 QCOMPARE(tester->events.size(), 3);
3314 QCOMPARE(tester->repaints, npaints + 1);
3315 QCOMPARE(tester->events.constLast(), QEvent::GraphicsSceneHoverMove);
3316
3317 // Send a hover leave event
3318 QGraphicsSceneHoverEvent hoverLeaveEvent(QEvent::GraphicsSceneHoverLeave);
3319 hoverLeaveEvent.setScenePos(QPointF(-100, -100));
3320 hoverLeaveEvent.setPos(QPointF(0, 0));
3321 QCoreApplication::sendEvent(receiver: &scene, event: &hoverLeaveEvent);
3322
3323 // Check that we get a repaint
3324 QCoreApplication::processEvents();
3325 QCoreApplication::processEvents();
3326
3327 QCOMPARE(tester->events.size(), 4);
3328 QCOMPARE(tester->repaints, npaints + 2);
3329 QCOMPARE(tester->events.constLast(), QEvent::GraphicsSceneHoverLeave);
3330}
3331
3332void tst_QGraphicsItem::boundingRects_data()
3333{
3334 QTest::addColumn<AbstractGraphicsShapeItemPtr>(name: "item");
3335 QTest::addColumn<QRectF>(name: "boundingRect");
3336
3337 QRectF rect(0, 0, 100, 100);
3338 QPainterPath path;
3339 path.addRect(rect);
3340
3341 QRectF adjustedRect(-0.5, -0.5, 101, 101);
3342
3343 QTest::newRow(dataTag: "path") << AbstractGraphicsShapeItemPtr(new QGraphicsPathItem(path)) << adjustedRect;
3344 QTest::newRow(dataTag: "rect") << AbstractGraphicsShapeItemPtr(new QGraphicsRectItem(rect)) << adjustedRect;
3345 QTest::newRow(dataTag: "ellipse") << AbstractGraphicsShapeItemPtr(new QGraphicsEllipseItem(rect)) << adjustedRect;
3346 QTest::newRow(dataTag: "polygon") << AbstractGraphicsShapeItemPtr(new QGraphicsPolygonItem(rect)) << adjustedRect;
3347}
3348
3349void tst_QGraphicsItem::boundingRects()
3350{
3351 QFETCH(AbstractGraphicsShapeItemPtr, item);
3352 QFETCH(QRectF, boundingRect);
3353
3354 item->setPen(QPen(Qt::black, 1));
3355 QCOMPARE(item->boundingRect(), boundingRect);
3356}
3357
3358void tst_QGraphicsItem::boundingRects2()
3359{
3360 QGraphicsPixmapItem pixmap(QPixmap::fromImage(image: QImage(100, 100, QImage::Format_ARGB32_Premultiplied)));
3361 QCOMPARE(pixmap.boundingRect(), QRectF(0, 0, 100, 100));
3362
3363 QGraphicsLineItem line(0, 0, 100, 0);
3364 line.setPen(QPen(Qt::black, 1));
3365 QCOMPARE(line.boundingRect(), QRectF(-0.5, -0.5, 101, 1));
3366}
3367
3368void tst_QGraphicsItem::sceneBoundingRect()
3369{
3370 QGraphicsScene scene;
3371 QGraphicsRectItem *item = scene.addRect(rect: QRectF(0, 0, 100, 100), pen: QPen(Qt::black, 0));
3372 item->setPos(ax: 100, ay: 100);
3373
3374 QCOMPARE(item->boundingRect(), QRectF(0, 0, 100, 100));
3375 QCOMPARE(item->sceneBoundingRect(), QRectF(100, 100, 100, 100));
3376
3377 item->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
3378
3379 QCOMPARE(item->boundingRect(), QRectF(0, 0, 100, 100));
3380 QCOMPARE(item->sceneBoundingRect(), QRectF(0, 100, 100, 100));
3381}
3382
3383void tst_QGraphicsItem::childrenBoundingRect()
3384{
3385 QGraphicsScene scene;
3386 QGraphicsRectItem *parent = scene.addRect(rect: QRectF(0, 0, 100, 100), pen: QPen(Qt::black, 0));
3387 QGraphicsRectItem *child = scene.addRect(rect: QRectF(0, 0, 100, 100), pen: QPen(Qt::black, 0));
3388 child->setParentItem(parent);
3389 parent->setPos(ax: 100, ay: 100);
3390 child->setPos(ax: 100, ay: 100);
3391
3392 QCOMPARE(parent->boundingRect(), QRectF(0, 0, 100, 100));
3393 QCOMPARE(child->boundingRect(), QRectF(0, 0, 100, 100));
3394 QCOMPARE(child->childrenBoundingRect(), QRectF());
3395 QCOMPARE(parent->childrenBoundingRect(), QRectF(100, 100, 100, 100));
3396
3397 QGraphicsRectItem *child2 = scene.addRect(rect: QRectF(0, 0, 100, 100), pen: QPen(Qt::black, 0));
3398 child2->setParentItem(parent);
3399 child2->setPos(ax: -100, ay: -100);
3400 QCOMPARE(parent->childrenBoundingRect(), QRectF(-100, -100, 300, 300));
3401
3402 QGraphicsRectItem *childChild = scene.addRect(rect: QRectF(0, 0, 100, 100), pen: QPen(Qt::black, 0));
3403 childChild->setParentItem(child);
3404 childChild->setPos(ax: 500, ay: 500);
3405 child->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
3406
3407 scene.addPolygon(polygon: parent->mapToScene(rect: parent->boundingRect() | parent->childrenBoundingRect()))->setPen(QPen(Qt::red));;
3408
3409 QGraphicsView view(&scene);
3410 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3411 view.show();
3412
3413 QVERIFY(QTest::qWaitForWindowExposed(&view));
3414
3415 QTRY_COMPARE(parent->childrenBoundingRect(), QRectF(-500, -100, 600, 800));
3416}
3417
3418void tst_QGraphicsItem::childrenBoundingRectTransformed()
3419{
3420 QGraphicsScene scene;
3421
3422 QGraphicsRectItem *rect = scene.addRect(rect: QRectF(0, 0, 100, 100));
3423 QGraphicsRectItem *rect2 = scene.addRect(rect: QRectF(0, 0, 100, 100));
3424 QGraphicsRectItem *rect3 = scene.addRect(rect: QRectF(0, 0, 100, 100));
3425 QGraphicsRectItem *rect4 = scene.addRect(rect: QRectF(0, 0, 100, 100));
3426 QGraphicsRectItem *rect5 = scene.addRect(rect: QRectF(0, 0, 100, 100));
3427 rect2->setParentItem(rect);
3428 rect3->setParentItem(rect2);
3429 rect4->setParentItem(rect3);
3430 rect5->setParentItem(rect4);
3431
3432 rect->setPen(QPen(Qt::black, 0));
3433 rect2->setPen(QPen(Qt::black, 0));
3434 rect3->setPen(QPen(Qt::black, 0));
3435 rect4->setPen(QPen(Qt::black, 0));
3436 rect5->setPen(QPen(Qt::black, 0));
3437
3438 rect2->setTransform(matrix: QTransform().translate(dx: 50, dy: 50).rotate(a: 45));
3439 rect2->setPos(ax: 25, ay: 25);
3440 rect3->setTransform(matrix: QTransform().translate(dx: 50, dy: 50).rotate(a: 45));
3441 rect3->setPos(ax: 25, ay: 25);
3442 rect4->setTransform(matrix: QTransform().translate(dx: 50, dy: 50).rotate(a: 45));
3443 rect4->setPos(ax: 25, ay: 25);
3444 rect5->setTransform(matrix: QTransform().translate(dx: 50, dy: 50).rotate(a: 45));
3445 rect5->setPos(ax: 25, ay: 25);
3446
3447 QRectF subTreeRect = rect->childrenBoundingRect();
3448 QCOMPARE(subTreeRect.left(), qreal(-206.0660171779821));
3449 QCOMPARE(subTreeRect.top(), qreal(75.0));
3450 QCOMPARE(subTreeRect.width(), qreal(351.7766952966369));
3451 QCOMPARE(subTreeRect.height(), qreal(251.7766952966369));
3452
3453 rect->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
3454 rect2->setTransform(matrix: QTransform().rotate(a: -45), combine: true);
3455 rect3->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
3456 rect4->setTransform(matrix: QTransform().rotate(a: -45), combine: true);
3457 rect5->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
3458
3459 subTreeRect = rect->childrenBoundingRect();
3460 QCOMPARE(rect->childrenBoundingRect(), QRectF(-100, 75, 275, 250));
3461}
3462
3463void tst_QGraphicsItem::childrenBoundingRect2()
3464{
3465 QGraphicsItemGroup box;
3466 QGraphicsLineItem l1(0, 0, 100, 0, &box);
3467 QGraphicsLineItem l2(100, 0, 100, 100, &box);
3468 QGraphicsLineItem l3(0, 0, 0, 100, &box);
3469 // Make sure lines (zero with/height) are included in the childrenBoundingRect.
3470 l1.setPen(QPen(Qt::black, 0));
3471 l2.setPen(QPen(Qt::black, 0));
3472 l3.setPen(QPen(Qt::black, 0));
3473 QCOMPARE(box.childrenBoundingRect(), QRectF(0, 0, 100, 100));
3474}
3475
3476void tst_QGraphicsItem::childrenBoundingRect3()
3477{
3478 QGraphicsScene scene;
3479
3480 QGraphicsRectItem *rect = scene.addRect(rect: QRectF(0, 0, 100, 100));
3481 QGraphicsRectItem *rect2 = scene.addRect(rect: QRectF(0, 0, 100, 100));
3482 QGraphicsRectItem *rect3 = scene.addRect(rect: QRectF(0, 0, 100, 100));
3483 QGraphicsRectItem *rect4 = scene.addRect(rect: QRectF(0, 0, 100, 100));
3484 QGraphicsRectItem *rect5 = scene.addRect(rect: QRectF(0, 0, 100, 100));
3485 rect2->setParentItem(rect);
3486 rect3->setParentItem(rect2);
3487 rect4->setParentItem(rect3);
3488 rect5->setParentItem(rect4);
3489
3490 rect->setPen(QPen(Qt::black, 0));
3491 rect2->setPen(QPen(Qt::black, 0));
3492 rect3->setPen(QPen(Qt::black, 0));
3493 rect4->setPen(QPen(Qt::black, 0));
3494 rect5->setPen(QPen(Qt::black, 0));
3495
3496 rect2->setTransform(matrix: QTransform().translate(dx: 50, dy: 50).rotate(a: 45));
3497 rect2->setPos(ax: 25, ay: 25);
3498 rect3->setTransform(matrix: QTransform().translate(dx: 50, dy: 50).rotate(a: 45));
3499 rect3->setPos(ax: 25, ay: 25);
3500 rect4->setTransform(matrix: QTransform().translate(dx: 50, dy: 50).rotate(a: 45));
3501 rect4->setPos(ax: 25, ay: 25);
3502 rect5->setTransform(matrix: QTransform().translate(dx: 50, dy: 50).rotate(a: 45));
3503 rect5->setPos(ax: 25, ay: 25);
3504
3505 // Try to mess up the cached bounding rect.
3506 (void)rect2->childrenBoundingRect();
3507
3508 QRectF subTreeRect = rect->childrenBoundingRect();
3509 QCOMPARE(subTreeRect.left(), qreal(-206.0660171779821));
3510 QCOMPARE(subTreeRect.top(), qreal(75.0));
3511 QCOMPARE(subTreeRect.width(), qreal(351.7766952966369));
3512 QCOMPARE(subTreeRect.height(), qreal(251.7766952966369));
3513}
3514
3515void tst_QGraphicsItem::childrenBoundingRect4()
3516{
3517 QGraphicsScene scene;
3518
3519 QGraphicsRectItem *rect = scene.addRect(rect: QRectF(0, 0, 10, 10));
3520 QGraphicsRectItem *rect2 = scene.addRect(rect: QRectF(0, 0, 20, 20));
3521 QGraphicsRectItem *rect3 = scene.addRect(rect: QRectF(0, 0, 30, 30));
3522 rect2->setParentItem(rect);
3523 rect3->setParentItem(rect);
3524
3525 QGraphicsView view(&scene);
3526 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3527 view.show();
3528
3529 QVERIFY(QTest::qWaitForWindowExposed(&view));
3530
3531 // Try to mess up the cached bounding rect.
3532 rect->childrenBoundingRect();
3533 rect2->childrenBoundingRect();
3534
3535 rect3->setOpacity(0.0);
3536 rect3->setParentItem(rect2);
3537
3538 QCOMPARE(rect->childrenBoundingRect(), rect3->boundingRect());
3539 QCOMPARE(rect2->childrenBoundingRect(), rect3->boundingRect());
3540}
3541
3542void tst_QGraphicsItem::childrenBoundingRect5()
3543{
3544 QGraphicsScene scene;
3545
3546 QGraphicsRectItem *parent = scene.addRect(rect: QRectF(0, 0, 100, 100));
3547 QGraphicsRectItem *child = scene.addRect(rect: QRectF(0, 0, 100, 100));
3548 child->setParentItem(parent);
3549
3550 parent->setPen(QPen(Qt::black, 0));
3551 child->setPen(QPen(Qt::black, 0));
3552
3553 QGraphicsView view(&scene);
3554 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3555 view.show();
3556
3557 QVERIFY(QTest::qWaitForWindowExposed(&view));
3558
3559 // Try to mess up the cached bounding rect.
3560 QRectF expectedChildrenBoundingRect = parent->boundingRect();
3561 QCOMPARE(parent->childrenBoundingRect(), expectedChildrenBoundingRect);
3562
3563 // Apply some effects.
3564 QGraphicsDropShadowEffect *dropShadow = new QGraphicsDropShadowEffect;
3565 dropShadow->setOffset(dx: 25, dy: 25);
3566 child->setGraphicsEffect(dropShadow);
3567 parent->setGraphicsEffect(new QGraphicsOpacityEffect);
3568
3569 QVERIFY(parent->childrenBoundingRect() != expectedChildrenBoundingRect);
3570 expectedChildrenBoundingRect |= dropShadow->boundingRect();
3571 QCOMPARE(parent->childrenBoundingRect(), expectedChildrenBoundingRect);
3572}
3573
3574void tst_QGraphicsItem::group()
3575{
3576 QGraphicsScene scene;
3577 QGraphicsRectItem *parent = scene.addRect(rect: QRectF(0, 0, 50, 50), pen: QPen(Qt::black, 0), brush: QBrush(Qt::green));
3578 QGraphicsRectItem *child = scene.addRect(rect: QRectF(0, 0, 50, 50), pen: QPen(Qt::black, 0), brush: QBrush(Qt::blue));
3579 QGraphicsRectItem *parent2 = scene.addRect(rect: QRectF(0, 0, 50, 50), pen: QPen(Qt::black, 0), brush: QBrush(Qt::red));
3580 parent2->setPos(ax: -50, ay: 50);
3581 child->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
3582 child->setParentItem(parent);
3583 parent->setPos(ax: 25, ay: 25);
3584 child->setPos(ax: 25, ay: 25);
3585
3586 QCOMPARE(parent->group(), nullptr);
3587 QCOMPARE(parent2->group(), nullptr);
3588 QCOMPARE(child->group(), nullptr);
3589
3590 QGraphicsView view(&scene);
3591 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3592 view.show();
3593 QVERIFY(QTest::qWaitForWindowExposed(&view));
3594
3595 QGraphicsItemGroup *group = new QGraphicsItemGroup;
3596 group->setSelected(true);
3597 scene.addItem(item: group);
3598
3599 QRectF parentSceneBoundingRect = parent->sceneBoundingRect();
3600 group->addToGroup(item: parent);
3601 QCOMPARE(parent->group(), group);
3602 QCOMPARE(parent->sceneBoundingRect(), parentSceneBoundingRect);
3603
3604 QCOMPARE(parent->parentItem(), group);
3605 QCOMPARE(group->childItems().size(), 1);
3606 QCOMPARE(scene.items().size(), 4);
3607 QCOMPARE(scene.items(group->sceneBoundingRect()).size(), 3);
3608
3609
3610 QRectF parent2SceneBoundingRect = parent2->sceneBoundingRect();
3611 group->addToGroup(item: parent2);
3612 QCOMPARE(parent2->group(), group);
3613 QCOMPARE(parent2->sceneBoundingRect(), parent2SceneBoundingRect);
3614
3615 QCOMPARE(parent2->parentItem(), group);
3616 QCOMPARE(group->childItems().size(), 2);
3617 QCOMPARE(scene.items().size(), 4);
3618 QCOMPARE(scene.items(group->sceneBoundingRect()).size(), 4);
3619
3620 GraphicsItems newItems;
3621 newItems.reserve(size: 100);
3622 for (int i = 0; i < 100; ++i) {
3623 QGraphicsItem *item = scene.addRect(rect: QRectF(-25, -25, 50, 50), pen: QPen(Qt::black, 0),
3624 brush: QBrush(QColor(QRandomGenerator::global()->bounded(highest: 255), QRandomGenerator::global()->bounded(highest: 255),
3625 QRandomGenerator::global()->bounded(highest: 255), QRandomGenerator::global()->bounded(highest: 255))));
3626 newItems << item;
3627 item->setPos(ax: -1000 + QRandomGenerator::global()->bounded(highest: 2000),
3628 ay: -1000 + QRandomGenerator::global()->bounded(highest: 2000));
3629 item->setTransform(matrix: QTransform().rotate(a: QRandomGenerator::global()->bounded(highest: 90)), combine: true);
3630 }
3631
3632 view.fitInView(rect: scene.itemsBoundingRect());
3633
3634 for (QGraphicsItem *item : qAsConst(t&: newItems)) {
3635 group->addToGroup(item);
3636 QCOMPARE(item->group(), group);
3637 }
3638}
3639
3640void tst_QGraphicsItem::setGroup()
3641{
3642 QGraphicsItemGroup group1;
3643 QGraphicsItemGroup group2;
3644
3645 const QScopedPointer<QGraphicsRectItem> rect(new QGraphicsRectItem);
3646 QCOMPARE(rect->group(), nullptr);
3647 QCOMPARE(rect->parentItem(), nullptr);
3648 rect->setGroup(&group1);
3649 QCOMPARE(rect->group(), &group1);
3650 QCOMPARE(rect->parentItem(), &group1);
3651 rect->setGroup(&group2);
3652 QCOMPARE(rect->group(), &group2);
3653 QCOMPARE(rect->parentItem(), &group2);
3654 rect->setGroup(nullptr);
3655 QCOMPARE(rect->group(), nullptr);
3656 QCOMPARE(rect->parentItem(), nullptr);
3657}
3658
3659void tst_QGraphicsItem::setGroup2()
3660{
3661 QGraphicsScene scene;
3662 QGraphicsItemGroup group;
3663 scene.addItem(item: &group);
3664
3665 QGraphicsRectItem *rect = scene.addRect(x: 50,y: 50,w: 50,h: 50,pen: Qt::NoPen,brush: Qt::black);
3666 rect->setTransformOriginPoint(ax: 50,ay: 50);
3667 rect->setRotation(45);
3668 rect->setScale(1.5);
3669 rect->setTransform(matrix: QTransform::fromTranslate(dx: 20,dy: 20), combine: true);
3670 group.setTransform(matrix: QTransform::fromTranslate(dx: -30, dy: -40), combine: true);
3671 group.setRotation(180);
3672 group.setScale(0.5);
3673
3674 QTransform oldSceneTransform = rect->sceneTransform();
3675 rect->setGroup(&group);
3676 QCOMPARE(rect->sceneTransform(), oldSceneTransform);
3677
3678 group.setRotation(20);
3679 group.setScale(2);
3680 rect->setRotation(90);
3681 rect->setScale(0.8);
3682
3683 oldSceneTransform = rect->sceneTransform();
3684 rect->setGroup(nullptr);
3685 qFuzzyCompare(t1: rect->sceneTransform(), t2: oldSceneTransform);
3686}
3687
3688void tst_QGraphicsItem::nestedGroups()
3689{
3690 QGraphicsItemGroup *group1 = new QGraphicsItemGroup;
3691 QGraphicsItemGroup *group2 = new QGraphicsItemGroup;
3692
3693 QGraphicsRectItem *rect = new QGraphicsRectItem;
3694 QGraphicsRectItem *rect2 = new QGraphicsRectItem;
3695 rect2->setParentItem(rect);
3696
3697 group1->addToGroup(item: rect);
3698 QCOMPARE(rect->group(), group1);
3699 QCOMPARE(rect2->group(), group1);
3700
3701 group2->addToGroup(item: group1);
3702 QCOMPARE(rect->group(), group1);
3703 QCOMPARE(rect2->group(), group1);
3704 QCOMPARE(group1->group(), group2);
3705 QCOMPARE(group2->group(), nullptr);
3706
3707 QGraphicsScene scene;
3708 scene.addItem(item: group1);
3709
3710 QCOMPARE(rect->group(), group1);
3711 QCOMPARE(rect2->group(), group1);
3712 QCOMPARE(group1->group(), nullptr);
3713 QVERIFY(group2->childItems().isEmpty());
3714
3715 delete group2;
3716}
3717
3718void tst_QGraphicsItem::warpChildrenIntoGroup()
3719{
3720 QGraphicsScene scene;
3721 QGraphicsRectItem *parentRectItem = scene.addRect(rect: QRectF(0, 0, 100, 100));
3722 QGraphicsRectItem *childRectItem = scene.addRect(rect: QRectF(0, 0, 100, 100));
3723 parentRectItem->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
3724 childRectItem->setPos(ax: -50, ay: -25);
3725 childRectItem->setParentItem(parentRectItem);
3726
3727 QCOMPARE(childRectItem->mapToScene(50, 0), QPointF(25, 0));
3728 QCOMPARE(childRectItem->scenePos(), QPointF(25, -50));
3729
3730 QGraphicsRectItem *parentOfGroup = scene.addRect(rect: QRectF(0, 0, 100, 100));
3731 parentOfGroup->setPos(ax: -200, ay: -200);
3732 parentOfGroup->setTransform(matrix: QTransform::fromScale(dx: 2, dy: 2), combine: true);
3733
3734 QGraphicsItemGroup *group = new QGraphicsItemGroup;
3735 group->setPos(ax: 50, ay: 50);
3736 group->setParentItem(parentOfGroup);
3737
3738 QCOMPARE(group->scenePos(), QPointF(-100, -100));
3739
3740 group->addToGroup(item: childRectItem);
3741
3742 QCOMPARE(childRectItem->mapToScene(50, 0), QPointF(25, 0));
3743 QCOMPARE(childRectItem->scenePos(), QPointF(25, -50));
3744}
3745
3746void tst_QGraphicsItem::removeFromGroup()
3747{
3748 QGraphicsScene scene;
3749 QGraphicsRectItem *rect1 = scene.addRect(rect: QRectF(-100, -100, 200, 200));
3750 QGraphicsRectItem *rect2 = scene.addRect(rect: QRectF(100, 100, 200, 200));
3751 rect1->setFlag(flag: QGraphicsItem::ItemIsSelectable);
3752 rect2->setFlag(flag: QGraphicsItem::ItemIsSelectable);
3753 rect1->setSelected(true);
3754 rect2->setSelected(true);
3755
3756 QGraphicsView view(&scene);
3757 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3758 view.show();
3759
3760 QCoreApplication::processEvents(); // index items
3761 QCoreApplication::processEvents(); // emit changed
3762
3763 QGraphicsItemGroup *group = scene.createItemGroup(items: scene.selectedItems());
3764 QVERIFY(group);
3765 QCOMPARE(group->childItems().size(), 2);
3766 QCoreApplication::processEvents(); // index items
3767 QCoreApplication::processEvents(); // emit changed
3768
3769 scene.destroyItemGroup(group); // calls removeFromGroup.
3770
3771 QCoreApplication::processEvents(); // index items
3772 QCoreApplication::processEvents(); // emit changed
3773
3774 QCOMPARE(scene.items().size(), 2);
3775 QVERIFY(!rect1->group());
3776 QVERIFY(!rect2->group());
3777}
3778
3779class ChildEventTester : public QGraphicsRectItem
3780{
3781public:
3782 using QGraphicsRectItem::QGraphicsRectItem;
3783
3784 int counter = 0;
3785
3786protected:
3787 void focusInEvent(QFocusEvent *event) override
3788 { ++counter; QGraphicsRectItem::focusInEvent(event); }
3789 void mousePressEvent(QGraphicsSceneMouseEvent *) override
3790 { ++counter; }
3791 void mouseMoveEvent(QGraphicsSceneMouseEvent *) override
3792 { ++counter; }
3793 void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override
3794 { ++counter; }
3795};
3796
3797void tst_QGraphicsItem::handlesChildEvents()
3798{
3799 ChildEventTester *blue = new ChildEventTester(QRectF(0, 0, 100, 100));
3800 ChildEventTester *red = new ChildEventTester(QRectF(0, 0, 50, 50));
3801 ChildEventTester *green = new ChildEventTester(QRectF(0, 0, 25, 25));
3802 ChildEventTester *gray = new ChildEventTester(QRectF(0, 0, 25, 25));
3803 ChildEventTester *yellow = new ChildEventTester(QRectF(0, 0, 50, 50));
3804
3805 blue->setBrush(QBrush(Qt::blue));
3806 red->setBrush(QBrush(Qt::red));
3807 yellow->setBrush(QBrush(Qt::yellow));
3808 green->setBrush(QBrush(Qt::green));
3809 gray->setBrush(QBrush(Qt::gray));
3810 red->setPos(ax: 50, ay: 0);
3811 yellow->setPos(ax: 50, ay: 50);
3812 green->setPos(ax: 25, ay: 0);
3813 gray->setPos(ax: 25, ay: 25);
3814 red->setParentItem(blue);
3815 yellow->setParentItem(blue);
3816 green->setParentItem(red);
3817 gray->setParentItem(red);
3818
3819 QGraphicsScene scene;
3820 scene.addItem(item: blue);
3821
3822 QGraphicsView view(&scene);
3823 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3824 view.show();
3825 QVERIFY(QTest::qWaitForWindowExposed(&view));
3826
3827 // Pull out the items, closest item first
3828 QList<QGraphicsItem *> items = scene.items(rect: scene.itemsBoundingRect());
3829 QCOMPARE(items.at(0), yellow);
3830 QCOMPARE(items.at(1), gray);
3831 QCOMPARE(items.at(2), green);
3832 QCOMPARE(items.at(3), red);
3833 QCOMPARE(items.at(4), blue);
3834
3835 QCOMPARE(blue->counter, 0);
3836
3837 // Send events to the toplevel item
3838 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
3839 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
3840
3841 pressEvent.setButton(Qt::LeftButton);
3842 pressEvent.setScenePos(blue->mapToScene(ax: 5, ay: 5));
3843 pressEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
3844 releaseEvent.setButton(Qt::LeftButton);
3845 releaseEvent.setScenePos(blue->mapToScene(ax: 5, ay: 5));
3846 releaseEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
3847 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
3848 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
3849
3850 QCOMPARE(blue->counter, 2);
3851
3852 // Send events to a level1 item
3853 pressEvent.setScenePos(red->mapToScene(ax: 5, ay: 5));
3854 pressEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
3855 releaseEvent.setScenePos(red->mapToScene(ax: 5, ay: 5));
3856 releaseEvent.setScreenPos(view.mapFromScene(point: releaseEvent.scenePos()));
3857 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
3858 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
3859
3860 QCOMPARE(blue->counter, 2);
3861 QCOMPARE(red->counter, 2);
3862
3863 // Send events to a level2 item
3864 pressEvent.setScenePos(green->mapToScene(ax: 5, ay: 5));
3865 pressEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
3866 releaseEvent.setScenePos(green->mapToScene(ax: 5, ay: 5));
3867 releaseEvent.setScreenPos(view.mapFromScene(point: releaseEvent.scenePos()));
3868 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
3869 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
3870
3871 QCOMPARE(blue->counter, 2);
3872 QCOMPARE(red->counter, 2);
3873 QCOMPARE(green->counter, 2);
3874
3875 blue->setHandlesChildEvents(true);
3876
3877 // Send events to a level1 item
3878 pressEvent.setScenePos(red->mapToScene(ax: 5, ay: 5));
3879 pressEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
3880 releaseEvent.setScenePos(red->mapToScene(ax: 5, ay: 5));
3881 releaseEvent.setScreenPos(view.mapFromScene(point: releaseEvent.scenePos()));
3882 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
3883 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
3884
3885 QCOMPARE(blue->counter, 4);
3886 QCOMPARE(red->counter, 2);
3887
3888 // Send events to a level2 item
3889 pressEvent.setScenePos(green->mapToScene(ax: 5, ay: 5));
3890 pressEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
3891 releaseEvent.setScenePos(green->mapToScene(ax: 5, ay: 5));
3892 releaseEvent.setScreenPos(view.mapFromScene(point: releaseEvent.scenePos()));
3893 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
3894 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
3895
3896 QCOMPARE(blue->counter, 6);
3897 QCOMPARE(red->counter, 2);
3898 QCOMPARE(green->counter, 2);
3899
3900 blue->setHandlesChildEvents(false);
3901
3902 // Send events to a level1 item
3903 pressEvent.setScenePos(red->mapToScene(ax: 5, ay: 5));
3904 pressEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
3905 releaseEvent.setScenePos(red->mapToScene(ax: 5, ay: 5));
3906 releaseEvent.setScreenPos(view.mapFromScene(point: releaseEvent.scenePos()));
3907 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
3908 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
3909
3910 QCOMPARE(blue->counter, 6);
3911 QCOMPARE(red->counter, 4);
3912
3913 // Send events to a level2 item
3914 pressEvent.setScenePos(green->mapToScene(ax: 5, ay: 5));
3915 pressEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
3916 releaseEvent.setScenePos(green->mapToScene(ax: 5, ay: 5));
3917 releaseEvent.setScreenPos(view.mapFromScene(point: releaseEvent.scenePos()));
3918 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
3919 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
3920
3921 QCOMPARE(blue->counter, 6);
3922 QCOMPARE(red->counter, 4);
3923 QCOMPARE(green->counter, 4);
3924}
3925
3926void tst_QGraphicsItem::handlesChildEvents2()
3927{
3928 ChildEventTester *root = new ChildEventTester(QRectF(0, 0, 10, 10));
3929 root->setHandlesChildEvents(true);
3930 QVERIFY(root->handlesChildEvents());
3931
3932 ChildEventTester *child = new ChildEventTester(QRectF(0, 0, 10, 10), root);
3933 QVERIFY(!child->handlesChildEvents());
3934
3935 ChildEventTester *child2 = new ChildEventTester(QRectF(0, 0, 10, 10));
3936 ChildEventTester *child3 = new ChildEventTester(QRectF(0, 0, 10, 10), child2);
3937 ChildEventTester *child4 = new ChildEventTester(QRectF(0, 0, 10, 10), child3);
3938 child2->setParentItem(root);
3939 QVERIFY(!child2->handlesChildEvents());
3940 QVERIFY(!child3->handlesChildEvents());
3941 QVERIFY(!child4->handlesChildEvents());
3942
3943 QGraphicsScene scene;
3944 scene.addItem(item: root);
3945
3946 QGraphicsView view(&scene);
3947 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3948 view.show();
3949 QVERIFY(QTest::qWaitForWindowExposed(&view));
3950 QApplication::processEvents();
3951
3952 QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(ax: 5, ay: 5),
3953 view.viewport()->mapToGlobal(view.mapFromScene(ax: 5, ay: 5)), Qt::LeftButton, {}, {});
3954 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
3955
3956 QTRY_COMPARE(root->counter, 1);
3957}
3958
3959void tst_QGraphicsItem::handlesChildEvents3()
3960{
3961 QGraphicsScene scene;
3962 QEvent activate(QEvent::WindowActivate);
3963 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
3964
3965 ChildEventTester *group2 = new ChildEventTester(QRectF(), nullptr);
3966 ChildEventTester *group1 = new ChildEventTester(QRectF(), group2);
3967 ChildEventTester *leaf = new ChildEventTester(QRectF(), group1);
3968 scene.addItem(item: group2);
3969
3970 leaf->setFlag(flag: QGraphicsItem::ItemIsFocusable);
3971 group1->setFlag(flag: QGraphicsItem::ItemIsFocusable);
3972 group1->setHandlesChildEvents(true);
3973 group2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
3974 group2->setHandlesChildEvents(true);
3975
3976 leaf->setFocus();
3977 QVERIFY(leaf->hasFocus()); // group2 stole the event, but leaf still got focus
3978 QVERIFY(!group1->hasFocus());
3979 QVERIFY(!group2->hasFocus());
3980 QCOMPARE(leaf->counter, 0);
3981 QCOMPARE(group1->counter, 0);
3982 QCOMPARE(group2->counter, 1);
3983
3984 group1->setFocus();
3985 QVERIFY(group1->hasFocus()); // group2 stole the event, but group1 still got focus
3986 QVERIFY(!leaf->hasFocus());
3987 QVERIFY(!group2->hasFocus());
3988 QCOMPARE(leaf->counter, 0);
3989 QCOMPARE(group1->counter, 0);
3990 QCOMPARE(group2->counter, 2);
3991
3992 group2->setFocus();
3993 QVERIFY(group2->hasFocus()); // group2 stole the event, and now group2 also has focus
3994 QVERIFY(!leaf->hasFocus());
3995 QVERIFY(!group1->hasFocus());
3996 QCOMPARE(leaf->counter, 0);
3997 QCOMPARE(group1->counter, 0);
3998 QCOMPARE(group2->counter, 3);
3999}
4000
4001
4002class ChildEventFilterTester : public ChildEventTester
4003{
4004public:
4005 ChildEventFilterTester(const QRectF &rect, QGraphicsItem *parent = nullptr)
4006 : ChildEventTester(rect, parent)
4007 { }
4008
4009 QEvent::Type filter = QEvent::None;
4010
4011protected:
4012 bool sceneEventFilter(QGraphicsItem *item, QEvent *event) override
4013 {
4014 Q_UNUSED(item);
4015 if (event->type() == filter) {
4016 ++counter;
4017 return true;
4018 }
4019 return false;
4020 }
4021};
4022
4023void tst_QGraphicsItem::filtersChildEvents()
4024{
4025 QGraphicsScene scene;
4026 ChildEventFilterTester *root = new ChildEventFilterTester(QRectF(0, 0, 10, 10));
4027 ChildEventFilterTester *filter = new ChildEventFilterTester(QRectF(10, 10, 10, 10), root);
4028 ChildEventTester *child = new ChildEventTester(QRectF(20, 20, 10, 10), filter);
4029
4030 // setup filter
4031 filter->setFiltersChildEvents(true);
4032 filter->filter = QEvent::GraphicsSceneMousePress;
4033
4034 scene.addItem(item: root);
4035
4036 QGraphicsView view(&scene);
4037 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
4038 view.show();
4039 QVERIFY(QTest::qWaitForWindowExposed(&view));
4040
4041 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
4042 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
4043
4044 // send event to child
4045 pressEvent.setButton(Qt::LeftButton);
4046 pressEvent.setScenePos(QPointF(25, 25));//child->mapToScene(5, 5));
4047 pressEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
4048 releaseEvent.setButton(Qt::LeftButton);
4049 releaseEvent.setScenePos(QPointF(25, 25));//child->mapToScene(5, 5));
4050 releaseEvent.setScreenPos(view.mapFromScene(point: pressEvent.scenePos()));
4051 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
4052 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
4053
4054 QTRY_COMPARE(child->counter, 1); // mouse release is not filtered
4055 QCOMPARE(filter->counter, 1); // mouse press is filtered
4056 QCOMPARE(root->counter, 0);
4057
4058 // add another filter
4059 root->setFiltersChildEvents(true);
4060 root->filter = QEvent::GraphicsSceneMouseRelease;
4061
4062 // send event to child
4063 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
4064 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
4065
4066 QCOMPARE(child->counter, 1);
4067 QCOMPARE(filter->counter, 2); // mouse press is filtered
4068 QCOMPARE(root->counter, 1); // mouse release is filtered
4069
4070 // reparent to another sub-graph
4071 ChildEventTester *parent = new ChildEventTester(QRectF(10, 10, 10, 10), root);
4072 child->setParentItem(parent);
4073
4074 // send event to child
4075 QCoreApplication::sendEvent(receiver: &scene, event: &pressEvent);
4076 QCoreApplication::sendEvent(receiver: &scene, event: &releaseEvent);
4077
4078 QCOMPARE(child->counter, 2); // mouse press is _not_ filtered
4079 QCOMPARE(parent->counter, 0);
4080 QCOMPARE(filter->counter, 2);
4081 QCOMPARE(root->counter, 2); // mouse release is filtered
4082}
4083
4084void tst_QGraphicsItem::filtersChildEvents2()
4085{
4086 ChildEventFilterTester *root = new ChildEventFilterTester(QRectF(0, 0, 10, 10));
4087 root->setFiltersChildEvents(true);
4088 root->filter = QEvent::GraphicsSceneMousePress;
4089 QVERIFY(root->filtersChildEvents());
4090
4091 ChildEventTester *child = new ChildEventTester(QRectF(0, 0, 10, 10), root);
4092 QVERIFY(!child->filtersChildEvents());
4093
4094 ChildEventTester *child2 = new ChildEventTester(QRectF(0, 0, 10, 10));
4095 ChildEventTester *child3 = new ChildEventTester(QRectF(0, 0, 10, 10), child2);
4096 ChildEventTester *child4 = new ChildEventTester(QRectF(0, 0, 10, 10), child3);
4097
4098 child2->setParentItem(root);
4099 QVERIFY(!child2->filtersChildEvents());
4100 QVERIFY(!child3->filtersChildEvents());
4101 QVERIFY(!child4->filtersChildEvents());
4102
4103 QGraphicsScene scene;
4104 scene.addItem(item: root);
4105
4106 QGraphicsView view(&scene);
4107 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
4108 view.show();
4109
4110 QVERIFY(QTest::qWaitForWindowExposed(&view));
4111 QApplication::processEvents();
4112
4113 QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(ax: 5, ay: 5),
4114 view.viewport()->mapToGlobal(view.mapFromScene(ax: 5, ay: 5)), Qt::LeftButton, {}, {});
4115 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
4116
4117 QTRY_COMPARE(root->counter, 1);
4118 QCOMPARE(child->counter, 0);
4119 QCOMPARE(child2->counter, 0);
4120 QCOMPARE(child3->counter, 0);
4121 QCOMPARE(child4->counter, 0);
4122}
4123
4124class CustomItem : public QGraphicsItem
4125{
4126public:
4127 QRectF boundingRect() const override
4128 { return QRectF(-110, -110, 220, 220); }
4129
4130 void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
4131 {
4132 for (int x = -100; x <= 100; x += 25)
4133 painter->drawLine(x1: x, y1: -100, x2: x, y2: 100);
4134 for (int y = -100; y <= 100; y += 25)
4135 painter->drawLine(x1: -100, y1: y, x2: 100, y2: y);
4136
4137 QFont font = painter->font();
4138 font.setPointSize(4);
4139 painter->setFont(font);
4140 for (int x = -100; x < 100; x += 25) {
4141 const QString prefix = QString::number(x) + QLatin1Char('x');
4142 for (int y = -100; y < 100; y += 25)
4143 painter->drawText(r: QRectF(x, y, 25, 25), flags: Qt::AlignCenter, text: prefix + QString::number(y));
4144 }
4145 }
4146};
4147
4148void tst_QGraphicsItem::ensureVisible()
4149{
4150 QGraphicsScene scene;
4151 scene.setSceneRect(x: -200, y: -200, w: 400, h: 400);
4152 QGraphicsItem *item = new CustomItem;
4153 scene.addItem(item);
4154
4155 QGraphicsView view(&scene);
4156 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
4157 view.setFixedSize(w: 300, h: 300);
4158 view.show();
4159 QVERIFY(QTest::qWaitForWindowExposed(&view));
4160
4161 for (int i = 0; i < 25; ++i) {
4162 view.scale(sx: qreal(1.06), sy: qreal(1.06));
4163 QApplication::processEvents();
4164 }
4165
4166 item->ensureVisible(ax: -100, ay: -100, w: 25, h: 25);
4167
4168 for (int x = -100; x < 100; x += 25) {
4169 for (int y = -100; y < 100; y += 25) {
4170 int xmargin = QRandomGenerator::global()->bounded(highest: 75);
4171 int ymargin = QRandomGenerator::global()->bounded(highest: 75);
4172 item->ensureVisible(ax: x, ay: y, w: 25, h: 25, xmargin, ymargin);
4173 QApplication::processEvents();
4174
4175 QPolygonF viewScenePoly;
4176 viewScenePoly << view.mapToScene(point: view.rect().topLeft())
4177 << view.mapToScene(point: view.rect().topRight())
4178 << view.mapToScene(point: view.rect().bottomRight())
4179 << view.mapToScene(point: view.rect().bottomLeft());
4180
4181 QVERIFY(scene.items(viewScenePoly).contains(item));
4182
4183 QPainterPath path;
4184 path.addPolygon(polygon: viewScenePoly);
4185 QVERIFY(path.contains(item->mapToScene(x + 12, y + 12)));
4186
4187 QPolygonF viewScenePolyMinusMargins;
4188 viewScenePolyMinusMargins << view.mapToScene(point: view.rect().topLeft() + QPoint(xmargin, ymargin))
4189 << view.mapToScene(point: view.rect().topRight() + QPoint(-xmargin, ymargin))
4190 << view.mapToScene(point: view.rect().bottomRight() + QPoint(-xmargin, -ymargin))
4191 << view.mapToScene(point: view.rect().bottomLeft() + QPoint(xmargin, -ymargin));
4192
4193 QPainterPath path2;
4194 path2.addPolygon(polygon: viewScenePolyMinusMargins);
4195 QVERIFY(path2.contains(item->mapToScene(x + 12, y + 12)));
4196 }
4197 }
4198
4199 item->ensureVisible(ax: 100, ay: 100, w: 25, h: 25);
4200 QTest::qWait(ms: 25);
4201}
4202
4203#ifndef QT_NO_CURSOR
4204void tst_QGraphicsItem::cursor()
4205{
4206 QGraphicsScene scene;
4207 QGraphicsView view(&scene);
4208 view.showFullScreen();
4209 QVERIFY(QTest::qWaitForWindowExposed(&view));
4210 const Qt::CursorShape viewportShape = view.viewport()->cursor().shape();
4211
4212 QGraphicsRectItem *item1 = scene.addRect(rect: QRectF(-100, 0, 50, 50));
4213 QGraphicsRectItem *item2 = scene.addRect(rect: QRectF(50, 0, 50, 50));
4214
4215 QVERIFY(!item1->hasCursor());
4216 QVERIFY(!item2->hasCursor());
4217
4218 item1->setCursor(Qt::IBeamCursor);
4219 item2->setCursor(Qt::PointingHandCursor);
4220
4221 QVERIFY(item1->hasCursor());
4222 QVERIFY(item2->hasCursor());
4223
4224 item1->setCursor(QCursor());
4225 item2->setCursor(QCursor());
4226
4227 QVERIFY(item1->hasCursor());
4228 QVERIFY(item2->hasCursor());
4229
4230 item1->unsetCursor();
4231 item2->unsetCursor();
4232
4233 QVERIFY(!item1->hasCursor());
4234 QVERIFY(!item2->hasCursor());
4235
4236 item1->setCursor(Qt::IBeamCursor);
4237 item2->setCursor(Qt::PointingHandCursor);
4238
4239 QPoint viewCenter = view.rect().center();
4240 QPoint item1Center = view.mapFromScene(point: item1->sceneBoundingRect().center());
4241 QPoint item2Center = view.mapFromScene(point: item2->sceneBoundingRect().center());
4242
4243 {
4244 QMouseEvent event(QEvent::MouseMove, viewCenter, view.viewport()->mapToGlobal(viewCenter), Qt::NoButton, {}, {});
4245 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
4246 }
4247
4248 QCOMPARE(view.viewport()->cursor().shape(), viewportShape);
4249
4250 {
4251 QMouseEvent event(QEvent::MouseMove, item1Center, view.viewport()->mapToGlobal(item1Center), Qt::NoButton, {}, {});
4252 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
4253 }
4254
4255 QCOMPARE(view.viewport()->cursor().shape(), item1->cursor().shape());
4256
4257 {
4258 QMouseEvent event(QEvent::MouseMove, item2Center, view.viewport()->mapToGlobal(item2Center), Qt::NoButton, {}, {});
4259 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
4260 }
4261
4262 QCOMPARE(view.viewport()->cursor().shape(), item2->cursor().shape());
4263
4264 {
4265 QMouseEvent event(QEvent::MouseMove, viewCenter, view.viewport()->mapToGlobal(viewCenter), Qt::NoButton, {}, {});
4266 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
4267 }
4268
4269 QCOMPARE(view.viewport()->cursor().shape(), viewportShape);
4270
4271 item1->setEnabled(false);
4272 {
4273 QMouseEvent event(QEvent::MouseMove, item1Center, view.viewport()->mapToGlobal(item1Center), Qt::NoButton, {}, {});
4274 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
4275 }
4276
4277 QCOMPARE(view.viewport()->cursor().shape(), viewportShape);
4278}
4279#endif
4280/*
4281void tst_QGraphicsItem::textControlGetterSetter()
4282{
4283 QGraphicsTextItem *item = new QGraphicsTextItem;
4284 QCOMPARE(item->textControl()->parent(), item);
4285 QPointer<QWidgetTextControl> control = item->textControl();
4286 delete item;
4287 QVERIFY(!control);
4288
4289 item = new QGraphicsTextItem;
4290
4291 QPointer<QWidgetTextControl> oldControl = control;
4292 control = new QWidgetTextControl;
4293
4294 item->setTextControl(control);
4295 QCOMPARE(item->textControl(), control);
4296 QVERIFY(!control->parent());
4297 QVERIFY(!oldControl);
4298
4299 // set some text to give it a size, to test that
4300 // setTextControl (re)connects signals
4301 const QRectF oldBoundingRect = item->boundingRect();
4302 QVERIFY(oldBoundingRect.isValid());
4303 item->setPlainText("Some text");
4304 item->adjustSize();
4305 QVERIFY(item->boundingRect().isValid());
4306 QVERIFY(item->boundingRect() != oldBoundingRect);
4307
4308 // test that on setting a control the item size
4309 // is adjusted
4310 oldControl = control;
4311 control = new QWidgetTextControl;
4312 control->setPlainText("foo!");
4313 item->setTextControl(control);
4314 QCOMPARE(item->boundingRect().size(), control->document()->documentLayout()->documentSize());
4315
4316 QVERIFY(oldControl);
4317 delete oldControl;
4318
4319 delete item;
4320 QVERIFY(control);
4321 delete control;
4322}
4323*/
4324
4325void tst_QGraphicsItem::defaultItemTest_QGraphicsLineItem()
4326{
4327 QGraphicsLineItem item;
4328 QCOMPARE(item.line(), QLineF());
4329 QCOMPARE(item.pen(), QPen());
4330 QCOMPARE(item.shape(), QPainterPath());
4331
4332 item.setPen(QPen(Qt::black, 1));
4333 QCOMPARE(item.pen(), QPen(Qt::black, 1));
4334 item.setLine(QLineF(0, 0, 10, 0));
4335 QCOMPARE(item.line(), QLineF(0, 0, 10, 0));
4336 QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 11, 1));
4337 QCOMPARE(item.shape().elementCount(), 11);
4338
4339 QPainterPath path;
4340 path.moveTo(x: 0, y: -0.5);
4341 path.lineTo(x: 10, y: -0.5);
4342 path.lineTo(x: 10.5, y: -0.5);
4343 path.lineTo(x: 10.5, y: 0.5);
4344 path.lineTo(x: 10, y: 0.5);
4345 path.lineTo(x: 0, y: 0.5);
4346 path.lineTo(x: -0.5, y: 0.5);
4347 path.lineTo(x: -0.5, y: -0.5);
4348 path.lineTo(x: 0, y: -0.5);
4349 path.lineTo(x: 0, y: 0);
4350 path.lineTo(x: 10, y: 0);
4351 path.closeSubpath();
4352
4353 for (int i = 0; i < 11; ++i)
4354 QCOMPARE(QPointF(item.shape().elementAt(i)), QPointF(path.elementAt(i)));
4355}
4356
4357void tst_QGraphicsItem::defaultItemTest_QGraphicsPixmapItem()
4358{
4359 QGraphicsPixmapItem item;
4360 QVERIFY(item.pixmap().isNull());
4361 QCOMPARE(item.offset(), QPointF());
4362 QCOMPARE(item.transformationMode(), Qt::FastTransformation);
4363
4364 QPixmap pixmap(300, 200);
4365 pixmap.fill(fillColor: Qt::red);
4366 item.setPixmap(pixmap);
4367 QCOMPARE(item.pixmap(), pixmap);
4368
4369 item.setTransformationMode(Qt::FastTransformation);
4370 QCOMPARE(item.transformationMode(), Qt::FastTransformation);
4371 item.setTransformationMode(Qt::SmoothTransformation);
4372 QCOMPARE(item.transformationMode(), Qt::SmoothTransformation);
4373
4374 item.setOffset(ax: -15, ay: -15);
4375 QCOMPARE(item.offset(), QPointF(-15, -15));
4376 item.setOffset(QPointF(-10, -10));
4377 QCOMPARE(item.offset(), QPointF(-10, -10));
4378
4379 QCOMPARE(item.boundingRect(), QRectF(-10, -10, 300, 200));
4380}
4381
4382void tst_QGraphicsItem::defaultItemTest_QGraphicsTextItem()
4383{
4384 QGraphicsTextItem *text = new QGraphicsTextItem;
4385 QVERIFY(!text->openExternalLinks());
4386 QVERIFY(text->textCursor().isNull());
4387 QCOMPARE(text->defaultTextColor(), QPalette().color(QPalette::Text));
4388 QVERIFY(text->document() != nullptr);
4389 QCOMPARE(text->font(), QApplication::font());
4390 QCOMPARE(text->textInteractionFlags(), Qt::TextInteractionFlags(Qt::NoTextInteraction));
4391 QCOMPARE(text->textWidth(), -1.0);
4392 QCOMPARE(text->toPlainText(), QString(""));
4393
4394 QGraphicsScene scene;
4395 scene.addItem(item: text);
4396 text->setPlainText("Hello world");
4397 text->setFlag(flag: QGraphicsItem::ItemIsMovable);
4398
4399 {
4400 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
4401 event.setScenePos(QPointF(1, 1));
4402 event.setButton(Qt::LeftButton);
4403 event.setButtons(Qt::LeftButton);
4404 QCoreApplication::sendEvent(receiver: &scene, event: &event);
4405 QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove);
4406 event2.setScenePos(QPointF(11, 11));
4407 event2.setButton(Qt::LeftButton);
4408 event2.setButtons(Qt::LeftButton);
4409 QCoreApplication::sendEvent(receiver: &scene, event: &event2);
4410 }
4411
4412 QCOMPARE(text->pos(), QPointF(10, 10));
4413
4414 text->setTextInteractionFlags(Qt::NoTextInteraction);
4415 QVERIFY(!(text->flags() & QGraphicsItem::ItemAcceptsInputMethod));
4416 text->setTextInteractionFlags(Qt::TextEditorInteraction);
4417 QCOMPARE(text->textInteractionFlags(), Qt::TextInteractionFlags(Qt::TextEditorInteraction));
4418 QVERIFY(text->flags() & QGraphicsItem::ItemAcceptsInputMethod);
4419
4420 {
4421 QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove);
4422 event2.setScenePos(QPointF(21, 21));
4423 event2.setButton(Qt::LeftButton);
4424 event2.setButtons(Qt::LeftButton);
4425 QCoreApplication::sendEvent(receiver: &scene, event: &event2);
4426 }
4427
4428 QCOMPARE(text->pos(), QPointF(20, 20)); // clicked on edge, item moved
4429}
4430
4431void tst_QGraphicsItem::defaultItemTest_QGraphicsEllipseItem()
4432{
4433 QGraphicsEllipseItem item;
4434 item.setPen(QPen(Qt::black, 0));
4435 QVERIFY(item.rect().isNull());
4436 QVERIFY(item.boundingRect().isNull());
4437 QVERIFY(item.shape().isEmpty());
4438 QCOMPARE(item.spanAngle(), 360 * 16);
4439 QCOMPARE(item.startAngle(), 0);
4440
4441 item.setRect(ax: 0, ay: 0, w: 100, h: 100);
4442 QCOMPARE(item.boundingRect(), QRectF(0, 0, 100, 100));
4443
4444 item.setSpanAngle(90 * 16);
4445 // for some reason, the bounding rect has very few significant digits
4446 // (i.e. it's likely that floats are being used inside it), so we
4447 // must force the conversion from qreals to float or these tests will fail
4448 QCOMPARE(float(item.boundingRect().left()), 50.0f);
4449 QVERIFY(qFuzzyIsNull(float(item.boundingRect().top())));
4450 QCOMPARE(float(item.boundingRect().width()), 50.0f);
4451 QCOMPARE(float(item.boundingRect().height()), 50.0f);
4452
4453 item.setPen(QPen(Qt::black, 1));
4454 QCOMPARE(item.boundingRect(), QRectF(49.5, -0.5, 51, 51));
4455
4456 item.setSpanAngle(180 * 16);
4457 QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 101, 51));
4458
4459 item.setSpanAngle(360 * 16);
4460 QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 101, 101));
4461}
4462
4463class ItemChangeTester : public QGraphicsRectItem
4464{
4465public:
4466 ItemChangeTester(QGraphicsItem *parent = nullptr) : QGraphicsRectItem(parent)
4467 {
4468 setFlag(flag: ItemSendsGeometryChanges);
4469 clear();
4470 }
4471
4472 void clear()
4473 {
4474 itemChangeReturnValue = QVariant();
4475 itemSceneChangeTargetScene = nullptr;
4476 changes.clear();
4477 values.clear();
4478 oldValues.clear();
4479 }
4480
4481 QVariant itemChangeReturnValue;
4482 QGraphicsScene *itemSceneChangeTargetScene;
4483
4484 QVector<GraphicsItemChange> changes;
4485 QVariantList values;
4486 QVariantList oldValues;
4487protected:
4488 QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
4489 {
4490 changes << change;
4491 values << value;
4492 switch (change) {
4493 case QGraphicsItem::ItemPositionChange:
4494 oldValues << pos();
4495 break;
4496 case QGraphicsItem::ItemPositionHasChanged:
4497 break;
4498#if QT_DEPRECATED_SINCE(5, 14)
4499 case QGraphicsItem::ItemMatrixChange: {
4500#if QT_DEPRECATED_SINCE(5, 13)
4501QT_WARNING_PUSH
4502QT_WARNING_DISABLE_DEPRECATED
4503 QVariant variant;
4504 variant.setValue<QMatrix>(matrix());
4505 oldValues << variant;
4506QT_WARNING_POP
4507#endif
4508 }
4509 break;
4510#endif
4511 case QGraphicsItem::ItemTransformChange: {
4512 QVariant variant;
4513 variant.setValue<QTransform>(transform());
4514 oldValues << variant;
4515 }
4516 break;
4517 case QGraphicsItem::ItemTransformHasChanged:
4518 break;
4519 case QGraphicsItem::ItemVisibleChange:
4520 oldValues << isVisible();
4521 break;
4522 case QGraphicsItem::ItemVisibleHasChanged:
4523 break;
4524 case QGraphicsItem::ItemEnabledChange:
4525 oldValues << isEnabled();
4526 break;
4527 case QGraphicsItem::ItemEnabledHasChanged:
4528 break;
4529 case QGraphicsItem::ItemSelectedChange:
4530 oldValues << isSelected();
4531 break;
4532 case QGraphicsItem::ItemSelectedHasChanged:
4533 break;
4534 case QGraphicsItem::ItemParentChange:
4535 oldValues << QVariant::fromValue<void *>(value: parentItem());
4536 break;
4537 case QGraphicsItem::ItemParentHasChanged:
4538 break;
4539 case QGraphicsItem::ItemChildAddedChange:
4540 oldValues << childItems().size();
4541 break;
4542 case QGraphicsItem::ItemChildRemovedChange:
4543 oldValues << childItems().size();
4544 break;
4545 case QGraphicsItem::ItemSceneChange:
4546 oldValues << QVariant::fromValue<QGraphicsScene *>(value: scene());
4547 if (itemSceneChangeTargetScene
4548 && qvariant_cast<QGraphicsScene *>(v: value)
4549 && itemSceneChangeTargetScene != qvariant_cast<QGraphicsScene *>(v: value)) {
4550 return QVariant::fromValue<QGraphicsScene *>(value: itemSceneChangeTargetScene);
4551 }
4552 return value;
4553 case QGraphicsItem::ItemSceneHasChanged:
4554 break;
4555 case QGraphicsItem::ItemCursorChange:
4556#ifndef QT_NO_CURSOR
4557 oldValues << cursor();
4558#endif
4559 break;
4560 case QGraphicsItem::ItemCursorHasChanged:
4561 break;
4562 case QGraphicsItem::ItemToolTipChange:
4563 oldValues << toolTip();
4564 break;
4565 case QGraphicsItem::ItemToolTipHasChanged:
4566 break;
4567 case QGraphicsItem::ItemFlagsChange:
4568 oldValues << quint32(flags());
4569 break;
4570 case QGraphicsItem::ItemFlagsHaveChanged:
4571 break;
4572 case QGraphicsItem::ItemZValueChange:
4573 oldValues << zValue();
4574 break;
4575 case QGraphicsItem::ItemZValueHasChanged:
4576 break;
4577 case QGraphicsItem::ItemOpacityChange:
4578 oldValues << opacity();
4579 break;
4580 case QGraphicsItem::ItemOpacityHasChanged:
4581 break;
4582 case QGraphicsItem::ItemScenePositionHasChanged:
4583 break;
4584 case QGraphicsItem::ItemRotationChange:
4585 oldValues << rotation();
4586 break;
4587 case QGraphicsItem::ItemRotationHasChanged:
4588 break;
4589 case QGraphicsItem::ItemScaleChange:
4590 oldValues << scale();
4591 break;
4592 case QGraphicsItem::ItemScaleHasChanged:
4593 break;
4594 case QGraphicsItem::ItemTransformOriginPointChange:
4595 oldValues << transformOriginPoint();
4596 break;
4597 case QGraphicsItem::ItemTransformOriginPointHasChanged:
4598 break;
4599 }
4600 return itemChangeReturnValue.isValid() ? itemChangeReturnValue : value;
4601 }
4602};
4603
4604void tst_QGraphicsItem::itemChange()
4605{
4606 ItemChangeTester tester;
4607 tester.itemSceneChangeTargetScene = nullptr;
4608
4609 ItemChangeTester testerHelper;
4610 QVERIFY(tester.changes.isEmpty());
4611 QVERIFY(tester.values.isEmpty());
4612
4613 int changeCount = 0;
4614 {
4615 // ItemEnabledChange
4616 tester.itemChangeReturnValue = true;
4617 tester.setEnabled(false);
4618 ++changeCount;
4619 ++changeCount; // HasChanged
4620 QCOMPARE(tester.changes.size(), changeCount);
4621 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemEnabledChange);
4622 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemEnabledHasChanged);
4623 QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(false));
4624 QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(true));
4625 QCOMPARE(tester.oldValues.constLast(), QVariant(true));
4626 QCOMPARE(tester.isEnabled(), true);
4627 }
4628#if QT_DEPRECATED_SINCE(5, 13)
4629 {
4630QT_WARNING_PUSH
4631QT_WARNING_DISABLE_DEPRECATED // QDesktopWidget::screen()
4632 // ItemMatrixChange / ItemTransformHasChanged
4633 tester.itemChangeReturnValue.setValue<QMatrix>(QMatrix().rotate(a: 90));
4634 tester.setMatrix(matrix: QMatrix().translate(dx: 50, dy: 0), combine: true);
4635 ++changeCount; // notification sent too
4636 QCOMPARE(tester.changes.size(), ++changeCount);
4637 QCOMPARE(int(tester.changes.at(tester.changes.size() - 2)), int(QGraphicsItem::ItemMatrixChange));
4638 QCOMPARE(int(tester.changes.last()), int(QGraphicsItem::ItemTransformHasChanged));
4639 QCOMPARE(qvariant_cast<QMatrix>(tester.values.at(tester.values.size() - 2)),
4640 QMatrix().translate(50, 0));
4641 QCOMPARE(tester.values.constLast(), QVariant(QTransform(QMatrix().rotate(90))));
4642 QVariant variant;
4643 variant.setValue<QMatrix>(QMatrix());
4644 QCOMPARE(tester.oldValues.constLast(), variant);
4645 QCOMPARE(tester.matrix(), QMatrix().rotate(90));
4646QT_WARNING_POP
4647 }
4648#endif
4649 {
4650 tester.resetTransform();
4651 ++changeCount;
4652 ++changeCount; // notification sent too
4653
4654 // ItemTransformChange / ItemTransformHasChanged
4655 tester.itemChangeReturnValue.setValue<QTransform>(QTransform().rotate(a: 90));
4656 tester.setTransform(matrix: QTransform::fromTranslate(dx: 50, dy: 0), combine: true);
4657 ++changeCount; // notification sent too
4658 ++changeCount;
4659 QCOMPARE(tester.changes.size(), changeCount);
4660 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemTransformChange);
4661 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemTransformHasChanged);
4662 QCOMPARE(qvariant_cast<QTransform>(tester.values.at(tester.values.size() - 2)),
4663 QTransform().translate(50, 0));
4664 QCOMPARE(qvariant_cast<QTransform>(tester.values.at(tester.values.size() - 1)),
4665 QTransform().rotate(90));
4666 QVariant variant;
4667 variant.setValue<QTransform>(QTransform());
4668 QCOMPARE(tester.oldValues.constLast(), variant);
4669 QCOMPARE(tester.transform(), QTransform().rotate(90));
4670 }
4671 {
4672 // ItemPositionChange / ItemPositionHasChanged
4673 tester.itemChangeReturnValue = QPointF(42, 0);
4674 tester.setPos(ax: 0, ay: 42);
4675 ++changeCount; // notification sent too
4676 ++changeCount;
4677 QCOMPARE(tester.changes.size(), changeCount);
4678 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemPositionChange);
4679 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemPositionHasChanged);
4680 QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(QPointF(0, 42)));
4681 QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(QPointF(42, 0)));
4682 QCOMPARE(tester.oldValues.constLast(), QVariant(QPointF()));
4683 QCOMPARE(tester.pos(), QPointF(42, 0));
4684 }
4685 {
4686 // ItemZValueChange / ItemZValueHasChanged
4687 tester.itemChangeReturnValue = qreal(2.0);
4688 tester.setZValue(1.0);
4689 ++changeCount; // notification sent too
4690 ++changeCount;
4691 QCOMPARE(tester.changes.size(), changeCount);
4692 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemZValueChange);
4693 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemZValueHasChanged);
4694 QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(1.0)));
4695 QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(2.0)));
4696 QCOMPARE(tester.oldValues.constLast(), QVariant(qreal(0.0)));
4697 QCOMPARE(tester.zValue(), qreal(2.0));
4698 }
4699 {
4700 // ItemRotationChange / ItemRotationHasChanged
4701 tester.itemChangeReturnValue = qreal(15.0);
4702 tester.setRotation(10.0);
4703 ++changeCount; // notification sent too
4704 ++changeCount;
4705 QCOMPARE(tester.changes.size(), changeCount);
4706 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemRotationChange);
4707 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemRotationHasChanged);
4708 QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(10.0)));
4709 QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(15.0)));
4710 QCOMPARE(tester.oldValues.constLast(), QVariant(qreal(0.0)));
4711 QCOMPARE(tester.rotation(), qreal(15.0));
4712 }
4713 {
4714 // ItemScaleChange / ItemScaleHasChanged
4715 tester.itemChangeReturnValue = qreal(2.0);
4716 tester.setScale(1.5);
4717 ++changeCount; // notification sent too
4718 ++changeCount;
4719 QCOMPARE(tester.changes.size(), changeCount);
4720 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemScaleChange);
4721 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemScaleHasChanged);
4722 QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(1.5)));
4723 QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(2.0)));
4724 QCOMPARE(tester.oldValues.constLast(), QVariant(qreal(1.0)));
4725 QCOMPARE(tester.scale(), qreal(2.0));
4726 }
4727 {
4728 // ItemTransformOriginPointChange / ItemTransformOriginPointHasChanged
4729 tester.itemChangeReturnValue = QPointF(2.0, 2.0);
4730 tester.setTransformOriginPoint(ax: 1.0, ay: 1.0);
4731 ++changeCount; // notification sent too
4732 ++changeCount;
4733 QCOMPARE(tester.changes.size(), changeCount);
4734 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemTransformOriginPointChange);
4735 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemTransformOriginPointHasChanged);
4736 QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(QPointF(1.0, 1.0)));
4737 QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(QPointF(2.0, 2.0)));
4738 QCOMPARE(tester.oldValues.constLast(), QVariant(QPointF(0.0, 0.0)));
4739 QCOMPARE(tester.transformOriginPoint(), QPointF(2.0, 2.0));
4740 }
4741 {
4742 // ItemFlagsChange
4743 tester.itemChangeReturnValue = QGraphicsItem::ItemIsSelectable;
4744 tester.setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: false);
4745 QCOMPARE(tester.changes.size(), changeCount); // No change
4746 tester.setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: true);
4747 ++changeCount;
4748 ++changeCount; // ItemFlagsHasChanged
4749 QCOMPARE(tester.changes.size(), changeCount);
4750 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemFlagsChange);
4751 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemFlagsHaveChanged);
4752 QVariant expectedFlags = QVariant::fromValue<quint32>(value: QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges));
4753 QCOMPARE(tester.values.at(tester.values.size() - 2), expectedFlags);
4754 QCOMPARE(tester.values.at(tester.values.size() - 1),
4755 QVariant::fromValue<quint32>(quint32(QGraphicsItem::ItemIsSelectable)));
4756 }
4757 {
4758 // ItemSelectedChange
4759 tester.setSelected(false);
4760 QCOMPARE(tester.changes.size(), changeCount); // No change :-)
4761 tester.itemChangeReturnValue = true;
4762 tester.setSelected(true);
4763 ++changeCount;
4764 ++changeCount; // ItemSelectedHasChanged
4765 QCOMPARE(tester.changes.size(), changeCount);
4766 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSelectedChange);
4767 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSelectedHasChanged);
4768 QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(true));
4769 QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(true));
4770 QCOMPARE(tester.oldValues.constLast(), QVariant(false));
4771 QCOMPARE(tester.isSelected(), true);
4772
4773 tester.itemChangeReturnValue = false;
4774 tester.setSelected(true);
4775
4776 // the value hasn't changed to the itemChange return value
4777 // bacause itemChange is never called (true -> true is a noop).
4778 QCOMPARE(tester.isSelected(), true);
4779 }
4780 {
4781 // ItemVisibleChange
4782 tester.itemChangeReturnValue = false;
4783 QVERIFY(tester.isVisible());
4784 tester.setVisible(false);
4785 ++changeCount; // ItemVisibleChange
4786 ++changeCount; // ItemSelectedChange
4787 ++changeCount; // ItemSelectedHasChanged
4788 ++changeCount; // ItemVisibleHasChanged
4789 QCOMPARE(tester.changes.size(), changeCount);
4790 QCOMPARE(tester.changes.at(tester.changes.size() - 4), QGraphicsItem::ItemVisibleChange);
4791 QCOMPARE(tester.changes.at(tester.changes.size() - 3), QGraphicsItem::ItemSelectedChange);
4792 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSelectedHasChanged);
4793 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemVisibleHasChanged);
4794 QCOMPARE(tester.values.at(tester.values.size() - 4), QVariant(false));
4795 QCOMPARE(tester.values.at(tester.values.size() - 3), QVariant(false));
4796 QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(false));
4797 QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(false));
4798 QCOMPARE(tester.isVisible(), false);
4799 }
4800 {
4801 // ItemParentChange
4802 tester.itemChangeReturnValue.setValue<QGraphicsItem *>(nullptr);
4803 tester.setParentItem(&testerHelper);
4804 QCOMPARE(tester.changes.size(), ++changeCount);
4805 QCOMPARE(tester.changes.constLast(), QGraphicsItem::ItemParentChange);
4806 QCOMPARE(qvariant_cast<QGraphicsItem *>(tester.values.last()), &testerHelper);
4807 QCOMPARE(qvariant_cast<QGraphicsItem *>(tester.oldValues.last()), nullptr);
4808 QCOMPARE(tester.parentItem(), nullptr);
4809 }
4810 {
4811 // ItemOpacityChange
4812 tester.itemChangeReturnValue = 1.0;
4813 tester.setOpacity(0.7);
4814 QCOMPARE(tester.changes.size(), ++changeCount);
4815 QCOMPARE(tester.changes.constLast(), QGraphicsItem::ItemOpacityChange);
4816 QVERIFY(qFuzzyCompare(qreal(tester.values.last().toDouble()), qreal(0.7)));
4817 QCOMPARE(tester.oldValues.last().toDouble(), double(1.0));
4818 QCOMPARE(tester.opacity(), qreal(1.0));
4819 tester.itemChangeReturnValue = 0.7;
4820 tester.setOpacity(0.7);
4821 ++changeCount; // ItemOpacityChange
4822 ++changeCount; // ItemOpacityHasChanged
4823 QCOMPARE(tester.changes.size(), changeCount);
4824 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemOpacityChange);
4825 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemOpacityHasChanged);
4826 QCOMPARE(tester.opacity(), qreal(0.7));
4827 }
4828 {
4829 // ItemChildAddedChange
4830 tester.itemChangeReturnValue.clear();
4831 testerHelper.setParentItem(&tester);
4832 QCOMPARE(tester.changes.size(), ++changeCount);
4833 QCOMPARE(tester.changes.constLast(), QGraphicsItem::ItemChildAddedChange);
4834 QCOMPARE(qvariant_cast<QGraphicsItem *>(tester.values.last()), &testerHelper);
4835 }
4836 {
4837 // ItemChildRemovedChange 1
4838 testerHelper.setParentItem(nullptr);
4839 QCOMPARE(tester.changes.size(), ++changeCount);
4840 QCOMPARE(tester.changes.constLast(), QGraphicsItem::ItemChildRemovedChange);
4841 QCOMPARE(qvariant_cast<QGraphicsItem *>(tester.values.last()), &testerHelper);
4842
4843 // ItemChildRemovedChange 1
4844 ItemChangeTester *test = new ItemChangeTester;
4845 test->itemSceneChangeTargetScene = nullptr;
4846 int count = 0;
4847 QGraphicsScene *scene = new QGraphicsScene;
4848 scene->addItem(item: test);
4849 count = test->changes.size();
4850 //We test here the fact that when a child is deleted the parent receive only one ItemChildRemovedChange
4851 QGraphicsRectItem *child = new QGraphicsRectItem(test);
4852 //We received ItemChildAddedChange
4853 QCOMPARE(test->changes.size(), ++count);
4854 QCOMPARE(test->changes.constLast(), QGraphicsItem::ItemChildAddedChange);
4855 delete child;
4856 child = nullptr;
4857 QCOMPARE(test->changes.size(), ++count);
4858 QCOMPARE(test->changes.constLast(), QGraphicsItem::ItemChildRemovedChange);
4859
4860 ItemChangeTester *childTester = new ItemChangeTester(test);
4861 //Changes contains all sceneHasChanged and so on, we don't want to test that
4862 int childCount = childTester->changes.size();
4863 //We received ItemChildAddedChange
4864 QCOMPARE(test->changes.size(), ++count);
4865 child = new QGraphicsRectItem(childTester);
4866 //We received ItemChildAddedChange
4867 QCOMPARE(childTester->changes.size(), ++childCount);
4868 QCOMPARE(childTester->changes.constLast(), QGraphicsItem::ItemChildAddedChange);
4869 //Delete the child of the top level with all its children
4870 delete childTester;
4871 //Only one removal
4872 QCOMPARE(test->changes.size(), ++count);
4873 QCOMPARE(test->changes.constLast(), QGraphicsItem::ItemChildRemovedChange);
4874 delete scene;
4875 }
4876 {
4877 // ItemChildRemovedChange 2
4878 ItemChangeTester parent;
4879 ItemChangeTester *child = new ItemChangeTester;
4880 child->setParentItem(&parent);
4881 QCOMPARE(parent.changes.constLast(), QGraphicsItem::ItemChildAddedChange);
4882 QCOMPARE(qvariant_cast<QGraphicsItem *>(parent.values.last()), child);
4883 delete child;
4884 QCOMPARE(parent.changes.constLast(), QGraphicsItem::ItemChildRemovedChange);
4885 QCOMPARE(qvariant_cast<QGraphicsItem *>(parent.values.last()), child);
4886 }
4887 {
4888 // !!! Note: If this test crashes because of double-deletion, there's
4889 // a bug somewhere in QGraphicsScene or QGraphicsItem.
4890
4891 // ItemSceneChange
4892 QGraphicsScene scene;
4893 QGraphicsScene scene2;
4894 scene.addItem(item: &tester);
4895 ++changeCount; // ItemSceneChange (scene)
4896 ++changeCount; // ItemSceneHasChanged (scene)
4897 QCOMPARE(tester.changes.size(), changeCount);
4898
4899 QCOMPARE(tester.scene(), &scene);
4900 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange);
4901 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged);
4902 // Item's old value was 0
4903 // Item's current value is scene
4904 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.last()), nullptr);
4905 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.last()), &scene);
4906 scene2.addItem(item: &tester);
4907 ++changeCount; // ItemSceneChange (0) was: (scene)
4908 ++changeCount; // ItemSceneHasChanged (0)
4909 ++changeCount; // ItemSceneChange (scene2) was: (0)
4910 ++changeCount; // ItemSceneHasChanged (scene2)
4911 QCOMPARE(tester.changes.size(), changeCount);
4912
4913 QCOMPARE(tester.scene(), &scene2);
4914 QCOMPARE(tester.changes.at(tester.changes.size() - 4), QGraphicsItem::ItemSceneChange);
4915 QCOMPARE(tester.changes.at(tester.changes.size() - 3), QGraphicsItem::ItemSceneHasChanged);
4916 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange);
4917 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged);
4918 // Item's last old value was scene
4919 // Item's last current value is 0
4920
4921 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.at(tester.oldValues.size() - 2)), &scene);
4922 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.at(tester.oldValues.size() - 1)), nullptr);
4923 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 4)), nullptr);
4924 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 3)), nullptr);
4925 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), &scene2);
4926 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), &scene2);
4927 // Item's last old value was 0
4928 // Item's last current value is scene2
4929 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.last()), nullptr);
4930 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.last()), &scene2);
4931
4932 scene2.removeItem(item: &tester);
4933 ++changeCount; // ItemSceneChange (0) was: (scene2)
4934 ++changeCount; // ItemSceneHasChanged (0)
4935 QCOMPARE(tester.changes.size(), changeCount);
4936
4937 QCOMPARE(tester.scene(), nullptr);
4938 QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange);
4939 QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged);
4940 // Item's last old value was scene2
4941 // Item's last current value is 0
4942 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.last()), &scene2);
4943 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), nullptr);
4944 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), nullptr);
4945
4946 tester.itemSceneChangeTargetScene = &scene;
4947 scene2.addItem(item: &tester);
4948 ++changeCount; // ItemSceneChange (scene2) was: (0)
4949 ++changeCount; // ItemSceneChange (scene) was: (0)
4950 ++changeCount; // ItemSceneHasChanged (scene)
4951 QCOMPARE(tester.values.size(), changeCount);
4952
4953 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 3)), &scene2);
4954 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), &scene);
4955 QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), &scene);
4956
4957 QCOMPARE(tester.scene(), &scene);
4958 tester.itemSceneChangeTargetScene = nullptr;
4959 tester.itemChangeReturnValue = QVariant();
4960 scene.removeItem(item: &tester);
4961 ++changeCount; // ItemSceneChange
4962 ++changeCount; // ItemSceneHasChanged
4963 QCOMPARE(tester.scene(), nullptr);
4964 }
4965 {
4966 // ItemToolTipChange/ItemToolTipHasChanged
4967 const QString toolTip(QLatin1String("I'm soo cool"));
4968 const QString overridenToolTip(QLatin1String("No, you are not soo cool"));
4969 tester.itemChangeReturnValue = overridenToolTip;
4970 tester.setToolTip(toolTip);
4971 ++changeCount; // ItemToolTipChange
4972 ++changeCount; // ItemToolTipHasChanged
4973 QCOMPARE(tester.changes.size(), changeCount);
4974 QCOMPARE(tester.changes.at(changeCount - 2), QGraphicsItem::ItemToolTipChange);
4975 QCOMPARE(tester.values.at(changeCount - 2).toString(), toolTip);
4976 QCOMPARE(tester.changes.at(changeCount - 1), QGraphicsItem::ItemToolTipHasChanged);
4977 QCOMPARE(tester.values.at(changeCount - 1).toString(), overridenToolTip);
4978 QCOMPARE(tester.toolTip(), overridenToolTip);
4979 tester.itemChangeReturnValue = QVariant();
4980 }
4981}
4982
4983class EventFilterTesterItem : public QGraphicsLineItem
4984{
4985public:
4986 QVector<QEvent::Type> filteredEvents;
4987 GraphicsItems filteredEventReceivers;
4988 QVector<QEvent::Type> receivedEvents;
4989 bool handlesSceneEvents = false;
4990
4991protected:
4992 bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
4993 {
4994 filteredEvents << event->type();
4995 filteredEventReceivers << watched;
4996 return handlesSceneEvents;
4997 }
4998
4999 bool sceneEvent(QEvent *event) override
5000 {
5001 return QGraphicsLineItem::sceneEvent(event);
5002 }
5003};
5004
5005void tst_QGraphicsItem::sceneEventFilter()
5006{
5007 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
5008 QSKIP("Window activation is not supported");
5009
5010 QGraphicsScene scene;
5011
5012 QGraphicsView view(&scene);
5013 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5014 view.show();
5015 QApplication::setActiveWindow(&view);
5016 QVERIFY(QTest::qWaitForWindowExposed(&view));
5017 QVERIFY(QTest::qWaitForWindowActive(&view));
5018
5019 QGraphicsTextItem *text1 = scene.addText(text: QLatin1String("Text1"));
5020 QGraphicsTextItem *text2 = scene.addText(text: QLatin1String("Text2"));
5021 QGraphicsTextItem *text3 = scene.addText(text: QLatin1String("Text3"));
5022 text1->setFlag(flag: QGraphicsItem::ItemIsFocusable);
5023 text2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
5024 text3->setFlag(flag: QGraphicsItem::ItemIsFocusable);
5025
5026 EventFilterTesterItem *tester = new EventFilterTesterItem;
5027 scene.addItem(item: tester);
5028
5029 QTRY_VERIFY(!text1->hasFocus());
5030 text1->installSceneEventFilter(filterItem: tester);
5031 text1->setFocus();
5032 QTRY_VERIFY(text1->hasFocus());
5033
5034 QCOMPARE(tester->filteredEvents.size(), 1);
5035 QCOMPARE(tester->filteredEvents.at(0), QEvent::FocusIn);
5036 QCOMPARE(tester->filteredEventReceivers.at(0), static_cast<QGraphicsItem *>(text1));
5037
5038 text2->installSceneEventFilter(filterItem: tester);
5039 text3->installSceneEventFilter(filterItem: tester);
5040
5041 text2->setFocus();
5042 text3->setFocus();
5043
5044 QCOMPARE(tester->filteredEvents.size(), 5);
5045 QCOMPARE(tester->filteredEvents.at(1), QEvent::FocusOut);
5046 QCOMPARE(tester->filteredEventReceivers.at(1), static_cast<QGraphicsItem *>(text1));
5047 QCOMPARE(tester->filteredEvents.at(2), QEvent::FocusIn);
5048 QCOMPARE(tester->filteredEventReceivers.at(2), static_cast<QGraphicsItem *>(text2));
5049 QCOMPARE(tester->filteredEvents.at(3), QEvent::FocusOut);
5050 QCOMPARE(tester->filteredEventReceivers.at(3), static_cast<QGraphicsItem *>(text2));
5051 QCOMPARE(tester->filteredEvents.at(4), QEvent::FocusIn);
5052 QCOMPARE(tester->filteredEventReceivers.at(4), static_cast<QGraphicsItem *>(text3));
5053
5054 text1->removeSceneEventFilter(filterItem: tester);
5055 text1->setFocus();
5056
5057 QCOMPARE(tester->filteredEvents.size(), 6);
5058 QCOMPARE(tester->filteredEvents.at(5), QEvent::FocusOut);
5059 QCOMPARE(tester->filteredEventReceivers.at(5), static_cast<QGraphicsItem *>(text3));
5060
5061 tester->handlesSceneEvents = true;
5062 text2->setFocus();
5063
5064 QCOMPARE(tester->filteredEvents.size(), 7);
5065 QCOMPARE(tester->filteredEvents.at(6), QEvent::FocusIn);
5066 QCOMPARE(tester->filteredEventReceivers.at(6), static_cast<QGraphicsItem *>(text2));
5067
5068 QVERIFY(text2->hasFocus());
5069
5070 //Let check if the items are correctly removed from the sceneEventFilters array
5071 //to avoid stale pointers.
5072 QGraphicsView gv;
5073 QGraphicsScene *anotherScene = new QGraphicsScene;
5074 QGraphicsTextItem *ti = anotherScene->addText(text: "This is a test #1");
5075 ti->moveBy(dx: 50, dy: 50);
5076 QGraphicsTextItem *ti2 = anotherScene->addText(text: "This is a test #2");
5077 QGraphicsTextItem *ti3 = anotherScene->addText(text: "This is a test #3");
5078 gv.setScene(anotherScene);
5079 gv.show();
5080 QVERIFY(QTest::qWaitForWindowExposed(&gv));
5081 ti->installSceneEventFilter(filterItem: ti2);
5082 ti3->installSceneEventFilter(filterItem: ti);
5083 delete ti2;
5084 //we souldn't crash
5085 QTest::mouseMove(widget: gv.viewport(), pos: gv.mapFromScene(point: ti->scenePos()));
5086 QTest::qWait(ms: 30);
5087 delete ti;
5088}
5089
5090class GeometryChanger : public QGraphicsRectItem
5091{
5092public:
5093 explicit GeometryChanger(QRectF r) : QGraphicsRectItem(r) {}
5094 void changeGeometry()
5095 { prepareGeometryChange(); }
5096};
5097
5098void tst_QGraphicsItem::prepareGeometryChange()
5099{
5100 {
5101 QGraphicsScene scene;
5102 const QRectF rect(0, 0, 100, 100);
5103 GeometryChanger item(rect);
5104 scene.addItem(item: &item);
5105 QVERIFY(scene.items(rect).contains(&item));
5106 item.changeGeometry();
5107 QVERIFY(scene.items(rect).contains(&item));
5108 }
5109}
5110
5111
5112class PaintTester : public QGraphicsRectItem
5113{
5114public:
5115 PaintTester() { setRect(QRectF(10, 10, 20, 20));}
5116
5117 void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *w) override
5118 {
5119 widget = w;
5120 painted++;
5121 }
5122
5123 QWidget *widget = nullptr;
5124 int painted = 0;
5125};
5126
5127void tst_QGraphicsItem::paint()
5128{
5129 QGraphicsScene scene;
5130
5131 PaintTester paintTester;
5132 scene.addItem(item: &paintTester);
5133
5134 QGraphicsView view(&scene);
5135 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5136 view.show();
5137 QVERIFY(QTest::qWaitForWindowExposed(&view));
5138 QApplication::processEvents();
5139#ifdef Q_OS_WIN32
5140 //we try to switch the desktop: if it fails, we skip the test
5141 if (::SwitchDesktop( ::GetThreadDesktop( ::GetCurrentThreadId() ) ) == 0) {
5142 QSKIP("The Graphics View doesn't get the paint events");
5143 }
5144#endif
5145
5146 QTRY_COMPARE(paintTester.widget, view.viewport());
5147
5148 view.hide();
5149
5150 QGraphicsScene scene2;
5151 QGraphicsView view2(&scene2);
5152 view2.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5153 view2.show();
5154 QVERIFY(QTest::qWaitForWindowExposed(&view2));
5155 QCoreApplication::processEvents(); // Process all queued paint events
5156
5157 PaintTester tester2;
5158 scene2.addItem(item: &tester2);
5159
5160 //First show at least one paint
5161 QCOMPARE(tester2.painted, 0);
5162 QTRY_VERIFY(tester2.painted > 0);
5163 int painted = tester2.painted;
5164
5165 //nominal case, update call paint
5166 tester2.update();
5167 QTRY_COMPARE(tester2.painted, painted + 1);
5168 painted = tester2.painted;
5169
5170 //we remove the item from the scene, number of updates is still the same
5171 tester2.update();
5172 scene2.removeItem(item: &tester2);
5173 QTRY_COMPARE(tester2.painted, painted);
5174
5175 //We re-add the item, the number of paint should increase
5176 scene2.addItem(item: &tester2);
5177 tester2.update();
5178 QTRY_COMPARE(tester2.painted, painted + 1);
5179}
5180
5181class HarakiriItem : public QGraphicsRectItem
5182{
5183public:
5184 HarakiriItem(int harakiriPoint)
5185 : QGraphicsRectItem(QRectF(0, 0, 100, 100)), harakiri(harakiriPoint)
5186 { dead = false; }
5187
5188 static bool dead;
5189
5190 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
5191 {
5192 QGraphicsRectItem::paint(painter, option, widget);
5193 if (harakiri == 0) {
5194 // delete unsupported since 4.5
5195 /*
5196 dead = true;
5197 delete this;
5198 */
5199 }
5200 }
5201
5202 void advance(int n) override
5203 {
5204 if (harakiri == 1 && n == 0) {
5205 // delete unsupported
5206 /*
5207 dead = true;
5208 delete this;
5209 */
5210 }
5211 if (harakiri == 2 && n == 1) {
5212 dead = true;
5213 delete this;
5214 }
5215 }
5216
5217protected:
5218#ifndef QT_NO_CONTEXTMENU
5219 void contextMenuEvent(QGraphicsSceneContextMenuEvent *) override
5220 {
5221 if (harakiri == 3) {
5222 dead = true;
5223 delete this;
5224 }
5225 }
5226#endif // QT_NO_CONTEXTMENU
5227
5228 void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override
5229 {
5230 // ??
5231 QGraphicsRectItem::dragEnterEvent(event);
5232 }
5233
5234 void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) override
5235 {
5236 // ??
5237 QGraphicsRectItem::dragLeaveEvent(event);
5238 }
5239
5240 void dragMoveEvent(QGraphicsSceneDragDropEvent *event) override
5241 {
5242 // ??
5243 QGraphicsRectItem::dragMoveEvent(event);
5244 }
5245
5246 void dropEvent(QGraphicsSceneDragDropEvent *event) override
5247 {
5248 // ??
5249 QGraphicsRectItem::dropEvent(event);
5250 }
5251
5252 void focusInEvent(QFocusEvent *) override
5253 {
5254 if (harakiri == 4) {
5255 dead = true;
5256 delete this;
5257 }
5258 }
5259
5260 void focusOutEvent(QFocusEvent *) override
5261 {
5262 if (harakiri == 5) {
5263 dead = true;
5264 delete this;
5265 }
5266 }
5267
5268 void hoverEnterEvent(QGraphicsSceneHoverEvent *) override
5269 {
5270 if (harakiri == 6) {
5271 dead = true;
5272 delete this;
5273 }
5274 }
5275
5276 void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override
5277 {
5278 if (harakiri == 7) {
5279 dead = true;
5280 delete this;
5281 }
5282 }
5283
5284 void hoverMoveEvent(QGraphicsSceneHoverEvent *) override
5285 {
5286 if (harakiri == 8) {
5287 dead = true;
5288 delete this;
5289 }
5290 }
5291
5292 void inputMethodEvent(QInputMethodEvent *event) override
5293 {
5294 // ??
5295 QGraphicsRectItem::inputMethodEvent(event);
5296 }
5297
5298 QVariant inputMethodQuery(Qt::InputMethodQuery query) const override
5299 {
5300 // ??
5301 return QGraphicsRectItem::inputMethodQuery(query);
5302 }
5303
5304 QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
5305 {
5306 // deletion not supported
5307 return QGraphicsRectItem::itemChange(change, value);
5308 }
5309
5310 void keyPressEvent(QKeyEvent *) override
5311 {
5312 if (harakiri == 9) {
5313 dead = true;
5314 delete this;
5315 }
5316 }
5317
5318 void keyReleaseEvent(QKeyEvent *) override
5319 {
5320 if (harakiri == 10) {
5321 dead = true;
5322 delete this;
5323 }
5324 }
5325
5326 void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override
5327 {
5328 if (harakiri == 11) {
5329 dead = true;
5330 delete this;
5331 }
5332 }
5333
5334 void mouseMoveEvent(QGraphicsSceneMouseEvent *) override
5335 {
5336 if (harakiri == 12) {
5337 dead = true;
5338 delete this;
5339 }
5340 }
5341
5342 void mousePressEvent(QGraphicsSceneMouseEvent *) override
5343 {
5344 if (harakiri == 13) {
5345 dead = true;
5346 delete this;
5347 }
5348 }
5349
5350 void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override
5351 {
5352 if (harakiri == 14) {
5353 dead = true;
5354 delete this;
5355 }
5356 }
5357
5358 bool sceneEvent(QEvent *event) override
5359 {
5360 // deletion not supported
5361 return QGraphicsRectItem::sceneEvent(event);
5362 }
5363
5364 bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
5365 {
5366 // deletion not supported
5367 return QGraphicsRectItem::sceneEventFilter(watched, event);
5368 }
5369
5370 void wheelEvent(QGraphicsSceneWheelEvent *) override
5371 {
5372 if (harakiri == 16) {
5373 dead = true;
5374 delete this;
5375 }
5376 }
5377
5378private:
5379 int harakiri;
5380};
5381
5382bool HarakiriItem::dead;
5383
5384void tst_QGraphicsItem::deleteItemInEventHandlers()
5385{
5386 for (int i = 0; i < 17; ++i) {
5387 QGraphicsScene scene;
5388 HarakiriItem *item = new HarakiriItem(i);
5389 item->setAcceptHoverEvents(true);
5390 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
5391
5392 scene.addItem(item);
5393
5394 item->installSceneEventFilter(filterItem: item); // <- ehey!
5395
5396 QGraphicsView view(&scene);
5397 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5398 view.show();
5399
5400 QCoreApplication::processEvents();
5401 QCoreApplication::processEvents();
5402
5403 if (!HarakiriItem::dead)
5404 scene.advance();
5405
5406#ifndef QT_NO_CONTEXTMENU
5407 if (!HarakiriItem::dead) {
5408 QContextMenuEvent event(QContextMenuEvent::Other,
5409 view.mapFromScene(point: item->scenePos()));
5410 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
5411 }
5412#endif // QT_NO_CONTEXTMENU
5413 if (!HarakiriItem::dead)
5414 QTest::mouseMove(widget: view.viewport(), pos: view.mapFromScene(point: item->scenePos()));
5415 if (!HarakiriItem::dead)
5416 QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item->scenePos()));
5417 if (!HarakiriItem::dead)
5418 QTest::mouseDClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: view.mapFromScene(point: item->scenePos()));
5419 if (!HarakiriItem::dead)
5420 QTest::mouseClick(widget: view.viewport(), button: Qt::RightButton, stateKey: {}, pos: view.mapFromScene(point: item->scenePos()));
5421 if (!HarakiriItem::dead)
5422 QTest::mouseMove(widget: view.viewport(), pos: view.mapFromScene(point: item->scenePos() + QPointF(20, -20)));
5423 if (!HarakiriItem::dead)
5424 item->setFocus();
5425 if (!HarakiriItem::dead)
5426 item->clearFocus();
5427 if (!HarakiriItem::dead)
5428 item->setFocus();
5429 if (!HarakiriItem::dead)
5430 QTest::keyPress(widget: view.viewport(), key: Qt::Key_A);
5431 if (!HarakiriItem::dead)
5432 QTest::keyRelease(widget: view.viewport(), key: Qt::Key_A);
5433 if (!HarakiriItem::dead)
5434 QTest::keyPress(widget: view.viewport(), key: Qt::Key_A);
5435 if (!HarakiriItem::dead)
5436 QTest::keyRelease(widget: view.viewport(), key: Qt::Key_A);
5437 }
5438}
5439
5440class ItemPaintsOutsideShape : public QGraphicsItem
5441{
5442public:
5443 QRectF boundingRect() const override
5444 {
5445 return QRectF(0, 0, 100, 100);
5446 }
5447
5448 void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
5449 {
5450 painter->fillRect(x: -50, y: -50, w: 200, h: 200, c: Qt::red);
5451 painter->fillRect(x: 0, y: 0, w: 100, h: 100, c: Qt::blue);
5452 }
5453};
5454
5455void tst_QGraphicsItem::itemClipsToShape()
5456{
5457 QGraphicsItem *clippedItem = new ItemPaintsOutsideShape;
5458 clippedItem->setFlag(flag: QGraphicsItem::ItemClipsToShape);
5459
5460 QGraphicsItem *unclippedItem = new ItemPaintsOutsideShape;
5461 unclippedItem->setPos(ax: 200, ay: 0);
5462
5463 QGraphicsScene scene(-50, -50, 400, 200);
5464 scene.addItem(item: clippedItem);
5465 scene.addItem(item: unclippedItem);
5466
5467 QImage image(400, 200, QImage::Format_ARGB32_Premultiplied);
5468 image.fill(pixel: 0);
5469 QPainter painter(&image);
5470 painter.setRenderHint(hint: QPainter::Antialiasing);
5471 scene.render(painter: &painter);
5472 painter.end();
5473
5474 QCOMPARE(image.pixel(45, 100), QRgb(0));
5475 QCOMPARE(image.pixel(100, 45), QRgb(0));
5476 QCOMPARE(image.pixel(155, 100), QRgb(0));
5477 QCOMPARE(image.pixel(45, 155), QRgb(0));
5478 QCOMPARE(image.pixel(55, 100), QColor(Qt::blue).rgba());
5479 QCOMPARE(image.pixel(100, 55), QColor(Qt::blue).rgba());
5480 QCOMPARE(image.pixel(145, 100), QColor(Qt::blue).rgba());
5481 QCOMPARE(image.pixel(55, 145), QColor(Qt::blue).rgba());
5482 QCOMPARE(image.pixel(245, 100), QColor(Qt::red).rgba());
5483 QCOMPARE(image.pixel(300, 45), QColor(Qt::red).rgba());
5484 QCOMPARE(image.pixel(355, 100), QColor(Qt::red).rgba());
5485 QCOMPARE(image.pixel(245, 155), QColor(Qt::red).rgba());
5486 QCOMPARE(image.pixel(255, 100), QColor(Qt::blue).rgba());
5487 QCOMPARE(image.pixel(300, 55), QColor(Qt::blue).rgba());
5488 QCOMPARE(image.pixel(345, 100), QColor(Qt::blue).rgba());
5489 QCOMPARE(image.pixel(255, 145), QColor(Qt::blue).rgba());
5490}
5491
5492void tst_QGraphicsItem::itemClipsChildrenToShape()
5493{
5494 QGraphicsScene scene;
5495 QGraphicsItem *rect = scene.addRect(x: 0, y: 0, w: 50, h: 50, pen: QPen(Qt::NoPen), brush: QBrush(Qt::yellow));
5496
5497 QGraphicsItem *ellipse = scene.addEllipse(x: 0, y: 0, w: 100, h: 100, pen: QPen(Qt::NoPen), brush: QBrush(Qt::green));
5498 ellipse->setParentItem(rect);
5499
5500 QGraphicsItem *clippedEllipse = scene.addEllipse(x: 0, y: 0, w: 50, h: 50, pen: QPen(Qt::NoPen), brush: QBrush(Qt::blue));
5501 clippedEllipse->setParentItem(ellipse);
5502
5503 QGraphicsItem *clippedEllipse2 = scene.addEllipse(x: 0, y: 0, w: 25, h: 25, pen: QPen(Qt::NoPen), brush: QBrush(Qt::red));
5504 clippedEllipse2->setParentItem(clippedEllipse);
5505
5506 QGraphicsItem *clippedEllipse3 = scene.addEllipse(x: 50, y: 50, w: 25, h: 25, pen: QPen(Qt::NoPen), brush: QBrush(Qt::red));
5507 clippedEllipse3->setParentItem(clippedEllipse);
5508
5509 QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape));
5510 ellipse->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
5511 QVERIFY((ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape));
5512
5513 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
5514 image.fill(pixel: 0);
5515 QPainter painter(&image);
5516 painter.setRenderHint(hint: QPainter::Antialiasing);
5517 scene.render(painter: &painter);
5518 painter.end();
5519
5520 QCOMPARE(image.pixel(16, 16), QColor(255, 0, 0).rgba());
5521 QCOMPARE(image.pixel(32, 32), QColor(0, 0, 255).rgba());
5522 QCOMPARE(image.pixel(50, 50), QColor(0, 255, 0).rgba());
5523 QCOMPARE(image.pixel(12, 12), QColor(255, 255, 0).rgba());
5524 QCOMPARE(image.pixel(60, 60), QColor(255, 0, 0).rgba());
5525}
5526
5527void tst_QGraphicsItem::itemClipsChildrenToShape2()
5528{
5529 QGraphicsRectItem *parent = new QGraphicsRectItem(QRectF(0, 0, 10, 10));
5530 QGraphicsEllipseItem *child1 = new QGraphicsEllipseItem(QRectF(50, 50, 100, 100));
5531 QGraphicsRectItem *child2 = new QGraphicsRectItem(QRectF(15, 15, 80, 80));
5532
5533 child1->setParentItem(parent);
5534 child1->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
5535 child2->setParentItem(child1);
5536
5537 parent->setBrush(Qt::blue);
5538 child1->setBrush(Qt::green);
5539 child2->setBrush(Qt::red);
5540
5541 QGraphicsScene scene;
5542 scene.addItem(item: parent);
5543
5544 QCOMPARE(scene.items(QPointF(5, 5)).value(0, nullptr), parent);
5545 QVERIFY(scene.items(QPointF(15, 5)).isEmpty());
5546 QVERIFY(scene.items(QPointF(5, 15)).isEmpty());
5547 QVERIFY(scene.items(QPointF(60, 60)).isEmpty());
5548 QVERIFY(scene.items(QPointF(140, 60)).isEmpty());
5549 QVERIFY(scene.items(QPointF(60, 140)).isEmpty());
5550 QVERIFY(scene.items(QPointF(140, 140)).isEmpty());
5551 QCOMPARE(scene.items(QPointF(75, 75)).value(0, nullptr), child2);
5552 QCOMPARE(scene.items(QPointF(75, 100)).value(0, nullptr), child1);
5553 QCOMPARE(scene.items(QPointF(100, 75)).value(0, nullptr), child1);
5554
5555 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
5556 image.fill(pixel: 0);
5557 QPainter painter(&image);
5558 scene.render(painter: &painter);
5559 painter.end();
5560
5561 QCOMPARE(image.pixel(5, 5), QColor(0, 0, 255).rgba());
5562 QCOMPARE(image.pixel(5, 10), QRgb(0));
5563 QCOMPARE(image.pixel(10, 5), QRgb(0));
5564 QCOMPARE(image.pixel(40, 40), QRgb(0));
5565 QCOMPARE(image.pixel(90, 40), QRgb(0));
5566 QCOMPARE(image.pixel(40, 90), QRgb(0));
5567 QCOMPARE(image.pixel(95, 95), QRgb(0));
5568 QCOMPARE(image.pixel(50, 70), QColor(0, 255, 0).rgba());
5569 QCOMPARE(image.pixel(70, 50), QColor(0, 255, 0).rgba());
5570 QCOMPARE(image.pixel(50, 60), QColor(255, 0, 0).rgba());
5571 QCOMPARE(image.pixel(60, 50), QColor(255, 0, 0).rgba());
5572}
5573
5574void tst_QGraphicsItem::itemClipsChildrenToShape3()
5575{
5576 // Construct a scene with nested children, each 50 pixels offset from the elder.
5577 // Set a top-level clipping flag
5578 QGraphicsScene scene;
5579 QGraphicsRectItem *parent = scene.addRect( x: 0, y: 0, w: 150, h: 150 );
5580 QGraphicsRectItem *child = scene.addRect( x: 0, y: 0, w: 150, h: 150 );
5581 QGraphicsRectItem *grandchild = scene.addRect( x: 0, y: 0, w: 150, h: 150 );
5582 child->setParentItem(parent);
5583 grandchild->setParentItem(child);
5584 child->setPos( ax: 50, ay: 50 );
5585 grandchild->setPos( ax: 50, ay: 50 );
5586 parent->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
5587
5588 QCOMPARE(scene.items(QPointF(25, 25)).value(0, nullptr), parent);
5589 QCOMPARE(scene.items(QPointF(75, 75)).value(0, nullptr), child);
5590 QCOMPARE(scene.items(QPointF(125, 125)).value(0, nullptr), grandchild);
5591 QVERIFY(scene.items(QPointF(175, 175)).isEmpty());
5592
5593 // Move child to fully overlap the parent. The grandchild should
5594 // now occupy two-thirds of the scene
5595 child->prepareGeometryChange();
5596 child->setPos( ax: 0, ay: 0 );
5597
5598 QCOMPARE(scene.items(QPointF(25, 25)).value(0, nullptr), child);
5599 QCOMPARE(scene.items(QPointF(75, 75)).value(0, nullptr), grandchild);
5600 QCOMPARE(scene.items(QPointF(125, 125)).value(0, nullptr), grandchild);
5601 QVERIFY(scene.items(QPointF(175, 175)).isEmpty());
5602}
5603
5604class MyProxyWidget : public QGraphicsProxyWidget
5605{
5606public:
5607 using QGraphicsProxyWidget::QGraphicsProxyWidget;
5608
5609 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
5610 {
5611 QGraphicsProxyWidget::paint(painter, option, widget);
5612 painted = true;
5613 }
5614
5615 bool painted = false;
5616};
5617
5618void tst_QGraphicsItem::itemClipsChildrenToShape4()
5619{
5620 QGraphicsScene scene;
5621 QGraphicsView view(&scene);
5622
5623 QGraphicsWidget * outerWidget = new QGraphicsWidget();
5624 outerWidget->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape, enabled: true);
5625 MyProxyWidget * innerWidget = new MyProxyWidget(outerWidget);
5626 QLabel * label = new QLabel();
5627 label->setText("Welcome back my friends to the show that never ends...");
5628 innerWidget->setWidget(label);
5629 view.resize(w: 300, h: 300);
5630 scene.addItem(item: outerWidget);
5631 outerWidget->resize( w: 200, h: 100 );
5632 scene.addEllipse( x: 100, y: 100, w: 100, h: 50 ); // <-- this is important to trigger the right codepath*
5633 //now the label is shown
5634 outerWidget->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape, enabled: false );
5635 QApplication::setActiveWindow(&view);
5636 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5637 view.show();
5638 QTRY_COMPARE(QApplication::activeWindow(), &view);
5639 QTRY_COMPARE(innerWidget->painted, true);
5640}
5641
5642//#define DEBUG_ITEM_CLIPS_CHILDREN_TO_SHAPE_5
5643static inline void renderSceneToImage(QGraphicsScene *scene, QImage *image, const QString &filename)
5644{
5645 image->fill(pixel: 0);
5646 QPainter painter(image);
5647 scene->render(painter: &painter);
5648 painter.end();
5649#ifdef DEBUG_ITEM_CLIPS_CHILDREN_TO_SHAPE_5
5650 image->save(filename);
5651#else
5652 Q_UNUSED(filename);
5653#endif
5654}
5655
5656void tst_QGraphicsItem::itemClipsChildrenToShape5()
5657{
5658 class ParentItem : public QGraphicsRectItem
5659 {
5660 public:
5661 using QGraphicsRectItem::QGraphicsRectItem;
5662
5663 QPainterPath shape() const override
5664 {
5665 QPainterPath path;
5666 path.addRect(x: 50, y: 50, w: 200, h: 200);
5667 return path;
5668 }
5669 };
5670
5671 ParentItem *parent = new ParentItem(0, 0, 300, 300);
5672 parent->setBrush(Qt::blue);
5673 parent->setOpacity(0.5);
5674
5675 const QRegion parentRegion(0, 0, 300, 300);
5676 const QRegion clippedParentRegion = parentRegion & QRect(50, 50, 200, 200);
5677 QRegion childRegion;
5678 QRegion grandChildRegion;
5679
5680 QGraphicsRectItem *topLeftChild = new QGraphicsRectItem(0, 0, 100, 100);
5681 topLeftChild->setBrush(Qt::red);
5682 topLeftChild->setParentItem(parent);
5683 childRegion += QRect(0, 0, 100, 100);
5684
5685 QGraphicsRectItem *topRightChild = new QGraphicsRectItem(0, 0, 100, 100);
5686 topRightChild->setBrush(Qt::red);
5687 topRightChild->setParentItem(parent);
5688 topRightChild->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
5689 topRightChild->setPos(ax: 200, ay: 0);
5690 childRegion += QRect(200, 0, 100, 100);
5691
5692 QGraphicsRectItem *topRightGrandChild = new QGraphicsRectItem(0, 0, 100, 100);
5693 topRightGrandChild->setBrush(Qt::green);
5694 topRightGrandChild->setParentItem(topRightChild);
5695 topRightGrandChild->setPos(ax: -40, ay: 40);
5696 grandChildRegion += QRect(200 - 40, 0 + 40, 100, 100) & QRect(200, 0, 100, 100);
5697
5698 QGraphicsRectItem *bottomLeftChild = new QGraphicsRectItem(0, 0, 100, 100);
5699 bottomLeftChild->setBrush(Qt::red);
5700 bottomLeftChild->setParentItem(parent);
5701 bottomLeftChild->setFlag(flag: QGraphicsItem::ItemClipsToShape);
5702 bottomLeftChild->setPos(ax: 0, ay: 200);
5703 childRegion += QRect(0, 200, 100, 100);
5704
5705 QGraphicsRectItem *bottomLeftGrandChild = new QGraphicsRectItem(0, 0, 160, 160);
5706 bottomLeftGrandChild->setBrush(Qt::green);
5707 bottomLeftGrandChild->setParentItem(bottomLeftChild);
5708 bottomLeftGrandChild->setFlag(flag: QGraphicsItem::ItemClipsToShape);
5709 bottomLeftGrandChild->setPos(ax: 0, ay: -60);
5710 grandChildRegion += QRect(0, 200 - 60, 160, 160);
5711
5712 QGraphicsRectItem *bottomRightChild = new QGraphicsRectItem(0, 0, 100, 100);
5713 bottomRightChild->setBrush(Qt::red);
5714 bottomRightChild->setParentItem(parent);
5715 bottomRightChild->setPos(ax: 200, ay: 200);
5716 childRegion += QRect(200, 200, 100, 100);
5717
5718 const QPoint controlPoints[17] = {
5719 {5, 5}, {95, 5}, {205, 5}, {295, 5},
5720 {5, 95}, {95, 95}, {205, 95}, {295, 95},
5721 {150, 150},
5722 {5, 205}, {95, 205}, {205, 205}, {295, 205},
5723 {5, 295}, {95, 295}, {205, 295}, {295, 295},
5724 };
5725
5726 const QRegion clippedChildRegion = childRegion & QRect(50, 50, 200, 200);
5727 const QRegion clippedGrandChildRegion = grandChildRegion & QRect(50, 50, 200, 200);
5728
5729 QGraphicsScene scene;
5730 scene.addItem(item: parent);
5731 QImage sceneImage(300, 300, QImage::Format_ARGB32);
5732
5733#define VERIFY_CONTROL_POINTS(pRegion, cRegion, gRegion) \
5734 for (const QPoint &controlPoint : controlPoints) { \
5735 QRgb pixel = sceneImage.pixel(controlPoint.x(), controlPoint.y()); \
5736 if (pRegion.contains(controlPoint)) \
5737 QVERIFY(qBlue(pixel) != 0); \
5738 else \
5739 QVERIFY(qBlue(pixel) == 0); \
5740 if (cRegion.contains(controlPoint)) \
5741 QVERIFY(qRed(pixel) != 0); \
5742 else \
5743 QVERIFY(qRed(pixel) == 0); \
5744 if (gRegion.contains(controlPoint)) \
5745 QVERIFY(qGreen(pixel) != 0); \
5746 else \
5747 QVERIFY(qGreen(pixel) == 0); \
5748 }
5749
5750 const QList<QGraphicsItem *> children = parent->childItems();
5751 const int childrenCount = children.count();
5752
5753 for (int i = 0; i < 5; ++i) {
5754 QString clipString;
5755 QString childString;
5756 switch (i) {
5757 case 0:
5758 // All children stacked in front.
5759 childString = QLatin1String("ChildrenInFront.png");
5760 for (QGraphicsItem *child : children)
5761 child->setFlag(flag: QGraphicsItem::ItemStacksBehindParent, enabled: false);
5762 break;
5763 case 1:
5764 // All children stacked behind.
5765 childString = QLatin1String("ChildrenBehind.png");
5766 for (QGraphicsItem *child : children)
5767 child->setFlag(flag: QGraphicsItem::ItemStacksBehindParent, enabled: true);
5768 break;
5769 case 2:
5770 // First half of the children behind, second half in front.
5771 childString = QLatin1String("FirstHalfBehind_SecondHalfInFront.png");
5772 for (int j = 0; j < childrenCount; ++j) {
5773 QGraphicsItem *child = children.at(i: j);
5774 child->setFlag(flag: QGraphicsItem::ItemStacksBehindParent, enabled: (j < childrenCount / 2));
5775 }
5776 break;
5777 case 3:
5778 // First half of the children in front, second half behind.
5779 childString = QLatin1String("FirstHalfInFront_SecondHalfBehind.png");
5780 for (int j = 0; j < childrenCount; ++j) {
5781 QGraphicsItem *child = children.at(i: j);
5782 child->setFlag(flag: QGraphicsItem::ItemStacksBehindParent, enabled: (j >= childrenCount / 2));
5783 }
5784 break;
5785 case 4:
5786 // Child2 and child4 behind, rest in front.
5787 childString = QLatin1String("Child2And4Behind_RestInFront.png");
5788 for (int j = 0; j < childrenCount; ++j) {
5789 QGraphicsItem *child = children.at(i: j);
5790 if (j == 1 || j == 3)
5791 child->setFlag(flag: QGraphicsItem::ItemStacksBehindParent, enabled: true);
5792 else
5793 child->setFlag(flag: QGraphicsItem::ItemStacksBehindParent, enabled: false);
5794 }
5795 break;
5796 default:
5797 qFatal(msg: "internal error");
5798 }
5799
5800 // Nothing is clipped.
5801 parent->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape, enabled: false);
5802 parent->setFlag(flag: QGraphicsItem::ItemClipsToShape, enabled: false);
5803 clipString = QLatin1String("nothingClipped_");
5804 renderSceneToImage(scene: &scene, image: &sceneImage, filename: clipString + childString);
5805 VERIFY_CONTROL_POINTS(parentRegion, childRegion, grandChildRegion);
5806
5807 // Parent clips children to shape.
5808 parent->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
5809 clipString = QLatin1String("parentClipsChildrenToShape_");
5810 renderSceneToImage(scene: &scene, image: &sceneImage, filename: clipString + childString);
5811 VERIFY_CONTROL_POINTS(parentRegion, clippedChildRegion, clippedGrandChildRegion);
5812
5813 // Parent clips itself and children to shape.
5814 parent->setFlag(flag: QGraphicsItem::ItemClipsToShape);
5815 clipString = QLatin1String("parentClipsItselfAndChildrenToShape_");
5816 renderSceneToImage(scene: &scene, image: &sceneImage, filename: clipString + childString);
5817 VERIFY_CONTROL_POINTS(clippedParentRegion, clippedChildRegion, clippedGrandChildRegion);
5818
5819 // Parent clips itself to shape.
5820 parent->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape, enabled: false);
5821 clipString = QLatin1String("parentClipsItselfToShape_");
5822 renderSceneToImage(scene: &scene, image: &sceneImage, filename: clipString + childString);
5823 VERIFY_CONTROL_POINTS(clippedParentRegion, childRegion, grandChildRegion);
5824 }
5825}
5826
5827void tst_QGraphicsItem::itemClipsTextChildToShape()
5828{
5829 // Construct a scene with a rect that clips its children, with one text
5830 // child that has text that exceeds the size of the rect.
5831 QGraphicsScene scene;
5832 QGraphicsItem *rect = scene.addRect(x: 0, y: 0, w: 50, h: 50, pen: QPen(Qt::black), brush: Qt::black);
5833 rect->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
5834 QGraphicsTextItem *text = new QGraphicsTextItem("This is a long sentence that's wider than 50 pixels.");
5835 text->setParentItem(rect);
5836
5837 // Render this scene to a transparent image.
5838 QRectF sr = scene.itemsBoundingRect();
5839 QImage image(sr.size().toSize(), QImage::Format_ARGB32_Premultiplied);
5840 image.fill(pixel: 0);
5841 QPainter painter(&image);
5842 scene.render(painter: &painter);
5843
5844 // Erase the area immediately underneath the rect.
5845 painter.setCompositionMode(QPainter::CompositionMode_Source);
5846 painter.fillRect(r: rect->sceneBoundingRect().translated(p: -sr.topLeft()).adjusted(xp1: -0.5, yp1: -0.5, xp2: 0.5, yp2: 0.5),
5847 c: Qt::transparent);
5848 painter.end();
5849
5850 // Check that you get a truly transparent image back (i.e., the text was
5851 // clipped away, so there should be no trails left after erasing only the
5852 // rect's area).
5853 QImage emptyImage(scene.itemsBoundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied);
5854 emptyImage.fill(pixel: 0);
5855 QCOMPARE(image, emptyImage);
5856}
5857
5858void tst_QGraphicsItem::itemClippingDiscovery()
5859{
5860 // A simple scene with an ellipse parent and two rect children, one a
5861 // child of the other.
5862 QGraphicsScene scene;
5863 QGraphicsEllipseItem *clipItem = scene.addEllipse(x: 0, y: 0, w: 100, h: 100);
5864 QGraphicsRectItem *leftRectItem = scene.addRect(x: 0, y: 0, w: 50, h: 100);
5865 QGraphicsRectItem *rightRectItem = scene.addRect(x: 50, y: 0, w: 50, h: 100);
5866 leftRectItem->setParentItem(clipItem);
5867 rightRectItem->setParentItem(clipItem);
5868
5869 // The rects item are both visible at these points.
5870 QCOMPARE(scene.items(QPointF(10, 10)).value(0, nullptr), leftRectItem);
5871 QCOMPARE(scene.items(QPointF(90, 90)).value(0, nullptr), rightRectItem);
5872
5873 // The ellipse clips the rects now.
5874 clipItem->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
5875
5876 // The rect items are no longer visible at these points.
5877 QVERIFY(scene.items(QPointF(10, 10)).isEmpty());
5878 if (sizeof(qreal) != sizeof(double))
5879 QSKIP("This fails due to internal rounding errors");
5880 QVERIFY(scene.items(QPointF(90, 90)).isEmpty());
5881}
5882
5883class ItemCountsBoundingRectCalls : public QGraphicsRectItem
5884{
5885public:
5886 using QGraphicsRectItem::QGraphicsRectItem;
5887
5888 QRectF boundingRect () const override
5889 {
5890 ++boundingRectCalls;
5891 return QGraphicsRectItem::boundingRect();
5892 }
5893
5894 mutable int boundingRectCalls = 0;
5895};
5896
5897void tst_QGraphicsItem::itemContainsChildrenInShape()
5898{
5899 ItemCountsBoundingRectCalls *parent = new ItemCountsBoundingRectCalls(QRectF(0,0, 10, 10));
5900 ItemCountsBoundingRectCalls *childOutsideShape = new ItemCountsBoundingRectCalls(QRectF(0,0, 10, 10), parent);
5901 childOutsideShape->setPos(ax: 20,ay: 0);
5902
5903 QGraphicsScene scene;
5904 scene.setItemIndexMethod(QGraphicsScene::NoIndex);
5905 scene.addItem(item: parent);
5906
5907 QCOMPARE(parent->boundingRectCalls, childOutsideShape->boundingRectCalls);
5908
5909 int oldParentBoundingRectCalls = parent->boundingRectCalls;
5910 int oldChildBoundingRectCalls = childOutsideShape->boundingRectCalls;
5911
5912 // First test that both items are searched if no optimization flags are set
5913 QGraphicsItem* item = scene.items(pos: QPointF(25, 5)).value(i: 0, defaultValue: nullptr);
5914
5915 QCOMPARE(item, childOutsideShape);
5916 QVERIFY(parent->boundingRectCalls > oldParentBoundingRectCalls);
5917 QVERIFY(childOutsideShape->boundingRectCalls > oldChildBoundingRectCalls);
5918 QCOMPARE(parent->boundingRectCalls, childOutsideShape->boundingRectCalls);
5919
5920 oldParentBoundingRectCalls = parent->boundingRectCalls;
5921 oldChildBoundingRectCalls = childOutsideShape->boundingRectCalls;
5922
5923 // Repeat the test to make sure that no caching/indexing is in effect
5924 item = scene.items(pos: QPointF(25, 5)).value(i: 0, defaultValue: nullptr);
5925
5926 QCOMPARE(item, childOutsideShape);
5927 QVERIFY(parent->boundingRectCalls > oldParentBoundingRectCalls);
5928 QVERIFY(childOutsideShape->boundingRectCalls > oldChildBoundingRectCalls);
5929 QCOMPARE(parent->boundingRectCalls, childOutsideShape->boundingRectCalls);
5930
5931 oldParentBoundingRectCalls = parent->boundingRectCalls;
5932 oldChildBoundingRectCalls = childOutsideShape->boundingRectCalls;
5933
5934 // Set the optimization flag and make sure that the child is not returned
5935 // and that the child's boundingRect() method is never called.
5936 parent->setFlag(flag: QGraphicsItem::ItemContainsChildrenInShape);
5937 item = scene.items(pos: QPointF(25, 5)).value(i: 0, defaultValue: nullptr);
5938
5939 QVERIFY(!(item));
5940 QVERIFY(parent->boundingRectCalls > oldParentBoundingRectCalls);
5941 QCOMPARE(childOutsideShape->boundingRectCalls, oldChildBoundingRectCalls);
5942 QVERIFY(parent->boundingRectCalls > childOutsideShape->boundingRectCalls);
5943}
5944
5945void tst_QGraphicsItem::itemContainsChildrenInShape2()
5946{
5947 //The tested flag behaves almost identically to ItemClipsChildrenToShape
5948 //in terms of optimizations but does not enforce the clip.
5949 //This test makes sure there is no clip.
5950 QGraphicsScene scene;
5951 QGraphicsItem *rect = scene.addRect(x: 0, y: 0, w: 50, h: 50, pen: QPen(Qt::NoPen), brush: QBrush(Qt::yellow));
5952
5953 QGraphicsItem *ellipse = scene.addEllipse(x: 0, y: 0, w: 100, h: 100, pen: QPen(Qt::NoPen), brush: QBrush(Qt::green));
5954 ellipse->setParentItem(rect);
5955
5956 QGraphicsItem *clippedEllipse = scene.addEllipse(x: 0, y: 0, w: 50, h: 50, pen: QPen(Qt::NoPen), brush: QBrush(Qt::blue));
5957 clippedEllipse->setParentItem(ellipse);
5958
5959 QGraphicsItem *clippedEllipse2 = scene.addEllipse(x: 0, y: 0, w: 25, h: 25, pen: QPen(Qt::NoPen), brush: QBrush(Qt::red));
5960 clippedEllipse2->setParentItem(clippedEllipse);
5961
5962 QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape));
5963 QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemContainsChildrenInShape));
5964 ellipse->setFlags(QGraphicsItem::ItemContainsChildrenInShape);
5965 QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape));
5966 QVERIFY((ellipse->flags() & QGraphicsItem::ItemContainsChildrenInShape));
5967
5968 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
5969 image.fill(pixel: 0);
5970 QPainter painter(&image);
5971 painter.setRenderHint(hint: QPainter::Antialiasing);
5972 scene.render(painter: &painter);
5973 painter.end();
5974
5975 QCOMPARE(image.pixel(2, 2), QColor(Qt::yellow).rgba());
5976 QCOMPARE(image.pixel(12, 12), QColor(Qt::red).rgba());
5977 QCOMPARE(image.pixel(2, 25), QColor(Qt::blue).rgba());
5978 QCOMPARE(image.pixel(2, 50), QColor(Qt::green).rgba());
5979}
5980
5981void tst_QGraphicsItem::ancestorFlags()
5982{
5983 QGraphicsItem *level1 = new QGraphicsRectItem;
5984 QGraphicsItem *level21 = new QGraphicsRectItem;
5985 level21->setParentItem(level1);
5986 QGraphicsItem *level22 = new QGraphicsRectItem;
5987 level22->setParentItem(level1);
5988 QGraphicsItem *level31 = new QGraphicsRectItem;
5989 level31->setParentItem(level21);
5990 QGraphicsItem *level32 = new QGraphicsRectItem;
5991 level32->setParentItem(level21);
5992
5993 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
5994 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
5995 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
5996 QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
5997 QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
5998
5999 // HandlesChildEvents: 1) Root level sets a flag
6000 level1->setHandlesChildEvents(true);
6001 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6002 QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
6003 QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
6004 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6005 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6006
6007 // HandlesChildEvents: 2) Root level set it again
6008 level1->setHandlesChildEvents(true);
6009 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6010 QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
6011 QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
6012 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6013 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6014
6015 // HandlesChildEvents: 3) Root level unsets a flag
6016 level1->setHandlesChildEvents(false);
6017 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6018 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6019 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6020 QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
6021 QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
6022
6023 // HandlesChildEvents: 4) Child item sets a flag
6024 level21->setHandlesChildEvents(true);
6025 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6026 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6027 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6028 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6029 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6030
6031 // HandlesChildEvents: 5) Parent item sets a flag
6032 level1->setHandlesChildEvents(true);
6033 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6034 QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
6035 QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
6036 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6037 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6038
6039 // HandlesChildEvents: 6) Child item unsets a flag
6040 level21->setHandlesChildEvents(false);
6041 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6042 QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
6043 QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
6044 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6045 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6046
6047 // HandlesChildEvents: 7) Parent item unsets a flag
6048 level21->setHandlesChildEvents(true);
6049 level1->setHandlesChildEvents(false);
6050 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6051 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6052 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6053 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6054 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6055
6056 // Reparent the child to root
6057 level21->setParentItem(nullptr);
6058 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6059 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6060 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6061 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6062
6063 // Reparent the child to level1 again.
6064 level1->setHandlesChildEvents(true);
6065 level21->setParentItem(level1);
6066 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6067 QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
6068 QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
6069 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6070 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6071
6072 // Reparenting level31 back to level1.
6073 level31->setParentItem(level1);
6074 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6075 QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
6076 QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
6077 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6078 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6079
6080 // Reparenting level31 back to level21.
6081 level31->setParentItem(nullptr);
6082 QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
6083 level31->setParentItem(level21);
6084 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6085 QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
6086 QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
6087 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6088 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6089
6090 // Level1 doesn't handle child events
6091 level1->setHandlesChildEvents(false);
6092 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6093 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6094 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6095 QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
6096 QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
6097
6098 // Nobody handles child events
6099 level21->setHandlesChildEvents(false);
6100
6101 for (int i = 0; i < 3; ++i) {
6102 QGraphicsItem::GraphicsItemFlag flag;
6103 int ancestorFlag;
6104
6105 switch (i) {
6106 case(0):
6107 flag = QGraphicsItem::ItemClipsChildrenToShape;
6108 ancestorFlag = QGraphicsItemPrivate::AncestorClipsChildren;
6109 break;
6110 case(1):
6111 flag = QGraphicsItem::ItemIgnoresTransformations;
6112 ancestorFlag = QGraphicsItemPrivate::AncestorIgnoresTransformations;
6113 break;
6114 case(2):
6115 flag = QGraphicsItem::ItemContainsChildrenInShape;
6116 ancestorFlag = QGraphicsItemPrivate::AncestorContainsChildren;
6117 break;
6118 default:
6119 qFatal(msg: "Unknown ancestor flag, please fix!");
6120 }
6121
6122 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6123 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6124 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6125 QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
6126 QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
6127
6128 // HandlesChildEvents: 1) Root level sets a flag
6129 level1->setFlag(flag, enabled: true);
6130 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6131 QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
6132 QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
6133 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6134 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6135
6136 // HandlesChildEvents: 2) Root level set it again
6137 level1->setFlag(flag, enabled: true);
6138 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6139 QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
6140 QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
6141 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6142 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6143
6144 // HandlesChildEvents: 3) Root level unsets a flag
6145 level1->setFlag(flag, enabled: false);
6146 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6147 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6148 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6149 QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
6150 QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
6151
6152 // HandlesChildEvents: 4) Child item sets a flag
6153 level21->setFlag(flag, enabled: true);
6154 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6155 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6156 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6157 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6158 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6159
6160 // HandlesChildEvents: 5) Parent item sets a flag
6161 level1->setFlag(flag, enabled: true);
6162 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6163 QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
6164 QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
6165 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6166 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6167
6168 // HandlesChildEvents: 6) Child item unsets a flag
6169 level21->setFlag(flag, enabled: false);
6170 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6171 QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
6172 QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
6173 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6174 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6175
6176 // HandlesChildEvents: 7) Parent item unsets a flag
6177 level21->setFlag(flag, enabled: true);
6178 level1->setFlag(flag, enabled: false);
6179 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6180 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6181 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6182 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6183 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6184
6185 // Reparent the child to root
6186 level21->setParentItem(nullptr);
6187 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6188 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6189 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6190 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6191
6192 // Reparent the child to level1 again.
6193 level1->setFlag(flag, enabled: true);
6194 level21->setParentItem(level1);
6195 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6196 QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
6197 QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
6198 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6199 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6200
6201 // Reparenting level31 back to level1.
6202 level31->setParentItem(level1);
6203 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6204 QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
6205 QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
6206 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6207 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6208
6209 // Reparenting level31 back to level21.
6210 level31->setParentItem(nullptr);
6211 QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
6212 level31->setParentItem(level21);
6213 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6214 QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
6215 QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
6216 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6217 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6218
6219 // Level1 doesn't handle child events
6220 level1->setFlag(flag, enabled: false);
6221 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6222 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6223 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6224 QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
6225 QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
6226
6227 // Nobody handles child events
6228 level21->setFlag(flag, enabled: false);
6229 QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
6230 QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
6231 QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
6232 QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
6233 QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
6234 }
6235
6236 delete level1;
6237}
6238
6239void tst_QGraphicsItem::untransformable()
6240{
6241 auto item1 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100));
6242 item1->setZValue(1);
6243 item1->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
6244 item1->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
6245 item1->setBrush(Qt::red);
6246
6247 auto item2 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100));
6248 item2->setParentItem(item1);
6249 item2->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
6250 item2->setPos(ax: 100, ay: 0);
6251 item2->setBrush(Qt::green);
6252
6253 auto item3 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100));
6254 item3->setParentItem(item2);
6255 item3->setPos(ax: 100, ay: 0);
6256 item3->setBrush(Qt::blue);
6257
6258 QGraphicsScene scene(-500, -500, 1000, 1000);
6259 scene.addItem(item: item1);
6260
6261 QWidget topLevel;
6262 QGraphicsView view(&scene,&topLevel);
6263 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6264 view.resize(w: 300, h: 300);
6265 topLevel.show();
6266 view.scale(sx: 8, sy: 8);
6267 view.centerOn(ax: 0, ay: 0);
6268
6269// Painting with the DiagCrossPattern is really slow on Mac
6270// when zoomed out. (The test times out). Task to fix is 155567.
6271#if !defined(Q_OS_MAC) || 1
6272 view.setBackgroundBrush(QBrush(Qt::black, Qt::DiagCrossPattern));
6273#endif
6274
6275 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
6276
6277 for (int i = 0; i < 10; ++i) {
6278 QPoint center = view.viewport()->rect().center();
6279 QCOMPARE(view.itemAt(center), item1);
6280 QCOMPARE(view.itemAt(center - QPoint(40, 0)), item1);
6281 QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item1);
6282 QCOMPARE(view.itemAt(center - QPoint(0, 40)), item1);
6283 QCOMPARE(view.itemAt(center - QPoint(0, -40)), item1);
6284
6285 center += QPoint(70, 70);
6286 QCOMPARE(view.itemAt(center - QPoint(40, 0)), item2);
6287 QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item2);
6288 QCOMPARE(view.itemAt(center - QPoint(0, 40)), item2);
6289 QCOMPARE(view.itemAt(center - QPoint(0, -40)), item2);
6290
6291 center += QPoint(0, 100);
6292 QCOMPARE(view.itemAt(center - QPoint(40, 0)), item3);
6293 QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item3);
6294 QCOMPARE(view.itemAt(center - QPoint(0, 40)), item3);
6295 QCOMPARE(view.itemAt(center - QPoint(0, -40)), item3);
6296
6297 view.scale(sx: 0.5, sy: 0.5);
6298 view.rotate(angle: 13);
6299 view.shear(sh: qreal(0.01), sv: qreal(0.01));
6300 view.translate(dx: 10, dy: 10);
6301 }
6302}
6303
6304#ifndef QT_NO_CONTEXTMENU
6305class ContextMenuItem : public QGraphicsRectItem
6306{
6307public:
6308 using QGraphicsRectItem::QGraphicsRectItem;
6309
6310 bool ignoreEvent = true;
6311 bool gotEvent = false;
6312 bool eventWasAccepted = false;
6313
6314protected:
6315 void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override
6316 {
6317 gotEvent = true;
6318 eventWasAccepted = event->isAccepted();
6319 if (ignoreEvent)
6320 event->ignore();
6321 }
6322};
6323
6324void tst_QGraphicsItem::contextMenuEventPropagation()
6325{
6326 ContextMenuItem *bottomItem = new ContextMenuItem;
6327 bottomItem->setRect(ax: 0, ay: 0, w: 100, h: 100);
6328 ContextMenuItem *topItem = new ContextMenuItem;
6329 topItem->setParentItem(bottomItem);
6330 topItem->setRect(ax: 0, ay: 0, w: 100, h: 100);
6331
6332 QGraphicsScene scene;
6333
6334 QGraphicsView view(&scene);
6335 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6336 view.setAlignment(Qt::AlignLeft | Qt::AlignTop);
6337 view.show();
6338 view.resize(w: 200, h: 200);
6339 QVERIFY(QTest::qWaitForWindowExposed(&view));
6340
6341 QContextMenuEvent event(QContextMenuEvent::Mouse, QPoint(10, 10),
6342 view.viewport()->mapToGlobal(QPoint(10, 10)));
6343 event.ignore();
6344 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
6345 QVERIFY(!event.isAccepted());
6346
6347 scene.addItem(item: bottomItem);
6348 topItem->ignoreEvent = true;
6349 bottomItem->ignoreEvent = true;
6350
6351 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
6352 QVERIFY(!event.isAccepted());
6353 QCOMPARE(topItem->gotEvent, true);
6354 QCOMPARE(topItem->eventWasAccepted, true);
6355 QCOMPARE(bottomItem->gotEvent, true);
6356 QCOMPARE(bottomItem->eventWasAccepted, true);
6357
6358 topItem->ignoreEvent = false;
6359 topItem->gotEvent = false;
6360 bottomItem->gotEvent = false;
6361
6362 QCoreApplication::sendEvent(receiver: view.viewport(), event: &event);
6363 QVERIFY(event.isAccepted());
6364 QCOMPARE(topItem->gotEvent, true);
6365 QCOMPARE(bottomItem->gotEvent, false);
6366 QCOMPARE(topItem->eventWasAccepted, true);
6367}
6368#endif // QT_NO_CONTEXTMENU
6369
6370void tst_QGraphicsItem::itemIsMovable()
6371{
6372 QGraphicsRectItem *rect = new QGraphicsRectItem(-50, -50, 100, 100);
6373 rect->setFlag(flag: QGraphicsItem::ItemIsMovable);
6374
6375 QGraphicsScene scene;
6376 scene.addItem(item: rect);
6377
6378 {
6379 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
6380 event.setButton(Qt::LeftButton);
6381 event.setButtons(Qt::LeftButton);
6382 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6383 }
6384 {
6385 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
6386 event.setButton(Qt::LeftButton);
6387 event.setButtons(Qt::LeftButton);
6388 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6389 }
6390 QCOMPARE(rect->pos(), QPointF(0, 0));
6391 {
6392 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
6393 event.setButtons(Qt::LeftButton);
6394 event.setScenePos(QPointF(10, 10));
6395 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6396 }
6397 QCOMPARE(rect->pos(), QPointF(10, 10));
6398 {
6399 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
6400 event.setButtons(Qt::RightButton);
6401 event.setScenePos(QPointF(20, 20));
6402 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6403 }
6404 QCOMPARE(rect->pos(), QPointF(10, 10));
6405 {
6406 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
6407 event.setButtons(Qt::LeftButton);
6408 event.setScenePos(QPointF(30, 30));
6409 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6410 }
6411 QCOMPARE(rect->pos(), QPointF(30, 30));
6412}
6413
6414class ItemAddScene : public QGraphicsScene
6415{
6416 Q_OBJECT
6417public:
6418 ItemAddScene()
6419 {
6420 QTimer::singleShot(interval: 500, receiver: this, slot: &ItemAddScene::newTextItem);
6421 }
6422
6423public slots:
6424 void newTextItem()
6425 {
6426 // Add a text item
6427 QGraphicsItem *item = addText(text: "This item will not ensure that it's visible");
6428 item->setPos(ax: .0, ay: .0);
6429 item->show();
6430 }
6431};
6432
6433void tst_QGraphicsItem::task141694_textItemEnsureVisible()
6434{
6435 ItemAddScene scene;
6436 scene.setSceneRect(x: -1000, y: -1000, w: 2000, h: 2000);
6437
6438 QGraphicsView view(&scene);
6439 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6440 view.setFixedSize(w: 200, h: 200);
6441 view.show();
6442 QVERIFY(QTest::qWaitForWindowExposed(&view));
6443
6444 view.ensureVisible(ax: -1000, ay: -1000, aw: 5, ah: 5);
6445 int hscroll = view.horizontalScrollBar()->value();
6446 int vscroll = view.verticalScrollBar()->value();
6447
6448 QTest::qWait(ms: 10);
6449
6450 // This should not cause the view to scroll
6451 QTRY_COMPARE(view.horizontalScrollBar()->value(), hscroll);
6452 QCOMPARE(view.verticalScrollBar()->value(), vscroll);
6453}
6454
6455void tst_QGraphicsItem::task128696_textItemEnsureMovable()
6456{
6457 QGraphicsTextItem *item = new QGraphicsTextItem;
6458 item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
6459 item->setTextInteractionFlags(Qt::TextEditorInteraction);
6460 item->setPlainText("abc de\nf ghi\n j k l");
6461
6462 QGraphicsScene scene;
6463 scene.setSceneRect(x: -100, y: -100, w: 200, h: 200);
6464 scene.addItem(item);
6465
6466 QGraphicsView view(&scene);
6467 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6468 view.setFixedSize(w: 200, h: 200);
6469 view.show();
6470
6471 QGraphicsSceneMouseEvent event1(QEvent::GraphicsSceneMousePress);
6472 event1.setScenePos(QPointF(0, 0));
6473 event1.setButton(Qt::LeftButton);
6474 event1.setButtons(Qt::LeftButton);
6475 QCoreApplication::sendEvent(receiver: &scene, event: &event1);
6476 QCOMPARE(scene.mouseGrabberItem(), item);
6477
6478 QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove);
6479 event2.setScenePos(QPointF(10, 10));
6480 event2.setButton(Qt::LeftButton);
6481 event2.setButtons(Qt::LeftButton);
6482 QCoreApplication::sendEvent(receiver: &scene, event: &event2);
6483 QCOMPARE(item->pos(), QPointF(10, 10));
6484}
6485
6486void tst_QGraphicsItem::task177918_lineItemUndetected()
6487{
6488 QGraphicsScene scene;
6489 QGraphicsLineItem *line = scene.addLine(x1: 10, y1: 10, x2: 10, y2: 10);
6490 QCOMPARE(line->boundingRect(), QRectF(10, 10, 0, 0));
6491
6492 const QRectF rect(9, 9, 2, 2);
6493 QVERIFY(!scene.items(rect, Qt::IntersectsItemShape).isEmpty());
6494 QVERIFY(!scene.items(rect, Qt::ContainsItemShape).isEmpty());
6495 QVERIFY(!scene.items(rect, Qt::IntersectsItemBoundingRect).isEmpty());
6496 QVERIFY(!scene.items(rect, Qt::ContainsItemBoundingRect).isEmpty());
6497}
6498
6499void tst_QGraphicsItem::task240400_clickOnTextItem_data()
6500{
6501 using Flags = QGraphicsItem::GraphicsItemFlags;
6502 QTest::addColumn<Flags>(name: "flags");
6503 QTest::addColumn<Qt::TextInteractionFlags>(name: "textFlags");
6504 QTest::newRow(dataTag: "editor, noflags")
6505 << Flags{}
6506 << Qt::TextInteractionFlags(Qt::TextEditorInteraction);
6507 QTest::newRow(dataTag: "editor, movable")
6508 << Flags(QGraphicsItem::ItemIsMovable)
6509 << Qt::TextInteractionFlags(Qt::TextEditorInteraction);
6510 QTest::newRow(dataTag: "editor, selectable")
6511 << Flags(QGraphicsItem::ItemIsSelectable)
6512 << Qt::TextInteractionFlags(Qt::TextEditorInteraction);
6513 QTest::newRow(dataTag: "editor, movable | selectable")
6514 << Flags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable)
6515 << Qt::TextInteractionFlags(Qt::TextEditorInteraction);
6516 QTest::newRow(dataTag: "noninteractive, noflags")
6517 << Flags{} << Qt::TextInteractionFlags(Qt::NoTextInteraction);
6518 QTest::newRow(dataTag: "noninteractive, movable")
6519 << Flags(QGraphicsItem::ItemIsMovable) << Qt::TextInteractionFlags(Qt::NoTextInteraction);
6520 QTest::newRow(dataTag: "noninteractive, selectable")
6521 << Flags(QGraphicsItem::ItemIsSelectable) << Qt::TextInteractionFlags(Qt::NoTextInteraction);
6522 QTest::newRow(dataTag: "noninteractive, movable | selectable")
6523 << Flags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable)
6524 << Qt::TextInteractionFlags(Qt::NoTextInteraction);
6525}
6526
6527void tst_QGraphicsItem::task240400_clickOnTextItem()
6528{
6529 QFETCH(QGraphicsItem::GraphicsItemFlags, flags);
6530 QFETCH(Qt::TextInteractionFlags, textFlags);
6531
6532 QGraphicsScene scene;
6533 QEvent activate(QEvent::WindowActivate);
6534 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
6535
6536 QGraphicsTextItem *item = scene.addText(text: "Hello");
6537 item->setFlags(flags);
6538 item->setTextInteractionFlags(textFlags);
6539 const bool focusable = item->flags().testFlag(flag: QGraphicsItem::ItemIsFocusable);
6540 QVERIFY(textFlags ? focusable : !focusable);
6541
6542 int column = item->textCursor().columnNumber();
6543 QCOMPARE(column, 0);
6544
6545 QVERIFY(!item->hasFocus());
6546
6547 // Click in the top-left corner of the item
6548 {
6549 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
6550 event.setScenePos(item->sceneBoundingRect().topLeft() + QPointF(0.1, 0.1));
6551 event.setButton(Qt::LeftButton);
6552 event.setButtons(Qt::LeftButton);
6553 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6554 }
6555 if (flags || textFlags)
6556 QCOMPARE(scene.mouseGrabberItem(), item);
6557 else
6558 QCOMPARE(scene.mouseGrabberItem(), nullptr);
6559 {
6560 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
6561 event.setScenePos(item->sceneBoundingRect().topLeft() + QPointF(0.1, 0.1));
6562 event.setButton(Qt::LeftButton);
6563 event.setButtons({});
6564 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6565 }
6566 if (textFlags)
6567 QVERIFY(item->hasFocus());
6568 else
6569 QVERIFY(!item->hasFocus());
6570 QVERIFY(!scene.mouseGrabberItem());
6571 bool selectable = flags.testFlag(flag: QGraphicsItem::ItemIsSelectable);
6572 QVERIFY(selectable ? item->isSelected() : !item->isSelected());
6573
6574 // Now click in the middle and check that the cursor moved.
6575 {
6576 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
6577 event.setScenePos(item->sceneBoundingRect().center());
6578 event.setButton(Qt::LeftButton);
6579 event.setButtons(Qt::LeftButton);
6580 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6581 }
6582 if (flags || textFlags)
6583 QCOMPARE(scene.mouseGrabberItem(), item);
6584 else
6585 QCOMPARE(scene.mouseGrabberItem(), nullptr);
6586 {
6587 QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
6588 event.setScenePos(item->sceneBoundingRect().center());
6589 event.setButton(Qt::LeftButton);
6590 event.setButtons({});
6591 QCoreApplication::sendEvent(receiver: &scene, event: &event);
6592 }
6593 if (textFlags)
6594 QVERIFY(item->hasFocus());
6595 else
6596 QVERIFY(!item->hasFocus());
6597 QVERIFY(!scene.mouseGrabberItem());
6598
6599 QVERIFY(selectable ? item->isSelected() : !item->isSelected());
6600
6601 //
6602 if (textFlags.testFlag(flag: Qt::TextEditorInteraction))
6603 QVERIFY(item->textCursor().columnNumber() > column);
6604 else
6605 QCOMPARE(item->textCursor().columnNumber(), 0);
6606}
6607
6608class TextItem : public QGraphicsSimpleTextItem
6609{
6610public:
6611 using QGraphicsSimpleTextItem::QGraphicsSimpleTextItem;
6612
6613 void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) override
6614 {
6615 updates++;
6616 QGraphicsSimpleTextItem::paint(painter, option, widget);
6617 }
6618
6619 int updates = 0;
6620};
6621
6622void tst_QGraphicsItem::ensureUpdateOnTextItem()
6623{
6624 QGraphicsScene scene;
6625 QGraphicsView view(&scene);
6626 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6627 view.show();
6628 QVERIFY(QTest::qWaitForWindowExposed(&view));
6629 QCoreApplication::processEvents(); // Process all queued paint events
6630 TextItem *text1 = new TextItem(QLatin1String("123"));
6631 scene.addItem(item: text1);
6632 QTRY_COMPARE(text1->updates,1);
6633
6634 //same bouding rect but we have to update
6635 text1->setText(QLatin1String("321"));
6636 QTRY_COMPARE(text1->updates,2);
6637}
6638
6639void tst_QGraphicsItem::task243707_addChildBeforeParent()
6640{
6641 // Task reports that adding the child before the parent leads to an
6642 // inconsistent internal state that can cause a crash. This test shows
6643 // one such crash.
6644 QGraphicsScene scene;
6645 QGraphicsWidget *widget = new QGraphicsWidget;
6646 QGraphicsWidget *widget2 = new QGraphicsWidget(widget);
6647 scene.addItem(item: widget2);
6648 QVERIFY(!widget2->parentItem());
6649 scene.addItem(item: widget);
6650 QVERIFY(!widget->commonAncestorItem(widget2));
6651 QVERIFY(!widget2->commonAncestorItem(widget));
6652}
6653
6654void tst_QGraphicsItem::task197802_childrenVisibility()
6655{
6656 QGraphicsScene scene;
6657 QGraphicsRectItem item(QRectF(0,0,20,20));
6658
6659 QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(0,0,10,10), &item);
6660 scene.addItem(item: &item);
6661
6662 //freshly created: both visible
6663 QVERIFY(item.isVisible());
6664 QVERIFY(item2->isVisible());
6665
6666 //hide child: parent visible, child not
6667 item2->hide();
6668 QVERIFY(item.isVisible());
6669 QVERIFY(!item2->isVisible());
6670
6671 //hide parent: parent and child invisible
6672 item.hide();
6673 QVERIFY(!item.isVisible());
6674 QVERIFY(!item2->isVisible());
6675
6676 //ask to show the child: parent and child invisible anyways
6677 item2->show();
6678 QVERIFY(!item.isVisible());
6679 QVERIFY(!item2->isVisible());
6680
6681 //show the parent: both parent and child visible
6682 item.show();
6683 QVERIFY(item.isVisible());
6684 QVERIFY(item2->isVisible());
6685
6686 delete item2;
6687}
6688
6689void tst_QGraphicsItem::boundingRegion_data()
6690{
6691 QTest::addColumn<QLineF>(name: "line");
6692 QTest::addColumn<qreal>(name: "granularity");
6693 QTest::addColumn<QTransform>(name: "transform");
6694 QTest::addColumn<QRegion>(name: "expectedRegion");
6695
6696 QTest::newRow(dataTag: "(0, 0, 10, 10) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 10) << qreal(0.0) << QTransform()
6697 << QRegion(QRect(0, 0, 10, 10));
6698 QTest::newRow(dataTag: "(0, 0, 10, 0) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 0) << qreal(0.0) << QTransform()
6699 << QRegion(QRect(0, 0, 10, 1));
6700 QTest::newRow(dataTag: "(0, 0, 10, 0) | 0.5 | identity | {(0, 0, 10, 1)}") << QLineF(0, 0, 10, 0) << qreal(0.5) << QTransform()
6701 << QRegion(QRect(0, 0, 10, 1));
6702 QTest::newRow(dataTag: "(0, 0, 10, 0) | 1.0 | identity | {(0, 0, 10, 1)}") << QLineF(0, 0, 10, 0) << qreal(1.0) << QTransform()
6703 << QRegion(QRect(0, 0, 10, 1));
6704 QTest::newRow(dataTag: "(0, 0, 0, 10) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 0, 10) << qreal(0.0) << QTransform()
6705 << QRegion(QRect(0, 0, 1, 10));
6706 QTest::newRow(dataTag: "(0, 0, 0, 10) | 0.5 | identity | {(0, 0, 1, 10)}") << QLineF(0, 0, 0, 10) << qreal(0.5) << QTransform()
6707 << QRegion(QRect(0, 0, 1, 10));
6708 QTest::newRow(dataTag: "(0, 0, 0, 10) | 1.0 | identity | {(0, 0, 1, 10)}") << QLineF(0, 0, 0, 10) << qreal(1.0) << QTransform()
6709 << QRegion(QRect(0, 0, 1, 10));
6710}
6711
6712void tst_QGraphicsItem::boundingRegion()
6713{
6714 QFETCH(QLineF, line);
6715 QFETCH(qreal, granularity);
6716 QFETCH(QTransform, transform);
6717 QFETCH(QRegion, expectedRegion);
6718
6719 QGraphicsLineItem item(line);
6720 item.setPen(QPen(Qt::black, 0));
6721 QCOMPARE(item.boundingRegionGranularity(), qreal(0.0));
6722 item.setBoundingRegionGranularity(granularity);
6723 QCOMPARE(item.boundingRegionGranularity(), granularity);
6724 QCOMPARE(item.boundingRegion(transform), expectedRegion);
6725}
6726
6727void tst_QGraphicsItem::itemTransform_parentChild()
6728{
6729 QGraphicsScene scene;
6730 QGraphicsItem *parent = scene.addRect(x: 0, y: 0, w: 100, h: 100);
6731 QGraphicsItem *child = scene.addRect(x: 0, y: 0, w: 100, h: 100);
6732 child->setParentItem(parent);
6733 child->setPos(ax: 10, ay: 10);
6734 child->setTransform(matrix: QTransform::fromScale(dx: 2, dy: 2), combine: true);
6735 child->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
6736
6737 QCOMPARE(child->itemTransform(parent).map(QPointF(10, 10)), QPointF(-10, 30));
6738 QCOMPARE(parent->itemTransform(child).map(QPointF(-10, 30)), QPointF(10, 10));
6739}
6740
6741void tst_QGraphicsItem::itemTransform_siblings()
6742{
6743 QGraphicsScene scene;
6744 QGraphicsItem *parent = scene.addRect(x: 0, y: 0, w: 100, h: 100);
6745 QGraphicsItem *brother = scene.addRect(x: 0, y: 0, w: 100, h: 100);
6746 QGraphicsItem *sister = scene.addRect(x: 0, y: 0, w: 100, h: 100);
6747 parent->setTransform(matrix: QTransform::fromScale(dx: 10, dy: 5), combine: true);
6748 parent->setTransform(matrix: QTransform().rotate(a: -180), combine: true);
6749 parent->setTransform(matrix: QTransform().shear(sh: 2, sv: 3), combine: true);
6750
6751 brother->setParentItem(parent);
6752 sister->setParentItem(parent);
6753
6754 brother->setPos(ax: 10, ay: 10);
6755 brother->setTransform(matrix: QTransform::fromScale(dx: 2, dy: 2), combine: true);
6756 brother->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
6757 sister->setPos(ax: 10, ay: 10);
6758 sister->setTransform(matrix: QTransform::fromScale(dx: 2, dy: 2), combine: true);
6759 sister->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
6760
6761 QCOMPARE(brother->itemTransform(sister).map(QPointF(10, 10)), QPointF(10, 10));
6762 QCOMPARE(sister->itemTransform(brother).map(QPointF(10, 10)), QPointF(10, 10));
6763}
6764
6765void tst_QGraphicsItem::itemTransform_unrelated()
6766{
6767 QGraphicsScene scene;
6768 QGraphicsItem *stranger1 = scene.addRect(x: 0, y: 0, w: 100, h: 100);
6769 QGraphicsItem *stranger2 = scene.addRect(x: 0, y: 0, w: 100, h: 100);
6770 stranger1->setPos(ax: 10, ay: 10);
6771 stranger1->setTransform(matrix: QTransform::fromScale(dx: 2, dy: 2), combine: true);
6772 stranger1->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
6773 stranger2->setPos(ax: 10, ay: 10);
6774 stranger2->setTransform(matrix: QTransform::fromScale(dx: 2, dy: 2), combine: true);
6775 stranger2->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
6776
6777 QCOMPARE(stranger1->itemTransform(stranger2).map(QPointF(10, 10)), QPointF(10, 10));
6778 QCOMPARE(stranger2->itemTransform(stranger1).map(QPointF(10, 10)), QPointF(10, 10));
6779}
6780
6781void tst_QGraphicsItem::opacity_data()
6782{
6783 QTest::addColumn<qreal>(name: "p_opacity");
6784 QTest::addColumn<int>(name: "p_opacityFlags");
6785 QTest::addColumn<qreal>(name: "c1_opacity");
6786 QTest::addColumn<int>(name: "c1_opacityFlags");
6787 QTest::addColumn<qreal>(name: "c2_opacity");
6788 QTest::addColumn<int>(name: "c2_opacityFlags");
6789 QTest::addColumn<qreal>(name: "p_effectiveOpacity");
6790 QTest::addColumn<qreal>(name: "c1_effectiveOpacity");
6791 QTest::addColumn<qreal>(name: "c2_effectiveOpacity");
6792 QTest::addColumn<qreal>(name: "c3_effectiveOpacity");
6793
6794 // Modify the opacity and see how it propagates
6795 QTest::newRow(dataTag: "A: 1.0 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0
6796 << qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
6797 QTest::newRow(dataTag: "B: 0.5 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0
6798 << qreal(0.5) << qreal(0.5) << qreal(0.5) << qreal(0.5);
6799 QTest::newRow(dataTag: "C: 0.5 0 0.1 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(0.1) << 0 << qreal(1.0) << 0
6800 << qreal(0.5) << qreal(0.05) << qreal(0.05) << qreal(0.05);
6801 QTest::newRow(dataTag: "D: 0.0 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0
6802 << qreal(0.0) << qreal(0.0) << qreal(0.0) << qreal(0.0);
6803
6804 // Parent doesn't propagate to children - now modify the opacity and see how it propagates
6805 int flags = QGraphicsItem::ItemDoesntPropagateOpacityToChildren;
6806 QTest::newRow(dataTag: "E: 1.0 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << flags << qreal(1.0) << 0 << qreal(1.0) << 0
6807 << qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
6808 QTest::newRow(dataTag: "F: 0.5 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << flags << qreal(1.0) << 0 << qreal(1.0) << 0
6809 << qreal(0.5) << qreal(1.0) << qreal(1.0) << qreal(1.0);
6810 QTest::newRow(dataTag: "G: 0.5 2 0.1 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << flags << qreal(0.1) << 0 << qreal(1.0) << 0
6811 << qreal(0.5) << qreal(0.1) << qreal(0.1) << qreal(0.1);
6812 QTest::newRow(dataTag: "H: 0.0 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << flags << qreal(1.0) << 0 << qreal(1.0) << 0
6813 << qreal(0.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
6814
6815 // Child ignores parent - now modify the opacity and see how it propagates
6816 flags = QGraphicsItem::ItemIgnoresParentOpacity;
6817 QTest::newRow(dataTag: "I: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 << qreal(1.0) << flags << qreal(1.0) << 0
6818 << qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
6819 QTest::newRow(dataTag: "J: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(0.5) << flags << qreal(0.5) << 0
6820 << qreal(0.5) << qreal(0.5) << qreal(0.25) << qreal(0.25);
6821 QTest::newRow(dataTag: "K: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.2) << 0 << qreal(0.2) << flags << qreal(0.2) << 0
6822 << qreal(0.2) << qreal(0.2) << qreal(0.04) << qreal(0.04);
6823 QTest::newRow(dataTag: "L: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << 0 << qreal(0.0) << flags << qreal(0.0) << 0
6824 << qreal(0.0) << qreal(0.0) << qreal(0.0) << qreal(0.0);
6825
6826 // Child ignores parent and doesn't propagate - now modify the opacity and see how it propagates
6827 flags = QGraphicsItem::ItemIgnoresParentOpacity | QGraphicsItem::ItemDoesntPropagateOpacityToChildren;
6828 QTest::newRow(dataTag: "M: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 // p
6829 << qreal(1.0) << flags // c1 (no prop)
6830 << qreal(1.0) << 0 // c2
6831 << qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
6832 QTest::newRow(dataTag: "M: 0.5 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p
6833 << qreal(1.0) << flags // c1 (no prop)
6834 << qreal(1.0) << 0 // c2
6835 << qreal(0.5) << qreal(1.0) << qreal(1.0) << qreal(1.0);
6836 QTest::newRow(dataTag: "M: 0.5 0 0.5 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p
6837 << qreal(0.5) << flags // c1 (no prop)
6838 << qreal(1.0) << 0 // c2
6839 << qreal(0.5) << qreal(0.5) << qreal(1.0) << qreal(1.0);
6840 QTest::newRow(dataTag: "M: 0.5 0 0.5 1 0.5 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p
6841 << qreal(0.5) << flags // c1 (no prop)
6842 << qreal(0.5) << 0 // c2
6843 << qreal(0.5) << qreal(0.5) << qreal(0.5) << qreal(0.5);
6844 QTest::newRow(dataTag: "M: 1.0 0 0.5 1 0.5 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 // p
6845 << qreal(0.5) << flags // c1 (no prop)
6846 << qreal(0.5) << 0 // c2
6847 << qreal(1.0) << qreal(0.5) << qreal(0.5) << qreal(0.5);
6848}
6849
6850void tst_QGraphicsItem::opacity()
6851{
6852 QFETCH(qreal, p_opacity);
6853 QFETCH(int, p_opacityFlags);
6854 QFETCH(qreal, p_effectiveOpacity);
6855 QFETCH(qreal, c1_opacity);
6856 QFETCH(int, c1_opacityFlags);
6857 QFETCH(qreal, c1_effectiveOpacity);
6858 QFETCH(qreal, c2_opacity);
6859 QFETCH(int, c2_opacityFlags);
6860 QFETCH(qreal, c2_effectiveOpacity);
6861 QFETCH(qreal, c3_effectiveOpacity);
6862
6863 const QScopedPointer<QGraphicsRectItem> p(new QGraphicsRectItem);
6864 QGraphicsRectItem *c1 = new QGraphicsRectItem(p.data());
6865 QGraphicsRectItem *c2 = new QGraphicsRectItem(c1);
6866 QGraphicsRectItem *c3 = new QGraphicsRectItem(c2);
6867
6868 QCOMPARE(p->opacity(), qreal(1.0));
6869 QCOMPARE(p->effectiveOpacity(), qreal(1.0));
6870 int opacityMask = QGraphicsItem::ItemIgnoresParentOpacity | QGraphicsItem::ItemDoesntPropagateOpacityToChildren;
6871 QVERIFY(!(p->flags() & opacityMask));
6872
6873 p->setOpacity(p_opacity);
6874 c1->setOpacity(c1_opacity);
6875 c2->setOpacity(c2_opacity);
6876 p->setFlags(QGraphicsItem::GraphicsItemFlags(p->flags() | p_opacityFlags));
6877 c1->setFlags(QGraphicsItem::GraphicsItemFlags(c1->flags() | c1_opacityFlags));
6878 c2->setFlags(QGraphicsItem::GraphicsItemFlags(c2->flags() | c2_opacityFlags));
6879
6880 QCOMPARE(int(p->flags() & opacityMask), p_opacityFlags);
6881 QCOMPARE(int(c1->flags() & opacityMask), c1_opacityFlags);
6882 QCOMPARE(int(c2->flags() & opacityMask), c2_opacityFlags);
6883 QCOMPARE(p->opacity(), p_opacity);
6884 QCOMPARE(p->effectiveOpacity(), p_effectiveOpacity);
6885 QCOMPARE(c1->effectiveOpacity(), c1_effectiveOpacity);
6886 QCOMPARE(c2->effectiveOpacity(), c2_effectiveOpacity);
6887 QCOMPARE(c3->effectiveOpacity(), c3_effectiveOpacity);
6888}
6889
6890void tst_QGraphicsItem::opacity2()
6891{
6892 EventTester *parent = new EventTester;
6893 EventTester *child = new EventTester(parent);
6894 EventTester *grandChild = new EventTester(child);
6895
6896 QGraphicsScene scene;
6897 scene.addItem(item: parent);
6898
6899 MyGraphicsView view(&scene);
6900 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6901 view.show();
6902 QVERIFY(QTest::qWaitForWindowExposed(&view));
6903 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
6904 QVERIFY(QTest::qWaitForWindowActive(&view));
6905 QCoreApplication::processEvents(); // Process all queued paint events
6906 QTRY_VERIFY(view.repaints >= 1);
6907
6908#define RESET_REPAINT_COUNTERS \
6909 parent->repaints = 0; \
6910 child->repaints = 0; \
6911 grandChild->repaints = 0; \
6912 view.repaints = 0;
6913
6914 RESET_REPAINT_COUNTERS
6915
6916 child->setOpacity(0.0);
6917 QTRY_COMPARE(view.repaints, 1);
6918 QCOMPARE(parent->repaints, 1);
6919 QCOMPARE(child->repaints, 0);
6920 QCOMPARE(grandChild->repaints, 0);
6921
6922 RESET_REPAINT_COUNTERS
6923
6924 child->setOpacity(1.0);
6925 QTRY_COMPARE(view.repaints, 1);
6926 QCOMPARE(parent->repaints, 1);
6927 QCOMPARE(child->repaints, 1);
6928 QCOMPARE(grandChild->repaints, 1);
6929
6930 RESET_REPAINT_COUNTERS
6931
6932 parent->setOpacity(0.0);
6933 QTRY_COMPARE(view.repaints, 1);
6934 QCOMPARE(parent->repaints, 0);
6935 QCOMPARE(child->repaints, 0);
6936 QCOMPARE(grandChild->repaints, 0);
6937
6938 RESET_REPAINT_COUNTERS
6939
6940 parent->setOpacity(1.0);
6941 QTRY_COMPARE(view.repaints, 1);
6942 QCOMPARE(parent->repaints, 1);
6943 QCOMPARE(child->repaints, 1);
6944 QCOMPARE(grandChild->repaints, 1);
6945
6946 grandChild->setFlag(flag: QGraphicsItem::ItemIgnoresParentOpacity);
6947 RESET_REPAINT_COUNTERS
6948
6949 child->setOpacity(0.0);
6950 QTRY_COMPARE(view.repaints, 1);
6951 QCOMPARE(parent->repaints, 1);
6952 QCOMPARE(child->repaints, 0);
6953 QCOMPARE(grandChild->repaints, 1);
6954
6955 RESET_REPAINT_COUNTERS
6956
6957 child->setOpacity(0.0); // Already 0.0; no change.
6958 QTest::qWait(ms: 10);
6959 QCOMPARE(view.repaints, 0);
6960 QCOMPARE(parent->repaints, 0);
6961 QCOMPARE(child->repaints, 0);
6962 QCOMPARE(grandChild->repaints, 0);
6963}
6964
6965void tst_QGraphicsItem::opacityZeroUpdates()
6966{
6967 EventTester *parent = new EventTester;
6968 EventTester *child = new EventTester(parent);
6969
6970 child->setPos(ax: 10, ay: 10);
6971
6972 QGraphicsScene scene;
6973 scene.addItem(item: parent);
6974
6975 MyGraphicsView view(&scene);
6976 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6977 view.show();
6978 QVERIFY(QTest::qWaitForWindowExposed(&view));
6979 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
6980 QVERIFY(QTest::qWaitForWindowActive(&view));
6981 QCoreApplication::processEvents(); // Process all queued paint events
6982 QTRY_VERIFY(view.repaints > 0);
6983
6984 view.reset();
6985 parent->setOpacity(0.0);
6986
6987 QTRY_COMPARE(view.repaints, 1);
6988
6989 // transforming items bounding rect to view coordinates
6990 const QRect childDeviceBoundingRect = child->deviceTransform(viewportTransform: view.viewportTransform())
6991 .mapRect(child->boundingRect()).toRect();
6992 const QRect parentDeviceBoundingRect = parent->deviceTransform(viewportTransform: view.viewportTransform())
6993 .mapRect(parent->boundingRect()).toRect();
6994
6995 QRegion expectedRegion = parentDeviceBoundingRect.adjusted(xp1: -2, yp1: -2, xp2: 2, yp2: 2);
6996 expectedRegion += childDeviceBoundingRect.adjusted(xp1: -2, yp1: -2, xp2: 2, yp2: 2);
6997
6998#ifdef Q_OS_WINRT
6999 QEXPECT_FAIL("", "Fails on WinRT. Figure out why - QTBUG-68297", Abort);
7000#endif
7001 COMPARE_REGIONS(view.paintedRegion, expectedRegion);
7002}
7003
7004class StacksBehindParentHelper : public QGraphicsRectItem
7005{
7006public:
7007 StacksBehindParentHelper(GraphicsItems *paintedItems, const QRectF &rect, QGraphicsItem *parent = nullptr)
7008 : QGraphicsRectItem(rect, parent), m_paintedItems(paintedItems)
7009 { }
7010
7011 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
7012 {
7013 QGraphicsRectItem::paint(painter, option, widget);
7014 m_paintedItems->append(t: this);
7015 }
7016
7017private:
7018 GraphicsItems *m_paintedItems;
7019};
7020
7021void tst_QGraphicsItem::itemStacksBehindParent()
7022{
7023 StacksBehindParentHelper *parent1 = new StacksBehindParentHelper(&paintedItems, QRectF(0, 0, 100, 50));
7024 StacksBehindParentHelper *child11 = new StacksBehindParentHelper(&paintedItems, QRectF(-10, 10, 50, 50), parent1);
7025 StacksBehindParentHelper *grandChild111 = new StacksBehindParentHelper(&paintedItems, QRectF(-20, 20, 50, 50), child11);
7026 StacksBehindParentHelper *child12 = new StacksBehindParentHelper(&paintedItems, QRectF(60, 10, 50, 50), parent1);
7027 StacksBehindParentHelper *grandChild121 = new StacksBehindParentHelper(&paintedItems, QRectF(70, 20, 50, 50), child12);
7028
7029 StacksBehindParentHelper *parent2 = new StacksBehindParentHelper(&paintedItems, QRectF(0, 0, 100, 50));
7030 StacksBehindParentHelper *child21 = new StacksBehindParentHelper(&paintedItems, QRectF(-10, 10, 50, 50), parent2);
7031 StacksBehindParentHelper *grandChild211 = new StacksBehindParentHelper(&paintedItems, QRectF(-20, 20, 50, 50), child21);
7032 StacksBehindParentHelper *child22 = new StacksBehindParentHelper(&paintedItems, QRectF(60, 10, 50, 50), parent2);
7033 StacksBehindParentHelper *grandChild221 = new StacksBehindParentHelper(&paintedItems, QRectF(70, 20, 50, 50), child22);
7034
7035 parent1->setData(key: 0, value: "parent1");
7036 child11->setData(key: 0, value: "child11");
7037 grandChild111->setData(key: 0, value: "grandChild111");
7038 child12->setData(key: 0, value: "child12");
7039 grandChild121->setData(key: 0, value: "grandChild121");
7040 parent2->setData(key: 0, value: "parent2");
7041 child21->setData(key: 0, value: "child21");
7042 grandChild211->setData(key: 0, value: "grandChild211");
7043 child22->setData(key: 0, value: "child22");
7044 grandChild221->setData(key: 0, value: "grandChild221");
7045
7046 // Disambiguate siblings
7047 parent1->setZValue(1);
7048 child11->setZValue(1);
7049 child21->setZValue(1);
7050
7051 QGraphicsScene scene;
7052 scene.addItem(item: parent1);
7053 scene.addItem(item: parent2);
7054
7055 QGraphicsView view(&scene);
7056 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7057 view.show();
7058 QVERIFY(QTest::qWaitForWindowExposed(&view));
7059 QTRY_VERIFY(!paintedItems.isEmpty());
7060 paintedItems.clear();
7061 view.viewport()->update();
7062 QApplication::processEvents();
7063 QRectF rect(0, 0, 100, 100);
7064 const GraphicsItemsList expected1{grandChild111, child11, grandChild121, child12, parent1,
7065 grandChild211, child21, grandChild221, child22, parent2};
7066 QTRY_COMPARE(scene.items(rect), expected1);
7067
7068 const GraphicsItems expected2{parent2, child22, grandChild221, child21, grandChild211,
7069 parent1, child12, grandChild121, child11, grandChild111};
7070 QTRY_COMPARE(paintedItems, expected2);
7071
7072 child11->setFlag(flag: QGraphicsItem::ItemStacksBehindParent);
7073 scene.update();
7074 paintedItems.clear();
7075
7076 const GraphicsItemsList expected3{grandChild121, child12, parent1, grandChild111, child11,
7077 grandChild211, child21, grandChild221, child22, parent2};
7078
7079 QTRY_COMPARE(scene.items(rect), expected3);
7080 const GraphicsItems expected4{parent2, child22, grandChild221, child21, grandChild211, child11, grandChild111,
7081 parent1, child12, grandChild121};
7082 QTRY_COMPARE(paintedItems, expected4);
7083
7084 child12->setFlag(flag: QGraphicsItem::ItemStacksBehindParent);
7085 paintedItems.clear();
7086 scene.update();
7087
7088 const GraphicsItemsList expected5{parent1, grandChild111, child11, grandChild121, child12,
7089 grandChild211, child21, grandChild221, child22, parent2};
7090 QTRY_COMPARE(scene.items(rect), expected5);
7091
7092 const GraphicsItems expected6{parent2, child22, grandChild221, child21, grandChild211,
7093 child12, grandChild121, child11, grandChild111, parent1};
7094 QTRY_COMPARE(paintedItems, expected6);
7095}
7096
7097class ClippingAndTransformsScene : public QGraphicsScene
7098{
7099 Q_OBJECT
7100public:
7101 GraphicsItems drawnItems;
7102protected:
7103 void drawItems(QPainter *painter, int numItems, QGraphicsItem *items[],
7104 const QStyleOptionGraphicsItem options[], QWidget *widget = nullptr) override
7105 {
7106 drawnItems.clear();
7107 for (int i = 0; i < numItems; ++i)
7108 drawnItems << items[i];
7109 QGraphicsScene::drawItems(painter, numItems, items, options, widget);
7110 }
7111};
7112
7113void tst_QGraphicsItem::nestedClipping()
7114{
7115 ClippingAndTransformsScene scene;
7116 scene.setSceneRect(x: -50, y: -50, w: 200, h: 200);
7117
7118 QGraphicsRectItem *root = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
7119 root->setBrush(QColor(0, 0, 255));
7120 root->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
7121 QGraphicsRectItem *l1 = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
7122 l1->setParentItem(root);
7123 l1->setPos(ax: -50, ay: 0);
7124 l1->setBrush(QColor(255, 0, 0));
7125 l1->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
7126 QGraphicsEllipseItem *l2 = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100));
7127 l2->setParentItem(l1);
7128 l2->setPos(ax: 50, ay: 50);
7129 l2->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
7130 l2->setBrush(QColor(255, 255, 0));
7131 QGraphicsRectItem *l3 = new QGraphicsRectItem(QRectF(0, 0, 25, 25));
7132 l3->setParentItem(l2);
7133 l3->setBrush(QColor(0, 255, 0));
7134 l3->setPos(ax: 50 - 12, ay: -12);
7135
7136 scene.addItem(item: root);
7137
7138 root->setData(key: 0, value: "root");
7139 l1->setData(key: 0, value: "l1");
7140 l2->setData(key: 0, value: "l2");
7141 l3->setData(key: 0, value: "l3");
7142
7143 QGraphicsView view(&scene);
7144 view.setOptimizationFlag(flag: QGraphicsView::IndirectPainting);
7145 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7146 view.show();
7147 QVERIFY(QTest::qWaitForWindowExposed(&view));
7148
7149 GraphicsItems expected{root, l1, l2, l3};
7150 QTRY_COMPARE(scene.drawnItems, expected);
7151
7152 QImage image(200, 200, QImage::Format_ARGB32_Premultiplied);
7153 image.fill(pixel: 0);
7154
7155 QPainter painter(&image);
7156 scene.render(painter: &painter);
7157 painter.end();
7158
7159 // Check transparent areas
7160 QCOMPARE(image.pixel(100, 25), qRgba(0, 0, 0, 0));
7161 QCOMPARE(image.pixel(100, 175), qRgba(0, 0, 0, 0));
7162 QCOMPARE(image.pixel(25, 100), qRgba(0, 0, 0, 0));
7163 QCOMPARE(image.pixel(175, 100), qRgba(0, 0, 0, 0));
7164 QCOMPARE(image.pixel(70, 80), qRgba(255, 0, 0, 255));
7165 QCOMPARE(image.pixel(80, 130), qRgba(255, 255, 0, 255));
7166 QCOMPARE(image.pixel(92, 105), qRgba(0, 255, 0, 255));
7167 QCOMPARE(image.pixel(105, 105), qRgba(0, 0, 255, 255));
7168#if 0
7169 // Enable this to compare if the test starts failing.
7170 image.save("nestedClipping_reference.png");
7171#endif
7172}
7173
7174class TransformDebugItem : public QGraphicsRectItem
7175{
7176public:
7177 TransformDebugItem()
7178 : QGraphicsRectItem(QRectF(-10, -10, 20, 20))
7179 {
7180 setPen(QPen(Qt::black, 0));
7181 setBrush(QColor(QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256), QRandomGenerator::global()->bounded(highest: 256)));
7182 }
7183
7184 QTransform x;
7185
7186 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
7187 QWidget *widget = nullptr) override
7188 {
7189 x = painter->worldTransform();
7190 QGraphicsRectItem::paint(painter, option, widget);
7191 }
7192};
7193
7194void tst_QGraphicsItem::nestedClippingTransforms()
7195{
7196 TransformDebugItem *rootClipper = new TransformDebugItem;
7197 rootClipper->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
7198 TransformDebugItem *child = new TransformDebugItem;
7199 child->setParentItem(rootClipper);
7200 child->setPos(ax: 2, ay: 2);
7201 TransformDebugItem *grandChildClipper = new TransformDebugItem;
7202 grandChildClipper->setParentItem(child);
7203 grandChildClipper->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
7204 grandChildClipper->setPos(ax: 4, ay: 4);
7205 TransformDebugItem *greatGrandChild = new TransformDebugItem;
7206 greatGrandChild->setPos(ax: 2, ay: 2);
7207 greatGrandChild->setParentItem(grandChildClipper);
7208 TransformDebugItem *grandChildClipper2 = new TransformDebugItem;
7209 grandChildClipper2->setParentItem(child);
7210 grandChildClipper2->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
7211 grandChildClipper2->setPos(ax: 8, ay: 8);
7212 TransformDebugItem *greatGrandChild2 = new TransformDebugItem;
7213 greatGrandChild2->setPos(ax: 2, ay: 2);
7214 greatGrandChild2->setParentItem(grandChildClipper2);
7215 TransformDebugItem *grandChildClipper3 = new TransformDebugItem;
7216 grandChildClipper3->setParentItem(child);
7217 grandChildClipper3->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
7218 grandChildClipper3->setPos(ax: 12, ay: 12);
7219 TransformDebugItem *greatGrandChild3 = new TransformDebugItem;
7220 greatGrandChild3->setPos(ax: 2, ay: 2);
7221 greatGrandChild3->setParentItem(grandChildClipper3);
7222
7223 QGraphicsScene scene;
7224 scene.addItem(item: rootClipper);
7225
7226 QImage image(scene.itemsBoundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied);
7227 image.fill(pixel: 0);
7228 QPainter p(&image);
7229 scene.render(painter: &p);
7230 p.end();
7231
7232 QCOMPARE(rootClipper->x, QTransform(1, 0, 0, 0, 1, 0, 10, 10, 1));
7233 QCOMPARE(child->x, QTransform(1, 0, 0, 0, 1, 0, 12, 12, 1));
7234 QCOMPARE(grandChildClipper->x, QTransform(1, 0, 0, 0, 1, 0, 16, 16, 1));
7235 QCOMPARE(greatGrandChild->x, QTransform(1, 0, 0, 0, 1, 0, 18, 18, 1));
7236 QCOMPARE(grandChildClipper2->x, QTransform(1, 0, 0, 0, 1, 0, 20, 20, 1));
7237 QCOMPARE(greatGrandChild2->x, QTransform(1, 0, 0, 0, 1, 0, 22, 22, 1));
7238 QCOMPARE(grandChildClipper3->x, QTransform(1, 0, 0, 0, 1, 0, 24, 24, 1));
7239 QCOMPARE(greatGrandChild3->x, QTransform(1, 0, 0, 0, 1, 0, 26, 26, 1));
7240}
7241
7242void tst_QGraphicsItem::sceneTransformCache()
7243{
7244 // Test that an item's scene transform is updated correctly when the
7245 // parent is transformed.
7246 QGraphicsScene scene;
7247
7248 const QScopedPointer<QGraphicsRectItem> rect(scene.addRect(x: 0, y: 0, w: 100, h: 100));
7249 rect->setPen(QPen(Qt::black, 0));
7250 QGraphicsRectItem *rect2 = scene.addRect(x: 0, y: 0, w: 100, h: 100);
7251 rect2->setPen(QPen(Qt::black, 0));
7252 rect2->setParentItem(rect.data());
7253 rect2->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
7254 rect->setTransform(matrix: QTransform::fromTranslate(dx: 0, dy: 50), combine: true);
7255 QGraphicsView view(&scene);
7256 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7257 view.show();
7258 QVERIFY(QTest::qWaitForWindowExposed(&view));
7259 rect->setTransform(matrix: QTransform::fromTranslate(dx: 0, dy: 100), combine: true);
7260 QTransform x;
7261 x.translate(dx: 0, dy: 150);
7262 x.rotate(a: 90);
7263 QCOMPARE(rect2->sceneTransform(), x);
7264
7265 scene.removeItem(item: rect.data());
7266
7267 //Crazy use case : rect4 child of rect3 so the transformation of rect4 will be cached.Good!
7268 //We remove rect4 from the scene, then the validTransform bit flag is set to 0 and the index of the cache
7269 //add to the freeTransformSlots. The problem was that sceneTransformIndex was not set to -1 so if a new item arrive
7270 //with a child (rect6) that will be cached then it will take the freeSlot (ex rect4) and put it his transform. But if rect4 is
7271 //added back to the scene then it will set the transform to his old sceneTransformIndex value that will erase the new
7272 //value of rect6 so rect6 transform will be wrong.
7273 QGraphicsRectItem *rect3 = scene.addRect(x: 0, y: 0, w: 100, h: 100);
7274 QGraphicsRectItem *rect4 = scene.addRect(x: 0, y: 0, w: 100, h: 100);
7275 rect3->setPos(QPointF(10,10));
7276 rect3->setPen(QPen(Qt::black, 0));
7277
7278 rect4->setParentItem(rect3);
7279 rect4->setPos(QPointF(10,10));
7280 rect4->setPen(QPen(Qt::black, 0));
7281
7282 QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(20,20));
7283
7284 scene.removeItem(item: rect4);
7285 //rect4 transform is local only
7286 QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(10,10));
7287
7288 QGraphicsRectItem *rect5 = scene.addRect(x: 0, y: 0, w: 100, h: 100);
7289 QGraphicsRectItem *rect6 = scene.addRect(x: 0, y: 0, w: 100, h: 100);
7290 rect5->setPos(QPointF(20,20));
7291 rect5->setPen(QPen(Qt::black, 0));
7292
7293 rect6->setParentItem(rect5);
7294 rect6->setPos(QPointF(10,10));
7295 rect6->setPen(QPen(Qt::black, 0));
7296 //test if rect6 transform is ok
7297 QCOMPARE(rect6->mapToScene(rect6->boundingRect().topLeft()), QPointF(30,30));
7298
7299 scene.addItem(item: rect4);
7300
7301 QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(10,10));
7302 //test if rect6 transform is still correct
7303 QCOMPARE(rect6->mapToScene(rect6->boundingRect().topLeft()), QPointF(30,30));
7304}
7305
7306void tst_QGraphicsItem::tabChangesFocus_data()
7307{
7308 QTest::addColumn<bool>(name: "tabChangesFocus");
7309 QTest::newRow(dataTag: "tab changes focus") << true;
7310 QTest::newRow(dataTag: "tab doesn't change focus") << false;
7311}
7312
7313void tst_QGraphicsItem::tabChangesFocus()
7314{
7315 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
7316 QSKIP("Window activation is not supported");
7317
7318 QFETCH(bool, tabChangesFocus);
7319
7320 QGraphicsScene scene;
7321 QGraphicsTextItem *item = scene.addText(text: "Hello");
7322 item->setTabChangesFocus(tabChangesFocus);
7323 item->setTextInteractionFlags(Qt::TextEditorInteraction);
7324 item->setFocus();
7325
7326 QDial *dial1 = new QDial;
7327 QGraphicsView *view = new QGraphicsView(&scene);
7328
7329 QDial *dial2 = new QDial;
7330 QVBoxLayout *layout = new QVBoxLayout;
7331 layout->addWidget(dial1);
7332 layout->addWidget(view);
7333 layout->addWidget(dial2);
7334
7335 QWidget widget;
7336 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7337 widget.setLayout(layout);
7338 widget.show();
7339 QVERIFY(QTest::qWaitForWindowActive(&widget));
7340
7341 QTRY_VERIFY(scene.isActive());
7342
7343 dial1->setFocus();
7344 QTRY_VERIFY(dial1->hasFocus());
7345
7346 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
7347 QTRY_VERIFY(view->hasFocus());
7348 QTRY_VERIFY(item->hasFocus());
7349
7350 QTest::keyPress(widget: QApplication::focusWidget(), key: Qt::Key_Tab);
7351
7352 if (tabChangesFocus) {
7353 QTRY_VERIFY(!view->hasFocus());
7354 QTRY_VERIFY(!item->hasFocus());
7355 QTRY_VERIFY(dial2->hasFocus());
7356 } else {
7357 QTRY_VERIFY(view->hasFocus());
7358 QTRY_VERIFY(item->hasFocus());
7359 QCOMPARE(item->toPlainText(), QString("\tHello"));
7360 }
7361}
7362
7363void tst_QGraphicsItem::cacheMode()
7364{
7365 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
7366 QSKIP("Wayland: This fails. Figure out why.");
7367
7368 QGraphicsScene scene(0, 0, 100, 100);
7369 QGraphicsView view(&scene);
7370 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7371 view.resize(w: 150, h: 150);
7372 view.show();
7373 QApplication::setActiveWindow(&view);
7374 QVERIFY(QTest::qWaitForWindowExposed(&view));
7375 QVERIFY(QTest::qWaitForWindowActive(&view));
7376 QCoreApplication::processEvents(); // Process all queued paint events
7377
7378 EventTester *tester = new EventTester;
7379 EventTester *testerChild = new EventTester;
7380 testerChild->setParentItem(tester);
7381 EventTester *testerChild2 = new EventTester;
7382 testerChild2->setParentItem(testerChild);
7383 testerChild2->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
7384
7385 scene.addItem(item: tester);
7386
7387 for (int i = 0; i < 2; ++i) {
7388 // No visual change.
7389 QTRY_COMPARE(tester->repaints, 1);
7390 QCOMPARE(testerChild->repaints, 1);
7391 QCOMPARE(testerChild2->repaints, 1);
7392 tester->setCacheMode(mode: QGraphicsItem::NoCache);
7393 testerChild->setCacheMode(mode: QGraphicsItem::NoCache);
7394 testerChild2->setCacheMode(mode: QGraphicsItem::NoCache);
7395 QTest::qWait(ms: 25);
7396 QTRY_COMPARE(tester->repaints, 1);
7397 QCOMPARE(testerChild->repaints, 1);
7398 QCOMPARE(testerChild2->repaints, 1);
7399 tester->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7400 testerChild->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7401 testerChild2->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7402 QTest::qWait(ms: 25);
7403 }
7404
7405 // The first move causes a repaint as the item is painted into its pixmap.
7406 // (Only occurs if the item has previously been painted without cache).
7407 tester->setPos(ax: 10, ay: 10);
7408 testerChild->setPos(ax: 10, ay: 10);
7409 testerChild2->setPos(ax: 10, ay: 10);
7410 QTRY_COMPARE(tester->repaints, 2);
7411 QCOMPARE(testerChild->repaints, 2);
7412 QCOMPARE(testerChild2->repaints, 2);
7413
7414 // Consecutive moves should not repaint.
7415 tester->setPos(ax: 20, ay: 20);
7416 testerChild->setPos(ax: 20, ay: 20);
7417 testerChild2->setPos(ax: 20, ay: 20);
7418 QTest::qWait(ms: 250);
7419 QCOMPARE(tester->repaints, 2);
7420 QCOMPARE(testerChild->repaints, 2);
7421 QCOMPARE(testerChild2->repaints, 2);
7422
7423 // Translating does not result in a repaint.
7424 tester->setTransform(matrix: QTransform::fromTranslate(dx: 10, dy: 10), combine: true);
7425 QTest::qWait(ms: 25);
7426 QCOMPARE(tester->repaints, 2);
7427 QCOMPARE(testerChild->repaints, 2);
7428 QCOMPARE(testerChild2->repaints, 2);
7429
7430 // Rotating results in a repaint.
7431 tester->setTransform(matrix: QTransform().rotate(a: 45), combine: true);
7432 QTRY_COMPARE(tester->repaints, 3);
7433 QCOMPARE(testerChild->repaints, 3);
7434 QCOMPARE(testerChild2->repaints, 2);
7435
7436 // Change to ItemCoordinateCache (triggers repaint).
7437 tester->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache); // autosize
7438 testerChild->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache); // autosize
7439 testerChild2->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache); // autosize
7440 QTRY_COMPARE(tester->repaints, 4);
7441 QCOMPARE(testerChild->repaints, 4);
7442 QCOMPARE(testerChild2->repaints, 3);
7443
7444 // Rotating items with ItemCoordinateCache doesn't cause a repaint.
7445 tester->setTransform(matrix: QTransform().rotate(a: 22), combine: true);
7446 testerChild->setTransform(matrix: QTransform().rotate(a: 22), combine: true);
7447 testerChild2->setTransform(matrix: QTransform().rotate(a: 22), combine: true);
7448 QTest::qWait(ms: 25);
7449 QCOMPARE(tester->repaints, 4);
7450 QCOMPARE(testerChild->repaints, 4);
7451 QCOMPARE(testerChild2->repaints, 3);
7452 tester->resetTransform();
7453 testerChild->resetTransform();
7454 testerChild2->resetTransform();
7455
7456 // Explicit update causes a repaint.
7457 tester->update(ax: 0, ay: 0, width: 5, height: 5);
7458 QTRY_COMPARE(tester->repaints, 5);
7459 QCOMPARE(testerChild->repaints, 4);
7460 QCOMPARE(testerChild2->repaints, 3);
7461
7462 // Updating outside the item's bounds does not cause a repaint.
7463 tester->update(ax: 10, ay: 10, width: 5, height: 5);
7464 QTest::qWait(ms: 25);
7465 QCOMPARE(tester->repaints, 5);
7466 QCOMPARE(testerChild->repaints, 4);
7467 QCOMPARE(testerChild2->repaints, 3);
7468
7469 // Resizing an item should cause a repaint of that item. (because of
7470 // autosize).
7471 tester->setGeometry(QRectF(-15, -15, 30, 30));
7472 QTRY_COMPARE(tester->repaints, 6);
7473 QCOMPARE(testerChild->repaints, 4);
7474 QCOMPARE(testerChild2->repaints, 3);
7475
7476 // Set fixed size.
7477 tester->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache, cacheSize: QSize(30, 30));
7478 testerChild->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache, cacheSize: QSize(30, 30));
7479 testerChild2->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache, cacheSize: QSize(30, 30));
7480 QTRY_COMPARE(tester->repaints, 7);
7481 QCOMPARE(testerChild->repaints, 5);
7482 QCOMPARE(testerChild2->repaints, 4);
7483
7484 // Resizing the item should cause a repaint.
7485 testerChild->setGeometry(QRectF(-15, -15, 30, 30));
7486 QTRY_COMPARE(testerChild->repaints, 6);
7487 QCOMPARE(tester->repaints, 7);
7488 QCOMPARE(testerChild2->repaints, 4);
7489
7490 // Scaling the view does not cause a repaint.
7491 view.scale(sx: 0.7, sy: 0.7);
7492 QTest::qWait(ms: 25);
7493 QCOMPARE(tester->repaints, 7);
7494 QCOMPARE(testerChild->repaints, 6);
7495 QCOMPARE(testerChild2->repaints, 4);
7496
7497 // Switch to device coordinate cache.
7498 tester->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7499 testerChild->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7500 testerChild2->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7501 QTRY_COMPARE(tester->repaints, 8);
7502 QCOMPARE(testerChild->repaints, 7);
7503 QCOMPARE(testerChild2->repaints, 5);
7504
7505 // Scaling the view back should cause repaints for two of the items.
7506 view.setTransform(matrix: QTransform());
7507 QTRY_COMPARE(tester->repaints, 9);
7508 QCOMPARE(testerChild->repaints, 8);
7509 QCOMPARE(testerChild2->repaints, 5);
7510
7511 // Rotating the base item (perspective) should repaint two items.
7512 tester->setTransform(matrix: QTransform().rotate(a: 10, axis: Qt::XAxis));
7513 QTRY_COMPARE(tester->repaints, 10);
7514 QCOMPARE(testerChild->repaints, 9);
7515 QCOMPARE(testerChild2->repaints, 5);
7516
7517 // Moving the middle item should cause a repaint even if it's a move,
7518 // because the parent is rotated with a perspective.
7519 testerChild->setPos(ax: 1, ay: 1);
7520 QTRY_COMPARE(tester->repaints, 11);
7521 QTRY_COMPARE(testerChild->repaints, 10);
7522 QCOMPARE(testerChild2->repaints, 5);
7523 tester->resetTransform();
7524
7525 // Make a huge item
7526 tester->setGeometry(QRectF(-4000, -4000, 8000, 8000));
7527 QTRY_COMPARE(tester->repaints, 12);
7528 QTRY_COMPARE(testerChild->repaints, 11);
7529 QCOMPARE(testerChild2->repaints, 5);
7530
7531 // Move the large item - will cause a repaint as the
7532 // cache is clipped.
7533 tester->setPos(ax: 5, ay: 0);
7534 QTRY_COMPARE(tester->repaints, 13);
7535 QTRY_COMPARE(testerChild->repaints, 11);
7536 QCOMPARE(testerChild2->repaints, 5);
7537
7538 // Hiding and showing should invalidate the cache
7539 tester->hide();
7540 QTest::qWait(ms: 25);
7541 tester->show();
7542 QTRY_COMPARE(tester->repaints, 14);
7543 QTRY_COMPARE(testerChild->repaints, 12);
7544 QTRY_COMPARE(testerChild2->repaints, 6);
7545}
7546
7547void tst_QGraphicsItem::cacheMode2()
7548{
7549 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
7550 QSKIP("Wayland: This fails. Figure out why.");
7551
7552 QGraphicsScene scene(0, 0, 100, 100);
7553 QGraphicsView view(&scene);
7554 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7555 view.resize(w: 150, h: 150);
7556 view.show();
7557 QApplication::setActiveWindow(&view);
7558 QVERIFY(QTest::qWaitForWindowExposed(&view));
7559 QVERIFY(QTest::qWaitForWindowActive(&view));
7560 QCoreApplication::processEvents(); // Process all queued paint events
7561
7562 EventTester *tester = new EventTester;
7563 scene.addItem(item: tester);
7564 QTRY_COMPARE(tester->repaints, 1);
7565
7566 // Switching from NoCache to NoCache (no repaint)
7567 tester->setCacheMode(mode: QGraphicsItem::NoCache);
7568 QTest::qWait(ms: 50);
7569 QCOMPARE(tester->repaints, 1);
7570
7571 // Switching from NoCache to DeviceCoordinateCache (no repaint)
7572 tester->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7573 QTest::qWait(ms: 50);
7574 QCOMPARE(tester->repaints, 1);
7575
7576 // Switching from DeviceCoordinateCache to DeviceCoordinateCache (no repaint)
7577 tester->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7578 QTest::qWait(ms: 50);
7579 QCOMPARE(tester->repaints, 1);
7580
7581 // Switching from DeviceCoordinateCache to NoCache (no repaint)
7582 tester->setCacheMode(mode: QGraphicsItem::NoCache);
7583 QTest::qWait(ms: 50);
7584 QCOMPARE(tester->repaints, 1);
7585
7586 // Switching from NoCache to ItemCoordinateCache (repaint)
7587 tester->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache);
7588 QTRY_COMPARE(tester->repaints, 2);
7589
7590 // Switching from ItemCoordinateCache to ItemCoordinateCache (no repaint)
7591 tester->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache);
7592 QTest::qWait(ms: 50);
7593 QCOMPARE(tester->repaints, 2);
7594
7595 // Switching from ItemCoordinateCache to ItemCoordinateCache with different size (repaint)
7596 tester->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache, cacheSize: QSize(100, 100));
7597 QTRY_COMPARE(tester->repaints, 3);
7598
7599 // Switching from ItemCoordinateCache to NoCache (repaint)
7600 tester->setCacheMode(mode: QGraphicsItem::NoCache);
7601 QTRY_COMPARE(tester->repaints, 4);
7602
7603 // Switching from DeviceCoordinateCache to ItemCoordinateCache (repaint)
7604 tester->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7605 QTest::qWait(ms: 50);
7606 QCOMPARE(tester->repaints, 4);
7607 tester->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache);
7608 QTRY_COMPARE(tester->repaints, 5);
7609
7610 // Switching from ItemCoordinateCache to DeviceCoordinateCache (repaint)
7611 tester->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
7612 QTRY_COMPARE(tester->repaints, 6);
7613}
7614
7615void tst_QGraphicsItem::updateCachedItemAfterMove()
7616{
7617 // A simple item that uses ItemCoordinateCache
7618 EventTester *tester = new EventTester;
7619 tester->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache);
7620
7621 // Add to a scene, show in a view, ensure it's painted and reset its
7622 // repaint counter.
7623 QGraphicsScene scene;
7624 scene.addItem(item: tester);
7625 QGraphicsView view(&scene);
7626 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7627 view.show();
7628 QVERIFY(QTest::qWaitForWindowExposed(&view));
7629
7630 QTRY_VERIFY(tester->repaints > 0);
7631 tester->repaints = 0;
7632
7633 // Move the item, should not cause repaints
7634 tester->setPos(ax: 10, ay: 0);
7635 QTest::qWait(ms: 12);
7636 QCOMPARE(tester->repaints, 0);
7637
7638 // Move then update, should cause one repaint
7639 tester->setPos(ax: 20, ay: 0);
7640 tester->update();
7641 QTRY_COMPARE(tester->repaints, 1);
7642
7643 // Hiding the item doesn't cause a repaint
7644 tester->hide();
7645 QTest::qWait(ms: 12);
7646 QCOMPARE(tester->repaints, 1);
7647
7648 // Moving a hidden item doesn't cause a repaint
7649 tester->setPos(ax: 30, ay: 0);
7650 tester->update();
7651 QTest::qWait(ms: 12);
7652 QCOMPARE(tester->repaints, 1);
7653}
7654
7655class Track : public QGraphicsRectItem
7656{
7657public:
7658 Track(const QRectF &rect)
7659 : QGraphicsRectItem(rect)
7660 {
7661 setAcceptHoverEvents(true);
7662 }
7663
7664 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override
7665 {
7666 QGraphicsRectItem::paint(painter, option, widget);
7667 const QString text = QString::number(p.x()) + QLatin1Char('x') + QString::number(p.y())
7668 + QLatin1Char('\n') + QString::number(sp.x()) + QLatin1Char('x') + QString::number(sp.y());
7669 painter->drawText(r: boundingRect(), flags: Qt::AlignCenter, text);
7670 }
7671
7672protected:
7673 void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override
7674 {
7675 p = event->pos();
7676 sp = event->widget()->mapFromGlobal(event->screenPos());
7677 update();
7678 }
7679private:
7680 QPointF p;
7681 QPoint sp;
7682};
7683
7684void tst_QGraphicsItem::deviceTransform_data()
7685{
7686 QTest::addColumn<bool>(name: "untransformable1");
7687 QTest::addColumn<bool>(name: "untransformable2");
7688 QTest::addColumn<bool>(name: "untransformable3");
7689 QTest::addColumn<qreal>(name: "rotation1");
7690 QTest::addColumn<qreal>(name: "rotation2");
7691 QTest::addColumn<qreal>(name: "rotation3");
7692 QTest::addColumn<QTransform>(name: "deviceX");
7693 QTest::addColumn<QPointF>(name: "mapResult1");
7694 QTest::addColumn<QPointF>(name: "mapResult2");
7695 QTest::addColumn<QPointF>(name: "mapResult3");
7696
7697 QTest::newRow(dataTag: "nil") << false << false << false
7698 << qreal(0.0) << qreal(0.0) << qreal(0.0)
7699 << QTransform()
7700 << QPointF(150, 150) << QPointF(250, 250) << QPointF(350, 350);
7701 QTest::newRow(dataTag: "deviceX rot 90") << false << false << false
7702 << qreal(0.0) << qreal(0.0) << qreal(0.0)
7703 << QTransform().rotate(a: 90)
7704 << QPointF(-150, 150) << QPointF(-250, 250) << QPointF(-350, 350);
7705 QTest::newRow(dataTag: "deviceX rot 90 100") << true << false << false
7706 << qreal(0.0) << qreal(0.0) << qreal(0.0)
7707 << QTransform().rotate(a: 90)
7708 << QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350);
7709 QTest::newRow(dataTag: "deviceX rot 90 010") << false << true << false
7710 << qreal(0.0) << qreal(0.0) << qreal(0.0)
7711 << QTransform().rotate(a: 90)
7712 << QPointF(-150, 150) << QPointF(-150, 250) << QPointF(-50, 350);
7713 QTest::newRow(dataTag: "deviceX rot 90 001") << false << false << true
7714 << qreal(0.0) << qreal(0.0) << qreal(0.0)
7715 << QTransform().rotate(a: 90)
7716 << QPointF(-150, 150) << QPointF(-250, 250) << QPointF(-250, 350);
7717 QTest::newRow(dataTag: "deviceX rot 90 111") << true << true << true
7718 << qreal(0.0) << qreal(0.0) << qreal(0.0)
7719 << QTransform().rotate(a: 90)
7720 << QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350);
7721 QTest::newRow(dataTag: "deviceX rot 90 101") << true << false << true
7722 << qreal(0.0) << qreal(0.0) << qreal(0.0)
7723 << QTransform().rotate(a: 90)
7724 << QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350);
7725}
7726
7727void tst_QGraphicsItem::deviceTransform()
7728{
7729 QFETCH(bool, untransformable1);
7730 QFETCH(bool, untransformable2);
7731 QFETCH(bool, untransformable3);
7732 QFETCH(qreal, rotation1);
7733 QFETCH(qreal, rotation2);
7734 QFETCH(qreal, rotation3);
7735 QFETCH(QTransform, deviceX);
7736 QFETCH(QPointF, mapResult1);
7737 QFETCH(QPointF, mapResult2);
7738 QFETCH(QPointF, mapResult3);
7739
7740 QGraphicsScene scene;
7741 Track *rect1 = new Track(QRectF(0, 0, 100, 100));
7742 Track *rect2 = new Track(QRectF(0, 0, 100, 100));
7743 Track *rect3 = new Track(QRectF(0, 0, 100, 100));
7744 rect2->setParentItem(rect1);
7745 rect3->setParentItem(rect2);
7746 rect1->setPos(ax: 100, ay: 100);
7747 rect2->setPos(ax: 100, ay: 100);
7748 rect3->setPos(ax: 100, ay: 100);
7749 rect1->setTransform(matrix: QTransform().rotate(a: rotation1), combine: true);
7750 rect2->setTransform(matrix: QTransform().rotate(a: rotation2), combine: true);
7751 rect3->setTransform(matrix: QTransform().rotate(a: rotation3), combine: true);
7752 rect1->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations, enabled: untransformable1);
7753 rect2->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations, enabled: untransformable2);
7754 rect3->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations, enabled: untransformable3);
7755 rect1->setBrush(Qt::red);
7756 rect2->setBrush(Qt::green);
7757 rect3->setBrush(Qt::blue);
7758 scene.addItem(item: rect1);
7759
7760 QCOMPARE(rect1->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult1);
7761 QCOMPARE(rect2->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult2);
7762 QCOMPARE(rect3->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult3);
7763}
7764
7765void tst_QGraphicsItem::update()
7766{
7767 QGraphicsScene scene;
7768 scene.setSceneRect(x: -100, y: -100, w: 200, h: 200);
7769 QWidget topLevel;
7770 topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7771 MyGraphicsView view(&scene,&topLevel);
7772
7773 topLevel.resize(w: 300, h: 300);
7774 topLevel.show();
7775 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
7776
7777 EventTester *item = new EventTester;
7778 scene.addItem(item);
7779 QTRY_VERIFY(item->repaints > 0); // Wait for painting
7780 item->repaints = 0;
7781
7782 item->update(); // Item marked as dirty
7783 scene.update(); // Entire scene marked as dirty
7784 QTRY_COMPARE(item->repaints, 1);
7785
7786 // Make sure the dirty state from the previous update is reset so that
7787 // the item don't think it is already dirty and discards this update.
7788 item->update();
7789 QTRY_COMPARE(item->repaints, 2);
7790
7791 // Make sure a partial update doesn't cause a full update to be discarded.
7792 view.reset();
7793 item->repaints = 0;
7794 item->update(rect: QRectF(0, 0, 5, 5));
7795 item->update();
7796 QTRY_COMPARE(item->repaints, 1);
7797 QTRY_COMPARE(view.repaints, 1);
7798 QRect itemDeviceBoundingRect = item->deviceTransform(viewportTransform: view.viewportTransform())
7799 .mapRect(item->boundingRect()).toAlignedRect();
7800 QRegion expectedRegion = itemDeviceBoundingRect.adjusted(xp1: -2, yp1: -2, xp2: 2, yp2: 2);
7801 // The entire item's bounding rect (adjusted for antialiasing) should have been painted.
7802 QCOMPARE(view.paintedRegion, expectedRegion);
7803
7804 // Make sure update requests outside the bounding rect are discarded.
7805 view.reset();
7806 item->repaints = 0;
7807 item->update(ax: -15, ay: -15, width: 5, height: 5); // Item's brect: (-10, -10, 20, 20)
7808 QCoreApplication::processEvents();
7809 QCOMPARE(item->repaints, 0);
7810 QCOMPARE(view.repaints, 0);
7811
7812 // Make sure the area occupied by an item is repainted when hiding it.
7813 view.reset();
7814 item->repaints = 0;
7815 item->update(); // Full update; all sub-sequent update requests are discarded.
7816 item->hide(); // visible set to 0. ignoreVisible must be set to 1; the item won't be processed otherwise.
7817 QCoreApplication::processEvents();
7818 QCOMPARE(item->repaints, 0);
7819 QCOMPARE(view.repaints, 1);
7820 // The entire item's bounding rect (adjusted for antialiasing) should have been painted.
7821 QCOMPARE(view.paintedRegion, expectedRegion);
7822
7823 // Make sure item is repainted when shown (after being hidden).
7824 view.reset();
7825 item->repaints = 0;
7826 item->show();
7827 QCoreApplication::processEvents();
7828 QCOMPARE(item->repaints, 1);
7829 QCOMPARE(view.repaints, 1);
7830 // The entire item's bounding rect (adjusted for antialiasing) should have been painted.
7831 QCOMPARE(view.paintedRegion, expectedRegion);
7832
7833 item->repaints = 0;
7834 item->hide();
7835 QCoreApplication::processEvents();
7836 view.reset();
7837 const QPointF originalPos = item->pos();
7838 item->setPos(ax: 5000, ay: 5000);
7839 QCoreApplication::processEvents();
7840 QCOMPARE(item->repaints, 0);
7841 QCOMPARE(view.repaints, 0);
7842 QCoreApplication::processEvents();
7843
7844 item->setPos(originalPos);
7845 QCoreApplication::processEvents();
7846 QCOMPARE(item->repaints, 0);
7847 QCOMPARE(view.repaints, 0);
7848 item->show();
7849 QCoreApplication::processEvents();
7850 QCOMPARE(item->repaints, 1);
7851 QCOMPARE(view.repaints, 1);
7852 // The entire item's bounding rect (adjusted for antialiasing) should have been painted.
7853 QCOMPARE(view.paintedRegion, expectedRegion);
7854
7855 QGraphicsViewPrivate *viewPrivate = static_cast<QGraphicsViewPrivate *>(qt_widget_private(widget: &view));
7856 item->setPos(originalPos + QPoint(50, 50));
7857 viewPrivate->updateAll();
7858 QVERIFY(viewPrivate->fullUpdatePending);
7859 QTRY_VERIFY(view.repaints > 1 && item->repaints > 1);
7860 item->repaints = 0;
7861 view.reset();
7862 item->setPos(originalPos);
7863 QTRY_COMPARE(item->repaints, 1);
7864 QCOMPARE(view.repaints, 1);
7865 COMPARE_REGIONS(view.paintedRegion, expectedRegion + expectedRegion.translated(50, 50));
7866
7867 // Make sure moving a parent item triggers an update on the children
7868 // (even though the parent itself is outside the viewport).
7869 QGraphicsRectItem *parent = new QGraphicsRectItem(0, 0, 10, 10);
7870 parent->setPos(ax: -400, ay: 0);
7871 item->setParentItem(parent);
7872 item->setPos(ax: 400, ay: 0);
7873 scene.addItem(item: parent);
7874 itemDeviceBoundingRect = item->deviceTransform(viewportTransform: view.viewportTransform())
7875 .mapRect(item->boundingRect()).toAlignedRect();
7876 expectedRegion = itemDeviceBoundingRect.adjusted(xp1: -2, yp1: -2, xp2: 2, yp2: 2);
7877 view.reset();
7878 item->repaints = 0;
7879 parent->setTransform(matrix: QTransform::fromTranslate(dx: -400, dy: 0), combine: true);
7880 QCoreApplication::processEvents();
7881 QCOMPARE(item->repaints, 0);
7882 QCOMPARE(view.repaints, 1);
7883 QCOMPARE(view.paintedRegion, expectedRegion);
7884 view.reset();
7885 item->repaints = 0;
7886 parent->setTransform(matrix: QTransform::fromTranslate(dx: 400, dy: 0), combine: true);
7887 QCoreApplication::processEvents();
7888 QCOMPARE(item->repaints, 1);
7889 QCOMPARE(view.repaints, 1);
7890 QCOMPARE(view.paintedRegion, expectedRegion);
7891 QCOMPARE(view.paintedRegion, expectedRegion);
7892}
7893
7894void tst_QGraphicsItem::setTransformProperties_data()
7895{
7896 QTest::addColumn<QPointF>(name: "origin");
7897 QTest::addColumn<qreal>(name: "rotation");
7898 QTest::addColumn<qreal>(name: "scale");
7899
7900 QTest::newRow(dataTag: "nothing") << QPointF() << qreal(0.0) << qreal(1.0);
7901
7902 QTest::newRow(dataTag: "rotation") << QPointF() << qreal(42.2) << qreal(1.0);
7903
7904 QTest::newRow(dataTag: "rotation dicentred") << QPointF(qreal(22.3), qreal(-56.2))
7905 << qreal(-2578.2)
7906 << qreal(1.0);
7907
7908 QTest::newRow(dataTag: "Scale") << QPointF() << qreal(0.0)
7909 << qreal(6);
7910
7911 QTest::newRow(dataTag: "Everything dicentred") << QPointF(qreal(22.3), qreal(-56.2)) << qreal(-175) << qreal(196);
7912}
7913
7914/**
7915 * the normal QCOMPARE doesn't work because it doesn't use qFuzzyCompare
7916 */
7917#define QCOMPARE_TRANSFORM(X1, X2) QVERIFY(((X1)*(X2).inverted()).isIdentity())
7918
7919void tst_QGraphicsItem::setTransformProperties()
7920{
7921 QFETCH(QPointF,origin);
7922 QFETCH(qreal,rotation);
7923 QFETCH(qreal,scale);
7924
7925 QTransform result;
7926 result.translate(dx: origin.x(), dy: origin.y());
7927 result.rotate(a: rotation, axis: Qt::ZAxis);
7928 result.scale(sx: scale, sy: scale);
7929 result.translate(dx: -origin.x(), dy: -origin.y());
7930
7931 QGraphicsScene scene;
7932 QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
7933 scene.addItem(item);
7934
7935 item->setRotation(rotation);
7936 item->setScale(scale);
7937 item->setTransformOriginPoint(origin);
7938
7939 QCOMPARE(item->rotation(), rotation);
7940 QCOMPARE(item->scale(), scale);
7941 QCOMPARE(item->transformOriginPoint(), origin);
7942
7943 QCOMPARE(QTransform(), item->transform());
7944 QCOMPARE(result, item->sceneTransform());
7945
7946 //-----------------------------------------------------------------
7947 //Change the rotation Z
7948 item->setRotation(45);
7949 QTransform result2;
7950 result2.translate(dx: origin.x(), dy: origin.y());
7951 result2.rotate(a: 45);
7952 result2.scale(sx: scale, sy: scale);
7953 result2.translate(dx: -origin.x(), dy: -origin.y());
7954
7955 QCOMPARE(item->rotation(), 45.);
7956 QCOMPARE(item->scale(), scale);
7957 QCOMPARE(item->transformOriginPoint(), origin);
7958
7959 QCOMPARE(QTransform(), item->transform());
7960 QCOMPARE(result2, item->sceneTransform());
7961
7962 //-----------------------------------------------------------------
7963 // calling setTransform() and setPos should change the sceneTransform
7964 item->setTransform(matrix: result);
7965 item->setPos(ax: 100, ay: -150.5);
7966
7967 QCOMPARE(item->rotation(), 45.);
7968 QCOMPARE(item->scale(), scale);
7969 QCOMPARE(item->transformOriginPoint(), origin);
7970 QCOMPARE(result, item->transform());
7971
7972 QTransform result3(result);
7973
7974 result3.translate(dx: origin.x(), dy: origin.y());
7975 result3.rotate(a: 45);
7976 result3.scale(sx: scale, sy: scale);
7977 result3.translate(dx: -origin.x(), dy: -origin.y());
7978
7979 result3 *= QTransform::fromTranslate(dx: 100, dy: -150.5); //the pos;
7980
7981 QCOMPARE(result3, item->sceneTransform());
7982
7983 //-----------------------------------------------------
7984 // setting the propertiees should be the same as setting a transform
7985 {//with center origin on the matrix
7986 QGraphicsRectItem *item1 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119));
7987 scene.addItem(item: item1);
7988 QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119));
7989 scene.addItem(item: item2);
7990
7991 item1->setPos(ax: 12.3, ay: -5);
7992 item2->setPos(ax: 12.3, ay: -5);
7993 item1->setRotation(rotation);
7994 item1->setScale(scale);
7995 item1->setTransformOriginPoint(origin);
7996
7997 item2->setTransform(matrix: result);
7998
7999 QCOMPARE_TRANSFORM(item1->sceneTransform(), item2->sceneTransform());
8000
8001 QCOMPARE_TRANSFORM(item1->itemTransform(item2), QTransform());
8002 QCOMPARE_TRANSFORM(item2->itemTransform(item1), QTransform());
8003 }
8004}
8005
8006class MyStyleOptionTester : public QGraphicsRectItem
8007{
8008public:
8009 using QGraphicsRectItem::QGraphicsRectItem;
8010
8011 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override
8012 {
8013 ++repaints;
8014 if (startTrack) {
8015 //Doesn't use the extended style option so the exposed rect is the boundingRect
8016 if (!(flags() & QGraphicsItem::ItemUsesExtendedStyleOption)) {
8017 QCOMPARE(option->exposedRect, boundingRect());
8018#if QT_DEPRECATED_SINCE(5, 13)
8019QT_WARNING_PUSH
8020QT_WARNING_DISABLE_DEPRECATED
8021 QCOMPARE(option->matrix, QMatrix());
8022QT_WARNING_POP
8023#endif
8024 } else {
8025 QVERIFY(option->exposedRect != QRect());
8026 QVERIFY(option->exposedRect != boundingRect());
8027#if QT_DEPRECATED_SINCE(5, 13)
8028QT_WARNING_PUSH
8029QT_WARNING_DISABLE_DEPRECATED
8030 QCOMPARE(option->matrix, sceneTransform().toAffine());
8031QT_WARNING_POP
8032#endif
8033 }
8034 }
8035 QGraphicsRectItem::paint(painter, option, widget);
8036 }
8037 bool startTrack = false;
8038 int repaints = 0;
8039};
8040
8041void tst_QGraphicsItem::itemUsesExtendedStyleOption()
8042{
8043 QGraphicsScene scene(0, 0, 300, 300);
8044 QGraphicsPixmapItem item;
8045 item.setFlag(flag: QGraphicsItem::ItemUsesExtendedStyleOption, enabled: true);
8046 QCOMPARE(item.flags(), QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemUsesExtendedStyleOption));
8047 item.setFlag(flag: QGraphicsItem::ItemUsesExtendedStyleOption, enabled: false);
8048 QCOMPARE(item.flags(), 0);
8049
8050 //We now test the content of the style option
8051 MyStyleOptionTester *rect = new MyStyleOptionTester(QRect(0, 0, 100, 100));
8052 scene.addItem(item: rect);
8053 rect->setPos(ax: 200, ay: 200);
8054 QWidget topLevel;
8055 topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8056 topLevel.resize(w: 200, h: 200);
8057 QGraphicsView view(&scene, &topLevel);
8058 topLevel.setWindowFlags(Qt::X11BypassWindowManagerHint);
8059 rect->startTrack = false;
8060 topLevel.show();
8061 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
8062 QCoreApplication::processEvents(); // Process all queued paint events
8063 QTRY_VERIFY(rect->repaints > 0);
8064 rect->repaints = 0;
8065 rect->startTrack = true;
8066 rect->update(ax: 10, ay: 10, width: 10, height: 10);
8067 QTRY_COMPARE(rect->repaints, 1);
8068 rect->repaints = 0;
8069 rect->startTrack = false;
8070 rect->setFlag(flag: QGraphicsItem::ItemUsesExtendedStyleOption, enabled: true);
8071 QVERIFY((rect->flags() & QGraphicsItem::ItemUsesExtendedStyleOption));
8072 QTRY_COMPARE(rect->repaints, 1);
8073 rect->repaints = 0;
8074 rect->startTrack = true;
8075 rect->update(ax: 10, ay: 10, width: 10, height: 10);
8076 QTest::qWait(ms: 60);
8077 // MyStyleOptionTester does not receive a paint event. Why not?
8078}
8079
8080void tst_QGraphicsItem::itemSendsGeometryChanges()
8081{
8082 ItemChangeTester item;
8083 item.setFlags({ });
8084 item.clear();
8085
8086 QTransform x = QTransform().rotate(a: 45);
8087 QPointF pos(10, 10);
8088 qreal o(0.5);
8089 qreal r(10.0);
8090 qreal s(1.5);
8091 QPointF origin(1.0, 1.0);
8092 item.setTransform(matrix: x);
8093 item.setPos(pos);
8094 item.setRotation(r);
8095 item.setScale(s);
8096 item.setTransformOriginPoint(origin);
8097 QCOMPARE(item.transform(), x);
8098 QCOMPARE(item.pos(), pos);
8099 QCOMPARE(item.rotation(), r);
8100 QCOMPARE(item.scale(), s);
8101 QCOMPARE(item.transformOriginPoint(), origin);
8102 QCOMPARE(item.changes.size(), 0);
8103
8104 item.setOpacity(o);
8105 QCOMPARE(item.changes.size(), 2); // opacity
8106
8107 item.setFlag(flag: QGraphicsItem::ItemSendsGeometryChanges);
8108 QCOMPARE(item.changes.size(), 4); // flags
8109 item.setTransform(matrix: QTransform());
8110 item.setPos(QPointF());
8111 QCOMPARE(item.changes.size(), 8); // transform + pos
8112 QCOMPARE(item.transform(), QTransform());
8113 QCOMPARE(item.pos(), QPointF());
8114 QCOMPARE(item.opacity(), o);
8115 item.setRotation(0.0);
8116 item.setScale(1.0);
8117 item.setTransformOriginPoint(ax: 0.0, ay: 0.0);
8118 QCOMPARE(item.changes.size(), 14); // rotation + scale + origin
8119 QCOMPARE(item.rotation(), qreal(0.0));
8120 QCOMPARE(item.scale(), qreal(1.0));
8121 QCOMPARE(item.transformOriginPoint(), QPointF(0.0, 0.0));
8122
8123 const QVector<QGraphicsItem::GraphicsItemChange> expected{QGraphicsItem::ItemOpacityChange,
8124 QGraphicsItem::ItemOpacityHasChanged,
8125 QGraphicsItem::ItemFlagsChange,
8126 QGraphicsItem::ItemFlagsHaveChanged,
8127 QGraphicsItem::ItemTransformChange,
8128 QGraphicsItem::ItemTransformHasChanged,
8129 QGraphicsItem::ItemPositionChange,
8130 QGraphicsItem::ItemPositionHasChanged,
8131 QGraphicsItem::ItemRotationChange,
8132 QGraphicsItem::ItemRotationHasChanged,
8133 QGraphicsItem::ItemScaleChange,
8134 QGraphicsItem::ItemScaleHasChanged,
8135 QGraphicsItem::ItemTransformOriginPointChange,
8136 QGraphicsItem::ItemTransformOriginPointHasChanged};
8137 QCOMPARE(item.changes, expected);
8138}
8139
8140// Make sure we update moved items correctly.
8141void tst_QGraphicsItem::moveItem()
8142{
8143 QGraphicsScene scene;
8144 scene.setSceneRect(x: -50, y: -50, w: 200, h: 200);
8145
8146 MyGraphicsView view(&scene);
8147 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8148 view.show();
8149 QVERIFY(QTest::qWaitForWindowExposed(&view));
8150 QCoreApplication::processEvents(); // Process all queued paint events
8151
8152 EventTester *parent = new EventTester;
8153 EventTester *child = new EventTester(parent);
8154 EventTester *grandChild = new EventTester(child);
8155
8156#define RESET_COUNTERS \
8157 parent->repaints = 0; \
8158 child->repaints = 0; \
8159 grandChild->repaints = 0; \
8160 view.reset();
8161
8162 RESET_COUNTERS
8163 scene.addItem(item: parent);
8164 QTRY_COMPARE(view.repaints, 1);
8165
8166 RESET_COUNTERS
8167
8168 // Item's boundingRect: (-10, -10, 20, 20).
8169 QRect parentDeviceBoundingRect = parent->deviceTransform(viewportTransform: view.viewportTransform())
8170 .mapRect(parent->boundingRect()).toAlignedRect()
8171 .adjusted(xp1: -2, yp1: -2, xp2: 2, yp2: 2); // Adjusted for antialiasing.
8172
8173 parent->setPos(ax: 20, ay: 20);
8174 QCoreApplication::processEvents();
8175 QCOMPARE(parent->repaints, 1);
8176 QCOMPARE(view.repaints, 1);
8177 QRegion expectedParentRegion = parentDeviceBoundingRect; // old position
8178 parentDeviceBoundingRect.translate(dx: 20, dy: 20);
8179 expectedParentRegion += parentDeviceBoundingRect; // new position
8180 COMPARE_REGIONS(view.paintedRegion, expectedParentRegion);
8181
8182 RESET_COUNTERS
8183
8184 child->setPos(ax: 20, ay: 20);
8185 QCoreApplication::processEvents();
8186 QCOMPARE(parent->repaints, 1);
8187 QCOMPARE(child->repaints, 1);
8188 QCOMPARE(view.repaints, 1);
8189 const QRegion expectedChildRegion = expectedParentRegion.translated(dx: 20, dy: 20);
8190 COMPARE_REGIONS(view.paintedRegion, expectedChildRegion);
8191
8192 RESET_COUNTERS
8193
8194 grandChild->setPos(ax: 20, ay: 20);
8195 QCoreApplication::processEvents();
8196 QCOMPARE(parent->repaints, 1);
8197 QCOMPARE(child->repaints, 1);
8198 QCOMPARE(grandChild->repaints, 1);
8199 QCOMPARE(view.repaints, 1);
8200 const QRegion expectedGrandChildRegion = expectedParentRegion.translated(dx: 40, dy: 40);
8201 COMPARE_REGIONS(view.paintedRegion, expectedGrandChildRegion);
8202
8203 RESET_COUNTERS
8204
8205 parent->setTransform(matrix: QTransform::fromTranslate(dx: 20, dy: 20), combine: true);
8206 QCoreApplication::processEvents();
8207 QCOMPARE(parent->repaints, 1);
8208 QCOMPARE(child->repaints, 1);
8209 QCOMPARE(grandChild->repaints, 1);
8210 QCOMPARE(view.repaints, 1);
8211 expectedParentRegion.translate(dx: 20, dy: 20);
8212 expectedParentRegion += expectedChildRegion.translated(dx: 20, dy: 20);
8213 expectedParentRegion += expectedGrandChildRegion.translated(dx: 20, dy: 20);
8214 COMPARE_REGIONS(view.paintedRegion, expectedParentRegion);
8215}
8216
8217void tst_QGraphicsItem::moveLineItem()
8218{
8219 QGraphicsScene scene;
8220 scene.setSceneRect(x: 0, y: 0, w: 200, h: 200);
8221 QGraphicsLineItem *item = new QGraphicsLineItem(0, 0, 100, 0);
8222 item->setPos(ax: 50, ay: 50);
8223 scene.addItem(item);
8224
8225 MyGraphicsView view(&scene);
8226 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8227 view.show();
8228 QVERIFY(QTest::qWaitForWindowExposed(&view));
8229 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
8230 QVERIFY(QTest::qWaitForWindowActive(&view));
8231 QCoreApplication::processEvents(); // Process all queued paint events
8232 view.reset();
8233
8234 QRectF brect = item->boundingRect();
8235 // Do same adjustments as in qgraphicsscene.cpp
8236 if (qFuzzyIsNull(d: brect.width()))
8237 brect.adjust(xp1: qreal(-0.00001), yp1: 0, xp2: qreal(0.00001), yp2: 0);
8238 if (qFuzzyIsNull(d: brect.height()))
8239 brect.adjust(xp1: 0, yp1: qreal(-0.00001), xp2: 0, yp2: qreal(0.00001));
8240 const QRect itemDeviceBoundingRect = item->deviceTransform(viewportTransform: view.viewportTransform())
8241 .mapRect(brect).toAlignedRect();
8242 QRegion expectedRegion = itemDeviceBoundingRect.adjusted(xp1: -2, yp1: -2, xp2: 2, yp2: 2); // antialiasing
8243
8244 // Make sure the calculated region is correct.
8245 item->update();
8246#ifdef Q_OS_WINRT
8247 QEXPECT_FAIL("", "Fails on WinRT. Figure out why - QTBUG-68297", Abort);
8248#endif
8249 QTRY_COMPARE(view.paintedRegion, expectedRegion);
8250 view.reset();
8251
8252 // Old position: (50, 50)
8253 item->setPos(ax: 50, ay: 100);
8254 expectedRegion += expectedRegion.translated(dx: 0, dy: 50);
8255 QTRY_COMPARE(view.paintedRegion, expectedRegion);
8256}
8257
8258void tst_QGraphicsItem::sorting_data()
8259{
8260 QTest::addColumn<int>(name: "index");
8261
8262 QTest::newRow(dataTag: "NoIndex") << int(QGraphicsScene::NoIndex);
8263 QTest::newRow(dataTag: "BspTreeIndex") << int(QGraphicsScene::BspTreeIndex);
8264}
8265
8266void tst_QGraphicsItem::sorting()
8267{
8268 if (qGuiApp->styleHints()->showIsFullScreen())
8269 QSKIP("Skipped because Platform is auto maximizing");
8270
8271 _paintedItems.clear();
8272
8273 QGraphicsScene scene;
8274 QGraphicsItem *grid[100][100];
8275 for (int x = 0; x < 100; ++x) {
8276 const QString prefix = QString::number(x) + QLatin1Char('x');
8277 for (int y = 0; y < 100; ++y) {
8278 PainterItem *item = new PainterItem;
8279 item->setPos(ax: x * 25, ay: y * 25);
8280 item->setData(key: 0, value: prefix + QString::number(y));
8281 grid[x][y] = item;
8282 scene.addItem(item);
8283 }
8284 }
8285
8286 PainterItem *item1 = new PainterItem;
8287 PainterItem *item2 = new PainterItem;
8288 item1->setData(key: 0, value: "item1");
8289 item2->setData(key: 0, value: "item2");
8290 scene.addItem(item: item1);
8291 scene.addItem(item: item2);
8292
8293 QGraphicsView view(&scene);
8294 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8295 // Use Qt::Tool as fully decorated windows have a minimum width of 160 on Windows.
8296 view.setWindowFlags(view.windowFlags() | Qt::Tool);
8297 view.setResizeAnchor(QGraphicsView::NoAnchor);
8298 view.setTransformationAnchor(QGraphicsView::NoAnchor);
8299 view.resize(w: 120, h: 100);
8300 view.setFrameStyle(0);
8301 view.show();
8302 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) {
8303 qApp->setActiveWindow(&view);
8304 QVERIFY(QTest::qWaitForWindowActive(&view));
8305 }
8306 QVERIFY(QTest::qWaitForWindowExposed(&view));
8307 QTRY_VERIFY(_paintedItems.count() > 0);
8308
8309 _paintedItems.clear();
8310
8311 view.viewport()->update();
8312 const GraphicsItems expected{grid[0][0], grid[0][1], grid[0][2], grid[0][3],
8313 grid[1][0], grid[1][1], grid[1][2], grid[1][3],
8314 grid[2][0], grid[2][1], grid[2][2], grid[2][3],
8315 grid[3][0], grid[3][1], grid[3][2], grid[3][3],
8316 grid[4][0], grid[4][1], grid[4][2], grid[4][3],
8317 item1, item2};
8318 QTRY_COMPARE(_paintedItems, expected);
8319}
8320
8321void tst_QGraphicsItem::itemHasNoContents()
8322{
8323 PainterItem *item1 = new PainterItem;
8324 PainterItem *item2 = new PainterItem;
8325 item2->setParentItem(item1);
8326 item2->setPos(ax: 50, ay: 50);
8327 item1->setFlag(flag: QGraphicsItem::ItemHasNoContents);
8328 item1->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
8329
8330 QGraphicsScene scene;
8331 scene.addItem(item: item1);
8332
8333 QGraphicsView view(&scene);
8334 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8335 view.show();
8336 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) {
8337 qApp->setActiveWindow(&view);
8338 QVERIFY(QTest::qWaitForWindowActive(&view));
8339 }
8340 QVERIFY(QTest::qWaitForWindowExposed(&view));
8341 QTRY_VERIFY(!_paintedItems.isEmpty());
8342
8343 _paintedItems.clear();
8344
8345 view.viewport()->update();
8346 QTRY_COMPARE(_paintedItems, GraphicsItems{item2});
8347}
8348
8349void tst_QGraphicsItem::hitTestUntransformableItem()
8350{
8351 QGraphicsScene scene;
8352 scene.setSceneRect(x: -100, y: -100, w: 200, h: 200);
8353
8354 QGraphicsView view(&scene);
8355 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8356 view.show();
8357 QVERIFY(QTest::qWaitForWindowExposed(&view));
8358
8359 // Confuse the BSP with dummy items.
8360 QGraphicsRectItem *dummy = new QGraphicsRectItem(0, 0, 20, 20);
8361 dummy->setPos(ax: -100, ay: -100);
8362 scene.addItem(item: dummy);
8363 for (int i = 0; i < 100; ++i) {
8364 QGraphicsItem *parent = dummy;
8365 dummy = new QGraphicsRectItem(0, 0, 20, 20);
8366 dummy->setPos(ax: -100 + i, ay: -100 + i);
8367 dummy->setParentItem(parent);
8368 }
8369
8370 QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, 20, 20);
8371 item1->setPos(ax: -200, ay: -200);
8372
8373 QGraphicsRectItem *item2 = new QGraphicsRectItem(0, 0, 20, 20);
8374 item2->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
8375 item2->setParentItem(item1);
8376 item2->setPos(ax: 200, ay: 200);
8377
8378 QGraphicsRectItem *item3 = new QGraphicsRectItem(0, 0, 20, 20);
8379 item3->setParentItem(item2);
8380 item3->setPos(ax: 80, ay: 80);
8381
8382 scene.addItem(item: item1);
8383
8384 QList<QGraphicsItem *> items = scene.items(pos: QPointF(80, 80));
8385 QCOMPARE(items.size(), 1);
8386 QCOMPARE(items.at(0), item3);
8387
8388 scene.setItemIndexMethod(QGraphicsScene::NoIndex);
8389 QTest::qWait(ms: 100);
8390
8391 items = scene.items(pos: QPointF(80, 80));
8392 QCOMPARE(items.size(), 1);
8393 QCOMPARE(items.at(0), static_cast<QGraphicsItem*>(item3));
8394}
8395
8396void tst_QGraphicsItem::hitTestGraphicsEffectItem()
8397{
8398 QGraphicsScene scene;
8399 scene.setSceneRect(x: -100, y: -100, w: 200, h: 200);
8400
8401 QWidget toplevel;
8402 toplevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8403
8404 QGraphicsView view(&scene, &toplevel);
8405 toplevel.resize(w: 300, h: 300);
8406 toplevel.show();
8407 QVERIFY(QTest::qWaitForWindowExposed(&toplevel));
8408 QCoreApplication::processEvents(); // Process all queued paint events
8409
8410 // Confuse the BSP with dummy items.
8411 QGraphicsRectItem *dummy = new QGraphicsRectItem(0, 0, 20, 20);
8412 dummy->setPos(ax: -100, ay: -100);
8413 scene.addItem(item: dummy);
8414 for (int i = 0; i < 100; ++i) {
8415 QGraphicsItem *parent = dummy;
8416 dummy = new QGraphicsRectItem(0, 0, 20, 20);
8417 dummy->setPos(ax: -100 + i, ay: -100 + i);
8418 dummy->setParentItem(parent);
8419 }
8420
8421 const QRectF itemBoundingRect(0, 0, 20, 20);
8422 EventTester *item1 = new EventTester;
8423 item1->br = itemBoundingRect;
8424 item1->setPos(ax: -200, ay: -200);
8425 item1->brush = Qt::red;
8426
8427 EventTester *item2 = new EventTester;
8428 item2->br = itemBoundingRect;
8429 item2->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
8430 item2->setParentItem(item1);
8431 item2->setPos(ax: 200, ay: 200);
8432 item2->brush = Qt::green;
8433
8434 EventTester *item3 = new EventTester;
8435 item3->br = itemBoundingRect;
8436 item3->setParentItem(item2);
8437 item3->setPos(ax: 80, ay: 80);
8438 item3->brush = Qt::blue;
8439
8440 scene.addItem(item: item1);
8441 QTRY_COMPARE(item2->repaints, 1);
8442 QCOMPARE(item3->repaints, 1);
8443
8444 item1->repaints = 0;
8445 item2->repaints = 0;
8446 item3->repaints = 0;
8447
8448 // Apply shadow effect to the entire sub-tree.
8449 QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;
8450 shadow->setOffset(dx: -20, dy: -20);
8451 item1->setGraphicsEffect(shadow);
8452
8453 // Make sure all visible items are repainted.
8454 QTRY_COMPARE(item1->repaints, 1);
8455 QCOMPARE(item2->repaints, 1);
8456 QCOMPARE(item3->repaints, 1);
8457
8458 // Make sure an item doesn't respond to a click on its shadow.
8459 QList<QGraphicsItem *> items = scene.items(pos: QPointF(75, 75));
8460 QVERIFY(items.isEmpty());
8461 items = scene.items(pos: QPointF(80, 80));
8462 QCOMPARE(items.size(), 1);
8463 QCOMPARE(items.at(0), item3);
8464
8465 scene.setItemIndexMethod(QGraphicsScene::NoIndex);
8466 QTest::qWait(ms: 100);
8467
8468 items = scene.items(pos: QPointF(75, 75));
8469 QVERIFY(items.isEmpty());
8470 items = scene.items(pos: QPointF(80, 80));
8471 QCOMPARE(items.size(), 1);
8472 QCOMPARE(items.at(0), static_cast<QGraphicsItem *>(item3));
8473}
8474
8475void tst_QGraphicsItem::focusProxy()
8476{
8477 QGraphicsScene scene;
8478 QEvent activate(QEvent::WindowActivate);
8479 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
8480
8481 QGraphicsItem *item = scene.addRect(x: 0, y: 0, w: 10, h: 10);
8482 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8483 QVERIFY(!item->focusProxy());
8484
8485 QGraphicsItem *item2 = scene.addRect(x: 0, y: 0, w: 10, h: 10);
8486 item2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8487 item->setFocusProxy(item2);
8488 QCOMPARE(item->focusProxy(), item2);
8489
8490 item->setFocus();
8491 QVERIFY(item->hasFocus());
8492 QVERIFY(item2->hasFocus());
8493
8494 // Try to make a focus chain loop
8495 QString err;
8496 QTextStream stream(&err);
8497 stream << "QGraphicsItem::setFocusProxy: "
8498 << static_cast<const void*>(item) << " is already in the focus proxy chain" << Qt::flush;
8499 QTest::ignoreMessage(type: QtWarningMsg, message: err.toLatin1().constData());
8500 item2->setFocusProxy(item); // fails
8501 QCOMPARE(item->focusProxy(), item2);
8502 QCOMPARE(item2->focusProxy(), nullptr);
8503
8504 // Try to assign self as focus proxy
8505 QTest::ignoreMessage(type: QtWarningMsg, message: "QGraphicsItem::setFocusProxy: cannot assign self as focus proxy");
8506 item->setFocusProxy(item); // fails
8507 QCOMPARE(item->focusProxy(), item2);
8508 QCOMPARE(item2->focusProxy(), nullptr);
8509
8510 // Reset the focus proxy
8511 item->setFocusProxy(nullptr);
8512 QCOMPARE(item->focusProxy(), nullptr);
8513 QVERIFY(!item->hasFocus());
8514 QVERIFY(item2->hasFocus());
8515
8516 // Test deletion
8517 item->setFocusProxy(item2);
8518 QCOMPARE(item->focusProxy(), item2);
8519 delete item2;
8520 QCOMPARE(item->focusProxy(), nullptr);
8521
8522 // Test event delivery
8523 item2 = scene.addRect(x: 0, y: 0, w: 10, h: 10);
8524 item2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8525 item->setFocusProxy(item2);
8526 item->clearFocus();
8527
8528 EventSpy focusInSpy(&scene, item, QEvent::FocusIn);
8529 EventSpy focusOutSpy(&scene, item, QEvent::FocusOut);
8530 EventSpy focusInSpy2(&scene, item2, QEvent::FocusIn);
8531 EventSpy focusOutSpy2(&scene, item2, QEvent::FocusOut);
8532 QCOMPARE(focusInSpy.count(), 0);
8533 QCOMPARE(focusOutSpy.count(), 0);
8534 QCOMPARE(focusInSpy2.count(), 0);
8535 QCOMPARE(focusOutSpy2.count(), 0);
8536
8537 item->setFocus();
8538 QCOMPARE(focusInSpy.count(), 0);
8539 QCOMPARE(focusInSpy2.count(), 1);
8540 item->clearFocus();
8541 QCOMPARE(focusOutSpy.count(), 0);
8542 QCOMPARE(focusOutSpy2.count(), 1);
8543
8544 // Test two items proxying one item.
8545 QGraphicsItem *item3 = scene.addRect(x: 0, y: 0, w: 10, h: 10);
8546 item3->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8547 item3->setFocusProxy(item2); // item and item3 use item2 as proxy
8548
8549 QCOMPARE(item->focusProxy(), item2);
8550 QCOMPARE(item2->focusProxy(), nullptr);
8551 QCOMPARE(item3->focusProxy(), item2);
8552 delete item2;
8553 QCOMPARE(item->focusProxy(), nullptr);
8554 QCOMPARE(item3->focusProxy(), nullptr);
8555}
8556
8557void tst_QGraphicsItem::subFocus()
8558{
8559 // Construct a text item that's not part of a scene (yet)
8560 // and has no parent. Setting focus on it will not make
8561 // the item gain input focus; that requires a scene. But
8562 // it does set subfocus, indicating that the item wishes
8563 // to gain focus later.
8564 QGraphicsTextItem *text = new QGraphicsTextItem("Hello");
8565 text->setTextInteractionFlags(Qt::TextEditorInteraction);
8566 QVERIFY(!text->hasFocus());
8567 text->setFocus();
8568 QVERIFY(!text->hasFocus());
8569 QCOMPARE(text->focusItem(), text);
8570
8571 // Add a sibling.
8572 QGraphicsTextItem *text2 = new QGraphicsTextItem("Hi");
8573 text2->setTextInteractionFlags(Qt::TextEditorInteraction);
8574 text2->setPos(ax: 30, ay: 30);
8575
8576 // Add both items to a scene and check that it's text that
8577 // got input focus.
8578 QGraphicsScene scene;
8579 QEvent activate(QEvent::WindowActivate);
8580 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
8581
8582 scene.addItem(item: text);
8583 scene.addItem(item: text2);
8584 QVERIFY(text->hasFocus());
8585
8586 text->setData(key: 0, value: "text");
8587 text2->setData(key: 0, value: "text2");
8588
8589 // Remove text2 and set subfocus on it. Then readd. Reparent it onto the
8590 // other item and see that it gains input focus.
8591 scene.removeItem(item: text2);
8592 text2->setFocus();
8593 scene.addItem(item: text2);
8594 QCOMPARE(text2->focusItem(), text2);
8595 text2->setParentItem(text);
8596 QCOMPARE(text->focusItem(), text2);
8597 QCOMPARE(text2->focusItem(), text2);
8598 QVERIFY(!text->hasFocus());
8599 QVERIFY(text2->hasFocus());
8600
8601 // Remove both items from the scene, restore subfocus and
8602 // readd them. Now the subfocus should kick in and give
8603 // text2 focus.
8604 scene.removeItem(item: text);
8605 QCOMPARE(text->focusItem(), nullptr);
8606 QCOMPARE(text2->focusItem(), nullptr);
8607 text2->setFocus();
8608 QCOMPARE(text->focusItem(), text2);
8609 QCOMPARE(text2->focusItem(), text2);
8610 scene.addItem(item: text);
8611
8612 // Hiding and showing text should pass focus to text2.
8613 QCOMPARE(text->focusItem(), text2);
8614 QVERIFY(text2->hasFocus());
8615
8616 // Subfocus should repropagate to root when reparenting.
8617 QGraphicsRectItem *rect = new QGraphicsRectItem;
8618 QGraphicsRectItem *rect2 = new QGraphicsRectItem(rect);
8619 QGraphicsRectItem *rect3 = new QGraphicsRectItem(rect2);
8620 rect3->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8621
8622 text->setData(key: 0, value: "text");
8623 text2->setData(key: 0, value: "text2");
8624 rect->setData(key: 0, value: "rect");
8625 rect2->setData(key: 0, value: "rect2");
8626 rect3->setData(key: 0, value: "rect3");
8627
8628 rect3->setFocus();
8629 QVERIFY(!rect3->hasFocus());
8630 QCOMPARE(rect->focusItem(), rect3);
8631 QCOMPARE(rect2->focusItem(), rect3);
8632 QCOMPARE(rect3->focusItem(), rect3);
8633 rect->setParentItem(text2);
8634 QCOMPARE(text->focusItem(), rect3);
8635 QCOMPARE(text2->focusItem(), rect3);
8636 QCOMPARE(rect->focusItem(), rect3);
8637 QCOMPARE(rect2->focusItem(), rect3);
8638 QCOMPARE(rect3->focusItem(), rect3);
8639 QVERIFY(!rect->hasFocus());
8640 QVERIFY(!rect2->hasFocus());
8641 QVERIFY(rect3->hasFocus());
8642
8643 delete rect2;
8644 QCOMPARE(text->focusItem(), nullptr);
8645 QCOMPARE(text2->focusItem(), nullptr);
8646 QCOMPARE(rect->focusItem(), nullptr);
8647}
8648
8649void tst_QGraphicsItem::focusProxyDeletion()
8650{
8651 QGraphicsRectItem *rect = new QGraphicsRectItem;
8652 QGraphicsRectItem *rect2 = new QGraphicsRectItem;
8653 rect->setFocusProxy(rect2);
8654 QCOMPARE(rect->focusProxy(), rect2);
8655
8656 delete rect2;
8657 QCOMPARE(rect->focusProxy(), nullptr);
8658
8659 rect2 = new QGraphicsRectItem;
8660 rect->setFocusProxy(rect2);
8661 QGraphicsItem **danglingFocusProxyRef = &rect->d_ptr->focusProxy;
8662 delete rect; // don't crash
8663 QVERIFY(!rect2->d_ptr->focusProxyRefs.contains(danglingFocusProxyRef));
8664
8665 rect = new QGraphicsRectItem;
8666 rect->setFocusProxy(rect2);
8667 QGraphicsScene *scene = new QGraphicsScene;
8668 scene->addItem(item: rect);
8669 scene->addItem(item: rect2);
8670 delete rect2;
8671 QCOMPARE(rect->focusProxy(), nullptr);
8672
8673 rect2 = new QGraphicsRectItem;
8674 QTest::ignoreMessage(type: QtWarningMsg, message: "QGraphicsItem::setFocusProxy: focus proxy must be in same scene");
8675 rect->setFocusProxy(rect2);
8676 QCOMPARE(rect->focusProxy(), nullptr);
8677 scene->addItem(item: rect2);
8678 rect->setFocusProxy(rect2);
8679 QCOMPARE(rect->focusProxy(), rect2);
8680 delete rect; // don't crash
8681
8682 rect = new QGraphicsRectItem;
8683 rect2 = new QGraphicsRectItem;
8684 rect->setFocusProxy(rect2);
8685 QCOMPARE(rect->focusProxy(), rect2);
8686 scene->addItem(item: rect);
8687 scene->addItem(item: rect2);
8688 rect->setFocusProxy(rect2);
8689 delete scene; // don't crash
8690}
8691
8692void tst_QGraphicsItem::negativeZStacksBehindParent()
8693{
8694 QGraphicsRectItem rect;
8695 QCOMPARE(rect.zValue(), qreal(0.0));
8696 QVERIFY(!(rect.flags() & QGraphicsItem::ItemNegativeZStacksBehindParent));
8697 QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent));
8698 rect.setZValue(-1);
8699 QCOMPARE(rect.zValue(), qreal(-1.0));
8700 QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent));
8701 rect.setZValue(0);
8702 rect.setFlag(flag: QGraphicsItem::ItemNegativeZStacksBehindParent);
8703 QVERIFY(rect.flags() & QGraphicsItem::ItemNegativeZStacksBehindParent);
8704 QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent));
8705 rect.setZValue(-1);
8706 QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent);
8707 rect.setZValue(0);
8708 QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent));
8709 rect.setFlag(flag: QGraphicsItem::ItemNegativeZStacksBehindParent, enabled: false);
8710 rect.setZValue(-1);
8711 rect.setFlag(flag: QGraphicsItem::ItemNegativeZStacksBehindParent, enabled: true);
8712 QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent);
8713 rect.setFlag(flag: QGraphicsItem::ItemNegativeZStacksBehindParent, enabled: false);
8714 QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent);
8715}
8716
8717void tst_QGraphicsItem::setGraphicsEffect()
8718{
8719 // Check that we don't have any effect by default.
8720 QGraphicsItem *item = new QGraphicsRectItem(0, 0, 10, 10);
8721 QVERIFY(!item->graphicsEffect());
8722
8723 // SetGet check.
8724 QPointer<QGraphicsEffect> blurEffect = new QGraphicsBlurEffect;
8725 item->setGraphicsEffect(blurEffect);
8726 QCOMPARE(item->graphicsEffect(), static_cast<QGraphicsEffect *>(blurEffect));
8727
8728 // Ensure the existing effect is deleted when setting a new one.
8729 QPointer<QGraphicsEffect> shadowEffect = new QGraphicsDropShadowEffect;
8730 item->setGraphicsEffect(shadowEffect);
8731 QVERIFY(!blurEffect);
8732 QCOMPARE(item->graphicsEffect(), static_cast<QGraphicsEffect *>(shadowEffect));
8733 blurEffect = new QGraphicsBlurEffect;
8734
8735 // Ensure the effect is uninstalled when setting it on a new target.
8736 QGraphicsItem *anotherItem = new QGraphicsRectItem(0, 0, 10, 10);
8737 anotherItem->setGraphicsEffect(blurEffect);
8738 item->setGraphicsEffect(blurEffect);
8739 QVERIFY(!anotherItem->graphicsEffect());
8740 QVERIFY(!shadowEffect);
8741
8742 // Ensure the existing effect is deleted when deleting the item.
8743 delete item;
8744 QVERIFY(!blurEffect);
8745 delete anotherItem;
8746
8747 // Ensure the effect is uninstalled when deleting it
8748 item = new QGraphicsRectItem(0, 0, 10, 10);
8749 blurEffect = new QGraphicsBlurEffect;
8750 item->setGraphicsEffect(blurEffect);
8751 delete blurEffect;
8752 QVERIFY(!item->graphicsEffect());
8753
8754 // Ensure the existing effect is uninstalled and deleted when setting a null effect
8755 blurEffect = new QGraphicsBlurEffect;
8756 item->setGraphicsEffect(blurEffect);
8757 item->setGraphicsEffect(nullptr);
8758 QVERIFY(!item->graphicsEffect());
8759 QVERIFY(!blurEffect);
8760
8761 delete item;
8762}
8763
8764void tst_QGraphicsItem::panel()
8765{
8766 QGraphicsScene scene;
8767
8768 QGraphicsRectItem *panel1 = new QGraphicsRectItem;
8769 QGraphicsRectItem *panel2 = new QGraphicsRectItem;
8770 QGraphicsRectItem *panel3 = new QGraphicsRectItem;
8771 QGraphicsRectItem *panel4 = new QGraphicsRectItem;
8772 QGraphicsRectItem *notPanel1 = new QGraphicsRectItem;
8773 QGraphicsRectItem *notPanel2 = new QGraphicsRectItem;
8774 panel1->setFlag(flag: QGraphicsItem::ItemIsPanel);
8775 panel2->setFlag(flag: QGraphicsItem::ItemIsPanel);
8776 panel3->setFlag(flag: QGraphicsItem::ItemIsPanel);
8777 panel4->setFlag(flag: QGraphicsItem::ItemIsPanel);
8778 scene.addItem(item: panel1);
8779 scene.addItem(item: panel2);
8780 scene.addItem(item: panel3);
8781 scene.addItem(item: panel4);
8782 scene.addItem(item: notPanel1);
8783 scene.addItem(item: notPanel2);
8784
8785 EventSpy spy_activate_panel1(&scene, panel1, QEvent::WindowActivate);
8786 EventSpy spy_deactivate_panel1(&scene, panel1, QEvent::WindowDeactivate);
8787 EventSpy spy_activate_panel2(&scene, panel2, QEvent::WindowActivate);
8788 EventSpy spy_deactivate_panel2(&scene, panel2, QEvent::WindowDeactivate);
8789 EventSpy spy_activate_panel3(&scene, panel3, QEvent::WindowActivate);
8790 EventSpy spy_deactivate_panel3(&scene, panel3, QEvent::WindowDeactivate);
8791 EventSpy spy_activate_panel4(&scene, panel4, QEvent::WindowActivate);
8792 EventSpy spy_deactivate_panel4(&scene, panel4, QEvent::WindowDeactivate);
8793 EventSpy spy_activate_notPanel1(&scene, notPanel1, QEvent::WindowActivate);
8794 EventSpy spy_deactivate_notPanel1(&scene, notPanel1, QEvent::WindowDeactivate);
8795 EventSpy spy_activate_notPanel2(&scene, notPanel1, QEvent::WindowActivate);
8796 EventSpy spy_deactivate_notPanel2(&scene, notPanel1, QEvent::WindowDeactivate);
8797
8798 QCOMPARE(spy_activate_panel1.count(), 0);
8799 QCOMPARE(spy_deactivate_panel1.count(), 0);
8800 QCOMPARE(spy_activate_panel2.count(), 0);
8801 QCOMPARE(spy_deactivate_panel2.count(), 0);
8802 QCOMPARE(spy_activate_panel3.count(), 0);
8803 QCOMPARE(spy_deactivate_panel3.count(), 0);
8804 QCOMPARE(spy_activate_panel4.count(), 0);
8805 QCOMPARE(spy_deactivate_panel4.count(), 0);
8806 QCOMPARE(spy_activate_notPanel1.count(), 0);
8807 QCOMPARE(spy_deactivate_notPanel1.count(), 0);
8808 QCOMPARE(spy_activate_notPanel2.count(), 0);
8809 QCOMPARE(spy_deactivate_notPanel2.count(), 0);
8810
8811 QVERIFY(!scene.activePanel());
8812 QVERIFY(!scene.isActive());
8813
8814 QEvent activate(QEvent::WindowActivate);
8815 QEvent deactivate(QEvent::WindowDeactivate);
8816
8817 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
8818
8819 // No previous activation, so the scene is active.
8820 QVERIFY(scene.isActive());
8821 QCOMPARE(scene.activePanel(), panel1);
8822 QVERIFY(panel1->isActive());
8823 QVERIFY(!panel2->isActive());
8824 QVERIFY(!panel3->isActive());
8825 QVERIFY(!panel4->isActive());
8826 QVERIFY(!notPanel1->isActive());
8827 QVERIFY(!notPanel2->isActive());
8828 QCOMPARE(spy_deactivate_notPanel1.count(), 0);
8829 QCOMPARE(spy_deactivate_notPanel2.count(), 0);
8830 QCOMPARE(spy_activate_panel1.count(), 1);
8831 QCOMPARE(spy_activate_panel2.count(), 0);
8832 QCOMPARE(spy_activate_panel3.count(), 0);
8833 QCOMPARE(spy_activate_panel4.count(), 0);
8834
8835 // Switch back to scene.
8836 scene.setActivePanel(nullptr);
8837 QVERIFY(!scene.activePanel());
8838 QVERIFY(!panel1->isActive());
8839 QVERIFY(!panel2->isActive());
8840 QVERIFY(!panel3->isActive());
8841 QVERIFY(!panel4->isActive());
8842 QVERIFY(notPanel1->isActive());
8843 QVERIFY(notPanel2->isActive());
8844 QCOMPARE(spy_activate_notPanel1.count(), 1);
8845 QCOMPARE(spy_activate_notPanel2.count(), 1);
8846
8847 // Deactivate the scene
8848 QCoreApplication::sendEvent(receiver: &scene, event: &deactivate);
8849 QVERIFY(!scene.activePanel());
8850 QVERIFY(!panel1->isActive());
8851 QVERIFY(!panel2->isActive());
8852 QVERIFY(!panel3->isActive());
8853 QVERIFY(!panel4->isActive());
8854 QVERIFY(!notPanel1->isActive());
8855 QVERIFY(!notPanel2->isActive());
8856 QCOMPARE(spy_deactivate_notPanel1.count(), 1);
8857 QCOMPARE(spy_deactivate_notPanel2.count(), 1);
8858
8859 // Reactivate the scene
8860 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
8861 QVERIFY(!scene.activePanel());
8862 QVERIFY(!panel1->isActive());
8863 QVERIFY(!panel2->isActive());
8864 QVERIFY(!panel3->isActive());
8865 QVERIFY(!panel4->isActive());
8866 QVERIFY(notPanel1->isActive());
8867 QVERIFY(notPanel2->isActive());
8868 QCOMPARE(spy_activate_notPanel1.count(), 2);
8869 QCOMPARE(spy_activate_notPanel2.count(), 2);
8870
8871 // Switch to panel1
8872 scene.setActivePanel(panel1);
8873 QVERIFY(panel1->isActive());
8874 QCOMPARE(spy_deactivate_notPanel1.count(), 2);
8875 QCOMPARE(spy_deactivate_notPanel2.count(), 2);
8876 QCOMPARE(spy_activate_panel1.count(), 2);
8877
8878 // Deactivate the scene
8879 QCoreApplication::sendEvent(receiver: &scene, event: &deactivate);
8880 QVERIFY(!panel1->isActive());
8881 QCOMPARE(spy_deactivate_panel1.count(), 2);
8882
8883 // Reactivate the scene
8884 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
8885 QVERIFY(panel1->isActive());
8886 QCOMPARE(spy_activate_panel1.count(), 3);
8887
8888 // Deactivate the scene
8889 QCoreApplication::sendEvent(receiver: &scene, event: &deactivate);
8890 QVERIFY(!panel1->isActive());
8891 QVERIFY(!scene.activePanel());
8892 scene.setActivePanel(nullptr);
8893
8894 // Reactivate the scene
8895 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
8896 QVERIFY(!panel1->isActive());
8897}
8898
8899void tst_QGraphicsItem::panelWithFocusItems()
8900{
8901 for (int i = 0; i < 2; ++i)
8902 {
8903 QGraphicsScene scene;
8904 QEvent activate(QEvent::WindowActivate);
8905 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
8906
8907 bool widget = (i == 1);
8908 auto parentPanel = widget
8909 ? static_cast<QGraphicsItem *>(new QGraphicsWidget)
8910 : static_cast<QGraphicsItem *>(new QGraphicsRectItem);
8911 auto parentPanelFocusItem = widget
8912 ? static_cast<QGraphicsItem *>(new QGraphicsWidget)
8913 : static_cast<QGraphicsItem *>(new QGraphicsRectItem);
8914 auto parentPanelFocusItemSibling = widget
8915 ? static_cast<QGraphicsItem *>(new QGraphicsWidget)
8916 : static_cast<QGraphicsItem *>(new QGraphicsRectItem);
8917 parentPanel->setFlag(flag: QGraphicsItem::ItemIsPanel);
8918 parentPanelFocusItem->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8919 parentPanelFocusItemSibling->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8920 if (widget) {
8921 static_cast<QGraphicsWidget *>(parentPanelFocusItem)->setFocusPolicy(Qt::StrongFocus);
8922 static_cast<QGraphicsWidget *>(parentPanelFocusItemSibling)->setFocusPolicy(Qt::StrongFocus);
8923 }
8924 parentPanelFocusItem->setParentItem(parentPanel);
8925 parentPanelFocusItemSibling->setParentItem(parentPanel);
8926 parentPanelFocusItem->setFocus();
8927 scene.addItem(item: parentPanel);
8928
8929 QVERIFY(parentPanel->isActive());
8930 QVERIFY(parentPanelFocusItem->hasFocus());
8931 QCOMPARE(parentPanel->focusItem(), parentPanelFocusItem);
8932 QCOMPARE(parentPanelFocusItem->focusItem(), parentPanelFocusItem);
8933
8934 auto childPanel = widget
8935 ? static_cast<QGraphicsItem *>(new QGraphicsWidget)
8936 : static_cast<QGraphicsItem *>(new QGraphicsRectItem);
8937 auto childPanelFocusItem = widget
8938 ? static_cast<QGraphicsItem *>(new QGraphicsWidget)
8939 : static_cast<QGraphicsItem *>(new QGraphicsRectItem);
8940 auto grandChildPanelFocusItem = widget
8941 ? static_cast<QGraphicsItem *>(new QGraphicsWidget)
8942 : static_cast<QGraphicsItem *>(new QGraphicsRectItem);
8943 auto grandChildPanelFocusItem2 = widget
8944 ? static_cast<QGraphicsItem *>(new QGraphicsWidget)
8945 : static_cast<QGraphicsItem *>(new QGraphicsRectItem);
8946
8947 childPanel->setFlag(flag: QGraphicsItem::ItemIsPanel);
8948 childPanelFocusItem->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8949 grandChildPanelFocusItem->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8950 grandChildPanelFocusItem2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
8951
8952 if (widget) {
8953 static_cast<QGraphicsWidget *>(childPanelFocusItem)->setFocusPolicy(Qt::StrongFocus);
8954 static_cast<QGraphicsWidget *>(grandChildPanelFocusItem)->setFocusPolicy(Qt::StrongFocus);
8955 static_cast<QGraphicsWidget *>(grandChildPanelFocusItem2)->setFocusPolicy(Qt::StrongFocus);
8956 }
8957 grandChildPanelFocusItem->setParentItem(childPanelFocusItem);
8958 grandChildPanelFocusItem2->setParentItem(childPanelFocusItem);
8959 childPanelFocusItem->setParentItem(childPanel);
8960 grandChildPanelFocusItem->setFocus();
8961
8962 QVERIFY(!grandChildPanelFocusItem->hasFocus());
8963 QCOMPARE(childPanel->focusItem(), grandChildPanelFocusItem);
8964 QCOMPARE(childPanelFocusItem->focusItem(), grandChildPanelFocusItem);
8965 QCOMPARE(grandChildPanelFocusItem->focusItem(), grandChildPanelFocusItem);
8966
8967 childPanel->setParentItem(parentPanel);
8968
8969 QVERIFY(!parentPanel->isActive());
8970 QVERIFY(!parentPanelFocusItem->hasFocus());
8971 QCOMPARE(parentPanel->focusItem(), parentPanelFocusItem);
8972 QCOMPARE(parentPanelFocusItem->focusItem(), parentPanelFocusItem);
8973
8974 QVERIFY(childPanel->isActive());
8975 QVERIFY(!childPanelFocusItem->hasFocus());
8976 QVERIFY(grandChildPanelFocusItem->hasFocus());
8977 QCOMPARE(childPanel->focusItem(), grandChildPanelFocusItem);
8978 QCOMPARE(childPanelFocusItem->focusItem(), grandChildPanelFocusItem);
8979
8980 childPanel->hide();
8981 QCOMPARE(childPanel->focusItem(), grandChildPanelFocusItem);
8982 QVERIFY(!childPanel->focusItem()->hasFocus());
8983 QVERIFY(parentPanel->isActive());
8984 QVERIFY(parentPanelFocusItem->hasFocus());
8985 QCOMPARE(parentPanel->focusItem(), parentPanelFocusItem);
8986 QCOMPARE(parentPanelFocusItem->focusItem(), parentPanelFocusItem);
8987 QCOMPARE(grandChildPanelFocusItem->focusItem(), grandChildPanelFocusItem);
8988
8989 childPanel->show();
8990 QVERIFY(childPanel->isActive());
8991 QVERIFY(grandChildPanelFocusItem->hasFocus());
8992 QCOMPARE(childPanel->focusItem(), grandChildPanelFocusItem);
8993 QCOMPARE(childPanelFocusItem->focusItem(), grandChildPanelFocusItem);
8994 QCOMPARE(grandChildPanelFocusItem->focusItem(), grandChildPanelFocusItem);
8995
8996 childPanel->hide();
8997
8998 QVERIFY(parentPanel->isActive());
8999 QVERIFY(parentPanelFocusItem->hasFocus());
9000 QCOMPARE(parentPanel->focusItem(), parentPanelFocusItem);
9001 QCOMPARE(parentPanelFocusItem->focusItem(), parentPanelFocusItem);
9002 }
9003}
9004
9005void tst_QGraphicsItem::addPanelToActiveScene()
9006{
9007 QGraphicsScene scene;
9008 QVERIFY(!scene.isActive());
9009
9010 QGraphicsRectItem *rect = new QGraphicsRectItem;
9011 scene.addItem(item: rect);
9012 QVERIFY(!rect->isActive());
9013 scene.removeItem(item: rect);
9014
9015 QEvent activate(QEvent::WindowActivate);
9016 QEvent deactivate(QEvent::WindowDeactivate);
9017
9018 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
9019 QVERIFY(scene.isActive());
9020 scene.addItem(item: rect);
9021 QVERIFY(rect->isActive());
9022 scene.removeItem(item: rect);
9023
9024 rect->setFlag(flag: QGraphicsItem::ItemIsPanel);
9025 scene.addItem(item: rect);
9026 QVERIFY(rect->isActive());
9027 QCOMPARE(scene.activePanel(), rect);
9028
9029 QGraphicsRectItem *rect2 = new QGraphicsRectItem;
9030 scene.addItem(item: rect2);
9031 QVERIFY(rect->isActive());
9032 QCOMPARE(scene.activePanel(), rect);
9033}
9034
9035void tst_QGraphicsItem::activate()
9036{
9037 QGraphicsScene scene;
9038 QGraphicsRectItem *rect = scene.addRect(x: -10, y: -10, w: 20, h: 20);
9039 QVERIFY(!rect->isActive());
9040
9041 QEvent activate(QEvent::WindowActivate);
9042 QEvent deactivate(QEvent::WindowDeactivate);
9043
9044 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
9045
9046 // Non-panel item (active when scene is active).
9047 QVERIFY(rect->isActive());
9048
9049 QGraphicsRectItem *rect2 = new QGraphicsRectItem;
9050 rect2->setFlag(flag: QGraphicsItem::ItemIsPanel);
9051 QGraphicsRectItem *rect3 = new QGraphicsRectItem;
9052 rect3->setFlag(flag: QGraphicsItem::ItemIsPanel);
9053
9054 // Test normal activation.
9055 QVERIFY(!rect2->isActive());
9056 scene.addItem(item: rect2);
9057 QVERIFY(rect2->isActive()); // first panel item is activated
9058 scene.addItem(item: rect3);
9059 QVERIFY(!rect3->isActive()); // second panel item is _not_ activated
9060 rect3->setActive(true);
9061 QVERIFY(rect3->isActive());
9062 scene.removeItem(item: rect3);
9063 QVERIFY(!rect3->isActive()); // no panel is active anymore
9064 QCOMPARE(scene.activePanel(), nullptr);
9065 scene.addItem(item: rect3);
9066 QVERIFY(rect3->isActive()); // second panel item is activated
9067
9068 // Test pending activation.
9069 scene.removeItem(item: rect3);
9070 rect2->setActive(true);
9071 QVERIFY(rect2->isActive()); // first panel item is activated
9072 rect3->setActive(true);
9073 QVERIFY(!rect3->isActive()); // not active (yet)
9074 scene.addItem(item: rect3);
9075 QVERIFY(rect3->isActive()); // now becomes active
9076
9077 // Test pending deactivation.
9078 scene.removeItem(item: rect3);
9079 rect3->setActive(false);
9080 scene.addItem(item: rect3);
9081 QVERIFY(!rect3->isActive()); // doesn't become active
9082
9083 // Child of panel activation.
9084 rect3->setActive(true);
9085 QGraphicsRectItem *rect4 = new QGraphicsRectItem;
9086 rect4->setFlag(flag: QGraphicsItem::ItemIsPanel);
9087 QGraphicsRectItem *rect5 = new QGraphicsRectItem(rect4);
9088 QGraphicsRectItem *rect6 = new QGraphicsRectItem(rect5);
9089 scene.addItem(item: rect4);
9090 QCOMPARE(scene.activePanel(), rect3);
9091 scene.removeItem(item: rect4);
9092 rect6->setActive(true);
9093 scene.addItem(item: rect4);
9094 QVERIFY(rect4->isActive());
9095 QVERIFY(rect5->isActive());
9096 QVERIFY(rect6->isActive());
9097 QCOMPARE(scene.activePanel(), rect4);
9098 scene.removeItem(item: rect4); // no active panel
9099 rect6->setActive(false);
9100 scene.addItem(item: rect4);
9101 QVERIFY(!rect4->isActive());
9102 QVERIFY(!rect5->isActive());
9103 QVERIFY(!rect6->isActive());
9104 QCOMPARE(scene.activePanel(), nullptr);
9105
9106 // Controlling auto-activation when the scene changes activation.
9107 rect4->setActive(true);
9108 QCoreApplication::sendEvent(receiver: &scene, event: &deactivate);
9109 QVERIFY(!scene.isActive());
9110 QVERIFY(!rect4->isActive());
9111 rect4->setActive(false);
9112 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
9113 QVERIFY(scene.isActive());
9114 QVERIFY(!scene.activePanel());
9115 QVERIFY(!rect4->isActive());
9116}
9117
9118void tst_QGraphicsItem::setActivePanelOnInactiveScene()
9119{
9120 QGraphicsScene scene;
9121 QGraphicsRectItem *item = scene.addRect(rect: QRectF());
9122 QGraphicsRectItem *panel = scene.addRect(rect: QRectF());
9123 panel->setFlag(flag: QGraphicsItem::ItemIsPanel);
9124
9125 EventSpy itemActivateSpy(&scene, item, QEvent::WindowActivate);
9126 EventSpy itemDeactivateSpy(&scene, item, QEvent::WindowDeactivate);
9127 EventSpy panelActivateSpy(&scene, panel, QEvent::WindowActivate);
9128 EventSpy panelDeactivateSpy(&scene, panel, QEvent::WindowDeactivate);
9129 EventSpy sceneActivationChangeSpy(&scene, QEvent::ActivationChange);
9130
9131 scene.setActivePanel(panel);
9132 QCOMPARE(scene.activePanel(), nullptr);
9133 QCOMPARE(itemActivateSpy.count(), 0);
9134 QCOMPARE(itemDeactivateSpy.count(), 0);
9135 QCOMPARE(panelActivateSpy.count(), 0);
9136 QCOMPARE(panelDeactivateSpy.count(), 0);
9137 QCOMPARE(sceneActivationChangeSpy.count(), 0);
9138}
9139
9140void tst_QGraphicsItem::activationOnShowHide()
9141{
9142 QGraphicsScene scene;
9143 QEvent activate(QEvent::WindowActivate);
9144 QCoreApplication::sendEvent(receiver: &scene, event: &activate);
9145
9146 QGraphicsRectItem *rootPanel = scene.addRect(rect: QRectF());
9147 rootPanel->setFlag(flag: QGraphicsItem::ItemIsPanel);
9148 rootPanel->setActive(true);
9149
9150 QGraphicsRectItem *subPanel = new QGraphicsRectItem;
9151 subPanel->setFlag(flag: QGraphicsItem::ItemIsPanel);
9152
9153 // Reparenting onto an active panel auto-activates the child panel.
9154 subPanel->setParentItem(rootPanel);
9155 QVERIFY(subPanel->isActive());
9156 QVERIFY(!rootPanel->isActive());
9157
9158 // Hiding an active child panel will reactivate the parent panel.
9159 subPanel->hide();
9160 QVERIFY(rootPanel->isActive());
9161
9162 // Showing a child panel will auto-activate it.
9163 subPanel->show();
9164 QVERIFY(subPanel->isActive());
9165 QVERIFY(!rootPanel->isActive());
9166
9167 // Adding an unrelated panel doesn't affect activation.
9168 QGraphicsRectItem *otherPanel = new QGraphicsRectItem;
9169 otherPanel->setFlag(flag: QGraphicsItem::ItemIsPanel);
9170 scene.addItem(item: otherPanel);
9171 QVERIFY(subPanel->isActive());
9172
9173 // Showing an unrelated panel doesn't affect activation.
9174 otherPanel->hide();
9175 otherPanel->show();
9176 QVERIFY(subPanel->isActive());
9177
9178 // Add a non-panel item.
9179 QGraphicsRectItem *otherItem = new QGraphicsRectItem;
9180 scene.addItem(item: otherItem);
9181 otherItem->setActive(true);
9182 QVERIFY(otherItem->isActive());
9183
9184 // Reparent a panel onto an active non-panel item.
9185 subPanel->setParentItem(otherItem);
9186 QVERIFY(subPanel->isActive());
9187
9188 // Showing a child panel of a non-panel item will activate it.
9189 subPanel->hide();
9190 QVERIFY(!subPanel->isActive());
9191 QVERIFY(otherItem->isActive());
9192 subPanel->show();
9193 QVERIFY(subPanel->isActive());
9194
9195 // Hiding a toplevel active panel will pass activation back
9196 // to the non-panel items.
9197 rootPanel->setActive(true);
9198 rootPanel->hide();
9199 QVERIFY(!rootPanel->isActive());
9200 QVERIFY(otherItem->isActive());
9201}
9202
9203class MoveWhileDying : public QGraphicsRectItem
9204{
9205public:
9206 using QGraphicsRectItem::QGraphicsRectItem;
9207
9208 ~MoveWhileDying()
9209 {
9210 const auto children = childItems();
9211 for (QGraphicsItem *c : children) {
9212 const auto grandChildren = c->childItems();
9213 for (QGraphicsItem *cc : grandChildren)
9214 cc->moveBy(dx: 10, dy: 10);
9215 c->moveBy(dx: 10, dy: 10);
9216 }
9217 if (QGraphicsItem *p = parentItem())
9218 p->moveBy(dx: 10, dy: 10);
9219 }
9220};
9221
9222void tst_QGraphicsItem::deactivateInactivePanel()
9223{
9224 QGraphicsScene scene;
9225 QGraphicsItem *panel1 = scene.addRect(rect: QRectF(0, 0, 10, 10));
9226 panel1->setFlag(flag: QGraphicsItem::ItemIsPanel);
9227
9228 QGraphicsItem *panel2 = scene.addRect(rect: QRectF(0, 0, 10, 10));
9229 panel2->setFlag(flag: QGraphicsItem::ItemIsPanel);
9230
9231 QEvent event(QEvent::WindowActivate);
9232 QCoreApplication::sendEvent(receiver: &scene, event: &event);
9233
9234 panel1->setActive(true);
9235 QVERIFY(scene.isActive());
9236 QVERIFY(panel1->isActive());
9237 QVERIFY(!panel2->isActive());
9238 QCOMPARE(scene.activePanel(), panel1);
9239
9240 panel2->setActive(true);
9241 QVERIFY(panel2->isActive());
9242 QVERIFY(!panel1->isActive());
9243 QCOMPARE(scene.activePanel(), panel2);
9244
9245 panel2->setActive(false);
9246 QVERIFY(panel1->isActive());
9247 QVERIFY(!panel2->isActive());
9248 QCOMPARE(scene.activePanel(), panel1);
9249
9250 panel2->setActive(false);
9251 QVERIFY(panel1->isActive());
9252 QVERIFY(!panel2->isActive());
9253 QCOMPARE(scene.activePanel(), panel1);
9254}
9255
9256void tst_QGraphicsItem::moveWhileDeleting()
9257{
9258 QGraphicsScene scene;
9259 QGraphicsRectItem *rect = new QGraphicsRectItem;
9260 MoveWhileDying *silly = new MoveWhileDying(rect);
9261 QGraphicsRectItem *child = new QGraphicsRectItem(silly);
9262 scene.addItem(item: rect);
9263 delete rect; // don't crash!
9264
9265 rect = new QGraphicsRectItem;
9266 silly = new MoveWhileDying(rect);
9267 child = new QGraphicsRectItem(silly);
9268
9269 QGraphicsView view(&scene);
9270 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9271 view.show();
9272 QVERIFY(QTest::qWaitForWindowExposed(&view));
9273
9274 delete rect;
9275
9276 rect = new QGraphicsRectItem;
9277 rect->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
9278 silly = new MoveWhileDying(rect);
9279 child = new QGraphicsRectItem(silly);
9280
9281 QTest::qWait(ms: 125);
9282
9283 delete rect;
9284
9285 rect = new MoveWhileDying;
9286 rect->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
9287 child = new QGraphicsRectItem(rect);
9288 silly = new MoveWhileDying(child);
9289
9290 QTest::qWait(ms: 125);
9291
9292 delete rect;
9293}
9294
9295class MyRectItem : public QGraphicsWidget
9296{
9297 Q_OBJECT
9298public:
9299 using QGraphicsWidget::QGraphicsWidget;
9300
9301 void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
9302 {
9303 painter->setBrush(brush);
9304 painter->drawRect(rect: boundingRect());
9305 }
9306 void move()
9307 {
9308 setPos(ax: -100,ay: -100);
9309 topLevel->collidingItems(mode: Qt::IntersectsItemBoundingRect);
9310 }
9311public:
9312 QGraphicsItem *topLevel = nullptr;
9313 QBrush brush;
9314};
9315
9316
9317void tst_QGraphicsItem::ensureDirtySceneTransform()
9318{
9319 QGraphicsScene scene;
9320
9321 MyRectItem *topLevel = new MyRectItem;
9322 topLevel->setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
9323 topLevel->setPos(ax: -50, ay: -50);
9324 topLevel->brush = QBrush(QColor(Qt::black));
9325 scene.addItem(item: topLevel);
9326
9327 MyRectItem *parent = new MyRectItem;
9328 parent->topLevel = topLevel;
9329 parent->setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
9330 parent->setPos(ax: 0, ay: 0);
9331 parent->brush = QBrush(QColor(Qt::magenta));
9332 parent->setObjectName("parent");
9333 scene.addItem(item: parent);
9334
9335 MyRectItem *child = new MyRectItem(parent);
9336 child->setGeometry(ax: 0, ay: 0, aw: 80, ah: 80);
9337 child->setPos(ax: 10, ay: 10);
9338 child->setObjectName("child");
9339 child->brush = QBrush(QColor(Qt::blue));
9340
9341 MyRectItem *child2 = new MyRectItem(parent);
9342 child2->setGeometry(ax: 0, ay: 0, aw: 80, ah: 80);
9343 child2->setPos(ax: 15, ay: 15);
9344 child2->setObjectName("child2");
9345 child2->brush = QBrush(QColor(Qt::green));
9346
9347 MyRectItem *child3 = new MyRectItem(parent);
9348 child3->setGeometry(ax: 0, ay: 0, aw: 80, ah: 80);
9349 child3->setPos(ax: 20, ay: 20);
9350 child3->setObjectName("child3");
9351 child3->brush = QBrush(QColor(Qt::gray));
9352
9353 QGraphicsView view(&scene);
9354 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9355 view.show();
9356 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) {
9357 QApplication::setActiveWindow(&view);
9358 QVERIFY(QTest::qWaitForWindowActive(&view));
9359 QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
9360 }
9361 QVERIFY(QTest::qWaitForWindowExposed(&view));
9362
9363 //We move the parent
9364 parent->move();
9365 QApplication::processEvents();
9366
9367 //We check if all items moved
9368 QCOMPARE(child->pos(), QPointF(10, 10));
9369 QCOMPARE(child2->pos(), QPointF(15, 15));
9370 QCOMPARE(child3->pos(), QPointF(20, 20));
9371
9372 QCOMPARE(child->sceneBoundingRect(), QRectF(-90, -90, 80, 80));
9373 QCOMPARE(child2->sceneBoundingRect(), QRectF(-85, -85, 80, 80));
9374 QCOMPARE(child3->sceneBoundingRect(), QRectF(-80, -80, 80, 80));
9375
9376 QCOMPARE(child->sceneTransform(), QTransform::fromTranslate(-90, -90));
9377 QCOMPARE(child2->sceneTransform(), QTransform::fromTranslate(-85, -85));
9378 QCOMPARE(child3->sceneTransform(), QTransform::fromTranslate(-80, -80));
9379}
9380
9381void tst_QGraphicsItem::focusScope()
9382{
9383 // ItemIsFocusScope is an internal feature (for now).
9384 QGraphicsScene scene;
9385
9386 QGraphicsRectItem *scope3 = new QGraphicsRectItem;
9387 scope3->setData(key: 0, value: "scope3");
9388 scope3->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
9389 scope3->setFocus();
9390 QVERIFY(!scope3->focusScopeItem());
9391 QCOMPARE(scope3->focusItem(), scope3);
9392
9393 QGraphicsRectItem *scope2 = new QGraphicsRectItem;
9394 scope2->setData(key: 0, value: "scope2");
9395 scope2->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
9396 scope2->setFocus();
9397 QVERIFY(!scope2->focusScopeItem());
9398 scope3->setParentItem(scope2);
9399 QCOMPARE(scope2->focusScopeItem(), scope3);
9400 QCOMPARE(scope2->focusItem(), scope3);
9401
9402 QGraphicsRectItem *scope1 = new QGraphicsRectItem;
9403 scope1->setData(key: 0, value: "scope1");
9404 scope1->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
9405 scope1->setFocus();
9406 QVERIFY(!scope1->focusScopeItem());
9407 scope2->setParentItem(scope1);
9408
9409 QCOMPARE(scope1->focusItem(), scope3);
9410 QCOMPARE(scope2->focusItem(), scope3);
9411 QCOMPARE(scope3->focusItem(), scope3);
9412 QCOMPARE(scope1->focusScopeItem(), scope2);
9413 QCOMPARE(scope2->focusScopeItem(), scope3);
9414 QCOMPARE(scope3->focusScopeItem(), nullptr);
9415
9416 scene.addItem(item: scope1);
9417
9418 QEvent windowActivate(QEvent::WindowActivate);
9419 qApp->sendEvent(receiver: &scene, event: &windowActivate);
9420 scene.setFocus();
9421
9422 QCOMPARE(scope1->focusItem(), scope3);
9423 QCOMPARE(scope2->focusItem(), scope3);
9424 QCOMPARE(scope3->focusItem(), scope3);
9425 QCOMPARE(scope1->focusScopeItem(), scope2);
9426 QCOMPARE(scope2->focusScopeItem(), scope3);
9427 QCOMPARE(scope3->focusScopeItem(), nullptr);
9428
9429 QVERIFY(scope3->hasFocus());
9430
9431 scope3->hide();
9432 QVERIFY(scope2->hasFocus());
9433 scope2->hide();
9434 QVERIFY(scope1->hasFocus());
9435 scope2->show();
9436 QVERIFY(scope2->hasFocus());
9437 scope3->show();
9438 QVERIFY(scope3->hasFocus());
9439 scope1->hide();
9440 QVERIFY(!scope3->hasFocus());
9441 scope1->show();
9442 QVERIFY(scope3->hasFocus());
9443 scope3->clearFocus();
9444 QVERIFY(scope2->hasFocus());
9445 scope2->clearFocus();
9446 QVERIFY(scope1->hasFocus());
9447 scope2->hide();
9448 scope2->show();
9449 QVERIFY(!scope2->hasFocus());
9450 QVERIFY(scope1->hasFocus());
9451 scope2->setFocus();
9452 QVERIFY(scope2->hasFocus());
9453 scope3->setFocus();
9454 QVERIFY(scope3->hasFocus());
9455
9456 // clearFocus() on a focus scope will remove focus from its children.
9457 scope1->clearFocus();
9458 QVERIFY(!scope1->hasFocus());
9459 QVERIFY(!scope2->hasFocus());
9460 QVERIFY(!scope3->hasFocus());
9461
9462 scope1->setFocus();
9463 QVERIFY(!scope1->hasFocus());
9464 QVERIFY(!scope2->hasFocus());
9465 QVERIFY(scope3->hasFocus());
9466
9467 scope2->clearFocus();
9468 QVERIFY(scope1->hasFocus());
9469 QVERIFY(!scope2->hasFocus());
9470 QVERIFY(!scope3->hasFocus());
9471
9472 scope2->setFocus();
9473 QVERIFY(!scope1->hasFocus());
9474 QVERIFY(!scope2->hasFocus());
9475 QVERIFY(scope3->hasFocus());
9476
9477 // Focus cleared while a parent doesn't have focus remains cleared
9478 // when the parent regains focus.
9479 scope1->clearFocus();
9480 scope3->clearFocus();
9481 QVERIFY(!scope1->hasFocus());
9482 QVERIFY(!scope2->hasFocus());
9483 QVERIFY(!scope3->hasFocus());
9484
9485 scope1->setFocus();
9486 QVERIFY(!scope1->hasFocus());
9487 QVERIFY(scope2->hasFocus());
9488 QVERIFY(!scope3->hasFocus());
9489
9490 scope3->setFocus();
9491 QVERIFY(!scope1->hasFocus());
9492 QVERIFY(!scope2->hasFocus());
9493 QVERIFY(scope3->hasFocus());
9494
9495 QGraphicsRectItem *rect4 = new QGraphicsRectItem;
9496 rect4->setData(key: 0, value: "rect4");
9497 rect4->setParentItem(scope3);
9498
9499 QGraphicsRectItem *rect5 = new QGraphicsRectItem;
9500 rect5->setData(key: 0, value: "rect5");
9501 rect5->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
9502 rect5->setFocus();
9503 rect5->setParentItem(rect4);
9504 QCOMPARE(scope3->focusScopeItem(), rect5);
9505 QVERIFY(rect5->hasFocus());
9506
9507 rect4->setParentItem(nullptr);
9508 QVERIFY(rect5->hasFocus());
9509 QCOMPARE(scope3->focusScopeItem(), nullptr);
9510 QCOMPARE(scope3->focusItem(), nullptr);
9511 QVERIFY(!scope3->hasFocus());
9512
9513 QGraphicsRectItem *rectA = new QGraphicsRectItem;
9514 QGraphicsRectItem *scopeA = new QGraphicsRectItem(rectA);
9515 scopeA->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
9516 scopeA->setFocus();
9517 QGraphicsRectItem *scopeB = new QGraphicsRectItem(scopeA);
9518 scopeB->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
9519 scopeB->setFocus();
9520
9521 scene.addItem(item: rectA);
9522 QVERIFY(rect5->hasFocus());
9523 QVERIFY(!scopeB->hasFocus());
9524
9525 scopeA->setFocus();
9526 QVERIFY(scopeB->hasFocus());
9527 QCOMPARE(scopeB->focusItem(), scopeB);
9528}
9529
9530void tst_QGraphicsItem::focusScope2()
9531{
9532 QGraphicsRectItem *child1 = new QGraphicsRectItem;
9533 child1->setFlags(QGraphicsItem::ItemIsFocusable);
9534 child1->setFocus();
9535 QCOMPARE(child1->focusItem(), child1);
9536
9537 QGraphicsRectItem *child2 = new QGraphicsRectItem;
9538 child2->setFlags(QGraphicsItem::ItemIsFocusable);
9539
9540 QGraphicsRectItem *rootFocusScope = new QGraphicsRectItem;
9541 rootFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
9542 rootFocusScope->setFocus();
9543 QCOMPARE(rootFocusScope->focusItem(), rootFocusScope);
9544
9545 child1->setParentItem(rootFocusScope);
9546 child2->setParentItem(rootFocusScope);
9547
9548 QCOMPARE(rootFocusScope->focusScopeItem(), child1);
9549 QCOMPARE(rootFocusScope->focusItem(), child1);
9550
9551 QGraphicsRectItem *siblingChild1 = new QGraphicsRectItem;
9552 siblingChild1->setFlags(QGraphicsItem::ItemIsFocusable);
9553 siblingChild1->setFocus();
9554
9555 QGraphicsRectItem *siblingChild2 = new QGraphicsRectItem;
9556 siblingChild2->setFlags(QGraphicsItem::ItemIsFocusable);
9557
9558 QGraphicsRectItem *siblingFocusScope = new QGraphicsRectItem;
9559 siblingFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
9560
9561 siblingChild1->setParentItem(siblingFocusScope);
9562 siblingChild2->setParentItem(siblingFocusScope);
9563
9564 QCOMPARE(siblingFocusScope->focusScopeItem(), siblingChild1);
9565 QCOMPARE(siblingFocusScope->focusItem(), nullptr);
9566
9567 QGraphicsItem *root = new QGraphicsRectItem;
9568 rootFocusScope->setParentItem(root);
9569 siblingFocusScope->setParentItem(root);
9570
9571 QCOMPARE(root->focusItem(), child1);
9572
9573 QGraphicsScene scene;
9574 scene.addItem(item: root);
9575
9576 QEvent activate(QEvent::WindowActivate);
9577 qApp->sendEvent(receiver: &scene, event: &activate);
9578 scene.setFocus();
9579
9580 QCOMPARE(scene.focusItem(), child1);
9581
9582 // You cannot set focus on a descendant of a focus scope directly;
9583 // this will only change the scope's focus scope item pointer. If
9584 // you want to give true input focus, you must set it directly on
9585 // the scope itself
9586 siblingChild2->setFocus();
9587 QVERIFY(!siblingChild2->hasFocus());
9588 QVERIFY(!siblingChild2->focusItem());
9589 QCOMPARE(siblingFocusScope->focusScopeItem(), siblingChild2);
9590 QCOMPARE(siblingFocusScope->focusItem(), nullptr);
9591
9592 // Set focus on the scope; focus is forwarded to the focus scope item.
9593 siblingFocusScope->setFocus();
9594 QVERIFY(siblingChild2->hasFocus());
9595 QVERIFY(siblingChild2->focusItem());
9596 QCOMPARE(siblingFocusScope->focusScopeItem(), siblingChild2);
9597 QCOMPARE(siblingFocusScope->focusItem(), siblingChild2);
9598}
9599
9600class FocusScopeItemPrivate;
9601class FocusScopeItem : public QGraphicsItem
9602{
9603 Q_DECLARE_PRIVATE(FocusScopeItem)
9604public:
9605 FocusScopeItem(QGraphicsItem *parent = nullptr);
9606 QRectF boundingRect() const override { return QRectF(); }
9607 void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override { }
9608
9609 int focusScopeChanged = 0;
9610 FocusScopeItemPrivate *d_ptr;
9611};
9612
9613class FocusScopeItemPrivate : QGraphicsItemPrivate
9614{
9615 Q_DECLARE_PUBLIC(FocusScopeItem)
9616public:
9617 void focusScopeItemChange(bool) override
9618 { ++q_func()->focusScopeChanged; }
9619};
9620
9621FocusScopeItem::FocusScopeItem(QGraphicsItem *parent)
9622 : QGraphicsItem(*new FocusScopeItemPrivate, parent)
9623{
9624 setFlag(flag: ItemIsFocusable);
9625}
9626
9627void tst_QGraphicsItem::focusScopeItemChangedWhileScopeDoesntHaveFocus()
9628{
9629 QGraphicsRectItem rect;
9630 rect.setFlags(QGraphicsItem::ItemIsFocusScope | QGraphicsItem::ItemIsFocusable);
9631
9632 FocusScopeItem *child1 = new FocusScopeItem(&rect);
9633 FocusScopeItem *child2 = new FocusScopeItem(&rect);
9634
9635 QCOMPARE(rect.focusScopeItem(), nullptr);
9636 QCOMPARE(child1->focusScopeChanged, 0);
9637 QCOMPARE(child2->focusScopeChanged, 0);
9638 child1->setFocus();
9639 QCOMPARE(rect.focusScopeItem(), child1);
9640 QCOMPARE(child1->focusScopeChanged, 1);
9641 QCOMPARE(child2->focusScopeChanged, 0);
9642 child2->setFocus();
9643 QCOMPARE(rect.focusScopeItem(), child2);
9644 QCOMPARE(child1->focusScopeChanged, 2);
9645 QCOMPARE(child2->focusScopeChanged, 1);
9646 child1->setFocus();
9647 QCOMPARE(rect.focusScopeItem(), child1);
9648 QCOMPARE(child1->focusScopeChanged, 3);
9649 QCOMPARE(child2->focusScopeChanged, 2);
9650 child1->clearFocus();
9651 QCOMPARE(rect.focusScopeItem(), nullptr);
9652 QCOMPARE(child1->focusScopeChanged, 4);
9653 QCOMPARE(child2->focusScopeChanged, 2);
9654}
9655
9656void tst_QGraphicsItem::stackBefore()
9657{
9658 QGraphicsRectItem parent;
9659 QGraphicsRectItem *child1 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent);
9660 QGraphicsRectItem *child2 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent);
9661 QGraphicsRectItem *child3 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent);
9662 QGraphicsRectItem *child4 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent);
9663 const GraphicsItemsList expected1234{child1, child2, child3, child4};
9664 QCOMPARE(parent.childItems(), expected1234);
9665 child1->setData(key: 0, value: "child1");
9666 child2->setData(key: 0, value: "child2");
9667 child3->setData(key: 0, value: "child3");
9668 child4->setData(key: 0, value: "child4");
9669
9670 // Remove and append
9671 child2->setParentItem(nullptr);
9672 child2->setParentItem(&parent);
9673 const GraphicsItemsList expected1342{child1, child3, child4, child2};
9674 QCOMPARE(parent.childItems(), expected1342);
9675
9676 // Move child2 before child1
9677 child2->stackBefore(sibling: child1); // 2134
9678 const GraphicsItemsList expected2134{child2, child1, child3, child4};
9679 QCOMPARE(parent.childItems(), expected2134);
9680 child2->stackBefore(sibling: child2); // 2134
9681 QCOMPARE(parent.childItems(), expected2134);
9682 child1->setZValue(1); // 2341
9683 const GraphicsItemsList expected2341{child2, child3, child4, child1};
9684 QCOMPARE(parent.childItems(), expected2341);
9685 child1->stackBefore(sibling: child2); // 2341
9686 QCOMPARE(parent.childItems(), expected2341);
9687 child1->setZValue(0); // 1234
9688 QCOMPARE(parent.childItems(), expected1234);
9689 child4->stackBefore(sibling: child1); // 4123
9690 const GraphicsItemsList expected4123{child4, child1, child2, child3};
9691 QCOMPARE(parent.childItems(), expected4123);
9692 child4->setZValue(1); // 1234 (4123)
9693 QCOMPARE(parent.childItems(), expected1234);
9694 child3->stackBefore(sibling: child1); // 3124 (4312)
9695 const GraphicsItemsList expected3124{child3, child1, child2, child4};
9696 QCOMPARE(parent.childItems(), expected3124);
9697 child4->setZValue(0); // 4312
9698 const GraphicsItemsList expected4312{child4, child3, child1, child2};
9699 QCOMPARE(parent.childItems(), expected4312);
9700
9701 // Make them all toplevels
9702 child1->setParentItem(nullptr);
9703 child2->setParentItem(nullptr);
9704 child3->setParentItem(nullptr);
9705 child4->setParentItem(nullptr);
9706
9707 QGraphicsScene scene;
9708 scene.addItem(item: child1);
9709 scene.addItem(item: child2);
9710 scene.addItem(item: child3);
9711 scene.addItem(item: child4);
9712 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected1234);
9713
9714 // Remove and append
9715 scene.removeItem(item: child2);
9716 scene.addItem(item: child2);
9717 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected1342);
9718
9719 // Move child2 before child1
9720 child2->stackBefore(sibling: child1); // 2134
9721 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected2134);
9722 child2->stackBefore(sibling: child2); // 2134
9723 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected2134);
9724 child1->setZValue(1); // 2341
9725 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected2341);
9726 child1->stackBefore(sibling: child2); // 2341
9727 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected2341);
9728 child1->setZValue(0); // 1234
9729 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected1234);
9730 child4->stackBefore(sibling: child1); // 4123
9731 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected4123);
9732 child4->setZValue(1); // 1234 (4123)
9733 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected1234);
9734 child3->stackBefore(sibling: child1); // 3124 (4312)
9735 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected3124);
9736 child4->setZValue(0); // 4312
9737 QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected4312);
9738}
9739
9740void tst_QGraphicsItem::QTBUG_4233_updateCachedWithSceneRect()
9741{
9742 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
9743 QSKIP("Window activation is not supported");
9744
9745 EventTester *tester = new EventTester;
9746 tester->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache);
9747
9748 QGraphicsScene scene;
9749 scene.addItem(item: tester);
9750 scene.setSceneRect(x: -100, y: -100, w: 200, h: 200); // contains the tester item
9751
9752 QGraphicsView view(&scene);
9753 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9754 view.show();
9755 QApplication::setActiveWindow(&view);
9756 QVERIFY(QTest::qWaitForWindowExposed(&view));
9757 QVERIFY(QTest::qWaitForWindowActive(&view));
9758 QCOMPARE(QApplication::activeWindow(), &view);
9759
9760 QTRY_COMPARE(tester->repaints, 1);
9761
9762 scene.update(); // triggers "updateAll" optimization
9763 QCoreApplication::processEvents();
9764 QCoreApplication::processEvents(); // in 4.6 only one processEvents is necessary
9765
9766 QCOMPARE(tester->repaints, 1);
9767
9768 scene.update(); // triggers "updateAll" optimization
9769 tester->update();
9770 QCoreApplication::processEvents();
9771 QCoreApplication::processEvents(); // in 4.6 only one processEvents is necessary
9772
9773 QCOMPARE(tester->repaints, 2);
9774}
9775
9776void tst_QGraphicsItem::sceneModality()
9777{
9778 // 1) Test mouse events (delivery/propagation/redirection)
9779 // 2) Test hover events (incl. leave on block, enter on unblock)
9780 // 3) Test cursor stuff (incl. unset on block, set on unblock)
9781 // 4) Test clickfocus
9782 // 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock)
9783 // 6) ### modality for non-panels is unsupported for now
9784 QGraphicsScene scene;
9785
9786 QGraphicsRectItem *bottomItem = scene.addRect(x: -150, y: -100, w: 300, h: 200);
9787 bottomItem->setFlag(flag: QGraphicsItem::ItemIsFocusable);
9788 bottomItem->setBrush(Qt::yellow);
9789
9790 QGraphicsRectItem *leftParent = scene.addRect(x: -50, y: -50, w: 100, h: 100);
9791 leftParent->setFlag(flag: QGraphicsItem::ItemIsPanel);
9792 leftParent->setBrush(Qt::blue);
9793
9794 QGraphicsRectItem *leftChild = scene.addRect(x: -25, y: -25, w: 50, h: 50);
9795 leftChild->setFlag(flag: QGraphicsItem::ItemIsPanel);
9796 leftChild->setBrush(Qt::green);
9797 leftChild->setParentItem(leftParent);
9798
9799 QGraphicsRectItem *rightParent = scene.addRect(x: -50, y: -50, w: 100, h: 100);
9800 rightParent->setFlag(flag: QGraphicsItem::ItemIsPanel);
9801 rightParent->setBrush(Qt::red);
9802 QGraphicsRectItem *rightChild = scene.addRect(x: -25, y: -25, w: 50, h: 50);
9803 rightChild->setFlag(flag: QGraphicsItem::ItemIsPanel);
9804 rightChild->setBrush(Qt::gray);
9805 rightChild->setParentItem(rightParent);
9806
9807 leftParent->setPos(ax: -75, ay: 0);
9808 rightParent->setPos(ax: 75, ay: 0);
9809
9810 bottomItem->setData(key: 0, value: "bottomItem");
9811 leftParent->setData(key: 0, value: "leftParent");
9812 leftChild->setData(key: 0, value: "leftChild");
9813 rightParent->setData(key: 0, value: "rightParent");
9814 rightChild->setData(key: 0, value: "rightChild");
9815
9816 scene.setSceneRect(scene.itemsBoundingRect().adjusted(xp1: -50, yp1: -50, xp2: 50, yp2: 50));
9817
9818 EventSpy2 leftParentSpy(&scene, leftParent);
9819 EventSpy2 leftChildSpy(&scene, leftChild);
9820 EventSpy2 rightParentSpy(&scene, rightParent);
9821 EventSpy2 rightChildSpy(&scene, rightChild);
9822 EventSpy2 bottomItemSpy(&scene, bottomItem);
9823
9824 // Scene modality, also test multiple scene modal items
9825 leftChild->setPanelModality(QGraphicsItem::SceneModal);
9826 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
9827 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
9828 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
9829 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
9830 QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); // not a panel
9831
9832 // Click inside left child
9833 sendMouseClick(scene: &scene, point: leftChild->scenePos(), button: Qt::LeftButton);
9834 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
9835 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
9836 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9837 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9838 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9839 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9840
9841 // Click on left parent, event goes to modal child
9842 sendMouseClick(scene: &scene, point: leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), button: Qt::LeftButton);
9843 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 2);
9844 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
9845 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9846 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9847 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9848 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9849
9850 // Click on all other items and outside the items
9851 sendMouseClick(scene: &scene, point: rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), button: Qt::LeftButton);
9852 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 3);
9853 sendMouseClick(scene: &scene, point: rightChild->scenePos(), button: Qt::LeftButton);
9854 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 4);
9855 sendMouseClick(scene: &scene, point: bottomItem->scenePos(), button: Qt::LeftButton);
9856 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 5);
9857 sendMouseClick(scene: &scene, point: QPointF(10000, 10000), button: Qt::LeftButton);
9858 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 6);
9859 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
9860 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9861 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9862 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9863 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9864
9865 leftChildSpy.counts.clear();
9866 rightChildSpy.counts.clear();
9867 leftParentSpy.counts.clear();
9868 rightParentSpy.counts.clear();
9869 bottomItemSpy.counts.clear();
9870
9871 leftChild->setPanelModality(QGraphicsItem::NonModal);
9872 QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
9873 QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1);
9874 QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1);
9875 QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 1);
9876 QCOMPARE(bottomItemSpy.counts[QEvent::WindowUnblocked], 0);
9877
9878 // Left parent enters scene modality.
9879 leftParent->setPanelModality(QGraphicsItem::SceneModal);
9880 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
9881 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
9882 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0);
9883 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
9884 QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
9885
9886 // Click inside left child.
9887 sendMouseClick(scene: &scene, point: leftChild->scenePos(), button: Qt::LeftButton);
9888 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
9889 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0);
9890 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // panel stops propagation
9891 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0);
9892 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9893 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9894
9895 // Click on left parent.
9896 sendMouseClick(scene: &scene, point: leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), button: Qt::LeftButton);
9897 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
9898 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0);
9899 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 1);
9900 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0);
9901 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0);
9902 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0);
9903
9904 // Click on all other items and outside the items
9905 sendMouseClick(scene: &scene, point: rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), button: Qt::LeftButton);
9906 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 2);
9907 sendMouseClick(scene: &scene, point: rightChild->scenePos(), button: Qt::LeftButton);
9908 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 3);
9909 sendMouseClick(scene: &scene, point: bottomItem->scenePos(), button: Qt::LeftButton);
9910 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 4);
9911 sendMouseClick(scene: &scene, point: QPointF(10000, 10000), button: Qt::LeftButton);
9912 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 5);
9913 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMouseRelease], 0);
9914 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
9915 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0);
9916 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0);
9917 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0);
9918
9919 leftChildSpy.counts.clear();
9920 rightChildSpy.counts.clear();
9921 leftParentSpy.counts.clear();
9922 rightParentSpy.counts.clear();
9923 bottomItemSpy.counts.clear();
9924
9925 // Now both left parent and child are scene modal. Left parent is blocked.
9926 leftChild->setPanelModality(QGraphicsItem::SceneModal);
9927 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
9928 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
9929 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
9930 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
9931 QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
9932
9933 // Click inside left child
9934 sendMouseClick(scene: &scene, point: leftChild->scenePos(), button: Qt::LeftButton);
9935 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
9936 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
9937 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9938 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9939 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9940 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9941
9942 // Click on left parent, event goes to modal child
9943 sendMouseClick(scene: &scene, point: leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), button: Qt::LeftButton);
9944 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 2);
9945 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
9946 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9947 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9948 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9949 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9950
9951 // Click on all other items and outside the items
9952 sendMouseClick(scene: &scene, point: rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), button: Qt::LeftButton);
9953 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 3);
9954 sendMouseClick(scene: &scene, point: rightChild->scenePos(), button: Qt::LeftButton);
9955 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 4);
9956 sendMouseClick(scene: &scene, point: bottomItem->scenePos(), button: Qt::LeftButton);
9957 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 5);
9958 sendMouseClick(scene: &scene, point: QPointF(10000, 10000), button: Qt::LeftButton);
9959 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 6);
9960 QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
9961 QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9962 QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9963 QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9964 QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
9965
9966 leftChildSpy.counts.clear();
9967 rightChildSpy.counts.clear();
9968 leftParentSpy.counts.clear();
9969 rightParentSpy.counts.clear();
9970 bottomItemSpy.counts.clear();
9971
9972 // Right child enters scene modality, only left child is blocked.
9973 rightChild->setPanelModality(QGraphicsItem::SceneModal);
9974 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1);
9975 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
9976 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0);
9977 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
9978 QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
9979}
9980
9981void tst_QGraphicsItem::panelModality()
9982{
9983 // 1) Test mouse events (delivery/propagation/redirection)
9984 // 2) Test hover events (incl. leave on block, enter on unblock)
9985 // 3) Test cursor stuff (incl. unset on block, set on unblock)
9986 // 4) Test clickfocus
9987 // 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock)
9988 // 6) ### modality for non-panels is unsupported for now
9989 QGraphicsScene scene;
9990
9991 QGraphicsRectItem *bottomItem = scene.addRect(x: -150, y: -100, w: 300, h: 200);
9992 bottomItem->setFlag(flag: QGraphicsItem::ItemIsFocusable);
9993 bottomItem->setBrush(Qt::yellow);
9994
9995 QGraphicsRectItem *leftParent = scene.addRect(x: -50, y: -50, w: 100, h: 100);
9996 leftParent->setFlag(flag: QGraphicsItem::ItemIsPanel);
9997 leftParent->setBrush(Qt::blue);
9998
9999 QGraphicsRectItem *leftChild = scene.addRect(x: -25, y: -25, w: 50, h: 50);
10000 leftChild->setFlag(flag: QGraphicsItem::ItemIsPanel);
10001 leftChild->setBrush(Qt::green);
10002 leftChild->setParentItem(leftParent);
10003
10004 QGraphicsRectItem *rightParent = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10005 rightParent->setFlag(flag: QGraphicsItem::ItemIsPanel);
10006 rightParent->setBrush(Qt::red);
10007 QGraphicsRectItem *rightChild = scene.addRect(x: -25, y: -25, w: 50, h: 50);
10008 rightChild->setFlag(flag: QGraphicsItem::ItemIsPanel);
10009 rightChild->setBrush(Qt::gray);
10010 rightChild->setParentItem(rightParent);
10011
10012 leftParent->setPos(ax: -75, ay: 0);
10013 rightParent->setPos(ax: 75, ay: 0);
10014
10015 bottomItem->setData(key: 0, value: "bottomItem");
10016 leftParent->setData(key: 0, value: "leftParent");
10017 leftChild->setData(key: 0, value: "leftChild");
10018 rightParent->setData(key: 0, value: "rightParent");
10019 rightChild->setData(key: 0, value: "rightChild");
10020
10021 scene.setSceneRect(scene.itemsBoundingRect().adjusted(xp1: -50, yp1: -50, xp2: 50, yp2: 50));
10022
10023 EventSpy2 leftParentSpy(&scene, leftParent);
10024 EventSpy2 leftChildSpy(&scene, leftChild);
10025 EventSpy2 rightParentSpy(&scene, rightParent);
10026 EventSpy2 rightChildSpy(&scene, rightChild);
10027 EventSpy2 bottomItemSpy(&scene, bottomItem);
10028
10029 // Left Child enters panel modality, only left parent is blocked.
10030 leftChild->setPanelModality(QGraphicsItem::PanelModal);
10031 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
10032 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
10033 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10034 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
10035 QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
10036
10037 leftChild->setPanelModality(QGraphicsItem::NonModal);
10038 leftChildSpy.counts.clear();
10039 rightChildSpy.counts.clear();
10040 leftParentSpy.counts.clear();
10041 rightParentSpy.counts.clear();
10042 bottomItemSpy.counts.clear();
10043
10044 // Left parent enter panel modality, nothing is blocked.
10045 leftParent->setPanelModality(QGraphicsItem::PanelModal);
10046 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
10047 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
10048 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0);
10049 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
10050 QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
10051
10052 // Left child enters panel modality, left parent is blocked again.
10053 leftChild->setPanelModality(QGraphicsItem::PanelModal);
10054 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
10055 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
10056 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10057 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
10058 QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
10059
10060 leftChildSpy.counts.clear();
10061 rightChildSpy.counts.clear();
10062 leftParentSpy.counts.clear();
10063 rightParentSpy.counts.clear();
10064 bottomItemSpy.counts.clear();
10065
10066 leftChild->setPanelModality(QGraphicsItem::NonModal);
10067 QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1);
10068 leftParent->setPanelModality(QGraphicsItem::NonModal);
10069 QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
10070 QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0);
10071 QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1);
10072 QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
10073 QCOMPARE(bottomItemSpy.counts[QEvent::WindowUnblocked], 0);
10074
10075 leftChildSpy.counts.clear();
10076 rightChildSpy.counts.clear();
10077 leftParentSpy.counts.clear();
10078 rightParentSpy.counts.clear();
10079 bottomItemSpy.counts.clear();
10080
10081 // Left and right child enter panel modality, both parents are blocked.
10082 rightChild->setPanelModality(QGraphicsItem::PanelModal);
10083 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0);
10084 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
10085 leftChild->setPanelModality(QGraphicsItem::PanelModal);
10086 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10087 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
10088}
10089
10090void tst_QGraphicsItem::mixedModality()
10091{
10092 // 1) Test mouse events (delivery/propagation/redirection)
10093 // 2) Test hover events (incl. leave on block, enter on unblock)
10094 // 3) Test cursor stuff (incl. unset on block, set on unblock)
10095 // 4) Test clickfocus
10096 // 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock)
10097 // 6) ### modality for non-panels is unsupported for now
10098 QGraphicsScene scene;
10099
10100 QGraphicsRectItem *bottomItem = scene.addRect(x: -150, y: -100, w: 300, h: 200);
10101 bottomItem->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10102 bottomItem->setBrush(Qt::yellow);
10103
10104 QGraphicsRectItem *leftParent = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10105 leftParent->setFlag(flag: QGraphicsItem::ItemIsPanel);
10106 leftParent->setBrush(Qt::blue);
10107
10108 QGraphicsRectItem *leftChild = scene.addRect(x: -25, y: -25, w: 50, h: 50);
10109 leftChild->setFlag(flag: QGraphicsItem::ItemIsPanel);
10110 leftChild->setBrush(Qt::green);
10111 leftChild->setParentItem(leftParent);
10112
10113 QGraphicsRectItem *rightParent = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10114 rightParent->setFlag(flag: QGraphicsItem::ItemIsPanel);
10115 rightParent->setBrush(Qt::red);
10116 QGraphicsRectItem *rightChild = scene.addRect(x: -25, y: -25, w: 50, h: 50);
10117 rightChild->setFlag(flag: QGraphicsItem::ItemIsPanel);
10118 rightChild->setBrush(Qt::gray);
10119 rightChild->setParentItem(rightParent);
10120
10121 leftParent->setPos(ax: -75, ay: 0);
10122 rightParent->setPos(ax: 75, ay: 0);
10123
10124 bottomItem->setData(key: 0, value: "bottomItem");
10125 leftParent->setData(key: 0, value: "leftParent");
10126 leftChild->setData(key: 0, value: "leftChild");
10127 rightParent->setData(key: 0, value: "rightParent");
10128 rightChild->setData(key: 0, value: "rightChild");
10129
10130 scene.setSceneRect(scene.itemsBoundingRect().adjusted(xp1: -50, yp1: -50, xp2: 50, yp2: 50));
10131
10132 EventSpy2 leftParentSpy(&scene, leftParent);
10133 EventSpy2 leftChildSpy(&scene, leftChild);
10134 EventSpy2 rightParentSpy(&scene, rightParent);
10135 EventSpy2 rightChildSpy(&scene, rightChild);
10136 EventSpy2 bottomItemSpy(&scene, bottomItem);
10137
10138 // Left Child enters panel modality, only left parent is blocked.
10139 leftChild->setPanelModality(QGraphicsItem::PanelModal);
10140 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
10141 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
10142 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10143 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
10144
10145 // Left parent enters scene modality, which blocks everything except the child.
10146 leftParent->setPanelModality(QGraphicsItem::SceneModal);
10147 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
10148 QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
10149 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
10150 QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0);
10151 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10152 QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
10153 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
10154 QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
10155
10156 // Right child enters panel modality (changes nothing).
10157 rightChild->setPanelModality(QGraphicsItem::PanelModal);
10158 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
10159 QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
10160 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
10161 QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0);
10162 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10163 QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
10164 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
10165 QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
10166
10167 // Left parent leaves modality. Right child is unblocked.
10168 leftParent->setPanelModality(QGraphicsItem::NonModal);
10169 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
10170 QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
10171 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
10172 QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1);
10173 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10174 QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
10175 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
10176 QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
10177
10178 // Right child "upgrades" its modality to scene modal. Left child is blocked.
10179 // Right parent is unaffected.
10180 rightChild->setPanelModality(QGraphicsItem::SceneModal);
10181 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1);
10182 QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
10183 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
10184 QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1);
10185 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10186 QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
10187 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
10188 QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
10189
10190 // "downgrade" right child back to panel modal, left child is unblocked
10191 rightChild->setPanelModality(QGraphicsItem::PanelModal);
10192 QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1);
10193 QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 1);
10194 QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
10195 QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1);
10196 QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
10197 QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
10198 QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
10199 QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
10200}
10201
10202void tst_QGraphicsItem::modality_hover()
10203{
10204 QGraphicsScene scene;
10205 QGraphicsRectItem *rect1 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10206 rect1->setFlag(flag: QGraphicsItem::ItemIsPanel);
10207 rect1->setAcceptHoverEvents(true);
10208 rect1->setData(key: 0, value: "rect1");
10209
10210 QGraphicsRectItem *rect2 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10211 rect2->setParentItem(rect1);
10212 rect2->setFlag(flag: QGraphicsItem::ItemIsPanel);
10213 rect2->setAcceptHoverEvents(true);
10214 rect2->setPos(ax: 50, ay: 50);
10215 rect2->setPanelModality(QGraphicsItem::SceneModal);
10216 rect2->setData(key: 0, value: "rect2");
10217
10218 EventSpy2 rect1Spy(&scene, rect1);
10219 EventSpy2 rect2Spy(&scene, rect2);
10220
10221 sendMouseMove(scene: &scene, point: QPointF(-25, -25));
10222
10223 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0);
10224 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0);
10225 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0);
10226
10227 sendMouseMove(scene: &scene, point: QPointF(75, 75));
10228
10229 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0);
10230 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0);
10231 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0);
10232 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 1);
10233 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 1);
10234 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 0);
10235
10236 sendMouseMove(scene: &scene, point: QPointF(-25, -25));
10237
10238 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 1);
10239 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0);
10240 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0);
10241 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0);
10242
10243 rect2->setPanelModality(QGraphicsItem::NonModal);
10244
10245 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1);
10246 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1);
10247
10248 sendMouseMove(scene: &scene, point: QPointF(75, 75));
10249
10250 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
10251 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 2);
10252
10253 rect2->setPanelModality(QGraphicsItem::SceneModal);
10254
10255 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1);
10256 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
10257 // changing modality causes a spurious GraphicsSceneHoveMove, even though the mouse didn't
10258 // actually move
10259 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3);
10260
10261 sendMouseMove(scene: &scene, point: QPointF(-25, -25));
10262
10263 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2);
10264 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1);
10265 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1);
10266 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1);
10267
10268 rect2->setPanelModality(QGraphicsItem::PanelModal);
10269
10270 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1);
10271 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1);
10272 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1);
10273 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
10274 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3);
10275 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2);
10276
10277 rect2->setPanelModality(QGraphicsItem::NonModal);
10278
10279 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
10280 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 2);
10281 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
10282 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3);
10283 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2);
10284}
10285
10286void tst_QGraphicsItem::modality_mouseGrabber()
10287{
10288 QGraphicsScene scene;
10289 QGraphicsRectItem *rect1 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10290 rect1->setFlag(flag: QGraphicsItem::ItemIsPanel);
10291 rect1->setFlag(flag: QGraphicsItem::ItemIsMovable);
10292 rect1->setData(key: 0, value: "rect1");
10293
10294 QGraphicsRectItem *rect2 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10295 rect2->setParentItem(rect1);
10296 rect2->setFlag(flag: QGraphicsItem::ItemIsPanel);
10297 rect2->setFlag(flag: QGraphicsItem::ItemIsMovable);
10298 rect2->setPos(ax: 50, ay: 50);
10299 rect2->setData(key: 0, value: "rect2");
10300
10301 EventSpy2 rect1Spy(&scene, rect1);
10302 EventSpy2 rect2Spy(&scene, rect2);
10303
10304 {
10305 // pressing mouse on rect1 starts implicit grab
10306 sendMousePress(scene: &scene, point: QPoint(-25, -25));
10307 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
10308 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1);
10309 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10310 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10311 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10312 QCOMPARE(scene.mouseGrabberItem(), rect1);
10313
10314 // grab lost when rect1 is modally shadowed
10315 rect2->setPanelModality(QGraphicsItem::SceneModal);
10316 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
10317 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
10318 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10319 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10320 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10321
10322 // releasing goes nowhere
10323 sendMouseRelease(scene: &scene, point: QPoint(-25, -25));
10324 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
10325 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
10326 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
10327 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10328 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
10329 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10330 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10331
10332 // pressing mouse on rect1 starts implicit grab on rect2 (since it is modal)
10333 sendMouseClick(scene: &scene, point: QPoint(-25, -25));
10334 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
10335 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1);
10336 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
10337 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
10338 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10339 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMousePress], 1);
10340 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 1);
10341 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10342
10343 rect2->setPanelModality(QGraphicsItem::NonModal);
10344
10345 // pressing mouse on rect1 starts implicit grab
10346 sendMousePress(scene: &scene, point: QPoint(-25, -25));
10347 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
10348 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 2);
10349 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
10350 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10351 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10352 QCOMPARE(scene.mouseGrabberItem(), rect1);
10353
10354 // grab lost to rect2 when rect1 is modally shadowed
10355 rect2->setPanelModality(QGraphicsItem::SceneModal);
10356 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
10357 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
10358 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10359 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10360 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10361
10362 // rect1 does *not* re-grab when rect2 is no longer modal
10363 rect2->setPanelModality(QGraphicsItem::NonModal);
10364 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
10365 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
10366 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10367 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10368 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10369
10370 // release goes nowhere
10371 sendMouseRelease(scene: &scene, point: QPoint(-25, -25));
10372 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
10373 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
10374 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
10375 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10376 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10377 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10378 }
10379 {
10380 // repeat the test using PanelModal
10381 rect2->setPanelModality(QGraphicsItem::NonModal);
10382 rect1Spy.counts.clear();
10383 rect2Spy.counts.clear();
10384
10385 // pressing mouse on rect1 starts implicit grab
10386 sendMousePress(scene: &scene, point: QPoint(-25, -25));
10387 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
10388 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1);
10389 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10390 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10391 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10392 QCOMPARE(scene.mouseGrabberItem(), rect1);
10393
10394 // grab lost when rect1 is modally shadowed
10395 rect2->setPanelModality(QGraphicsItem::PanelModal);
10396 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
10397 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
10398 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10399 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10400 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10401
10402 // releasing goes nowhere
10403 sendMouseRelease(scene: &scene, point: QPoint(-25, -25));
10404 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
10405 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
10406 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
10407 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10408 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
10409 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10410 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10411
10412 // pressing mouse on rect1 starts implicit grab on rect2 (since it is modal)
10413 sendMouseClick(scene: &scene, point: QPoint(-25, -25));
10414 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
10415 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1);
10416 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
10417 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
10418 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10419 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMousePress], 1);
10420 QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 1);
10421 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10422
10423 rect2->setPanelModality(QGraphicsItem::NonModal);
10424
10425 // pressing mouse on rect1 starts implicit grab
10426 sendMousePress(scene: &scene, point: QPoint(-25, -25));
10427 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
10428 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 2);
10429 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
10430 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10431 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10432 QCOMPARE(scene.mouseGrabberItem(), rect1);
10433
10434 // grab lost to rect2 when rect1 is modally shadowed
10435 rect2->setPanelModality(QGraphicsItem::PanelModal);
10436 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
10437 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
10438 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10439 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10440 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10441
10442 // rect1 does *not* re-grab when rect2 is no longer modal
10443 rect2->setPanelModality(QGraphicsItem::NonModal);
10444 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
10445 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
10446 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10447 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10448 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10449
10450 // release goes nowhere
10451 sendMouseRelease(scene: &scene, point: QPoint(-25, -25));
10452 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
10453 QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
10454 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
10455 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
10456 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
10457 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10458 }
10459
10460 {
10461 // repeat the PanelModal tests, but this time the mouse events will be on a non-modal item,
10462 // meaning normal grabbing should work
10463 rect2->setPanelModality(QGraphicsItem::NonModal);
10464 rect1Spy.counts.clear();
10465 rect2Spy.counts.clear();
10466
10467 QGraphicsRectItem *rect3 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10468 rect3->setFlag(flag: QGraphicsItem::ItemIsPanel);
10469 rect3->setFlag(flag: QGraphicsItem::ItemIsMovable);
10470 rect3->setPos(ax: 150, ay: 50);
10471 rect3->setData(key: 0, value: "rect3");
10472
10473 EventSpy2 rect3Spy(&scene, rect3);
10474
10475 // pressing mouse on rect3 starts implicit grab
10476 sendMousePress(scene: &scene, point: QPoint(150, 50));
10477 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
10478 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10479 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10480 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10481 QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1);
10482 QCOMPARE(rect3Spy.counts[QEvent::GraphicsSceneMousePress], 1);
10483 QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 0);
10484 QCOMPARE(scene.mouseGrabberItem(), rect3);
10485
10486 // grab is *not* lost when rect1 is modally shadowed by rect2
10487 rect2->setPanelModality(QGraphicsItem::PanelModal);
10488 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
10489 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10490 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10491 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10492 QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1);
10493 QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 0);
10494 QCOMPARE(scene.mouseGrabberItem(), rect3);
10495
10496 // releasing goes to rect3
10497 sendMouseRelease(scene: &scene, point: QPoint(150, 50));
10498 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
10499 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10500 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10501 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10502 QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1);
10503 QCOMPARE(rect3Spy.counts[QEvent::GraphicsSceneMouseRelease], 1);
10504 QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1);
10505 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10506
10507 rect2->setPanelModality(QGraphicsItem::NonModal);
10508
10509 // pressing mouse on rect3 starts implicit grab
10510 sendMousePress(scene: &scene, point: QPoint(150, 50));
10511 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
10512 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10513 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10514 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10515 QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2);
10516 QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1);
10517 QCOMPARE(scene.mouseGrabberItem(), rect3);
10518
10519 // grab is not lost
10520 rect2->setPanelModality(QGraphicsItem::PanelModal);
10521 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
10522 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10523 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10524 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10525 QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2);
10526 QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1);
10527 QCOMPARE(scene.mouseGrabberItem(), rect3);
10528
10529 // grab stays on rect3
10530 rect2->setPanelModality(QGraphicsItem::NonModal);
10531 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
10532 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10533 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10534 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10535 QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2);
10536 QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1);
10537 QCOMPARE(scene.mouseGrabberItem(), rect3);
10538
10539 // release goes to rect3
10540 sendMouseRelease(scene: &scene, point: QPoint(150, 50));
10541 QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
10542 QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
10543 QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
10544 QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
10545 QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2);
10546 QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 2);
10547 QCOMPARE(scene.mouseGrabberItem(), nullptr);
10548 }
10549}
10550
10551void tst_QGraphicsItem::modality_clickFocus()
10552{
10553 QGraphicsScene scene;
10554 QGraphicsRectItem *rect1 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10555 rect1->setFlag(flag: QGraphicsItem::ItemIsPanel);
10556 rect1->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10557 rect1->setData(key: 0, value: "rect1");
10558
10559 QGraphicsRectItem *rect2 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10560 rect2->setParentItem(rect1);
10561 rect2->setFlag(flag: QGraphicsItem::ItemIsPanel);
10562 rect2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10563 rect2->setPos(ax: 50, ay: 50);
10564 rect2->setData(key: 0, value: "rect2");
10565
10566 QEvent windowActivateEvent(QEvent::WindowActivate);
10567 QCoreApplication::sendEvent(receiver: &scene, event: &windowActivateEvent);
10568
10569 EventSpy2 rect1Spy(&scene, rect1);
10570 EventSpy2 rect2Spy(&scene, rect2);
10571
10572 // activate rect1, it should get focus
10573 rect1->setActive(true);
10574 QCOMPARE(scene.focusItem(), rect1);
10575
10576 // focus stays when rect2 becomes modal
10577 rect2->setPanelModality(QGraphicsItem::SceneModal);
10578 QCOMPARE(scene.focusItem(), rect1);
10579 QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 1);
10580 QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0);
10581 QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 0);
10582 QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0);
10583
10584 // clicking on rect1 should not set it's focus item
10585 rect1->clearFocus();
10586 sendMouseClick(scene: &scene, point: QPointF(-25, -25));
10587 QCOMPARE(rect1->focusItem(), nullptr);
10588 QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 1);
10589 QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 1);
10590 QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 0);
10591 QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0);
10592
10593 // clicking on rect2 gives it focus
10594 rect2->setActive(true);
10595 sendMouseClick(scene: &scene, point: QPointF(75, 75));
10596 QCOMPARE(scene.focusItem(), rect2);
10597 QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 1);
10598 QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 1);
10599 QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1);
10600 QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0);
10601
10602 // clicking on rect1 does *not* give it focus
10603 rect1->setActive(true);
10604 rect1->clearFocus();
10605 sendMouseClick(scene: &scene, point: QPointF(-25, -25));
10606 QCOMPARE(scene.focusItem(), nullptr);
10607 QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 2);
10608 QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 2);
10609 QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1);
10610 QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1);
10611
10612 // focus doesn't change when leaving modality either
10613 rect2->setPanelModality(QGraphicsItem::NonModal);
10614 QCOMPARE(scene.focusItem(), nullptr);
10615 QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 2);
10616 QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 2);
10617 QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1);
10618 QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1);
10619
10620 // click on rect1, it should get focus now
10621 sendMouseClick(scene: &scene, point: QPointF(-25, -25));
10622 QCOMPARE(scene.focusItem(), rect1);
10623 QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 3);
10624 QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 2);
10625 QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1);
10626 QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1);
10627}
10628
10629void tst_QGraphicsItem::modality_keyEvents()
10630{
10631 QGraphicsScene scene;
10632 QGraphicsRectItem *rect1 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10633 rect1->setFlag(flag: QGraphicsItem::ItemIsPanel);
10634 rect1->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10635 rect1->setData(key: 0, value: "rect1");
10636
10637 QGraphicsRectItem *rect1child = scene.addRect(x: -10, y: -10, w: 20, h: 20);
10638 rect1child->setParentItem(rect1);
10639 rect1child->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10640 rect1child->setData(key: 0, value: "rect1child1");
10641
10642 QGraphicsRectItem *rect2 = scene.addRect(x: -50, y: -50, w: 100, h: 100);
10643 rect2->setParentItem(rect1);
10644 rect2->setFlag(flag: QGraphicsItem::ItemIsPanel);
10645 rect2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10646 rect2->setPos(ax: 50, ay: 50);
10647 rect2->setData(key: 0, value: "rect2");
10648
10649 QGraphicsRectItem *rect2child = scene.addRect(x: -10, y: -10, w: 20, h: 20);
10650 rect2child->setParentItem(rect2);
10651 rect2child->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10652 rect2child->setData(key: 0, value: "rect2child1");
10653
10654 QEvent windowActivateEvent(QEvent::WindowActivate);
10655 QCoreApplication::sendEvent(receiver: &scene, event: &windowActivateEvent);
10656
10657 EventSpy2 rect1Spy(&scene, rect1);
10658 EventSpy2 rect1childSpy(&scene, rect1child);
10659 EventSpy2 rect2Spy(&scene, rect2);
10660 EventSpy2 rect2childSpy(&scene, rect2child);
10661
10662 // activate rect1 and give it rect1child focus
10663 rect1->setActive(true);
10664 rect1child->setFocus();
10665 QCOMPARE(scene.focusItem(), rect1child);
10666
10667 // focus stays on rect1child when rect2 becomes modal
10668 rect2->setPanelModality(QGraphicsItem::SceneModal);
10669 QCOMPARE(scene.focusItem(), rect1child);
10670
10671 // but key events to rect1child should be neither delivered nor propagated
10672 sendKeyClick(scene: &scene, key: Qt::Key_A);
10673 sendKeyClick(scene: &scene, key: Qt::Key_S);
10674 sendKeyClick(scene: &scene, key: Qt::Key_D);
10675 sendKeyClick(scene: &scene, key: Qt::Key_F);
10676 QCOMPARE(rect1childSpy.counts[QEvent::KeyPress], 0);
10677 QCOMPARE(rect1childSpy.counts[QEvent::KeyRelease], 0);
10678 QCOMPARE(rect1Spy.counts[QEvent::KeyPress], 0);
10679 QCOMPARE(rect1Spy.counts[QEvent::KeyRelease], 0);
10680
10681 // change to panel modality, rect1child1 keeps focus
10682 rect2->setPanelModality(QGraphicsItem::PanelModal);
10683 QCOMPARE(scene.focusItem(), rect1child);
10684
10685 // still no key events
10686 sendKeyClick(scene: &scene, key: Qt::Key_J);
10687 sendKeyClick(scene: &scene, key: Qt::Key_K);
10688 sendKeyClick(scene: &scene, key: Qt::Key_L);
10689 sendKeyClick(scene: &scene, key: Qt::Key_Semicolon);
10690 QCOMPARE(rect1childSpy.counts[QEvent::KeyPress], 0);
10691 QCOMPARE(rect1childSpy.counts[QEvent::KeyRelease], 0);
10692 QCOMPARE(rect1Spy.counts[QEvent::KeyPress], 0);
10693 QCOMPARE(rect1Spy.counts[QEvent::KeyRelease], 0);
10694}
10695
10696void tst_QGraphicsItem::itemIsInFront()
10697{
10698 QGraphicsScene scene;
10699 QGraphicsRectItem *rect1 = new QGraphicsRectItem;
10700 rect1->setData(key: 0, value: "rect1");
10701 scene.addItem(item: rect1);
10702
10703 QGraphicsRectItem *rect1child1 = new QGraphicsRectItem(rect1);
10704 rect1child1->setZValue(1);
10705 rect1child1->setData(key: 0, value: "rect1child1");
10706
10707 QGraphicsRectItem *rect1child2 = new QGraphicsRectItem(rect1);
10708 rect1child2->setParentItem(rect1);
10709 rect1child2->setData(key: 0, value: "rect1child2");
10710
10711 QGraphicsRectItem *rect1child1_1 = new QGraphicsRectItem(rect1child1);
10712 rect1child1_1->setData(key: 0, value: "rect1child1_1");
10713
10714 QGraphicsRectItem *rect1child1_2 = new QGraphicsRectItem(rect1child1);
10715 rect1child1_2->setFlag(flag: QGraphicsItem::ItemStacksBehindParent);
10716 rect1child1_2->setData(key: 0, value: "rect1child1_2");
10717
10718 QGraphicsRectItem *rect2 = new QGraphicsRectItem;
10719 rect2->setData(key: 0, value: "rect2");
10720 scene.addItem(item: rect2);
10721
10722 QGraphicsRectItem *rect2child1 = new QGraphicsRectItem(rect2);
10723 rect2child1->setData(key: 0, value: "rect2child1");
10724
10725 QCOMPARE(qt_closestItemFirst(rect1, rect1), false);
10726 QCOMPARE(qt_closestItemFirst(rect1, rect2), false);
10727 QCOMPARE(qt_closestItemFirst(rect1child1, rect2child1), false);
10728 QCOMPARE(qt_closestItemFirst(rect1child1, rect1child2), true);
10729 QCOMPARE(qt_closestItemFirst(rect1child1_1, rect1child2), true);
10730 QCOMPARE(qt_closestItemFirst(rect1child1_1, rect1child1), true);
10731 QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1child2), true);
10732 QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1child1), false);
10733 QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1), true);
10734 QCOMPARE(qt_closestItemFirst(rect1child1_2, rect2), false);
10735 QCOMPARE(qt_closestItemFirst(rect1child1_2, rect2child1), false);
10736}
10737
10738using ScenePosChangeTester = ItemChangeTester;
10739
10740void tst_QGraphicsItem::scenePosChange()
10741{
10742 ScenePosChangeTester* root = new ScenePosChangeTester;
10743 ScenePosChangeTester* child1 = new ScenePosChangeTester(root);
10744 const QScopedPointer<ScenePosChangeTester> grandChild1(new ScenePosChangeTester(child1));
10745 ScenePosChangeTester* child2 = new ScenePosChangeTester(root);
10746 ScenePosChangeTester* grandChild2 = new ScenePosChangeTester(child2);
10747
10748 child1->setFlag(flag: QGraphicsItem::ItemSendsScenePositionChanges, enabled: true);
10749 grandChild2->setFlag(flag: QGraphicsItem::ItemSendsScenePositionChanges, enabled: true);
10750
10751 QVERIFY(child1->flags() & QGraphicsItem::ItemSendsScenePositionChanges);
10752 QVERIFY(grandChild2->flags() & QGraphicsItem::ItemSendsScenePositionChanges);
10753
10754 QGraphicsScene scene;
10755 scene.addItem(item: root);
10756
10757 // ignore uninteresting changes
10758 child1->clear();
10759 child2->clear();
10760 grandChild1->clear();
10761 grandChild2->clear();
10762
10763 // move whole tree
10764 root->moveBy(dx: 1.0, dy: 1.0);
10765 QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
10766 QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10767 QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10768 QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
10769
10770 // move subtree
10771 child2->moveBy(dx: 1.0, dy: 1.0);
10772 QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
10773 QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10774 QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10775 QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2);
10776
10777 // reparent
10778 grandChild2->setParentItem(child1);
10779 child1->moveBy(dx: 1.0, dy: 1.0);
10780 QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2);
10781 QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10782 QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10783 QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3);
10784
10785 // change flags
10786 grandChild1->setFlag(flag: QGraphicsItem::ItemSendsScenePositionChanges, enabled: true);
10787 grandChild2->setFlag(flag: QGraphicsItem::ItemSendsScenePositionChanges, enabled: false);
10788 QCoreApplication::processEvents(); // QGraphicsScenePrivate::_q_updateScenePosDescendants()
10789 child1->moveBy(dx: 1.0, dy: 1.0);
10790 QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3);
10791 QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
10792 QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10793 QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3);
10794
10795 // remove
10796 scene.removeItem(item: grandChild1.data());
10797 delete grandChild2; grandChild2 = nullptr;
10798 QCoreApplication::processEvents(); // QGraphicsScenePrivate::_q_updateScenePosDescendants()
10799 root->moveBy(dx: 1.0, dy: 1.0);
10800 QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 4);
10801 QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
10802 QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10803
10804 root->setX(1);
10805 QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 5);
10806 QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
10807 QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10808
10809 root->setY(1);
10810 QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 6);
10811 QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
10812 QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
10813}
10814
10815void tst_QGraphicsItem::textItem_shortcuts()
10816{
10817 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
10818 QSKIP("Window activation is not supported");
10819
10820 QWidget w;
10821 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10822 auto l = new QVBoxLayout(&w);
10823 QGraphicsScene scene;
10824 QGraphicsView view(&scene);
10825 l->addWidget(&view);
10826 QPushButton b("Push Me");
10827 l->addWidget(&b);
10828
10829 QGraphicsTextItem *item = scene.addText(text: "Troll Text");
10830 item->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10831 item->setTextInteractionFlags(Qt::TextEditorInteraction);
10832 w.show();
10833 QVERIFY(QTest::qWaitForWindowExposed(&w));
10834
10835 item->setFocus();
10836 QTRY_VERIFY(item->hasFocus());
10837 QVERIFY(item->textCursor().selectedText().isEmpty());
10838
10839 // Shortcut should work (select all)
10840 QTest::keyClick(widget: &view, key: Qt::Key_A, modifier: Qt::ControlModifier);
10841 QTRY_COMPARE(item->textCursor().selectedText(), item->toPlainText());
10842 QTextCursor tc = item->textCursor();
10843 tc.clearSelection();
10844 item->setTextCursor(tc);
10845 QVERIFY(item->textCursor().selectedText().isEmpty());
10846
10847 // Shortcut should also work if the text item has the focus and another widget
10848 // has the same shortcut.
10849 b.setShortcut(QKeySequence("CTRL+A"));
10850 QTest::keyClick(widget: &view, key: Qt::Key_A, modifier: Qt::ControlModifier);
10851 QTRY_COMPARE(item->textCursor().selectedText(), item->toPlainText());
10852}
10853
10854void tst_QGraphicsItem::scroll()
10855{
10856 // Create two overlapping rectangles in the scene:
10857 // +-------+
10858 // | | <- item1
10859 // | +-------+
10860 // | | |
10861 // +---| | <- item2
10862 // | |
10863 // +-------+
10864
10865 EventTester *item1 = new EventTester;
10866 item1->br = QRectF(0, 0, 200, 200);
10867 item1->brush = Qt::red;
10868 item1->setFlag(flag: QGraphicsItem::ItemUsesExtendedStyleOption);
10869
10870 EventTester *item2 = new EventTester;
10871 item2->br = QRectF(0, 0, 200, 200);
10872 item2->brush = Qt::blue;
10873 item2->setFlag(flag: QGraphicsItem::ItemUsesExtendedStyleOption);
10874 item2->setPos(ax: 100, ay: 100);
10875
10876 QGraphicsScene scene(0, 0, 300, 300);
10877 scene.addItem(item: item1);
10878 scene.addItem(item: item2);
10879
10880 MyGraphicsView view(&scene);
10881 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10882 view.setFrameStyle(0);
10883 view.show();
10884 QVERIFY(QTest::qWaitForWindowExposed(&view));
10885 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
10886 QVERIFY(QTest::qWaitForWindowActive(&view));
10887 QTRY_VERIFY(view.repaints > 0);
10888
10889 view.reset();
10890 item1->reset();
10891 item2->reset();
10892
10893 const QRectF item1BoundingRect = item1->boundingRect();
10894 const QRectF item2BoundingRect = item2->boundingRect();
10895
10896 // Scroll item1:
10897 // Item1 should get full exposure
10898 // Item2 should get exposure for the part that overlaps item1.
10899 item1->scroll(dx: 0, dy: -10);
10900 QTRY_VERIFY(view.repaints > 0);
10901 QCOMPARE(item1->lastExposedRect, item1BoundingRect);
10902
10903 QRectF expectedItem2Expose = item2BoundingRect;
10904 // NB! Adjusted by 2 pixels for antialiasing
10905 expectedItem2Expose &= item1->mapRectToItem(item: item2, rect: item1BoundingRect.adjusted(xp1: -2, yp1: -2, xp2: 2, yp2: 2));
10906 QCOMPARE(item2->lastExposedRect, expectedItem2Expose);
10907
10908 // Enable ItemCoordinateCache on item1.
10909 view.reset();
10910 item1->setCacheMode(mode: QGraphicsItem::ItemCoordinateCache);
10911 QTRY_VERIFY(view.repaints > 0);
10912 view.reset();
10913 item1->reset();
10914 item2->reset();
10915
10916 // Scroll item1:
10917 // Item1 should only get expose for the newly exposed area (accelerated scroll).
10918 // Item2 should get exposure for the part that overlaps item1.
10919 item1->scroll(dx: 0, dy: -10, rect: QRectF(50, 50, 100, 100));
10920 QTRY_VERIFY(view.repaints > 0);
10921 QCOMPARE(item1->lastExposedRect, QRectF(50, 140, 100, 10));
10922
10923 expectedItem2Expose = item2BoundingRect;
10924 // NB! Adjusted by 2 pixels for antialiasing
10925 expectedItem2Expose &= item1->mapRectToItem(item: item2, rect: QRectF(50, 50, 100, 100).adjusted(xp1: -2, yp1: -2, xp2: 2, yp2: 2));
10926 QCOMPARE(item2->lastExposedRect, expectedItem2Expose);
10927}
10928
10929Q_DECLARE_METATYPE(QGraphicsItem::GraphicsItemFlag);
10930
10931void tst_QGraphicsItem::focusHandling_data()
10932{
10933 QTest::addColumn<QGraphicsItem::GraphicsItemFlag>(name: "focusFlag");
10934 QTest::addColumn<bool>(name: "useStickyFocus");
10935 QTest::addColumn<int>(name: "expectedFocusItem"); // 0: none, 1: focusableUnder, 2: itemWithFocus
10936
10937 QTest::newRow(dataTag: "Focus goes through.")
10938 << static_cast<QGraphicsItem::GraphicsItemFlag>(0x0) << false << 1;
10939
10940 QTest::newRow(dataTag: "Focus goes through, even with sticky scene.")
10941 << static_cast<QGraphicsItem::GraphicsItemFlag>(0x0) << true << 1;
10942
10943 QTest::newRow(dataTag: "With ItemStopsClickFocusPropagation, we cannot focus the item beneath the flagged one (but can still focus-out).")
10944 << QGraphicsItem::ItemStopsClickFocusPropagation << false << 0;
10945
10946 QTest::newRow(dataTag: "With ItemStopsClickFocusPropagation, we cannot focus the item beneath the flagged one (and cannot focus-out if scene is sticky).")
10947 << QGraphicsItem::ItemStopsClickFocusPropagation << true << 2;
10948
10949 QTest::newRow(dataTag: "With ItemStopsFocusHandling, focus cannot be changed by presses.")
10950 << QGraphicsItem::ItemStopsFocusHandling << false << 2;
10951
10952 QTest::newRow(dataTag: "With ItemStopsFocusHandling, focus cannot be changed by presses (even if scene is sticky).")
10953 << QGraphicsItem::ItemStopsFocusHandling << true << 2;
10954}
10955
10956void tst_QGraphicsItem::focusHandling()
10957{
10958 QFETCH(QGraphicsItem::GraphicsItemFlag, focusFlag);
10959 QFETCH(bool, useStickyFocus);
10960 QFETCH(int, expectedFocusItem);
10961
10962 class MyItem : public QGraphicsRectItem
10963 {
10964 public:
10965 MyItem() : QGraphicsRectItem(0, 0, 100, 100) {}
10966 void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
10967 {
10968 painter->fillRect(boundingRect(), hasFocus() ? QBrush(Qt::red) : brush());
10969 }
10970 };
10971
10972 QGraphicsRectItem *noFocusOnTop = new MyItem;
10973 noFocusOnTop->setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: false);
10974 noFocusOnTop->setBrush(Qt::yellow);
10975
10976 QGraphicsRectItem *focusableUnder = new MyItem;
10977 focusableUnder->setBrush(Qt::blue);
10978 focusableUnder->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10979 focusableUnder->setPos(ax: 50, ay: 50);
10980
10981 QGraphicsRectItem *itemWithFocus = new MyItem;
10982 itemWithFocus->setBrush(Qt::black);
10983 itemWithFocus->setFlag(flag: QGraphicsItem::ItemIsFocusable);
10984 itemWithFocus->setPos(ax: 250, ay: 10);
10985
10986 QGraphicsScene scene(-50, -50, 400, 400);
10987 scene.addItem(item: noFocusOnTop);
10988 scene.addItem(item: focusableUnder);
10989 scene.addItem(item: itemWithFocus);
10990 scene.setStickyFocus(useStickyFocus);
10991
10992 noFocusOnTop->setFlag(flag: focusFlag);
10993 focusableUnder->stackBefore(sibling: noFocusOnTop);
10994 itemWithFocus->setFocus();
10995
10996 QGraphicsView view(&scene);
10997 view.show();
10998 QVERIFY(QTest::qWaitForWindowExposed(&view));
10999
11000 QApplication::setActiveWindow(&view);
11001 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
11002 QVERIFY(itemWithFocus->hasFocus());
11003
11004 const QPointF mousePressPoint = noFocusOnTop->mapToScene(point: noFocusOnTop->boundingRect().center());
11005 const QList<QGraphicsItem *> itemsAtMousePressPosition = scene.items(pos: mousePressPoint);
11006 QVERIFY(itemsAtMousePressPosition.contains(noFocusOnTop));
11007
11008 sendMousePress(scene: &scene, point: mousePressPoint);
11009
11010 switch (expectedFocusItem) {
11011 case 0:
11012 QCOMPARE(scene.focusItem(), nullptr);
11013 break;
11014 case 1:
11015 QCOMPARE(scene.focusItem(), focusableUnder);
11016 break;
11017 case 2:
11018 QCOMPARE(scene.focusItem(), itemWithFocus);
11019 break;
11020 }
11021
11022 // Sanity check - manually setting the focus must work regardless of our
11023 // focus handling flags:
11024 focusableUnder->setFocus();
11025 QCOMPARE(scene.focusItem(), focusableUnder);
11026}
11027
11028class TouchEventTestee : public QGraphicsRectItem
11029{
11030public:
11031 using TouchPoints = QVector<QTouchEvent::TouchPoint>;
11032
11033 TouchEventTestee(const QSizeF &size = QSizeF(100, 100)) :
11034 QGraphicsRectItem(QRectF(QPointF(), size))
11035 {
11036 setAcceptTouchEvents(true);
11037 setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: false);
11038 }
11039
11040 TouchPoints touchBeginPoints() const { return m_touchBeginPoints; }
11041 int touchBeginEventCount() const { return m_touchBeginPoints.size(); }
11042
11043 TouchPoints touchUpdatePoints() const { return m_touchUpdatePoints; }
11044 int touchUpdateEventCount() const { return m_touchUpdatePoints.size(); }
11045
11046protected:
11047 bool sceneEvent(QEvent *ev) override
11048 {
11049 switch (ev->type()) {
11050 case QEvent::TouchBegin:
11051 m_touchBeginPoints.append(t: static_cast<const QTouchEvent *>(ev)->touchPoints().constFirst());
11052 ev->accept();
11053 return true;
11054 case QEvent::TouchUpdate:
11055 m_touchUpdatePoints.append(t: static_cast<const QTouchEvent *>(ev)->touchPoints().constFirst());
11056 ev->accept();
11057 return true;
11058 default:
11059 break;
11060 }
11061
11062 return QGraphicsRectItem::sceneEvent(event: ev);
11063 }
11064
11065private:
11066 TouchPoints m_touchBeginPoints;
11067 TouchPoints m_touchUpdatePoints;
11068};
11069
11070static QList<QTouchEvent::TouchPoint>
11071 createTouchPoints(const QGraphicsView &view,
11072 const QPointF &scenePos,
11073 const QSizeF &ellipseDiameters,
11074 Qt::TouchPointState state = Qt::TouchPointPressed)
11075{
11076 QTouchEvent::TouchPoint tp(0);
11077 tp.setState(state);
11078 tp.setScenePos(scenePos);
11079 tp.setStartScenePos(scenePos);
11080 tp.setLastScenePos(scenePos);
11081 const QPointF screenPos = view.viewport()->mapToGlobal(view.mapFromScene(point: scenePos));
11082 tp.setScreenPos(screenPos);
11083 tp.setStartScreenPos(screenPos);
11084 tp.setLastScreenPos(screenPos);
11085 tp.setEllipseDiameters(ellipseDiameters);
11086 const QSizeF screenSize = view.screen()->geometry().size();
11087 tp.setNormalizedPos(QPointF(screenPos.x() / screenSize.width(), screenPos.y() / screenSize.height()));
11088 return QList<QTouchEvent::TouchPoint>() << tp;
11089}
11090
11091static bool comparePointF(const QPointF &p1, const QPointF &p2)
11092{
11093 return qFuzzyCompare(p1: p1.x(), p2: p2.x()) && qFuzzyCompare(p1: p1.y(), p2: p2.y());
11094}
11095
11096static bool compareSizeF(const QSizeF &s1, const QSizeF &s2)
11097{
11098 return qFuzzyCompare(p1: s1.width(), p2: s2.width()) && qFuzzyCompare(p1: s1.height(), p2: s2.height());
11099}
11100
11101static QByteArray msgPointFComparisonFailed(const QPointF &p1, const QPointF &p2)
11102{
11103 return QByteArray::number(p1.x()) + ", " + QByteArray::number(p1.y())
11104 + " != " + QByteArray::number(p2.x()) + ", " + QByteArray::number(p2.y());
11105}
11106
11107static QByteArray msgSizeFComparisonFailed(const QSizeF &s1, const QSizeF &s2)
11108{
11109 return QByteArray::number(s1.width()) + 'x' + QByteArray::number(s1.height())
11110 + " != " + QByteArray::number(s2.width()) + 'x' + QByteArray::number(s2.height());
11111}
11112
11113#define COMPARE_POINTF(ACTUAL, EXPECTED) \
11114 QVERIFY2(comparePointF(ACTUAL, EXPECTED), msgPointFComparisonFailed(ACTUAL, EXPECTED).constData())
11115
11116#define COMPARE_SIZEF(ACTUAL, EXPECTED) \
11117 QVERIFY2(compareSizeF(ACTUAL, EXPECTED), msgSizeFComparisonFailed(ACTUAL, EXPECTED).constData())
11118
11119void tst_QGraphicsItem::touchEventPropagation_data()
11120{
11121 QTest::addColumn<QGraphicsItem::GraphicsItemFlag>(name: "flag");
11122 QTest::addColumn<int>(name: "expectedCount");
11123
11124 QTest::newRow(dataTag: "ItemIsPanel")
11125 << QGraphicsItem::ItemIsPanel << 0;
11126 QTest::newRow(dataTag: "ItemStopsClickFocusPropagation")
11127 << QGraphicsItem::ItemStopsClickFocusPropagation << 1;
11128 QTest::newRow(dataTag: "ItemStopsFocusHandling")
11129 << QGraphicsItem::ItemStopsFocusHandling << 1;
11130}
11131
11132void tst_QGraphicsItem::touchEventPropagation()
11133{
11134 QFETCH(QGraphicsItem::GraphicsItemFlag, flag);
11135 QFETCH(int, expectedCount);
11136
11137 TouchEventTestee *touchEventReceiver = new TouchEventTestee;
11138 QGraphicsItem *topMost = new QGraphicsRectItem(touchEventReceiver->boundingRect());
11139
11140 QGraphicsScene scene;
11141 scene.addItem(item: topMost);
11142 scene.addItem(item: touchEventReceiver);
11143
11144 topMost->setAcceptTouchEvents(true);
11145 topMost->setZValue(FLT_MAX);
11146 topMost->setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: false);
11147 topMost->setFlag(flag, enabled: true);
11148
11149 QGraphicsView view(&scene);
11150 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
11151 + QLatin1String(QTest::currentDataTag()));
11152 view.setSceneRect(touchEventReceiver->boundingRect());
11153 view.show();
11154 QVERIFY(QTest::qWaitForWindowExposed(&view));
11155
11156 QCOMPARE(touchEventReceiver->touchBeginEventCount(), 0);
11157
11158 const QPointF scenePos = view.sceneRect().center();
11159 sendMousePress(scene: &scene, point: scenePos);
11160 if (m_touchDevice == nullptr)
11161 m_touchDevice = QTest::createTouchDevice();
11162 QTouchEvent touchBegin(QEvent::TouchBegin, m_touchDevice, Qt::NoModifier, Qt::TouchPointPressed,
11163 createTouchPoints(view, scenePos, ellipseDiameters: QSizeF(10, 10)));
11164 touchBegin.setTarget(view.viewport());
11165
11166 qApp->sendEvent(receiver: &scene, event: &touchBegin);
11167 QCOMPARE(touchEventReceiver->touchBeginEventCount(), expectedCount);
11168}
11169
11170void tst_QGraphicsItem::touchEventTransformation_data()
11171{
11172 QTest::addColumn<QGraphicsItem::GraphicsItemFlag>(name: "flag");
11173 QTest::addColumn<QTransform>(name: "viewTransform");
11174 QTest::addColumn<QPointF>(name: "touchScenePos");
11175 QTest::addColumn<QSizeF>(name: "ellipseDiameters");
11176 QTest::addColumn<QPointF>(name: "expectedItemPos");
11177
11178 QTest::newRow(dataTag: "notransform")
11179 << QGraphicsItem::ItemIsSelectable << QTransform()
11180 << QPointF(150, 150) << QSizeF(7, 8) << QPointF(50, 50);
11181 QTest::newRow(dataTag: "scaled")
11182 << QGraphicsItem::ItemIsSelectable << QTransform::fromScale(dx: 0.5, dy: 0.5)
11183 << QPointF(150, 150) << QSizeF(7, 8) << QPointF(50, 50);
11184 // QTBUG-66192: When the item ignores the downscaling transformation,
11185 // it will receive the touch point at 25,25 instead of 50,50.
11186 QTest::newRow(dataTag: "scaled/ItemIgnoresTransformations")
11187 << QGraphicsItem::ItemIgnoresTransformations << QTransform::fromScale(dx: 0.5, dy: 0.5)
11188 << QPointF(150, 150) << QSizeF(7, 8) << QPointF(25, 25);
11189}
11190
11191void tst_QGraphicsItem::touchEventTransformation()
11192{
11193 QFETCH(QGraphicsItem::GraphicsItemFlag, flag);
11194 QFETCH(QTransform, viewTransform);
11195 QFETCH(QPointF, touchScenePos);
11196 QFETCH(QSizeF, ellipseDiameters);
11197 QFETCH(QPointF, expectedItemPos);
11198
11199 TouchEventTestee *touchEventReceiver = new TouchEventTestee;
11200
11201 QGraphicsScene scene;
11202 scene.addItem(item: touchEventReceiver);
11203 const QPointF itemPos(100, 100);
11204
11205 touchEventReceiver->setPos(itemPos);
11206
11207 touchEventReceiver->setFlag(flag, enabled: true);
11208
11209 QGraphicsView view(&scene);
11210 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
11211 + QLatin1String(QTest::currentDataTag()));
11212 view.setSceneRect(QRectF(QPointF(0, 0), QSizeF(300, 300)));
11213 view.setTransform(matrix: viewTransform);
11214 view.show();
11215 QVERIFY(QTest::qWaitForWindowExposed(&view));
11216
11217 QCOMPARE(touchEventReceiver->touchBeginEventCount(), 0);
11218
11219 if (m_touchDevice == nullptr)
11220 m_touchDevice = QTest::createTouchDevice();
11221 QTouchEvent touchBegin(QEvent::TouchBegin, m_touchDevice, Qt::NoModifier, Qt::TouchPointPressed,
11222 createTouchPoints(view, scenePos: touchScenePos, ellipseDiameters));
11223 touchBegin.setTarget(view.viewport());
11224
11225 QCoreApplication::sendEvent(receiver: &scene, event: &touchBegin);
11226 QCOMPARE(touchEventReceiver->touchBeginEventCount(), 1);
11227
11228 const QTouchEvent::TouchPoint touchBeginPoint = touchEventReceiver->touchBeginPoints().constFirst();
11229
11230 COMPARE_POINTF(touchBeginPoint.scenePos(), touchScenePos);
11231 COMPARE_POINTF(touchBeginPoint.startScenePos(), touchScenePos);
11232 COMPARE_POINTF(touchBeginPoint.lastScenePos(), touchScenePos);
11233 COMPARE_POINTF(touchBeginPoint.pos(), expectedItemPos);
11234 COMPARE_SIZEF(touchBeginPoint.ellipseDiameters(), ellipseDiameters); // Must remain untransformed
11235
11236 QTouchEvent touchUpdate(QEvent::TouchUpdate, m_touchDevice, Qt::NoModifier, Qt::TouchPointMoved,
11237 createTouchPoints(view, scenePos: touchScenePos, ellipseDiameters, state: Qt::TouchPointMoved));
11238 touchUpdate.setTarget(view.viewport());
11239
11240 QCoreApplication::sendEvent(receiver: &scene, event: &touchUpdate);
11241 QCOMPARE(touchEventReceiver->touchUpdateEventCount(), 1);
11242
11243 const QTouchEvent::TouchPoint touchUpdatePoint = touchEventReceiver->touchUpdatePoints().constFirst();
11244
11245 COMPARE_POINTF(touchUpdatePoint.scenePos(), touchScenePos);
11246 COMPARE_POINTF(touchBeginPoint.startScenePos(), touchScenePos);
11247 COMPARE_POINTF(touchUpdatePoint.lastScenePos(), touchScenePos);
11248 COMPARE_POINTF(touchUpdatePoint.pos(), expectedItemPos);
11249 COMPARE_SIZEF(touchUpdatePoint.ellipseDiameters(), ellipseDiameters); // Must remain untransformed
11250
11251}
11252
11253void tst_QGraphicsItem::deviceCoordinateCache_simpleRotations()
11254{
11255 // Make sure we don't invalidate the cache when applying simple
11256 // (90, 180, 270, 360) rotation transforms to the item.
11257 QGraphicsRectItem *item = new QGraphicsRectItem(0, 0, 300, 200);
11258 item->setBrush(Qt::red);
11259 item->setCacheMode(mode: QGraphicsItem::DeviceCoordinateCache);
11260
11261 QGraphicsScene scene;
11262 scene.setSceneRect(x: 0, y: 0, w: 300, h: 200);
11263 scene.addItem(item);
11264
11265 MyGraphicsView view(&scene);
11266 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11267 view.show();
11268 QVERIFY(QTest::qWaitForWindowExposed(&view));
11269 QTRY_VERIFY(view.repaints > 0);
11270
11271 QGraphicsItemCache *itemCache = QGraphicsItemPrivate::get(item)->extraItemCache();
11272 QVERIFY(itemCache);
11273 QPixmapCache::Key currentKey = itemCache->deviceData.value(key: view.viewport()).key;
11274
11275 // Trigger an update and verify that the cache is unchanged.
11276 QPixmapCache::Key oldKey = currentKey;
11277 view.reset();
11278 view.viewport()->update();
11279 QTRY_VERIFY(view.repaints > 0);
11280 currentKey = itemCache->deviceData.value(key: view.viewport()).key;
11281 QCOMPARE(currentKey, oldKey);
11282
11283 // Check 90, 180, 270 and 360 degree rotations.
11284 for (int angle = 90; angle <= 360; angle += 90) {
11285 // Rotate item and verify that the cache was invalidated.
11286 oldKey = currentKey;
11287 view.reset();
11288 QTransform transform;
11289 transform.translate(dx: 150, dy: 100);
11290 transform.rotate(a: angle);
11291 transform.translate(dx: -150, dy: -100);
11292 item->setTransform(matrix: transform);
11293 QTRY_VERIFY(view.repaints > 0);
11294 currentKey = itemCache->deviceData.value(key: view.viewport()).key;
11295 QVERIFY(currentKey != oldKey);
11296
11297 // IMPORTANT PART:
11298 // Trigger an update and verify that the cache is unchanged.
11299 oldKey = currentKey;
11300 view.reset();
11301 view.viewport()->update();
11302 QTRY_VERIFY(view.repaints > 0);
11303 currentKey = itemCache->deviceData.value(key: view.viewport()).key;
11304 QCOMPARE(currentKey, oldKey);
11305 }
11306
11307 // 45 degree rotation.
11308 oldKey = currentKey;
11309 view.reset();
11310 QTransform transform;
11311 transform.translate(dx: 150, dy: 100);
11312 transform.rotate(a: 45);
11313 transform.translate(dx: -150, dy: -100);
11314 item->setTransform(matrix: transform);
11315 QTRY_VERIFY(view.repaints > 0);
11316 currentKey = itemCache->deviceData.value(key: view.viewport()).key;
11317 QVERIFY(currentKey != oldKey);
11318
11319 // Trigger an update and verify that the cache was invalidated.
11320 // We should always invalidate the cache for non-trivial transforms.
11321 oldKey = currentKey;
11322 view.reset();
11323 view.viewport()->update();
11324 QTRY_VERIFY(view.repaints > 0);
11325 currentKey = itemCache->deviceData.value(key: view.viewport()).key;
11326 QVERIFY(currentKey != oldKey);
11327}
11328
11329void tst_QGraphicsItem::QTBUG_5418_textItemSetDefaultColor()
11330{
11331 struct Item : public QGraphicsTextItem
11332 {
11333 int painted;
11334 void paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *wid) override
11335 {
11336 painted++;
11337 QGraphicsTextItem::paint(painter, option: opt, widget: wid);
11338 }
11339 };
11340
11341 Item *i = new Item;
11342 i->painted = 0;
11343 i->setPlainText("I AM A TROLL");
11344
11345 QGraphicsScene scene;
11346 QGraphicsView view(&scene);
11347 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11348 view.show();
11349 QVERIFY(QTest::qWaitForWindowExposed(&view));
11350 scene.addItem(item: i);
11351 QApplication::processEvents();
11352 QTRY_VERIFY(i->painted);
11353 QApplication::processEvents();
11354
11355 i->painted = 0;
11356 QColor col(Qt::red);
11357 i->setDefaultTextColor(col);
11358 QApplication::processEvents();
11359 QTRY_COMPARE(i->painted, 1); //check that changing the color force an update
11360
11361 i->painted = false;
11362 QImage image(400, 200, QImage::Format_RGB32);
11363 image.fill(pixel: 0);
11364 QPainter painter(&image);
11365 scene.render(painter: &painter);
11366 painter.end();
11367 QCOMPARE(i->painted, 1);
11368
11369 int numRedPixel = 0;
11370 QRgb rgb = col.rgb();
11371 for (int y = 0; y < image.height(); ++y) {
11372 for (int x = 0; x < image.width(); ++x) {
11373 // Because of antialiasing we allow a certain range of errors here.
11374 QRgb pixel = image.pixel(x, y);
11375 if (qAbs(t: int(pixel & 0xff) - int(rgb & 0xff)) +
11376 qAbs(t: int((pixel & 0xff00) >> 8) - int((rgb & 0xff00) >> 8)) +
11377 qAbs(t: int((pixel & 0xff0000) >> 16) - int((rgb & 0xff0000) >> 16)) <= 50) {
11378 if (++numRedPixel >= 10) {
11379 return;
11380 }
11381 }
11382 }
11383 }
11384 QCOMPARE(numRedPixel, -1); //color not found, FAIL!
11385
11386 i->painted = 0;
11387 i->setDefaultTextColor(col);
11388 QApplication::processEvents();
11389 QCOMPARE(i->painted, 0); //same color as before should not trigger an update (QTBUG-6242)
11390}
11391
11392void tst_QGraphicsItem::QTBUG_6738_missingUpdateWithSetParent()
11393{
11394 // In all 3 test cases below the reparented item should disappear
11395 EventTester *parent = new EventTester;
11396 EventTester *child = new EventTester(parent);
11397 EventTester *child2 = new EventTester(parent);
11398 EventTester *child3 = new EventTester(parent);
11399 EventTester *child4 = new EventTester(parent);
11400
11401 child->setPos(ax: 10, ay: 10);
11402 child2->setPos(ax: 20, ay: 20);
11403 child3->setPos(ax: 30, ay: 30);
11404 child4->setPos(ax: 40, ay: 40);
11405
11406 QGraphicsScene scene;
11407 scene.addItem(item: parent);
11408
11409 MyGraphicsView view(&scene);
11410 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11411 view.show();
11412 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) {
11413 qApp->setActiveWindow(&view);
11414 QVERIFY(QTest::qWaitForWindowActive(&view));
11415 }
11416 QVERIFY(QTest::qWaitForWindowExposed(&view));
11417 QCoreApplication::processEvents(); // Process all queued paint events
11418 QTRY_VERIFY(view.repaints > 0);
11419
11420 // test case #1
11421 view.reset();
11422 child2->setVisible(false);
11423 child2->setParentItem(child);
11424
11425 QTRY_COMPARE(view.repaints, 1);
11426
11427 // test case #2
11428 view.reset();
11429 child3->setOpacity(0.0);
11430 child3->setParentItem(child);
11431
11432 QTRY_COMPARE(view.repaints, 1);
11433
11434 // test case #3
11435 view.reset();
11436 child4->setParentItem(child);
11437 child4->setVisible(false);
11438
11439 QTRY_COMPARE(view.repaints, 1);
11440}
11441
11442void tst_QGraphicsItem::QT_2653_fullUpdateDiscardingOpacityUpdate()
11443{
11444 QGraphicsScene scene(0, 0, 200, 200);
11445 MyGraphicsView view(&scene);
11446 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11447
11448 EventTester *parentGreen = new EventTester();
11449 parentGreen->setGeometry(QRectF(20, 20, 100, 100));
11450 parentGreen->brush = Qt::green;
11451
11452 EventTester *childYellow = new EventTester(parentGreen);
11453 childYellow->setGeometry(QRectF(10, 10, 50, 50));
11454 childYellow->brush = Qt::yellow;
11455
11456 scene.addItem(item: parentGreen);
11457
11458 childYellow->setOpacity(0.0);
11459 parentGreen->setOpacity(0.0);
11460
11461 // set any of the flags below to trigger a fullUpdate to reproduce the bug:
11462 // ItemIgnoresTransformations, ItemClipsChildrenToShape, ItemIsSelectable
11463 parentGreen->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
11464
11465 view.show();
11466 QVERIFY(QTest::qWaitForWindowExposed(&view));
11467 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
11468 QVERIFY(QTest::qWaitForWindowActive(&view));
11469 QCoreApplication::processEvents(); // Process all queued paint events
11470 view.reset();
11471
11472 parentGreen->setOpacity(1.0);
11473
11474 QTRY_COMPARE(view.repaints, 1);
11475
11476 view.reset();
11477 childYellow->repaints = 0;
11478
11479 childYellow->setOpacity(1.0);
11480
11481 QTRY_COMPARE(view.repaints, 1);
11482 QTRY_COMPARE(childYellow->repaints, 1);
11483}
11484
11485void tst_QGraphicsItem::QTBUG_7714_fullUpdateDiscardingOpacityUpdate2()
11486{
11487 QGraphicsScene scene(0, 0, 200, 200);
11488 MyGraphicsView view(&scene);
11489 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11490 MyGraphicsView origView(&scene);
11491
11492 EventTester *parentGreen = new EventTester();
11493 parentGreen->setGeometry(QRectF(20, 20, 100, 100));
11494 parentGreen->brush = Qt::green;
11495
11496 EventTester *childYellow = new EventTester(parentGreen);
11497 childYellow->setGeometry(QRectF(10, 10, 50, 50));
11498 childYellow->brush = Qt::yellow;
11499
11500 scene.addItem(item: parentGreen);
11501
11502 origView.show();
11503 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
11504 QVERIFY(QTest::qWaitForWindowActive(&origView));
11505 QVERIFY(QTest::qWaitForWindowExposed(&origView));
11506 QCoreApplication::processEvents(); // Process all queued paint events
11507
11508 origView.setGeometry(ax: origView.x() + origView.width() + 20, ay: origView.y() + 20,
11509 aw: origView.width(), ah: origView.height());
11510
11511 parentGreen->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
11512
11513 origView.reset();
11514 childYellow->setOpacity(0.0);
11515
11516 QTRY_VERIFY(origView.repaints > 0);
11517
11518 view.show();
11519 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) {
11520 qApp->setActiveWindow(&view);
11521 QVERIFY(QTest::qWaitForWindowActive(&view));
11522 }
11523 QVERIFY(QTest::qWaitForWindowExposed(&view));
11524 view.reset();
11525 origView.reset();
11526
11527 childYellow->setOpacity(1.0);
11528
11529#ifdef Q_OS_WINRT
11530 QEXPECT_FAIL("", "Fails on WinRT. Figure out why - QTBUG-68297", Abort);
11531#endif
11532
11533 QTRY_VERIFY(origView.repaints > 0);
11534 QTRY_VERIFY(view.repaints > 0);
11535}
11536
11537void tst_QGraphicsItem::QT_2649_focusScope()
11538{
11539 QGraphicsScene *scene = new QGraphicsScene;
11540
11541 QGraphicsRectItem *subFocusItem = new QGraphicsRectItem;
11542 subFocusItem->setFlags(QGraphicsItem::ItemIsFocusable);
11543 subFocusItem->setFocus();
11544 QCOMPARE(subFocusItem->focusItem(), subFocusItem);
11545
11546 QGraphicsRectItem *scope = new QGraphicsRectItem;
11547 scope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
11548 scope->setFocus();
11549 subFocusItem->setParentItem(scope);
11550 QCOMPARE(subFocusItem->focusItem(), subFocusItem);
11551 QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
11552 QCOMPARE(scope->focusItem(), subFocusItem);
11553 QCOMPARE(scope->focusScopeItem(), subFocusItem);
11554
11555 QGraphicsRectItem *rootItem = new QGraphicsRectItem;
11556 rootItem->setFlags(QGraphicsItem::ItemIsFocusable);
11557 scope->setParentItem(rootItem);
11558 QCOMPARE(rootItem->focusItem(), subFocusItem);
11559 QCOMPARE(rootItem->focusScopeItem(), nullptr);
11560 QCOMPARE(subFocusItem->focusItem(), subFocusItem);
11561 QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
11562 QCOMPARE(scope->focusItem(), subFocusItem);
11563 QCOMPARE(scope->focusScopeItem(), subFocusItem);
11564
11565 scene->addItem(item: rootItem);
11566
11567 QEvent windowActivate(QEvent::WindowActivate);
11568 qApp->sendEvent(receiver: scene, event: &windowActivate);
11569 scene->setFocus();
11570
11571 QCOMPARE(rootItem->focusItem(), subFocusItem);
11572 QCOMPARE(scope->focusItem(), subFocusItem);
11573 QCOMPARE(subFocusItem->focusItem(), subFocusItem);
11574 QCOMPARE(rootItem->focusScopeItem(), nullptr);
11575 QCOMPARE(scope->focusScopeItem(), subFocusItem);
11576 QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
11577 QVERIFY(subFocusItem->hasFocus());
11578
11579 scope->hide();
11580
11581 QCOMPARE(rootItem->focusItem(), nullptr);
11582 QCOMPARE(scope->focusItem(), nullptr);
11583 QCOMPARE(subFocusItem->focusItem(), nullptr);
11584 QCOMPARE(rootItem->focusScopeItem(), nullptr);
11585 QCOMPARE(scope->focusScopeItem(), subFocusItem);
11586 QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
11587 QVERIFY(!subFocusItem->hasFocus());
11588
11589 scope->show();
11590
11591 QCOMPARE(rootItem->focusItem(), subFocusItem);
11592 QCOMPARE(scope->focusItem(), subFocusItem);
11593 QCOMPARE(subFocusItem->focusItem(), subFocusItem);
11594 QCOMPARE(rootItem->focusScopeItem(), nullptr);
11595 QCOMPARE(scope->focusScopeItem(), subFocusItem);
11596 QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
11597 QVERIFY(subFocusItem->hasFocus());
11598
11599 // This should not crash
11600 scope->hide();
11601 delete scene;
11602}
11603
11604class MyGraphicsItemWithItemChange : public QGraphicsWidget
11605{
11606public:
11607 using QGraphicsWidget::QGraphicsWidget;
11608
11609 QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
11610 {
11611 if (change == QGraphicsItem::ItemSceneHasChanged) {
11612 const auto views = scene()->views();
11613 for (QGraphicsView *view : views) // We trigger a sort of unindexed items in the BSP
11614 view->sceneRect();
11615 }
11616 return QGraphicsWidget::itemChange(change, value);
11617 }
11618};
11619
11620void tst_QGraphicsItem::sortItemsWhileAdding()
11621{
11622 QGraphicsScene scene;
11623 QGraphicsView view(&scene);
11624 QGraphicsWidget grandGrandParent;
11625 grandGrandParent.resize(w: 200, h: 200);
11626 scene.addItem(item: &grandGrandParent);
11627 QGraphicsWidget grandParent;
11628 grandParent.resize(w: 200, h: 200);
11629 QGraphicsWidget parent(&grandParent);
11630 parent.resize(w: 200, h: 200);
11631 MyGraphicsItemWithItemChange item(&parent);
11632 grandParent.setParentItem(&grandGrandParent);
11633}
11634
11635void tst_QGraphicsItem::doNotMarkFullUpdateIfNotInScene()
11636{
11637 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
11638 QSKIP("Window activation is not supported");
11639
11640 struct Item : public QGraphicsTextItem
11641 {
11642 int painted = 0;
11643 void paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *wid) override
11644 {
11645 painted++;
11646 QGraphicsTextItem::paint(painter, option: opt, widget: wid);
11647 }
11648 };
11649 QGraphicsScene scene;
11650 MyGraphicsView view(&scene);
11651 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11652 Item *item = new Item;
11653 item->setPlainText("Grandparent");
11654 Item *item2 = new Item;
11655 item2->setPlainText("parent");
11656 Item *item3 = new Item;
11657 item3->setPlainText("child");
11658 QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect;
11659 effect->setOpacity(0.5);
11660 item2->setGraphicsEffect(effect);
11661 item3->setParentItem(item2);
11662 item2->setParentItem(item);
11663 scene.addItem(item);
11664 view.show();
11665 QVERIFY(QTest::qWaitForWindowExposed(view.windowHandle()));
11666 QVERIFY(QTest::qWaitForWindowActive(view.windowHandle()));
11667 QCoreApplication::processEvents(); // Process all queued paint events
11668 view.activateWindow();
11669 QTRY_VERIFY(view.isActiveWindow());
11670 QTRY_VERIFY(view.repaints >= 1);
11671 int count = view.repaints;
11672 QTRY_COMPARE(item->painted, count);
11673 // cached as graphics effects, not painted multiple times
11674 QTRY_COMPARE(item2->painted, 1);
11675 QTRY_COMPARE(item3->painted, 1);
11676 item2->update();
11677 QApplication::processEvents();
11678 QTRY_COMPARE(item->painted, count + 1);
11679 QTRY_COMPARE(item2->painted, 2);
11680 QTRY_COMPARE(item3->painted, 2);
11681 item2->update();
11682 QApplication::processEvents();
11683 QTRY_COMPARE(item->painted, count + 2);
11684 QTRY_COMPARE(item2->painted, 3);
11685 QTRY_COMPARE(item3->painted, 3);
11686}
11687
11688void tst_QGraphicsItem::itemDiesDuringDraggingOperation()
11689{
11690 QGraphicsScene scene;
11691 QGraphicsView view(&scene);
11692 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11693 QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
11694 item->setFlag(flag: QGraphicsItem::ItemIsMovable);
11695 item->setAcceptDrops(true);
11696 scene.addItem(item);
11697 view.show();
11698 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) {
11699 QApplication::setActiveWindow(&view);
11700 QVERIFY(QTest::qWaitForWindowActive(&view));
11701 QCOMPARE(QApplication::activeWindow(), &view);
11702 }
11703 QVERIFY(QTest::qWaitForWindowExposed(&view));
11704 QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter);
11705 dragEnter.setScenePos(item->boundingRect().center());
11706 QCoreApplication::sendEvent(receiver: &scene, event: &dragEnter);
11707 QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove);
11708 event.setScenePos(item->boundingRect().center());
11709 QCoreApplication::sendEvent(receiver: &scene, event: &event);
11710 QCOMPARE(QGraphicsScenePrivate::get(&scene)->dragDropItem, item);
11711 delete item;
11712 QVERIFY(!QGraphicsScenePrivate::get(&scene)->dragDropItem);
11713}
11714
11715void tst_QGraphicsItem::QTBUG_12112_focusItem()
11716{
11717 QGraphicsScene scene;
11718 QGraphicsView view(&scene);
11719 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11720 QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, 20, 20);
11721 item1->setFlag(flag: QGraphicsItem::ItemIsFocusable);
11722 QGraphicsRectItem *item2 = new QGraphicsRectItem(20, 20, 20, 20);
11723 item2->setFlag(flag: QGraphicsItem::ItemIsFocusable);
11724 item1->setFocus();
11725 scene.addItem(item: item2);
11726 scene.addItem(item: item1);
11727
11728 view.show();
11729 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) {
11730 QApplication::setActiveWindow(&view);
11731 QVERIFY(QTest::qWaitForWindowActive(&view));
11732 QCOMPARE(QApplication::activeWindow(), &view);
11733 }
11734 QVERIFY(QTest::qWaitForWindowExposed(&view));
11735
11736 QVERIFY(item1->focusItem());
11737 QVERIFY(!item2->focusItem());
11738
11739 item2->setFocus();
11740 QVERIFY(!item1->focusItem());
11741 QVERIFY(item2->focusItem());
11742}
11743
11744void tst_QGraphicsItem::QTBUG_13473_sceneposchange()
11745{
11746 ScenePosChangeTester* parent = new ScenePosChangeTester;
11747 ScenePosChangeTester* child = new ScenePosChangeTester(parent);
11748
11749 // parent's disabled ItemSendsGeometryChanges flag must not affect
11750 // child's scene pos change notifications
11751 parent->setFlag(flag: QGraphicsItem::ItemSendsGeometryChanges, enabled: false);
11752 child->setFlag(flag: QGraphicsItem::ItemSendsScenePositionChanges, enabled: true);
11753
11754 QGraphicsScene scene;
11755 scene.addItem(item: parent);
11756
11757 // ignore uninteresting changes
11758 parent->clear();
11759 child->clear();
11760
11761 // move
11762 parent->moveBy(dx: 1.0, dy: 1.0);
11763 QCOMPARE(child->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
11764
11765 // transform
11766 parent->setTransform(matrix: QTransform::fromScale(dx: 0.5, dy: 0.5));
11767 QCOMPARE(child->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2);
11768}
11769
11770class MyGraphicsWidget : public QGraphicsWidget
11771{
11772 Q_OBJECT
11773public:
11774 MyGraphicsWidget() : QGraphicsWidget(nullptr)
11775 {
11776 QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical);
11777 QLatin1String wiseWords("AZ BUKI VEDI");
11778 QString sentence(wiseWords);
11779 QStringList words = sentence.split(sep: QLatin1Char(' '), behavior: Qt::SkipEmptyParts);
11780 for (int i = 0; i < words.count(); ++i) {
11781 QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(this);
11782 QLabel *label = new QLabel(words.at(i));
11783 proxy->setWidget(label);
11784 proxy->setFocusPolicy(Qt::StrongFocus);
11785 proxy->setFlag(flag: QGraphicsItem::ItemAcceptsInputMethod, enabled: true);
11786 if (i % 2 == 0)
11787 proxy->setVisible(false);
11788 proxy->setFocus();
11789 lay->addItem(item: proxy);
11790 }
11791 setLayout(lay);
11792 }
11793};
11794
11795class MyWidgetWindow : public QGraphicsWidget
11796{
11797public:
11798 MyWidgetWindow() : QGraphicsWidget(nullptr, Qt::Window)
11799 {
11800 QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical);
11801 MyGraphicsWidget *widget = new MyGraphicsWidget();
11802 lay->addItem(item: widget);
11803 setLayout(lay);
11804 }
11805};
11806
11807void tst_QGraphicsItem::QTBUG_16374_crashInDestructor()
11808{
11809 QGraphicsScene scene;
11810 QGraphicsView view(&scene);
11811 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11812
11813 MyWidgetWindow win;
11814 scene.addItem(item: &win);
11815
11816 view.show();
11817 QVERIFY(QTest::qWaitForWindowExposed(&view));
11818}
11819
11820void tst_QGraphicsItem::QTBUG_20699_focusScopeCrash()
11821{
11822 QGraphicsScene scene;
11823 QGraphicsView view(&scene);
11824 view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11825 QGraphicsPixmapItem fs;
11826 fs.setFlags(QGraphicsItem::ItemIsFocusScope | QGraphicsItem::ItemIsFocusable);
11827 scene.addItem(item: &fs);
11828 QGraphicsPixmapItem* fs2 = new QGraphicsPixmapItem(&fs);
11829 fs2->setFlags(QGraphicsItem::ItemIsFocusScope | QGraphicsItem::ItemIsFocusable);
11830 QGraphicsPixmapItem* fi2 = new QGraphicsPixmapItem(&fs);
11831 fi2->setFlags(QGraphicsItem::ItemIsFocusable);
11832 QGraphicsPixmapItem* fi = new QGraphicsPixmapItem(fs2);
11833 fi->setFlags(QGraphicsItem::ItemIsFocusable);
11834 fs.setFocus();
11835 fi->setFocus();
11836
11837 view.show();
11838 QVERIFY(QTest::qWaitForWindowExposed(&view));
11839
11840 fi->setParentItem(fi2);
11841 fi->setFocus();
11842 fs.setFocus();
11843 fi->setParentItem(fs2);
11844 fi->setFocus();
11845 fs2->setFocus();
11846 fs.setFocus();
11847 fi->setParentItem(fi2);
11848 fi->setFocus();
11849 fs.setFocus();
11850}
11851
11852void tst_QGraphicsItem::QTBUG_30990_rightClickSelection()
11853{
11854 QGraphicsScene scene;
11855 QGraphicsItem *item1 = scene.addRect(x: 10, y: 10, w: 10, h: 10);
11856 item1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
11857 QGraphicsItem *item2 = scene.addRect(x: 100, y: 100, w: 10, h: 10);
11858 item2->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
11859
11860 // right mouse press & release over an item should not make it selected
11861 sendMousePress(scene: &scene, point: item1->boundingRect().center(), button: Qt::RightButton);
11862 QVERIFY(!item1->isSelected());
11863 sendMouseRelease(scene: &scene, point: item1->boundingRect().center(), button: Qt::RightButton);
11864 QVERIFY(!item1->isSelected());
11865
11866 // right mouse press over one item, moving over another item,
11867 // and then releasing should make neither of the items selected
11868 sendMousePress(scene: &scene, point: item1->boundingRect().center(), button: Qt::RightButton);
11869 QVERIFY(!item1->isSelected());
11870 QVERIFY(!item2->isSelected());
11871 sendMouseMove(scene: &scene, point: item2->boundingRect().center(), button: Qt::RightButton);
11872 QVERIFY(!item1->isSelected());
11873 QVERIFY(!item2->isSelected());
11874 sendMouseRelease(scene: &scene, point: item2->boundingRect().center(), button: Qt::RightButton);
11875 QVERIFY(!item1->isSelected());
11876 QVERIFY(!item2->isSelected());
11877}
11878
11879void tst_QGraphicsItem::QTBUG_21618_untransformable_sceneTransform()
11880{
11881 QGraphicsScene scene(0, 0, 150, 150);
11882 scene.addRect(x: -2, y: -2, w: 4, h: 4);
11883
11884 QGraphicsItem *item1 = scene.addRect(x: 0, y: 0, w: 100, h: 100, pen: QPen(), brush: Qt::red);
11885 item1->setPos(ax: 50, ay: 50);
11886 item1->setTransform(matrix: QTransform::fromTranslate(dx: 50, dy: 50), combine: true);
11887 item1->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
11888 QGraphicsItem *item2 = scene.addRect(x: 0, y: 0, w: 100, h: 100, pen: QPen(), brush: Qt::green);
11889 item2->setPos(ax: 50, ay: 50);
11890 item2->setTransform(matrix: QTransform::fromTranslate(dx: 50, dy: 50), combine: true);
11891 item2->setTransform(matrix: QTransform().rotate(a: 90), combine: true);
11892 item2->setFlags(QGraphicsItem::ItemIgnoresTransformations);
11893
11894 QGraphicsRectItem *item1_topleft = new QGraphicsRectItem(QRectF(-2, -2, 4, 4));
11895 item1_topleft->setParentItem(item1);
11896 item1_topleft->setBrush(Qt::black);
11897 QGraphicsRectItem *item1_bottomright = new QGraphicsRectItem(QRectF(-2, -2, 4, 4));
11898 item1_bottomright->setParentItem(item1);
11899 item1_bottomright->setPos(ax: 100, ay: 100);
11900 item1_bottomright->setBrush(Qt::yellow);
11901
11902 QGraphicsRectItem *item2_topleft = new QGraphicsRectItem(QRectF(-2, -2, 4, 4));
11903 item2_topleft->setParentItem(item2);
11904 item2_topleft->setBrush(Qt::black);
11905 QGraphicsRectItem *item2_bottomright = new QGraphicsRectItem(QRectF(-2, -2, 4, 4));
11906 item2_bottomright->setParentItem(item2);
11907 item2_bottomright->setPos(ax: 100, ay: 100);
11908 item2_bottomright->setBrush(Qt::yellow);
11909
11910 QCOMPARE(item1->sceneTransform(), item2->sceneTransform());
11911 QCOMPARE(item1_topleft->sceneTransform(), item2_topleft->sceneTransform());
11912 QCOMPARE(item1_bottomright->sceneTransform(), item2_bottomright->sceneTransform());
11913 QCOMPARE(item1->deviceTransform(QTransform()), item2->deviceTransform(QTransform()));
11914 QCOMPARE(item1->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 100));
11915 QCOMPARE(item2->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 100));
11916 QCOMPARE(item1->deviceTransform(QTransform()).map(QPointF(100, 100)), QPointF(0, 200));
11917 QCOMPARE(item2->deviceTransform(QTransform()).map(QPointF(100, 100)), QPointF(0, 200));
11918 QCOMPARE(item1_topleft->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 100));
11919 QCOMPARE(item2_topleft->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 100));
11920 QCOMPARE(item1_bottomright->deviceTransform(QTransform()).map(QPointF()), QPointF(0, 200));
11921 QCOMPARE(item2_bottomright->deviceTransform(QTransform()).map(QPointF()), QPointF(0, 200));
11922
11923 item2->setParentItem(item1);
11924
11925 QCOMPARE(item2->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 200));
11926 QCOMPARE(item2->deviceTransform(QTransform()).map(QPointF(100, 100)), QPointF(0, 300));
11927 QCOMPARE(item2_topleft->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 200));
11928 QCOMPARE(item2_bottomright->deviceTransform(QTransform()).map(QPointF()), QPointF(0, 300));
11929
11930 QTransform tx = QTransform::fromTranslate(dx: 100, dy: 0);
11931 QCOMPARE(item1->deviceTransform(tx).map(QPointF()), QPointF(200, 100));
11932 QCOMPARE(item1->deviceTransform(tx).map(QPointF(100, 100)), QPointF(100, 200));
11933 QCOMPARE(item2->deviceTransform(tx).map(QPointF()), QPointF(200, 200));
11934 QCOMPARE(item2->deviceTransform(tx).map(QPointF(100, 100)), QPointF(100, 300));
11935 QCOMPARE(item2_topleft->deviceTransform(tx).map(QPointF()), QPointF(200, 200));
11936 QCOMPARE(item2_bottomright->deviceTransform(tx).map(QPointF()), QPointF(100, 300));
11937}
11938
11939void tst_QGraphicsItem::resolvePaletteForItemChildren()
11940{
11941 QGraphicsScene scene;
11942 QGraphicsRectItem item(0, 0, 50, -150);
11943 scene.addItem(item: &item);
11944 QGraphicsWidget widget;
11945 widget.setParentItem(&item);
11946
11947 QColor green(Qt::green);
11948 QPalette paletteForScene = scene.palette();
11949 paletteForScene.setColor(acg: QPalette::Active, acr: QPalette::Window, acolor: green);
11950 scene.setPalette(paletteForScene);
11951
11952 QCOMPARE(widget.palette().color(QPalette::Active, QPalette::Window), green);
11953}
11954
11955QTEST_MAIN(tst_QGraphicsItem)
11956#include "tst_qgraphicsitem.moc"
11957

source code of qtbase/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp