diff options
Diffstat (limited to 'tests/auto/qgraphicsitem')
-rw-r--r-- | tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 2257 |
1 files changed, 2132 insertions, 125 deletions
diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index 55e9b34..d08e74b 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -44,6 +44,8 @@ #include <private/qtextcontrol_p.h> #include <private/qgraphicsitem_p.h> +#include <private/qgraphicsview_p.h> +#include <QStyleOptionGraphicsItem> #include <QAbstractTextDocumentLayout> #include <QBitmap> #include <QCursor> @@ -56,6 +58,8 @@ #include <QPainter> #include <QScrollBar> #include <QVBoxLayout> +#include <QGraphicsEffect> + #include "../../shared/util.h" //TESTED_CLASS= @@ -76,6 +80,46 @@ Q_DECLARE_METATYPE(QRectF) #define Q_CHECK_PAINTEVENTS #endif +class EventSpy : public QGraphicsWidget +{ + Q_OBJECT +public: + EventSpy(QObject *watched, QEvent::Type type) + : _count(0), spied(type) + { + watched->installEventFilter(this); + } + + EventSpy(QGraphicsScene *scene, QGraphicsItem *watched, QEvent::Type type) + : _count(0), spied(type) + { + scene->addItem(this); + watched->installSceneEventFilter(this); + } + + int count() const { return _count; } + +protected: + bool eventFilter(QObject *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + int _count; + QEvent::Type spied; +}; + class EventTester : public QGraphicsItem { public: @@ -122,11 +166,13 @@ private slots: void construction(); void constructionWithParent(); void destruction(); + void deleteChildItem(); void scene(); void parentItem(); void setParentItem(); void children(); void flags(); + void inputMethodHints(); void toolTip(); void visible(); void explicitlyVisible(); @@ -171,7 +217,9 @@ private slots: void boundingRects2(); void sceneBoundingRect(); void childrenBoundingRect(); + void childrenBoundingRectTransformed(); void childrenBoundingRect2(); + void childrenBoundingRect3(); void group(); void setGroup(); void nestedGroups(); @@ -180,6 +228,8 @@ private slots: void handlesChildEvents(); void handlesChildEvents2(); void handlesChildEvents3(); + void filtersChildEvents(); + void filtersChildEvents2(); void ensureVisible(); void cursor(); //void textControlGetterSetter(); @@ -221,6 +271,31 @@ private slots: void updateCachedItemAfterMove(); void deviceTransform_data(); void deviceTransform(); + void update(); + void setTransformProperties_data(); + void setTransformProperties(); + void itemUsesExtendedStyleOption(); + void itemSendsGeometryChanges(); + void moveItem(); + void sorting_data(); + void sorting(); + void itemHasNoContents(); + void hitTestUntransformableItem(); + void hitTestGraphicsEffectItem(); + void focusProxy(); + void subFocus(); + void focusProxyDeletion(); + void negativeZStacksBehindParent(); + void setGraphicsEffect(); + void panel(); + void addPanelToActiveScene(); + void activate(); + void setActivePanelOnInactiveScene(); + void activationOnShowHide(); + void moveWhileDeleting(); + void ensureDirtySceneTransform(); + void focusScope(); + void stackBefore(); // task specific tests below me void task141694_textItemEnsureVisible(); @@ -232,6 +307,9 @@ private slots: void task243707_addChildBeforeParent(); void task197802_childrenVisibility(); void QTBUG_4233_updateCachedWithSceneRect(); + +private: + QList<QGraphicsItem *> paintedItems; }; void tst_QGraphicsItem::init() @@ -251,50 +329,59 @@ void tst_QGraphicsItem::construction() QCOMPARE(int(item->type()), int(QGraphicsEllipseItem::Type)); QCOMPARE(qgraphicsitem_cast<QGraphicsEllipseItem *>(item), (QGraphicsEllipseItem *)item); QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); break; case 1: item = new QGraphicsLineItem; QCOMPARE(int(item->type()), int(QGraphicsLineItem::Type)); QCOMPARE(qgraphicsitem_cast<QGraphicsLineItem *>(item), (QGraphicsLineItem *)item); QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); break; case 2: item = new QGraphicsPathItem; QCOMPARE(int(item->type()), int(QGraphicsPathItem::Type)); QCOMPARE(qgraphicsitem_cast<QGraphicsPathItem *>(item), (QGraphicsPathItem *)item); QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); break; case 3: item = new QGraphicsPixmapItem; QCOMPARE(int(item->type()), int(QGraphicsPixmapItem::Type)); QCOMPARE(qgraphicsitem_cast<QGraphicsPixmapItem *>(item), (QGraphicsPixmapItem *)item); QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); break; case 4: item = new QGraphicsPolygonItem; QCOMPARE(int(item->type()), int(QGraphicsPolygonItem::Type)); QCOMPARE(qgraphicsitem_cast<QGraphicsPolygonItem *>(item), (QGraphicsPolygonItem *)item); QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); break; case 5: item = new QGraphicsRectItem; QCOMPARE(int(item->type()), int(QGraphicsRectItem::Type)); QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)item); QCOMPARE(qgraphicsitem_cast<QGraphicsLineItem *>(item), (QGraphicsLineItem *)0); + QCOMPARE(item->flags(), 0); break; case 6: - default: item = new QGraphicsTextItem; QCOMPARE(int(item->type()), int(QGraphicsTextItem::Type)); QCOMPARE(qgraphicsitem_cast<QGraphicsTextItem *>(item), (QGraphicsTextItem *)item); QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + // This is the only item that uses an extended style option. + QCOMPARE(item->flags(), QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemUsesExtendedStyleOption)); + break; + default: + qFatal("You broke the logic, please fix!"); break; } QCOMPARE(item->scene(), (QGraphicsScene *)0); QCOMPARE(item->parentItem(), (QGraphicsItem *)0); QVERIFY(item->children().isEmpty()); - QCOMPARE(item->flags(), 0); QVERIFY(item->isVisible()); QVERIFY(item->isEnabled()); QVERIFY(!item->isSelected()); @@ -519,6 +606,29 @@ void tst_QGraphicsItem::destruction() QCOMPARE(itemDeleted, 59); } QCOMPARE(itemDeleted, 109); + { + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsRectItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + parent->setVisible(false); + scene->addItem(parent); + QCOMPARE(child->parentItem(), static_cast<QGraphicsItem*>(parent)); + delete scene; + QCOMPARE(itemDeleted, 110); + } +} + +void tst_QGraphicsItem::deleteChildItem() +{ + QGraphicsScene scene; + QGraphicsItem *rect = scene.addRect(QRectF()); + QGraphicsItem *child1 = new QGraphicsRectItem(rect); + QGraphicsItem *child2 = new QGraphicsRectItem(rect); + QGraphicsItem *child3 = new QGraphicsRectItem(rect); + delete child1; + child2->setParentItem(0); + delete child2; } void tst_QGraphicsItem::scene() @@ -598,6 +708,9 @@ void tst_QGraphicsItem::flags() QCOMPARE(item->flags(), 0); QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + scene.addItem(item); { @@ -686,6 +799,18 @@ void tst_QGraphicsItem::flags() } } +class ImhTester : public QGraphicsItem +{ + QRectF boundingRect() const { return QRectF(); } + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) {} +}; + +void tst_QGraphicsItem::inputMethodHints() +{ + ImhTester item; + QCOMPARE(item.inputMethodHints(), Qt::ImhNone); +} + void tst_QGraphicsItem::toolTip() { QString toolTip = "Qt rocks!"; @@ -770,6 +895,9 @@ void tst_QGraphicsItem::visible() QVERIFY(item->isVisible()); QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + scene.addItem(item); QVERIFY(item->isVisible()); QCOMPARE(scene.itemAt(0, 0), item); @@ -917,6 +1045,9 @@ void tst_QGraphicsItem::enabled() item->setEnabled(false); item->setFlag(QGraphicsItem::ItemIsFocusable); QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + scene.addItem(item); item->setFocus(); QVERIFY(!item->hasFocus()); @@ -1122,6 +1253,7 @@ void tst_QGraphicsItem::selected() QVERIFY(!item->isSelected()); // Click inside and check that it's selected + QTest::mouseMove(view.viewport()); QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos())); QCOMPARE(item->values.size(), 11); QCOMPARE(item->values.last(), true); @@ -1129,7 +1261,6 @@ void tst_QGraphicsItem::selected() // Click outside and check that it's not selected QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos() + QPointF(item->boundingRect().width(), item->boundingRect().height()))); - QCOMPARE(item->values.size(), 12); QCOMPARE(item->values.last(), false); QVERIFY(!item->isSelected()); @@ -1596,6 +1727,9 @@ void tst_QGraphicsItem::hasFocus() QVERIFY(!line->hasFocus()); QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + scene.addItem(line); line->setFocus(); @@ -1605,6 +1739,8 @@ void tst_QGraphicsItem::hasFocus() QVERIFY(line->hasFocus()); QGraphicsScene scene2; + QApplication::sendEvent(&scene2, &activate); + scene2.addItem(line); QVERIFY(!line->hasFocus()); @@ -1775,15 +1911,15 @@ void tst_QGraphicsItem::setMatrix() QCOMPARE(rlist.at(2), unrotatedRect); // From post-update (update current state) } -static QList<QGraphicsItem *> paintedItems; +static QList<QGraphicsItem *> _paintedItems; class PainterItem : public QGraphicsItem { protected: QRectF boundingRect() const { return QRectF(-10, -10, 20, 20); } - void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) - { paintedItems << this; } + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { _paintedItems << this; painter->fillRect(boundingRect(), Qt::red); } }; void tst_QGraphicsItem::zValue() @@ -1815,10 +1951,10 @@ void tst_QGraphicsItem::zValue() QApplication::sendPostedEvents(); //glib workaround #endif - QVERIFY(!paintedItems.isEmpty()); - QVERIFY((paintedItems.size() % 4) == 0); + QTRY_VERIFY(!_paintedItems.isEmpty()); + QVERIFY((_paintedItems.size() % 4) == 0); for (int i = 0; i < 3; ++i) - QVERIFY(paintedItems.at(i)->zValue() < paintedItems.at(i + 1)->zValue()); + QVERIFY(_paintedItems.at(i)->zValue() < _paintedItems.at(i + 1)->zValue()); } void tst_QGraphicsItem::shape() @@ -2786,19 +2922,18 @@ void tst_QGraphicsItem::hoverEventsGenerateRepaints() Q_CHECK_PAINTEVENTS QGraphicsScene scene; - EventTester *tester = new EventTester; - scene.addItem(tester); - tester->setAcceptsHoverEvents(true); - QGraphicsView view(&scene); view.show(); #ifdef Q_WS_X11 qt_x11_wait_for_window_manager(&view); #endif + QTest::qWait(250); - qApp->processEvents(); - qApp->processEvents(); + EventTester *tester = new EventTester; + scene.addItem(tester); + tester->setAcceptsHoverEvents(true); + QTRY_COMPARE(tester->repaints, 1); // Send a hover enter event QGraphicsSceneHoverEvent hoverEnterEvent(QEvent::GraphicsSceneHoverEnter); @@ -2810,7 +2945,7 @@ void tst_QGraphicsItem::hoverEventsGenerateRepaints() int npaints = tester->repaints; qApp->processEvents(); qApp->processEvents(); - QCOMPARE(tester->events.size(), 2); // enter + move + QCOMPARE(tester->events.size(), 2); // enter + move QCOMPARE(tester->repaints, npaints + 1); QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverMove); @@ -2917,9 +3052,57 @@ void tst_QGraphicsItem::childrenBoundingRect() childChild->setParentItem(child); childChild->setPos(500, 500); child->rotate(90); + + + scene.addPolygon(parent->mapToScene(parent->boundingRect() | parent->childrenBoundingRect()))->setPen(QPen(Qt::red));; + + QGraphicsView view(&scene); + view.show(); + + QTest::qWait(5000); + QCOMPARE(parent->childrenBoundingRect(), QRectF(-500, -100, 600, 800)); } +void tst_QGraphicsItem::childrenBoundingRectTransformed() +{ + QGraphicsScene scene; + + QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect2 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect3 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect4 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect5 = scene.addRect(QRectF(0, 0, 100, 100)); + rect2->setParentItem(rect); + rect3->setParentItem(rect2); + rect4->setParentItem(rect3); + rect5->setParentItem(rect4); + + rect2->setTransform(QTransform().translate(50, 50).rotate(45)); + rect2->setPos(25, 25); + rect3->setTransform(QTransform().translate(50, 50).rotate(45)); + rect3->setPos(25, 25); + rect4->setTransform(QTransform().translate(50, 50).rotate(45)); + rect4->setPos(25, 25); + rect5->setTransform(QTransform().translate(50, 50).rotate(45)); + rect5->setPos(25, 25); + + QRectF subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(subTreeRect.left(), qreal(-206.0660171779821)); + QCOMPARE(subTreeRect.top(), qreal(75.0)); + QCOMPARE(subTreeRect.width(), qreal(351.7766952966369)); + QCOMPARE(subTreeRect.height(), qreal(251.7766952966369)); + + rect->rotate(45); + rect2->rotate(-45); + rect3->rotate(45); + rect4->rotate(-45); + rect5->rotate(45); + + subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(rect->childrenBoundingRect(), QRectF(-100, 75, 275, 250)); +} + void tst_QGraphicsItem::childrenBoundingRect2() { QGraphicsItemGroup box; @@ -2930,6 +3113,39 @@ void tst_QGraphicsItem::childrenBoundingRect2() QCOMPARE(box.childrenBoundingRect(), QRectF(0, 0, 100, 100)); } +void tst_QGraphicsItem::childrenBoundingRect3() +{ + QGraphicsScene scene; + + QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect2 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect3 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect4 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect5 = scene.addRect(QRectF(0, 0, 100, 100)); + rect2->setParentItem(rect); + rect3->setParentItem(rect2); + rect4->setParentItem(rect3); + rect5->setParentItem(rect4); + + rect2->setTransform(QTransform().translate(50, 50).rotate(45)); + rect2->setPos(25, 25); + rect3->setTransform(QTransform().translate(50, 50).rotate(45)); + rect3->setPos(25, 25); + rect4->setTransform(QTransform().translate(50, 50).rotate(45)); + rect4->setPos(25, 25); + rect5->setTransform(QTransform().translate(50, 50).rotate(45)); + rect5->setPos(25, 25); + + // Try to mess up the cached bounding rect. + (void)rect2->childrenBoundingRect(); + + QRectF subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(subTreeRect.left(), qreal(-206.0660171779821)); + QCOMPARE(subTreeRect.top(), qreal(75.0)); + QCOMPARE(subTreeRect.width(), qreal(351.7766952966369)); + QCOMPARE(subTreeRect.height(), qreal(251.7766952966369)); +} + void tst_QGraphicsItem::group() { QGraphicsScene scene; @@ -3128,150 +3344,134 @@ protected: void mouseReleaseEvent(QGraphicsSceneMouseEvent *) { ++counter; } }; + void tst_QGraphicsItem::handlesChildEvents() { - ChildEventTester *item2_level2 = new ChildEventTester(QRectF(0, 0, 25, 25)); - ChildEventTester *item1_level1 = new ChildEventTester(QRectF(0, 0, 50, 50)); - ChildEventTester *item_level0 = new ChildEventTester(QRectF(0, 0, 100, 100)); - ChildEventTester *item1_level2 = new ChildEventTester(QRectF(0, 0, 25, 25)); - ChildEventTester *item2_level1 = new ChildEventTester(QRectF(0, 0, 50, 50)); - - item_level0->setBrush(QBrush(Qt::blue)); - item1_level1->setBrush(QBrush(Qt::red)); - item2_level1->setBrush(QBrush(Qt::yellow)); - item1_level2->setBrush(QBrush(Qt::green)); - item2_level2->setBrush(QBrush(Qt::gray)); - item1_level1->setPos(50, 0); - item2_level1->setPos(50, 50); - item1_level2->setPos(25, 0); - item2_level2->setPos(25, 25); - item1_level1->setParentItem(item_level0); - item2_level1->setParentItem(item_level0); - item1_level2->setParentItem(item1_level1); - item2_level2->setParentItem(item1_level1); + ChildEventTester *blue = new ChildEventTester(QRectF(0, 0, 100, 100)); + ChildEventTester *red = new ChildEventTester(QRectF(0, 0, 50, 50)); + ChildEventTester *green = new ChildEventTester(QRectF(0, 0, 25, 25)); + ChildEventTester *gray = new ChildEventTester(QRectF(0, 0, 25, 25)); + ChildEventTester *yellow = new ChildEventTester(QRectF(0, 0, 50, 50)); + + blue->setBrush(QBrush(Qt::blue)); + red->setBrush(QBrush(Qt::red)); + yellow->setBrush(QBrush(Qt::yellow)); + green->setBrush(QBrush(Qt::green)); + gray->setBrush(QBrush(Qt::gray)); + red->setPos(50, 0); + yellow->setPos(50, 50); + green->setPos(25, 0); + gray->setPos(25, 25); + red->setParentItem(blue); + yellow->setParentItem(blue); + green->setParentItem(red); + gray->setParentItem(red); QGraphicsScene scene; - scene.addItem(item_level0); - - // Pull out the items, closest item first - QList<QGraphicsItem *> items = scene.items(scene.itemsBoundingRect()); - QCOMPARE(items.at(4), (QGraphicsItem *)item_level0); - if (item1_level1 < item2_level1) { - QCOMPARE(items.at(3), (QGraphicsItem *)item1_level1); - if (item1_level2 < item2_level2) { - QCOMPARE(items.at(2), (QGraphicsItem *)item1_level2); - QCOMPARE(items.at(1), (QGraphicsItem *)item2_level2); - } else { - QCOMPARE(items.at(2), (QGraphicsItem *)item2_level2); - QCOMPARE(items.at(1), (QGraphicsItem *)item1_level2); - } - QCOMPARE(items.at(0), (QGraphicsItem *)item2_level1); - } else { - QCOMPARE(items.at(3), (QGraphicsItem *)item2_level1); - QCOMPARE(items.at(2), (QGraphicsItem *)item1_level1); - if (item1_level2 < item2_level2) { - QCOMPARE(items.at(1), (QGraphicsItem *)item1_level2); - QCOMPARE(items.at(0), (QGraphicsItem *)item2_level2); - } else { - QCOMPARE(items.at(1), (QGraphicsItem *)item2_level2); - QCOMPARE(items.at(0), (QGraphicsItem *)item1_level2); - } - } + scene.addItem(blue); QGraphicsView view(&scene); view.show(); QTest::qWait(1000); - QCOMPARE(item_level0->counter, 0); + // Pull out the items, closest item first + QList<QGraphicsItem *> items = scene.items(scene.itemsBoundingRect()); + QCOMPARE(items.at(0), (QGraphicsItem *)yellow); + QCOMPARE(items.at(1), (QGraphicsItem *)gray); + QCOMPARE(items.at(2), (QGraphicsItem *)green); + QCOMPARE(items.at(3), (QGraphicsItem *)red); + QCOMPARE(items.at(4), (QGraphicsItem *)blue); + + QCOMPARE(blue->counter, 0); // Send events to the toplevel item QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); pressEvent.setButton(Qt::LeftButton); - pressEvent.setScenePos(item_level0->mapToScene(5, 5)); + pressEvent.setScenePos(blue->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); releaseEvent.setButton(Qt::LeftButton); - releaseEvent.setScenePos(item_level0->mapToScene(5, 5)); + releaseEvent.setScenePos(blue->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 2); + QCOMPARE(blue->counter, 2); // Send events to a level1 item - pressEvent.setScenePos(item1_level1->mapToScene(5, 5)); + pressEvent.setScenePos(red->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level1->mapToScene(5, 5)); + releaseEvent.setScenePos(red->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 2); - QCOMPARE(item1_level1->counter, 2); + QCOMPARE(blue->counter, 2); + QCOMPARE(red->counter, 2); // Send events to a level2 item - pressEvent.setScenePos(item1_level2->mapToScene(5, 5)); + pressEvent.setScenePos(green->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level2->mapToScene(5, 5)); + releaseEvent.setScenePos(green->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 2); - QCOMPARE(item1_level1->counter, 2); - QCOMPARE(item1_level2->counter, 2); + QCOMPARE(blue->counter, 2); + QCOMPARE(red->counter, 2); + QCOMPARE(green->counter, 2); - item_level0->setHandlesChildEvents(true); + blue->setHandlesChildEvents(true); // Send events to a level1 item - pressEvent.setScenePos(item1_level1->mapToScene(5, 5)); + pressEvent.setScenePos(red->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level1->mapToScene(5, 5)); + releaseEvent.setScenePos(red->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 4); - QCOMPARE(item1_level1->counter, 2); + QCOMPARE(blue->counter, 4); + QCOMPARE(red->counter, 2); // Send events to a level2 item - pressEvent.setScenePos(item1_level2->mapToScene(5, 5)); + pressEvent.setScenePos(green->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level2->mapToScene(5, 5)); + releaseEvent.setScenePos(green->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 6); - QCOMPARE(item1_level1->counter, 2); - QCOMPARE(item1_level2->counter, 2); + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 2); + QCOMPARE(green->counter, 2); - item_level0->setHandlesChildEvents(false); + blue->setHandlesChildEvents(false); // Send events to a level1 item - pressEvent.setScenePos(item1_level1->mapToScene(5, 5)); + pressEvent.setScenePos(red->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level1->mapToScene(5, 5)); + releaseEvent.setScenePos(red->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 6); - QCOMPARE(item1_level1->counter, 4); + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 4); // Send events to a level2 item - pressEvent.setScenePos(item1_level2->mapToScene(5, 5)); + pressEvent.setScenePos(green->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level2->mapToScene(5, 5)); + releaseEvent.setScenePos(green->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 6); - QCOMPARE(item1_level1->counter, 4); - QCOMPARE(item1_level2->counter, 4); + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 4); + QCOMPARE(green->counter, 4); } void tst_QGraphicsItem::handlesChildEvents2() @@ -3309,6 +3509,9 @@ void tst_QGraphicsItem::handlesChildEvents2() void tst_QGraphicsItem::handlesChildEvents3() { QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + ChildEventTester *group2 = new ChildEventTester(QRectF(), 0); ChildEventTester *group1 = new ChildEventTester(QRectF(), group2); ChildEventTester *leaf = new ChildEventTester(QRectF(), group1); @@ -3345,6 +3548,127 @@ void tst_QGraphicsItem::handlesChildEvents3() QCOMPARE(group2->counter, 3); } + +class ChildEventFilterTester : public ChildEventTester +{ +public: + ChildEventFilterTester(const QRectF &rect, QGraphicsItem *parent = 0) + : ChildEventTester(rect, parent), filter(QEvent::None) + { } + + QEvent::Type filter; + +protected: + bool sceneEventFilter(QGraphicsItem *item, QEvent *event) + { + Q_UNUSED(item); + if (event->type() == filter) { + ++counter; + return true; + } + return false; + } +}; + +void tst_QGraphicsItem::filtersChildEvents() +{ + QGraphicsScene scene; + ChildEventFilterTester *root = new ChildEventFilterTester(QRectF(0, 0, 10, 10)); + ChildEventFilterTester *filter = new ChildEventFilterTester(QRectF(10, 10, 10, 10), root); + ChildEventTester *child = new ChildEventTester(QRectF(20, 20, 10, 10), filter); + + // setup filter + filter->setFiltersChildEvents(true); + filter->filter = QEvent::GraphicsSceneMousePress; + + scene.addItem(root); + + QGraphicsView view(&scene); + view.show(); + + QTest::qWait(1000); + + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + + // send event to child + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(25, 25));//child->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setButton(Qt::LeftButton); + releaseEvent.setScenePos(QPointF(25, 25));//child->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(child->counter, 1); // mouse release is not filtered + QCOMPARE(filter->counter, 1); // mouse press is filtered + QCOMPARE(root->counter, 0); + + // add another filter + root->setFiltersChildEvents(true); + root->filter = QEvent::GraphicsSceneMouseRelease; + + // send event to child + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(child->counter, 1); + QCOMPARE(filter->counter, 2); // mouse press is filtered + QCOMPARE(root->counter, 1); // mouse release is filtered + + // reparent to another sub-graph + ChildEventTester *parent = new ChildEventTester(QRectF(10, 10, 10, 10), root); + child->setParentItem(parent); + + // send event to child + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(child->counter, 2); // mouse press is _not_ filtered + QCOMPARE(parent->counter, 0); + QCOMPARE(filter->counter, 2); + QCOMPARE(root->counter, 2); // mouse release is filtered +} + +void tst_QGraphicsItem::filtersChildEvents2() +{ + ChildEventFilterTester *root = new ChildEventFilterTester(QRectF(0, 0, 10, 10)); + root->setFiltersChildEvents(true); + root->filter = QEvent::GraphicsSceneMousePress; + QVERIFY(root->filtersChildEvents()); + + ChildEventTester *child = new ChildEventTester(QRectF(0, 0, 10, 10), root); + QVERIFY(!child->filtersChildEvents()); + + ChildEventTester *child2 = new ChildEventTester(QRectF(0, 0, 10, 10)); + ChildEventTester *child3 = new ChildEventTester(QRectF(0, 0, 10, 10), child2); + ChildEventTester *child4 = new ChildEventTester(QRectF(0, 0, 10, 10), child3); + + child2->setParentItem(root); + QVERIFY(!child2->filtersChildEvents()); + QVERIFY(!child3->filtersChildEvents()); + QVERIFY(!child4->filtersChildEvents()); + + QGraphicsScene scene; + scene.addItem(root); + + QGraphicsView view(&scene); + view.show(); + + QTestEventLoop::instance().enterLoop(1); + + QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(5, 5), + view.viewport()->mapToGlobal(view.mapFromScene(5, 5)), Qt::LeftButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + + QCOMPARE(child->counter, 0); + QCOMPARE(child2->counter, 0); + QCOMPARE(child3->counter, 0); + QCOMPARE(child4->counter, 0); + QCOMPARE(root->counter, 1); +} + class CustomItem : public QGraphicsItem { public: @@ -3689,8 +4013,20 @@ void tst_QGraphicsItem::defaultItemTest_QGraphicsEllipseItem() class ItemChangeTester : public QGraphicsRectItem { public: - ItemChangeTester(){} - ItemChangeTester(QGraphicsItem *parent) : QGraphicsRectItem(parent) {} + ItemChangeTester() + { setFlag(ItemSendsGeometryChanges); clear(); } + ItemChangeTester(QGraphicsItem *parent) : QGraphicsRectItem(parent) + { setFlag(ItemSendsGeometryChanges); clear(); } + + void clear() + { + itemChangeReturnValue = QVariant(); + itemSceneChangeTargetScene = 0; + changes.clear(); + values.clear(); + oldValues.clear(); + } + QVariant itemChangeReturnValue; QGraphicsScene *itemSceneChangeTargetScene; @@ -3867,6 +4203,20 @@ void tst_QGraphicsItem::itemChange() QCOMPARE(tester.pos(), QPointF(42, 0)); } { + // ItemZValueChange / ItemZValueHasChanged + tester.itemChangeReturnValue = qreal(2.0); + tester.setZValue(1.0); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemZValueChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemZValueHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(1.0))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(2.0))); + QCOMPARE(tester.oldValues.last(), QVariant(qreal(0.0))); + QCOMPARE(tester.zValue(), qreal(2.0)); + } + { // ItemFlagsChange tester.itemChangeReturnValue = QGraphicsItem::ItemIsSelectable; tester.setFlag(QGraphicsItem::ItemIsSelectable, false); @@ -3877,7 +4227,8 @@ void tst_QGraphicsItem::itemChange() QCOMPARE(tester.changes.size(), changeCount); QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemFlagsChange); QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemFlagsHaveChanged); - QCOMPARE(tester.values.at(tester.values.size() - 2), qVariantFromValue<quint32>(QGraphicsItem::ItemIsSelectable)); + QVariant expectedFlags = qVariantFromValue<quint32>(QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges)); + QCOMPARE(tester.values.at(tester.values.size() - 2), expectedFlags); QCOMPARE(tester.values.at(tester.values.size() - 1), qVariantFromValue<quint32>(QGraphicsItem::ItemIsSelectable)); } { @@ -4134,8 +4485,13 @@ protected: void tst_QGraphicsItem::sceneEventFilter() { QGraphicsScene scene; + QGraphicsView view(&scene); view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(250); QGraphicsTextItem *text1 = scene.addText(QLatin1String("Text1")); QGraphicsTextItem *text2 = scene.addText(QLatin1String("Text2")); @@ -4252,8 +4608,10 @@ void tst_QGraphicsItem::paint() QGraphicsView view(&scene); view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif QTest::qWait(250); - #ifdef Q_OS_WIN32 //we try to switch the desktop: if it fails, we skip the test if (::SwitchDesktop( ::GetThreadDesktop( ::GetCurrentThreadId() ) ) == 0) { @@ -4261,20 +4619,18 @@ void tst_QGraphicsItem::paint() } #endif - QVERIFY(paintTester.widget == view.viewport()); - + QCOMPARE(paintTester.widget, view.viewport()); view.hide(); QGraphicsScene scene2; + QGraphicsView view2(&scene2); + view2.show(); + QTest::qWait(250); PaintTester tester2; scene2.addItem(&tester2); - - QGraphicsView view2(&scene2); - view2.show(); qApp->processEvents(); - QTest::qWait(250); //First show one paint QVERIFY(tester2.painted == 1); @@ -4675,7 +5031,7 @@ void tst_QGraphicsItem::itemClipsChildrenToShape2() QPainter painter(&image); scene.render(&painter); painter.end(); - + QCOMPARE(image.pixel(5, 5), QColor(0, 0, 255).rgba()); QCOMPARE(image.pixel(5, 10), QRgb(0)); QCOMPARE(image.pixel(10, 5), QRgb(0)); @@ -5122,6 +5478,7 @@ void tst_QGraphicsItem::contextMenuEventPropagation() qt_x11_wait_for_window_manager(&view); #endif view.resize(200, 200); + QTest::qWait(250); QContextMenuEvent event(QContextMenuEvent::Mouse, QPoint(10, 10), view.viewport()->mapToGlobal(QPoint(10, 10))); @@ -5298,6 +5655,9 @@ void tst_QGraphicsItem::task240400_clickOnTextItem() QFETCH(int, textFlags); QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + QGraphicsTextItem *item = scene.addText("Hello"); item->setFlags(QGraphicsItem::GraphicsItemFlags(flags)); item->setTextInteractionFlags(Qt::TextInteractionFlags(textFlags)); @@ -5363,7 +5723,7 @@ void tst_QGraphicsItem::task240400_clickOnTextItem() QVERIFY(selectable ? item->isSelected() : !item->isSelected()); - // + // if (textFlags & Qt::TextEditorInteraction) QVERIFY(item->textCursor().columnNumber() > column); else @@ -5390,11 +5750,12 @@ public: void tst_QGraphicsItem::ensureUpdateOnTextItem() { QGraphicsScene scene; - TextItem *text1 = new TextItem(QLatin1String("123")); - scene.addItem(text1); QGraphicsView view(&scene); view.show(); QTest::qWait(250); + TextItem *text1 = new TextItem(QLatin1String("123")); + scene.addItem(text1); + qApp->processEvents(); QCOMPARE(text1->updates,1); //same bouding rect but we have to update @@ -5803,19 +6164,36 @@ void tst_QGraphicsItem::opacityZeroUpdates() QCOMPARE(view.paintedRegion, expectedRegion); } +class StacksBehindParentHelper : public QGraphicsRectItem +{ +public: + StacksBehindParentHelper(QList<QGraphicsItem *> *paintedItems, const QRectF &rect, QGraphicsItem *parent = 0) + : QGraphicsRectItem(rect, parent), paintedItems(paintedItems) + { } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QGraphicsRectItem::paint(painter, option, widget); + paintedItems->append(this); + } + +private: + QList<QGraphicsItem *> *paintedItems; +}; + void tst_QGraphicsItem::itemStacksBehindParent() { - QGraphicsRectItem *parent1 = new QGraphicsRectItem(QRectF(0, 0, 100, 50)); - QGraphicsRectItem *child11 = new QGraphicsRectItem(QRectF(-10, 10, 50, 50), parent1); - QGraphicsRectItem *grandChild111 = new QGraphicsRectItem(QRectF(-20, 20, 50, 50), child11); - QGraphicsRectItem *child12 = new QGraphicsRectItem(QRectF(60, 10, 50, 50), parent1); - QGraphicsRectItem *grandChild121 = new QGraphicsRectItem(QRectF(70, 20, 50, 50), child12); + StacksBehindParentHelper *parent1 = new StacksBehindParentHelper(&paintedItems, QRectF(0, 0, 100, 50)); + StacksBehindParentHelper *child11 = new StacksBehindParentHelper(&paintedItems, QRectF(-10, 10, 50, 50), parent1); + StacksBehindParentHelper *grandChild111 = new StacksBehindParentHelper(&paintedItems, QRectF(-20, 20, 50, 50), child11); + StacksBehindParentHelper *child12 = new StacksBehindParentHelper(&paintedItems, QRectF(60, 10, 50, 50), parent1); + StacksBehindParentHelper *grandChild121 = new StacksBehindParentHelper(&paintedItems, QRectF(70, 20, 50, 50), child12); - QGraphicsRectItem *parent2 = new QGraphicsRectItem(QRectF(0, 0, 100, 50)); - QGraphicsRectItem *child21 = new QGraphicsRectItem(QRectF(-10, 10, 50, 50), parent2); - QGraphicsRectItem *grandChild211 = new QGraphicsRectItem(QRectF(-20, 20, 50, 50), child21); - QGraphicsRectItem *child22 = new QGraphicsRectItem(QRectF(60, 10, 50, 50), parent2); - QGraphicsRectItem *grandChild221 = new QGraphicsRectItem(QRectF(70, 20, 50, 50), child22); + StacksBehindParentHelper *parent2 = new StacksBehindParentHelper(&paintedItems, QRectF(0, 0, 100, 50)); + StacksBehindParentHelper *child21 = new StacksBehindParentHelper(&paintedItems, QRectF(-10, 10, 50, 50), parent2); + StacksBehindParentHelper *grandChild211 = new StacksBehindParentHelper(&paintedItems, QRectF(-20, 20, 50, 50), child21); + StacksBehindParentHelper *child22 = new StacksBehindParentHelper(&paintedItems, QRectF(60, 10, 50, 50), parent2); + StacksBehindParentHelper *grandChild221 = new StacksBehindParentHelper(&paintedItems, QRectF(70, 20, 50, 50), child22); parent1->setData(0, "parent1"); child11->setData(0, "child11"); @@ -5837,25 +6215,58 @@ void tst_QGraphicsItem::itemStacksBehindParent() scene.addItem(parent1); scene.addItem(parent2); + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(250); + paintedItems.clear(); + view.viewport()->update(); + QTest::qWait(100); + QCOMPARE(scene.items(0, 0, 100, 100), (QList<QGraphicsItem *>() << grandChild111 << child11 << grandChild121 << child12 << parent1 << grandChild211 << child21 << grandChild221 << child22 << parent2)); + QCOMPARE(paintedItems, QList<QGraphicsItem *>() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << parent1 << child12 << grandChild121 + << child11 << grandChild111); child11->setFlag(QGraphicsItem::ItemStacksBehindParent); + scene.update(); + paintedItems.clear(); + QTest::qWait(250); + QCOMPARE(scene.items(0, 0, 100, 100), (QList<QGraphicsItem *>() << grandChild121 << child12 << parent1 << grandChild111 << child11 << grandChild211 << child21 << grandChild221 << child22 << parent2)); + QCOMPARE(paintedItems, QList<QGraphicsItem *>() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << child11 << grandChild111 + << parent1 << child12 << grandChild121); child12->setFlag(QGraphicsItem::ItemStacksBehindParent); + paintedItems.clear(); + scene.update(); + QTest::qWait(250); + QCOMPARE(scene.items(0, 0, 100, 100), (QList<QGraphicsItem *>() << parent1 << grandChild111 << child11 << grandChild121 << child12 << grandChild211 << child21 << grandChild221 << child22 << parent2)); + QCOMPARE(paintedItems, QList<QGraphicsItem *>() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << child12 << grandChild121 + << child11 << grandChild111 << parent1); } class ClippingAndTransformsScene : public QGraphicsScene @@ -5902,8 +6313,9 @@ void tst_QGraphicsItem::nestedClipping() l1->setData(0, "l1"); l2->setData(0, "l2"); l3->setData(0, "l3"); - + QGraphicsView view(&scene); + view.setOptimizationFlag(QGraphicsView::IndirectPainting); view.show(); #ifdef Q_WS_X11 qt_x11_wait_for_window_manager(&view); @@ -5930,7 +6342,7 @@ void tst_QGraphicsItem::nestedClipping() QCOMPARE(image.pixel(80, 130), qRgba(255, 255, 0, 255)); QCOMPARE(image.pixel(92, 105), qRgba(0, 255, 0, 255)); QCOMPARE(image.pixel(105, 105), qRgba(0, 0, 255, 255)); -#if 0 +#if 0 // Enable this to compare if the test starts failing. image.save("nestedClipping_reference.png"); #endif @@ -6072,7 +6484,7 @@ void tst_QGraphicsItem::tabChangesFocus_data() void tst_QGraphicsItem::tabChangesFocus() { QFETCH(bool, tabChangesFocus); - + QGraphicsScene scene; QGraphicsTextItem *item = scene.addText("Hello"); item->setTabChangesFocus(tabChangesFocus); @@ -6081,6 +6493,7 @@ void tst_QGraphicsItem::tabChangesFocus() QDial *dial1 = new QDial; QGraphicsView *view = new QGraphicsView(&scene); + QDial *dial2 = new QDial; QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(dial1); @@ -6093,6 +6506,8 @@ void tst_QGraphicsItem::tabChangesFocus() #ifdef Q_WS_X11 qt_x11_wait_for_window_manager(&widget); #endif + QTest::qWait(250); + QVERIFY(scene.isActive()); dial1->setFocus(); QTest::qWait(125); @@ -6126,6 +6541,9 @@ void tst_QGraphicsItem::cacheMode() #ifdef Q_WS_X11 qt_x11_wait_for_window_manager(&view); #endif + // Increase the probability of window activation + // not causing another repaint of test items. + QTest::qWait(250); EventTester *tester = new EventTester; EventTester *testerChild = new EventTester; @@ -6319,10 +6737,10 @@ void tst_QGraphicsItem::updateCachedItemAfterMove() scene.addItem(tester); QGraphicsView view(&scene); view.show(); -#ifdef Q_WS_X11 - qt_x11_wait_for_window_manager(&view); -#endif + QTest::qWaitForWindowShown(&view); + QTest::qWait(125); + QTRY_VERIFY(tester->repaints > 0); tester->repaints = 0; // Move the item, should not cause repaints @@ -6362,7 +6780,7 @@ public: QGraphicsRectItem::paint(painter, option, widget); painter->drawText(boundingRect(), Qt::AlignCenter, QString("%1x%2\n%3x%4").arg(p.x()).arg(p.y()).arg(sp.x()).arg(sp.y())); } - + protected: void hoverMoveEvent(QGraphicsSceneHoverEvent *event) { @@ -6456,6 +6874,1595 @@ void tst_QGraphicsItem::deviceTransform() QCOMPARE(rect3->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult3); } +class MyGraphicsView : public QGraphicsView +{ +public: + int repaints; + QRegion paintedRegion; + MyGraphicsView(QGraphicsScene *scene) : QGraphicsView(scene), repaints(0) {} + void paintEvent(QPaintEvent *e) + { + paintedRegion += e->region(); + ++repaints; + QGraphicsView::paintEvent(e); + } + void reset() { repaints = 0; paintedRegion = QRegion(); } +}; + +void tst_QGraphicsItem::update() +{ + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + MyGraphicsView view(&scene); + + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + EventTester *item = new EventTester; + scene.addItem(item); + QTest::qWait(100); // Make sure all pending updates are processed. + item->repaints = 0; + + item->update(); // Item marked as dirty + scene.update(); // Entire scene marked as dirty + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + + // Make sure the dirty state from the previous update is reset so that + // the item don't think it is already dirty and discards this update. + item->update(); + qApp->processEvents(); + QCOMPARE(item->repaints, 2); + + // Make sure a partial update doesn't cause a full update to be discarded. + view.reset(); + item->repaints = 0; + item->update(QRectF(0, 0, 5, 5)); + item->update(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + QRect itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform()) + .mapRect(item->boundingRect()).toRect(); + QRegion expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + // Make sure update requests outside the bounding rect are discarded. + view.reset(); + item->repaints = 0; + item->update(-15, -15, 5, 5); // Item's brect: (-10, -10, 20, 20) + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + + // Make sure the area occupied by an item is repainted when hiding it. + view.reset(); + item->repaints = 0; + item->update(); // Full update; all sub-sequent update requests are discarded. + item->hide(); // visible set to 0. ignoreVisible must be set to 1; the item won't be processed otherwise. + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + // Make sure item is repainted when shown (after being hidden). + view.reset(); + item->repaints = 0; + item->show(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + item->repaints = 0; + item->hide(); + qApp->processEvents(); + view.reset(); + const QPointF originalPos = item->pos(); + item->setPos(5000, 5000); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + qApp->processEvents(); + + item->setPos(originalPos); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + item->show(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + QGraphicsViewPrivate *viewPrivate = static_cast<QGraphicsViewPrivate *>(qt_widget_private(&view)); + item->setPos(originalPos + QPoint(50, 50)); + viewPrivate->updateAll(); + QVERIFY(viewPrivate->fullUpdatePending); + QTest::qWait(50); + item->repaints = 0; + view.reset(); + item->setPos(originalPos); + QTest::qWait(50); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + QCOMPARE(view.paintedRegion, expectedRegion + expectedRegion.translated(50, 50)); + + // Make sure moving a parent item triggers an update on the children + // (even though the parent itself is outside the viewport). + QGraphicsRectItem *parent = new QGraphicsRectItem(0, 0, 10, 10); + parent->setPos(-400, 0); + item->setParentItem(parent); + item->setPos(400, 0); + scene.addItem(parent); + QTest::qWait(50); + itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform()) + .mapRect(item->boundingRect()).toRect(); + expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2); + view.reset(); + item->repaints = 0; + parent->translate(-400, 0); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 1); + QCOMPARE(view.paintedRegion, expectedRegion); + view.reset(); + item->repaints = 0; + parent->translate(400, 0); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + QCOMPARE(view.paintedRegion, expectedRegion); + QCOMPARE(view.paintedRegion, expectedRegion); +} + +void tst_QGraphicsItem::setTransformProperties_data() +{ + QTest::addColumn<QPointF>("origin"); + QTest::addColumn<qreal>("rotation"); + QTest::addColumn<qreal>("scale"); + + QTest::newRow("nothing") << QPointF() << qreal(0.0) << qreal(1.0); + + QTest::newRow("rotation") << QPointF() << qreal(42.2) << qreal(1.0); + + QTest::newRow("rotation dicentred") << QPointF(qreal(22.3), qreal(-56.2)) + << qreal(-2578.2) + << qreal(1.0); + + QTest::newRow("Scale") << QPointF() << qreal(0.0) + << qreal(6); + + QTest::newRow("Everything dicentred") << QPointF(qreal(22.3), qreal(-56.2)) << qreal(-175) << qreal(196); +} + +/** + * the normal QCOMPARE doesn't work because it doesn't use qFuzzyCompare + */ +#define QCOMPARE_TRANSFORM(X1, X2) QVERIFY(((X1)*(X2).inverted()).isIdentity()) + +void tst_QGraphicsItem::setTransformProperties() +{ + QFETCH(QPointF,origin); + QFETCH(qreal,rotation); + QFETCH(qreal,scale); + + QTransform result; + result.translate(origin.x(), origin.y()); + result.rotate(rotation, Qt::ZAxis); + result.scale(scale, scale); + result.translate(-origin.x(), -origin.y()); + + QGraphicsScene scene; + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + scene.addItem(item); + + item->setRotation(rotation); + item->setScale(scale); + item->setTransformOriginPoint(origin); + + QCOMPARE(item->rotation(), rotation); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + + QCOMPARE(QTransform(), item->transform()); + QCOMPARE(result, item->sceneTransform()); + + //----------------------------------------------------------------- + //Change the rotation Z + item->setRotation(45); + QTransform result2; + result2.translate(origin.x(), origin.y()); + result2.rotate(45); + result2.scale(scale, scale); + result2.translate(-origin.x(), -origin.y()); + + QCOMPARE(item->rotation(), 45.); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + + QCOMPARE(QTransform(), item->transform()); + QCOMPARE(result2, item->sceneTransform()); + + //----------------------------------------------------------------- + // calling setTransform() and setPos should change the sceneTransform + item->setTransform(result); + item->setPos(100, -150.5); + + QCOMPARE(item->rotation(), 45.); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + QCOMPARE(result, item->transform()); + + QTransform result3(result); + + result3.translate(origin.x(), origin.y()); + result3.rotate(45); + result3.scale(scale, scale); + result3.translate(-origin.x(), -origin.y()); + + result3 *= QTransform::fromTranslate(100, -150.5); //the pos; + + QCOMPARE(result3, item->sceneTransform()); + + //----------------------------------------------------- + // setting the propertiees should be the same as setting a transform + {//with center origin on the matrix + QGraphicsRectItem *item1 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item1); + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item2); + + item1->setPos(12.3, -5); + item2->setPos(12.3, -5); + item1->setRotation(rotation); + item1->setScale(scale); + item1->setTransformOriginPoint(origin); + + item2->setTransform(result); + + QCOMPARE_TRANSFORM(item1->sceneTransform(), item2->sceneTransform()); + + QCOMPARE_TRANSFORM(item1->itemTransform(item2), QTransform()); + QCOMPARE_TRANSFORM(item2->itemTransform(item1), QTransform()); + } +} + +class MyStyleOptionTester : public QGraphicsRectItem +{ +public: + MyStyleOptionTester(const QRectF &rect) + : QGraphicsRectItem(rect), startTrack(false) + {} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) + { + if (startTrack) { + //Doesn't use the extended style option so the exposed rect is the boundingRect + if (!(flags() & QGraphicsItem::ItemUsesExtendedStyleOption)) { + QCOMPARE(option->exposedRect, boundingRect()); + QCOMPARE(option->matrix, QMatrix()); + } else { + QVERIFY(option->exposedRect != QRect()); + QVERIFY(option->exposedRect != boundingRect()); + QCOMPARE(option->matrix, sceneTransform().toAffine()); + } + } + QGraphicsRectItem::paint(painter, option, widget); + } + bool startTrack; +}; + +void tst_QGraphicsItem::itemUsesExtendedStyleOption() +{ + QGraphicsScene scene(0, 0, 300, 300); + QGraphicsPixmapItem item; + item.setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); + QCOMPARE(item.flags(), QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemUsesExtendedStyleOption)); + item.setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false); + QCOMPARE(item.flags(), 0); + + //We now test the content of the style option + MyStyleOptionTester *rect = new MyStyleOptionTester(QRect(0, 0, 100, 100)); + scene.addItem(rect); + rect->setPos(200, 200); + QGraphicsView view(&scene); + rect->startTrack = false; + view.show(); + QTest::qWait(500); + rect->startTrack = true; + rect->update(10, 10, 10, 10); + QTest::qWait(125); + rect->startTrack = false; + rect->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); + QVERIFY((rect->flags() & QGraphicsItem::ItemUsesExtendedStyleOption)); + QTest::qWait(125); + rect->startTrack = true; + rect->update(10, 10, 10, 10); + QTest::qWait(125); +} + +void tst_QGraphicsItem::itemSendsGeometryChanges() +{ + ItemChangeTester item; + item.setFlags(0); + item.clear(); + + QTransform x = QTransform().rotate(45); + QPointF pos(10, 10); + qreal o(0.5); + item.setTransform(x); + item.setPos(pos); + QCOMPARE(item.transform(), x); + QCOMPARE(item.pos(), pos); + QCOMPARE(item.changes.size(), 0); + + item.setOpacity(o); + QCOMPARE(item.changes.size(), 2); // opacity + + item.setFlag(QGraphicsItem::ItemSendsGeometryChanges); + QCOMPARE(item.changes.size(), 4); // flags + item.setTransform(QTransform()); + item.setPos(QPointF()); + QCOMPARE(item.changes.size(), 8); // transform + pos + QCOMPARE(item.transform(), QTransform()); + QCOMPARE(item.pos(), QPointF()); + QCOMPARE(item.opacity(), o); + + QCOMPARE(item.changes, QList<QGraphicsItem::GraphicsItemChange>() + << QGraphicsItem::ItemOpacityChange + << QGraphicsItem::ItemOpacityHasChanged + << QGraphicsItem::ItemFlagsChange + << QGraphicsItem::ItemFlagsHaveChanged + << QGraphicsItem::ItemTransformChange + << QGraphicsItem::ItemTransformHasChanged + << QGraphicsItem::ItemPositionChange + << QGraphicsItem::ItemPositionHasChanged); +} + +// Make sure we update moved items correctly. +void tst_QGraphicsItem::moveItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-50, -50, 200, 200); + + MyGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + EventTester *parent = new EventTester; + EventTester *child = new EventTester(parent); + EventTester *grandChild = new EventTester(child); + +#define RESET_COUNTERS \ + parent->repaints = 0; \ + child->repaints = 0; \ + grandChild->repaints = 0; \ + view.reset(); + + scene.addItem(parent); + QTest::qWait(100); + + RESET_COUNTERS + + // Item's boundingRect: (-10, -10, 20, 20). + QRect parentDeviceBoundingRect = parent->deviceTransform(view.viewportTransform()) + .mapRect(parent->boundingRect()).toRect() + .adjusted(-2, -2, 2, 2); // Adjusted for antialiasing. + + parent->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(view.repaints, 1); + QRegion expectedParentRegion = parentDeviceBoundingRect; // old position + parentDeviceBoundingRect.translate(20, 20); + expectedParentRegion += parentDeviceBoundingRect; // new position + QCOMPARE(view.paintedRegion, expectedParentRegion); + + RESET_COUNTERS + + child->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(view.repaints, 1); + const QRegion expectedChildRegion = expectedParentRegion.translated(20, 20); + QCOMPARE(view.paintedRegion, expectedChildRegion); + + RESET_COUNTERS + + grandChild->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + QCOMPARE(view.repaints, 1); + const QRegion expectedGrandChildRegion = expectedParentRegion.translated(40, 40); + QCOMPARE(view.paintedRegion, expectedGrandChildRegion); + + RESET_COUNTERS + + parent->translate(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + QCOMPARE(view.repaints, 1); + expectedParentRegion.translate(20, 20); + expectedParentRegion += expectedChildRegion.translated(20, 20); + expectedParentRegion += expectedGrandChildRegion.translated(20, 20); + QCOMPARE(view.paintedRegion, expectedParentRegion); +} + +void tst_QGraphicsItem::sorting_data() +{ + QTest::addColumn<int>("index"); + + QTest::newRow("NoIndex") << int(QGraphicsScene::NoIndex); + QTest::newRow("BspTreeIndex") << int(QGraphicsScene::BspTreeIndex); +} + +void tst_QGraphicsItem::sorting() +{ + _paintedItems.clear(); + + QGraphicsScene scene; + QGraphicsItem *grid[100][100]; + for (int x = 0; x < 100; ++x) { + for (int y = 0; y < 100; ++y) { + PainterItem *item = new PainterItem; + item->setPos(x * 25, y * 25); + item->setData(0, QString("%1x%2").arg(x).arg(y)); + grid[x][y] = item; + scene.addItem(item); + } + } + + PainterItem *item1 = new PainterItem; + PainterItem *item2 = new PainterItem; + item1->setData(0, "item1"); + item2->setData(0, "item2"); + scene.addItem(item1); + scene.addItem(item2); + + QGraphicsView view(&scene); + view.setResizeAnchor(QGraphicsView::NoAnchor); + view.setTransformationAnchor(QGraphicsView::NoAnchor); + view.resize(120, 100); + view.setFrameStyle(0); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + _paintedItems.clear(); + + view.viewport()->repaint(); +#ifdef Q_WS_MAC + // There's no difference between repaint and update on the Mac, + // so we have to process events here to make sure we get the event. + QTest::qWait(100); +#endif + + QCOMPARE(_paintedItems, QList<QGraphicsItem *>() + << grid[0][0] << grid[0][1] << grid[0][2] << grid[0][3] + << grid[1][0] << grid[1][1] << grid[1][2] << grid[1][3] + << grid[2][0] << grid[2][1] << grid[2][2] << grid[2][3] + << grid[3][0] << grid[3][1] << grid[3][2] << grid[3][3] + << grid[4][0] << grid[4][1] << grid[4][2] << grid[4][3] + << item1 << item2); +} + +void tst_QGraphicsItem::itemHasNoContents() +{ + PainterItem *item1 = new PainterItem; + PainterItem *item2 = new PainterItem; + item2->setParentItem(item1); + item2->setPos(50, 50); + item1->setFlag(QGraphicsItem::ItemHasNoContents); + item1->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + QGraphicsScene scene; + scene.addItem(item1); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + _paintedItems.clear(); + + view.viewport()->repaint(); +#ifdef Q_WS_MAC + // There's no difference between update() and repaint() on the Mac, + // so we have to process events here to make sure we get the event. + QTest::qWait(100); +#endif + + QCOMPARE(_paintedItems, QList<QGraphicsItem *>() << item2); +} + +void tst_QGraphicsItem::hitTestUntransformableItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + // Confuse the BSP with dummy items. + QGraphicsRectItem *dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100, -100); + scene.addItem(dummy); + for (int i = 0; i < 100; ++i) { + QGraphicsItem *parent = dummy; + dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100 + i, -100 + i); + dummy->setParentItem(parent); + } + + QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, 20, 20); + item1->setPos(-200, -200); + + QGraphicsRectItem *item2 = new QGraphicsRectItem(0, 0, 20, 20); + item2->setFlag(QGraphicsItem::ItemIgnoresTransformations); + item2->setParentItem(item1); + item2->setPos(200, 200); + + QGraphicsRectItem *item3 = new QGraphicsRectItem(0, 0, 20, 20); + item3->setParentItem(item2); + item3->setPos(80, 80); + + scene.addItem(item1); + QTest::qWait(100); + + QList<QGraphicsItem *> items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast<QGraphicsItem*>(item3)); + + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + QTest::qWait(100); + + items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast<QGraphicsItem*>(item3)); +} + +void tst_QGraphicsItem::hitTestGraphicsEffectItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + // Confuse the BSP with dummy items. + QGraphicsRectItem *dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100, -100); + scene.addItem(dummy); + for (int i = 0; i < 100; ++i) { + QGraphicsItem *parent = dummy; + dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100 + i, -100 + i); + dummy->setParentItem(parent); + } + + const QRectF itemBoundingRect(0, 0, 20, 20); + EventTester *item1 = new EventTester; + item1->br = itemBoundingRect; + item1->setPos(-200, -200); + + EventTester *item2 = new EventTester; + item2->br = itemBoundingRect; + item2->setFlag(QGraphicsItem::ItemIgnoresTransformations); + item2->setParentItem(item1); + item2->setPos(200, 200); + + EventTester *item3 = new EventTester; + item3->br = itemBoundingRect; + item3->setParentItem(item2); + item3->setPos(80, 80); + + scene.addItem(item1); + QTest::qWait(100); + + item1->repaints = 0; + item2->repaints = 0; + item3->repaints = 0; + + // Apply shadow effect to the entire sub-tree. + QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect; + shadow->setOffset(-20, -20); + item1->setGraphicsEffect(shadow); + QTest::qWait(50); + + // Make sure all items are repainted. + QCOMPARE(item1->repaints, 1); + QCOMPARE(item2->repaints, 1); + QCOMPARE(item3->repaints, 1); + + // Make sure an item doesn't respond to a click on its shadow. + QList<QGraphicsItem *> items = scene.items(QPointF(75, 75)); + QVERIFY(items.isEmpty()); + items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast<EventTester *>(item3)); + + item1->repaints = 0; + item2->repaints = 0; + item3->repaints = 0; + + view.viewport()->update(75, 75, 20, 20); + QTest::qWait(50); + + // item1 is the effect source and must therefore be repainted. + // item2 intersects with the exposed region + // item3 is just another child outside the exposed region + QCOMPARE(item1->repaints, 1); + QCOMPARE(item2->repaints, 1); + QCOMPARE(item3->repaints, 0); + + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + QTest::qWait(100); + + items = scene.items(QPointF(75, 75)); + QVERIFY(items.isEmpty()); + items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast<EventTester *>(item3)); +} + +void tst_QGraphicsItem::focusProxy() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsItem *item = scene.addRect(0, 0, 10, 10); + item->setFlag(QGraphicsItem::ItemIsFocusable); + QVERIFY(!item->focusProxy()); + + QGraphicsItem *item2 = scene.addRect(0, 0, 10, 10); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFocusProxy(item2); + QCOMPARE(item->focusProxy(), item2); + + item->setFocus(); + QVERIFY(item->hasFocus()); + QVERIFY(item2->hasFocus()); + + // Try to make a focus chain loop + QString err; + QTextStream stream(&err); + stream << "QGraphicsItem::setFocusProxy: " + << (void*)item << " is already in the focus proxy chain" << flush; + QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData()); + item2->setFocusProxy(item); // fails + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + + // Try to assign self as focus proxy + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::setFocusProxy: cannot assign self as focus proxy"); + item->setFocusProxy(item); // fails + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + + // Reset the focus proxy + item->setFocusProxy(0); + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + QVERIFY(!item->hasFocus()); + QVERIFY(item2->hasFocus()); + + // Test deletion + item->setFocusProxy(item2); + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + delete item2; + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + + // Test event delivery + item2 = scene.addRect(0, 0, 10, 10); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFocusProxy(item2); + item->clearFocus(); + + EventSpy focusInSpy(&scene, item, QEvent::FocusIn); + EventSpy focusOutSpy(&scene, item, QEvent::FocusOut); + EventSpy focusInSpy2(&scene, item2, QEvent::FocusIn); + EventSpy focusOutSpy2(&scene, item2, QEvent::FocusOut); + QCOMPARE(focusInSpy.count(), 0); + QCOMPARE(focusOutSpy.count(), 0); + QCOMPARE(focusInSpy2.count(), 0); + QCOMPARE(focusOutSpy2.count(), 0); + + item->setFocus(); + QCOMPARE(focusInSpy.count(), 0); + QCOMPARE(focusInSpy2.count(), 1); + item->clearFocus(); + QCOMPARE(focusOutSpy.count(), 0); + QCOMPARE(focusOutSpy2.count(), 1); + + // Test two items proxying one item. + QGraphicsItem *item3 = scene.addRect(0, 0, 10, 10); + item3->setFlag(QGraphicsItem::ItemIsFocusable); + item3->setFocusProxy(item2); // item and item3 use item2 as proxy + + QCOMPARE(item->focusProxy(), item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + QCOMPARE(item3->focusProxy(), item2); + delete item2; + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + QCOMPARE(item3->focusProxy(), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::subFocus() +{ + // Construct a text item that's not part of a scene (yet) + // and has no parent. Setting focus on it will not make + // the item gain input focus; that requires a scene. But + // it does set subfocus, indicating that the item wishes + // to gain focus later. + QGraphicsTextItem *text = new QGraphicsTextItem("Hello"); + text->setTextInteractionFlags(Qt::TextEditorInteraction); + QVERIFY(!text->hasFocus()); + text->setFocus(); + QVERIFY(!text->hasFocus()); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text); + + // Add a sibling. + QGraphicsTextItem *text2 = new QGraphicsTextItem("Hi"); + text2->setTextInteractionFlags(Qt::TextEditorInteraction); + text2->setPos(30, 30); + + // Add both items to a scene and check that it's text that + // got input focus. + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(text); + scene.addItem(text2); + QVERIFY(text->hasFocus()); + + text->setData(0, "text"); + text2->setData(0, "text2"); + + // Remove text2 and set subfocus on it. Then readd. Reparent it onto the + // other item and see that it gains input focus. + scene.removeItem(text2); + text2->setFocus(); + scene.addItem(text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + text2->setParentItem(text); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + QVERIFY(!text->hasFocus()); + QVERIFY(text2->hasFocus()); + + // Remove both items from the scene, restore subfocus and + // readd them. Now the subfocus should kick in and give + // text2 focus. + scene.removeItem(text); + QCOMPARE(text->focusItem(), (QGraphicsItem *)0); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)0); + text2->setFocus(); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + scene.addItem(text); + + // Hiding and showing text should pass focus to text2. + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QVERIFY(text2->hasFocus()); + + // Subfocus should repropagate to root when reparenting. + QGraphicsRectItem *rect = new QGraphicsRectItem; + QGraphicsRectItem *rect2 = new QGraphicsRectItem(rect); + QGraphicsRectItem *rect3 = new QGraphicsRectItem(rect2); + rect3->setFlag(QGraphicsItem::ItemIsFocusable); + + text->setData(0, "text"); + text2->setData(0, "text2"); + rect->setData(0, "rect"); + rect2->setData(0, "rect2"); + rect3->setData(0, "rect3"); + + rect3->setFocus(); + QVERIFY(!rect3->hasFocus()); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect3->focusItem(), (QGraphicsItem *)rect3); + rect->setParentItem(text2); + QCOMPARE(text->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect3->focusItem(), (QGraphicsItem *)rect3); + QVERIFY(!rect->hasFocus()); + QVERIFY(!rect2->hasFocus()); + QVERIFY(rect3->hasFocus()); + + delete rect2; + QCOMPARE(text->focusItem(), (QGraphicsItem *)0); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)0); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::focusProxyDeletion() +{ + QGraphicsRectItem *rect = new QGraphicsRectItem; + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + + delete rect2; + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + + rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + delete rect; // don't crash + + rect = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QGraphicsScene *scene = new QGraphicsScene; + scene->addItem(rect); + scene->addItem(rect2); + delete rect2; + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + + rect2 = new QGraphicsRectItem; + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::setFocusProxy: focus proxy must be in same scene"); + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + scene->addItem(rect2); + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + delete rect; // don't crash + + rect = new QGraphicsRectItem; + rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + scene->addItem(rect); + scene->addItem(rect2); + rect->setFocusProxy(rect2); + delete scene; // don't crash +} + +void tst_QGraphicsItem::negativeZStacksBehindParent() +{ + QGraphicsRectItem rect; + QCOMPARE(rect.zValue(), qreal(0.0)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemNegativeZStacksBehindParent)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(-1); + QCOMPARE(rect.zValue(), qreal(-1.0)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(0); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent); + QVERIFY(rect.flags() & QGraphicsItem::ItemNegativeZStacksBehindParent); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(-1); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); + rect.setZValue(0); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, false); + rect.setZValue(-1); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, true); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, false); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); +} + +void tst_QGraphicsItem::setGraphicsEffect() +{ + // Check that we don't have any effect by default. + QGraphicsItem *item = new QGraphicsRectItem(0, 0, 10, 10); + QVERIFY(!item->graphicsEffect()); + + // SetGet check. + QPointer<QGraphicsEffect> blurEffect = new QGraphicsBlurEffect; + item->setGraphicsEffect(blurEffect); + QCOMPARE(item->graphicsEffect(), static_cast<QGraphicsEffect *>(blurEffect)); + + // Ensure the existing effect is deleted when setting a new one. + QPointer<QGraphicsEffect> shadowEffect = new QGraphicsDropShadowEffect; + item->setGraphicsEffect(shadowEffect); + QVERIFY(!blurEffect); + QCOMPARE(item->graphicsEffect(), static_cast<QGraphicsEffect *>(shadowEffect)); + blurEffect = new QGraphicsBlurEffect; + + // Ensure the effect is uninstalled when setting it on a new target. + QGraphicsItem *anotherItem = new QGraphicsRectItem(0, 0, 10, 10); + anotherItem->setGraphicsEffect(blurEffect); + item->setGraphicsEffect(blurEffect); + QVERIFY(!anotherItem->graphicsEffect()); + QVERIFY(!shadowEffect); + + // Ensure the existing effect is deleted when deleting the item. + delete item; + QVERIFY(!blurEffect); + delete anotherItem; +} + +void tst_QGraphicsItem::panel() +{ + QGraphicsScene scene; + + QGraphicsRectItem *panel1 = new QGraphicsRectItem; + QGraphicsRectItem *panel2 = new QGraphicsRectItem; + QGraphicsRectItem *panel3 = new QGraphicsRectItem; + QGraphicsRectItem *panel4 = new QGraphicsRectItem; + QGraphicsRectItem *notPanel1 = new QGraphicsRectItem; + QGraphicsRectItem *notPanel2 = new QGraphicsRectItem; + panel1->setFlag(QGraphicsItem::ItemIsPanel); + panel2->setFlag(QGraphicsItem::ItemIsPanel); + panel3->setFlag(QGraphicsItem::ItemIsPanel); + panel4->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(panel1); + scene.addItem(panel2); + scene.addItem(panel3); + scene.addItem(panel4); + scene.addItem(notPanel1); + scene.addItem(notPanel2); + + EventSpy spy_activate_panel1(&scene, panel1, QEvent::WindowActivate); + EventSpy spy_deactivate_panel1(&scene, panel1, QEvent::WindowDeactivate); + EventSpy spy_activate_panel2(&scene, panel2, QEvent::WindowActivate); + EventSpy spy_deactivate_panel2(&scene, panel2, QEvent::WindowDeactivate); + EventSpy spy_activate_panel3(&scene, panel3, QEvent::WindowActivate); + EventSpy spy_deactivate_panel3(&scene, panel3, QEvent::WindowDeactivate); + EventSpy spy_activate_panel4(&scene, panel4, QEvent::WindowActivate); + EventSpy spy_deactivate_panel4(&scene, panel4, QEvent::WindowDeactivate); + EventSpy spy_activate_notPanel1(&scene, notPanel1, QEvent::WindowActivate); + EventSpy spy_deactivate_notPanel1(&scene, notPanel1, QEvent::WindowDeactivate); + EventSpy spy_activate_notPanel2(&scene, notPanel1, QEvent::WindowActivate); + EventSpy spy_deactivate_notPanel2(&scene, notPanel1, QEvent::WindowDeactivate); + + QCOMPARE(spy_activate_panel1.count(), 0); + QCOMPARE(spy_deactivate_panel1.count(), 0); + QCOMPARE(spy_activate_panel2.count(), 0); + QCOMPARE(spy_deactivate_panel2.count(), 0); + QCOMPARE(spy_activate_panel3.count(), 0); + QCOMPARE(spy_deactivate_panel3.count(), 0); + QCOMPARE(spy_activate_panel4.count(), 0); + QCOMPARE(spy_deactivate_panel4.count(), 0); + QCOMPARE(spy_activate_notPanel1.count(), 0); + QCOMPARE(spy_deactivate_notPanel1.count(), 0); + QCOMPARE(spy_activate_notPanel2.count(), 0); + QCOMPARE(spy_deactivate_notPanel2.count(), 0); + + QVERIFY(!scene.activePanel()); + QVERIFY(!scene.isActive()); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + + // No previous activation, so the scene is active. + QVERIFY(scene.isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)panel1); + QVERIFY(panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(!notPanel1->isActive()); + QVERIFY(!notPanel2->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 0); + QCOMPARE(spy_deactivate_notPanel2.count(), 0); + QCOMPARE(spy_activate_panel1.count(), 1); + QCOMPARE(spy_activate_panel2.count(), 0); + QCOMPARE(spy_activate_panel3.count(), 0); + QCOMPARE(spy_activate_panel4.count(), 0); + + // Switch back to scene. + scene.setActivePanel(0); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 1); + QCOMPARE(spy_activate_notPanel2.count(), 1); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(!notPanel1->isActive()); + QVERIFY(!notPanel2->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 1); + QCOMPARE(spy_deactivate_notPanel2.count(), 1); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 2); + QCOMPARE(spy_activate_notPanel2.count(), 2); + + // Switch to panel1 + scene.setActivePanel(panel1); + QVERIFY(panel1->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 2); + QCOMPARE(spy_deactivate_notPanel2.count(), 2); + QCOMPARE(spy_activate_panel1.count(), 2); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!panel1->isActive()); + QCOMPARE(spy_deactivate_panel1.count(), 2); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(panel1->isActive()); + QCOMPARE(spy_activate_panel1.count(), 3); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!panel1->isActive()); + QVERIFY(!scene.activePanel()); + scene.setActivePanel(0); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(!panel1->isActive()); +} + +void tst_QGraphicsItem::addPanelToActiveScene() +{ + QGraphicsScene scene; + QVERIFY(!scene.isActive()); + + QGraphicsRectItem *rect = new QGraphicsRectItem; + scene.addItem(rect); + QVERIFY(!rect->isActive()); + scene.removeItem(rect); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + QVERIFY(scene.isActive()); + scene.addItem(rect); + QVERIFY(rect->isActive()); + scene.removeItem(rect); + + rect->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(rect); + QVERIFY(rect->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect); + + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + scene.addItem(rect2); + QVERIFY(rect->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect); +} + +void tst_QGraphicsItem::activate() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect = scene.addRect(-10, -10, 20, 20); + QVERIFY(!rect->isActive()); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + + // Non-panel item (active when scene is active). + QVERIFY(rect->isActive()); + + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + rect2->setFlag(QGraphicsItem::ItemIsPanel); + QGraphicsRectItem *rect3 = new QGraphicsRectItem; + rect3->setFlag(QGraphicsItem::ItemIsPanel); + + // Test normal activation. + QVERIFY(!rect2->isActive()); + scene.addItem(rect2); + QVERIFY(rect2->isActive()); // first panel item is activated + scene.addItem(rect3); + QVERIFY(!rect3->isActive()); // second panel item is _not_ activated + rect3->setActive(true); + QVERIFY(rect3->isActive()); + scene.removeItem(rect3); + QVERIFY(!rect3->isActive()); // no panel is active anymore + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + scene.addItem(rect3); + QVERIFY(rect3->isActive()); // second panel item is activated + + // Test pending activation. + scene.removeItem(rect3); + rect2->setActive(true); + QVERIFY(rect2->isActive()); // first panel item is activated + rect3->setActive(true); + QVERIFY(!rect3->isActive()); // not active (yet) + scene.addItem(rect3); + QVERIFY(rect3->isActive()); // now becomes active + + // Test pending deactivation. + scene.removeItem(rect3); + rect3->setActive(false); + scene.addItem(rect3); + QVERIFY(!rect3->isActive()); // doesn't become active + + // Child of panel activation. + rect3->setActive(true); + QGraphicsRectItem *rect4 = new QGraphicsRectItem; + rect4->setFlag(QGraphicsItem::ItemIsPanel); + QGraphicsRectItem *rect5 = new QGraphicsRectItem(rect4); + QGraphicsRectItem *rect6 = new QGraphicsRectItem(rect5); + scene.addItem(rect4); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect3); + scene.removeItem(rect4); + rect6->setActive(true); + scene.addItem(rect4); + QVERIFY(rect4->isActive()); + QVERIFY(rect5->isActive()); + QVERIFY(rect6->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect4); + scene.removeItem(rect4); // no active panel + rect6->setActive(false); + scene.addItem(rect4); + QVERIFY(!rect4->isActive()); + QVERIFY(!rect5->isActive()); + QVERIFY(!rect6->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + + // Controlling auto-activation when the scene changes activation. + rect4->setActive(true); + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!scene.isActive()); + QVERIFY(!rect4->isActive()); + rect4->setActive(false); + QApplication::sendEvent(&scene, &activate); + QVERIFY(scene.isActive()); + QVERIFY(!scene.activePanel()); + QVERIFY(!rect4->isActive()); +} + +void tst_QGraphicsItem::setActivePanelOnInactiveScene() +{ + QGraphicsScene scene; + QGraphicsRectItem *item = scene.addRect(QRectF()); + QGraphicsRectItem *panel = scene.addRect(QRectF()); + panel->setFlag(QGraphicsItem::ItemIsPanel); + + EventSpy itemActivateSpy(&scene, item, QEvent::WindowActivate); + EventSpy itemDeactivateSpy(&scene, item, QEvent::WindowDeactivate); + EventSpy panelActivateSpy(&scene, panel, QEvent::WindowActivate); + EventSpy panelDeactivateSpy(&scene, panel, QEvent::WindowDeactivate); + EventSpy sceneActivationChangeSpy(&scene, QEvent::ActivationChange); + + scene.setActivePanel(panel); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + QCOMPARE(itemActivateSpy.count(), 0); + QCOMPARE(itemDeactivateSpy.count(), 0); + QCOMPARE(panelActivateSpy.count(), 0); + QCOMPARE(panelDeactivateSpy.count(), 0); + QCOMPARE(sceneActivationChangeSpy.count(), 0); +} + +void tst_QGraphicsItem::activationOnShowHide() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsRectItem *rootPanel = scene.addRect(QRectF()); + rootPanel->setFlag(QGraphicsItem::ItemIsPanel); + rootPanel->setActive(true); + + QGraphicsRectItem *subPanel = new QGraphicsRectItem; + subPanel->setFlag(QGraphicsItem::ItemIsPanel); + + // Reparenting onto an active panel auto-activates the child panel. + subPanel->setParentItem(rootPanel); + QVERIFY(subPanel->isActive()); + QVERIFY(!rootPanel->isActive()); + + // Hiding an active child panel will reactivate the parent panel. + subPanel->hide(); + QVERIFY(rootPanel->isActive()); + + // Showing a child panel will auto-activate it. + subPanel->show(); + QVERIFY(subPanel->isActive()); + QVERIFY(!rootPanel->isActive()); + + // Adding an unrelated panel doesn't affect activation. + QGraphicsRectItem *otherPanel = new QGraphicsRectItem; + otherPanel->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(otherPanel); + QVERIFY(subPanel->isActive()); + + // Showing an unrelated panel doesn't affect activation. + otherPanel->hide(); + otherPanel->show(); + QVERIFY(subPanel->isActive()); + + // Add a non-panel item. + QGraphicsRectItem *otherItem = new QGraphicsRectItem; + scene.addItem(otherItem); + otherItem->setActive(true); + QVERIFY(otherItem->isActive()); + + // Reparent a panel onto an active non-panel item. + subPanel->setParentItem(otherItem); + QVERIFY(subPanel->isActive()); + + // Showing a child panel of a non-panel item will activate it. + subPanel->hide(); + QVERIFY(!subPanel->isActive()); + QVERIFY(otherItem->isActive()); + subPanel->show(); + QVERIFY(subPanel->isActive()); + + // Hiding a toplevel active panel will pass activation back + // to the non-panel items. + rootPanel->setActive(true); + rootPanel->hide(); + QVERIFY(!rootPanel->isActive()); + QVERIFY(otherItem->isActive()); +} + +class MoveWhileDying : public QGraphicsRectItem +{ +public: + MoveWhileDying(QGraphicsItem *parent = 0) + : QGraphicsRectItem(parent) + { } + ~MoveWhileDying() + { + foreach (QGraphicsItem *c, childItems()) { + foreach (QGraphicsItem *cc, c->childItems()) { + cc->moveBy(10, 10); + } + c->moveBy(10, 10); + } + if (QGraphicsItem *p = parentItem()) { p->moveBy(10, 10); } + } +}; + +void tst_QGraphicsItem::moveWhileDeleting() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect = new QGraphicsRectItem; + MoveWhileDying *silly = new MoveWhileDying(rect); + QGraphicsRectItem *child = new QGraphicsRectItem(silly); + scene.addItem(rect); + delete rect; // don't crash! + + rect = new QGraphicsRectItem; + silly = new MoveWhileDying(rect); + child = new QGraphicsRectItem(silly); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(125); + + delete rect; + + rect = new QGraphicsRectItem; + rect->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + silly = new MoveWhileDying(rect); + child = new QGraphicsRectItem(silly); + + QTest::qWait(125); + + delete rect; + + rect = new MoveWhileDying; + rect->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + child = new QGraphicsRectItem(rect); + silly = new MoveWhileDying(child); + + QTest::qWait(125); + + delete rect; +} + +class MyRectItem : public QGraphicsWidget +{ + Q_OBJECT +public: + MyRectItem(QGraphicsItem *parent = 0) : QGraphicsWidget(parent) + { + + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->setBrush(brush); + painter->drawRect(boundingRect()); + } + void move() + { + setPos(-100,-100); + topLevel->collidingItems(Qt::IntersectsItemBoundingRect); + } +public: + QGraphicsItem *topLevel; + QBrush brush; +}; + + +void tst_QGraphicsItem::ensureDirtySceneTransform() +{ + QGraphicsScene scene; + + MyRectItem *topLevel = new MyRectItem; + topLevel->setGeometry(0, 0, 100, 100); + topLevel->setPos(-50, -50); + topLevel->brush = QBrush(QColor(Qt::black)); + scene.addItem(topLevel); + + MyRectItem *parent = new MyRectItem; + parent->topLevel = topLevel; + parent->setGeometry(0, 0, 100, 100); + parent->setPos(0, 0); + parent->brush = QBrush(QColor(Qt::magenta)); + parent->setObjectName("parent"); + scene.addItem(parent); + + MyRectItem *child = new MyRectItem(parent); + child->setGeometry(0, 0, 80, 80); + child->setPos(10, 10); + child->setObjectName("child"); + child->brush = QBrush(QColor(Qt::blue)); + + MyRectItem *child2 = new MyRectItem(parent); + child2->setGeometry(0, 0, 80, 80); + child2->setPos(15, 15); + child2->setObjectName("child2"); + child2->brush = QBrush(QColor(Qt::green)); + + MyRectItem *child3 = new MyRectItem(parent); + child3->setGeometry(0, 0, 80, 80); + child3->setPos(20, 20); + child3->setObjectName("child3"); + child3->brush = QBrush(QColor(Qt::gray)); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), &view); + + //We move the parent + parent->move(); + QApplication::processEvents(); + + //We check if all items moved + QCOMPARE(child->pos(), QPointF(10, 10)); + QCOMPARE(child2->pos(), QPointF(15, 15)); + QCOMPARE(child3->pos(), QPointF(20, 20)); + + QCOMPARE(child->sceneBoundingRect(), QRectF(-90, -90, 80, 80)); + QCOMPARE(child2->sceneBoundingRect(), QRectF(-85, -85, 80, 80)); + QCOMPARE(child3->sceneBoundingRect(), QRectF(-80, -80, 80, 80)); + + QCOMPARE(child->sceneTransform(), QTransform::fromTranslate(-90, -90)); + QCOMPARE(child2->sceneTransform(), QTransform::fromTranslate(-85, -85)); + QCOMPARE(child3->sceneTransform(), QTransform::fromTranslate(-80, -80)); +} + +void tst_QGraphicsItem::focusScope() +{ + // ItemIsFocusScope is an internal feature (for now). + QGraphicsScene scene; + + QGraphicsRectItem *scope3 = new QGraphicsRectItem; + scope3->setData(0, "scope3"); + scope3->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope3->setFocus(); + QVERIFY(!scope3->focusScopeItem()); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + + QGraphicsRectItem *scope2 = new QGraphicsRectItem; + scope2->setData(0, "scope2"); + scope2->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope2->setFocus(); + QVERIFY(!scope2->focusScopeItem()); + scope3->setParentItem(scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope2); + + QGraphicsRectItem *scope1 = new QGraphicsRectItem; + scope1->setData(0, "scope1"); + scope1->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope1->setFocus(); + QVERIFY(!scope1->focusScopeItem()); + scope2->setParentItem(scope1); + + QCOMPARE(scope1->focusItem(), (QGraphicsItem *)scope1); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)0); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)0); + QCOMPARE(scope1->focusScopeItem(), (QGraphicsItem *)scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + + scene.addItem(scope1); + + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + scene.setFocus(); + + QCOMPARE(scope1->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope1->focusScopeItem(), (QGraphicsItem *)scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + + QVERIFY(scope3->hasFocus()); + + scope3->hide(); + QVERIFY(scope2->hasFocus()); + scope2->hide(); + QVERIFY(scope1->hasFocus()); + scope2->show(); + QVERIFY(scope2->hasFocus()); + scope3->show(); + QVERIFY(scope3->hasFocus()); + scope1->hide(); + QVERIFY(!scope3->hasFocus()); + scope1->show(); + QVERIFY(scope3->hasFocus()); + scope3->clearFocus(); + QVERIFY(scope2->hasFocus()); + scope2->clearFocus(); + QVERIFY(scope1->hasFocus()); + scope2->hide(); + scope2->show(); + QVERIFY(!scope2->hasFocus()); + QVERIFY(scope3->hasFocus()); + + QGraphicsRectItem *rect4 = new QGraphicsRectItem; + rect4->setData(0, "rect4"); + rect4->setParentItem(scope3); + + QGraphicsRectItem *rect5 = new QGraphicsRectItem; + rect5->setData(0, "rect5"); + rect5->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + rect5->setFocus(); + rect5->setParentItem(rect4); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)rect5); + QVERIFY(!rect5->hasFocus()); + + rect4->setParentItem(0); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + QVERIFY(scope3->hasFocus()); + + QGraphicsRectItem *rectA = new QGraphicsRectItem; + QGraphicsRectItem *scopeA = new QGraphicsRectItem(rectA); + scopeA->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scopeA->setFocus(); + QGraphicsRectItem *scopeB = new QGraphicsRectItem(scopeA); + scopeB->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scopeB->setFocus(); + + scene.addItem(rectA); + QVERIFY(!rect5->hasFocus()); + QVERIFY(!scopeB->hasFocus()); + + scopeA->setFocus(); + QVERIFY(scopeB->hasFocus()); + QCOMPARE(scopeB->focusItem(), (QGraphicsItem *)scopeB); +} + +void tst_QGraphicsItem::stackBefore() +{ + QGraphicsRectItem parent; + QGraphicsRectItem *child1 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child2 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child3 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child4 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child1 << child2 << child3 << child4)); + child1->setData(0, "child1"); + child2->setData(0, "child2"); + child3->setData(0, "child3"); + child4->setData(0, "child4"); + + // Remove and append + child2->setParentItem(0); + child2->setParentItem(&parent); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child1 << child3 << child4 << child2)); + + // Move child2 before child1 + child2->stackBefore(child1); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child2->stackBefore(child2); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child1->setZValue(1); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child3 << child4 << child1)); + child1->stackBefore(child2); // no effect + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child3 << child4 << child1)); + child1->setZValue(0); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child4->stackBefore(child1); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child4 << child1 << child3)); + child4->setZValue(1); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child3->stackBefore(child1); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child3 << child1 << child4)); + child4->setZValue(0); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child4 << child3 << child1)); + + // Make them all toplevels + child1->setParentItem(0); + child2->setParentItem(0); + child3->setParentItem(0); + child4->setParentItem(0); + + QGraphicsScene scene; + scene.addItem(child1); + scene.addItem(child2); + scene.addItem(child3); + scene.addItem(child4); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), + (QList<QGraphicsItem *>() << child1 << child2 << child3 << child4)); + + // Remove and append + scene.removeItem(child2); + scene.addItem(child2); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child1 << child3 << child4 << child2)); + + // Move child2 before child1 + child2->stackBefore(child1); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child2->stackBefore(child2); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child1->setZValue(1); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child3 << child4 << child1)); + child1->stackBefore(child2); // no effect + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child3 << child4 << child1)); + child1->setZValue(0); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child4->stackBefore(child1); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child4 << child1 << child3)); + child4->setZValue(1); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child3->stackBefore(child1); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child3 << child1 << child4)); + child4->setZValue(0); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child4 << child3 << child1)); +} + void tst_QGraphicsItem::QTBUG_4233_updateCachedWithSceneRect() { EventTester *tester = new EventTester; |