qt6windows7/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp
2023-10-29 23:33:08 +01:00

11884 lines
440 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtTest/private/qtesthelpers_p.h>
#include <private/qgraphicsitem_p.h>
#include <private/qgraphicsview_p.h>
#include <private/qgraphicsscene_p.h>
#include <private/qinputdevice_p.h>
#include <QRandomGenerator>
#include <QStyleOptionGraphicsItem>
#include <QAbstractTextDocumentLayout>
#include <QBitmap>
#include <QCursor>
#include <QScreen>
#include <QLabel>
#include <QDial>
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QGraphicsSceneEvent>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QGraphicsProxyWidget>
#include <QPainter>
#include <QScrollBar>
#include <QVBoxLayout>
#include <QGraphicsEffect>
#include <QPushButton>
#include <QLineEdit>
#include <QGraphicsLinearLayout>
#include <QTransform>
#include <QSharedPointer>
#include <float.h>
#include <QStyleHints>
#include <QPainterPath>
#include <QSignalSpy>
#include <QTimer>
#include <QtGui/private/qeventpoint_p.h>
#include <QtWidgets/private/qapplication_p.h>
using AbstractGraphicsShapeItemPtr = QSharedPointer<QAbstractGraphicsShapeItem>;
using GraphicsItems = QList<QGraphicsItem *>;
using GraphicsItemsList = QList<QGraphicsItem *>;
Q_DECLARE_METATYPE(AbstractGraphicsShapeItemPtr)
Q_DECLARE_METATYPE(QGraphicsItem::GraphicsItemFlags)
Q_DECLARE_METATYPE(QPainterPath)
Q_DECLARE_METATYPE(QSizeF)
Q_DECLARE_METATYPE(QTransform)
#if defined(Q_OS_WIN)
#include <qt_windows.h>
#define Q_CHECK_PAINTEVENTS \
if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
QSKIP("The Graphics View doesn't get the paint events");
#else
#define Q_CHECK_PAINTEVENTS
#endif
#if defined(Q_OS_MAC)
// On mac (cocoa) we always get full update.
// So check that the expected region is contained inside the actual
#define COMPARE_REGIONS(ACTUAL, EXPECTED) QVERIFY((EXPECTED).subtracted(ACTUAL).isEmpty())
#else
#define COMPARE_REGIONS QTRY_COMPARE
#endif
static QGraphicsRectItem staticItem; //QTBUG-7629, we should not crash at exit.
static void sendMousePress(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton)
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setScenePos(point);
event.setButton(button);
event.setButtons(button);
QCoreApplication::sendEvent(scene, &event);
}
static void sendMouseMove(QGraphicsScene *scene, const QPointF &point,
Qt::MouseButton button = Qt::NoButton, Qt::MouseButtons /* buttons */ = {})
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
event.setScenePos(point);
event.setButton(button);
event.setButtons(button);
QCoreApplication::sendEvent(scene, &event);
}
static void sendMouseRelease(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton)
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
event.setScenePos(point);
event.setButton(button);
QCoreApplication::sendEvent(scene, &event);
}
static void sendMouseClick(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton)
{
sendMousePress(scene, point, button);
sendMouseRelease(scene, point, button);
}
static void sendKeyPress(QGraphicsScene *scene, Qt::Key key)
{
QKeyEvent keyEvent(QEvent::KeyPress, key, Qt::NoModifier);
QCoreApplication::sendEvent(scene, &keyEvent);
}
static void sendKeyRelease(QGraphicsScene *scene, Qt::Key key)
{
QKeyEvent keyEvent(QEvent::KeyRelease, key, Qt::NoModifier);
QCoreApplication::sendEvent(scene, &keyEvent);
}
static void sendKeyClick(QGraphicsScene *scene, Qt::Key key)
{
sendKeyPress(scene, key);
sendKeyRelease(scene, key);
}
class EventSpy : public QGraphicsWidget
{
Q_OBJECT
public:
EventSpy(QObject *watched, QEvent::Type type)
: spied(type)
{
watched->installEventFilter(this);
}
EventSpy(QGraphicsScene *scene, QGraphicsItem *watched, QEvent::Type type)
: spied(type)
{
scene->addItem(this);
watched->installSceneEventFilter(this);
}
int count() const { return _count; }
protected:
bool eventFilter(QObject *watched, QEvent *event) override
{
Q_UNUSED(watched);
if (event->type() == spied)
++_count;
return false;
}
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
{
Q_UNUSED(watched);
if (event->type() == spied)
++_count;
return false;
}
int _count = 0;
const QEvent::Type spied;
};
class EventSpy2 : public QGraphicsWidget
{
Q_OBJECT
public:
EventSpy2(QObject *watched)
{
watched->installEventFilter(this);
}
EventSpy2(QGraphicsScene *scene, QGraphicsItem *watched)
{
scene->addItem(this);
watched->installSceneEventFilter(this);
}
QMap<QEvent::Type, int> counts;
protected:
bool eventFilter(QObject *watched, QEvent *event) override
{
Q_UNUSED(watched);
++counts[event->type()];
return false;
}
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
{
Q_UNUSED(watched);
++counts[event->type()];
return false;
}
};
class EventTester : public QGraphicsItem
{
public:
using QGraphicsItem::QGraphicsItem;
void setGeometry(const QRectF &rect)
{
prepareGeometryChange();
br = rect;
update();
}
QRectF boundingRect() const override
{ return br; }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *o, QWidget *) override
{
hints = painter->renderHints();
painter->setBrush(brush);
painter->drawRect(boundingRect());
lastExposedRect = o->exposedRect;
++repaints;
}
bool sceneEvent(QEvent *event) override
{
events << event->type();
return QGraphicsItem::sceneEvent(event);
}
void reset()
{
events.clear();
hints = QPainter::RenderHints{};
repaints = 0;
lastExposedRect = QRectF();
}
QList<QEvent::Type> events;
QPainter::RenderHints hints;
int repaints = 0;
QRectF br = QRectF(-10, -10, 20, 20);
QRectF lastExposedRect;
QBrush brush;
};
class MyGraphicsView : public QGraphicsView
{
Q_OBJECT
public:
QRegion paintedRegion;
int repaints = 0;
using QGraphicsView::QGraphicsView;
void paintEvent(QPaintEvent *e) override
{
paintedRegion += e->region();
++repaints;
QGraphicsView::paintEvent(e);
}
void reset()
{
repaints = 0;
paintedRegion = QRegion();
}
};
class tst_QGraphicsItem : public QObject
{
Q_OBJECT
private slots:
void cleanup();
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 isVisibleTo();
void explicitlyVisible();
void enabled();
void explicitlyEnabled();
void selected();
void selected2();
void selected_group();
void selected_textItem();
void selected_multi();
void acceptedMouseButtons();
void acceptHoverEvents();
void childAcceptsHoverEvents();
void hasFocus();
void pos();
void scenePos();
void matrix();
void sceneTransform();
void setTransform();
void zValue();
void shape();
void contains();
void collidesWith_item();
void collidesWith_path_data();
void collidesWith_path();
void collidesWithItemWithClip();
void isObscuredBy();
void isObscured();
void mapFromToParent();
void mapFromToScene();
void mapFromToItem();
void mapRectFromToParent_data();
void mapRectFromToParent();
void isAncestorOf();
void commonAncestorItem();
void data();
void type();
void graphicsitem_cast();
void hoverEventsGenerateRepaints();
void boundingRects_data();
void boundingRects();
void boundingRects2();
void sceneBoundingRect();
void childrenBoundingRect();
void childrenBoundingRectTransformed();
void childrenBoundingRect2();
void childrenBoundingRect3();
void childrenBoundingRect4();
void childrenBoundingRect5();
void group();
void setGroup();
void setGroup2();
void nestedGroups();
void warpChildrenIntoGroup();
void removeFromGroup();
void handlesChildEvents();
void handlesChildEvents2();
void handlesChildEvents3();
void filtersChildEvents();
void filtersChildEvents2();
void ensureVisible();
#ifndef QT_NO_CURSOR
void cursor();
#endif
//void textControlGetterSetter();
void defaultItemTest_QGraphicsLineItem();
void defaultItemTest_QGraphicsPixmapItem();
void defaultItemTest_QGraphicsTextItem();
void defaultItemTest_QGraphicsEllipseItem();
void itemChange();
void sceneEventFilter();
void prepareGeometryChange();
void paint();
void deleteItemInEventHandlers();
void itemClipsToShape();
void itemClipsChildrenToShape();
void itemClipsChildrenToShape2();
void itemClipsChildrenToShape3();
void itemClipsChildrenToShape4();
void itemClipsChildrenToShape5();
void itemClipsTextChildToShape();
void itemClippingDiscovery();
void itemContainsChildrenInShape();
void itemContainsChildrenInShape2();
void ancestorFlags();
void untransformable();
#ifndef QT_NO_CONTEXTMENU
void contextMenuEventPropagation();
#endif // QT_NO_CONTEXTMENU
void itemIsMovable();
void boundingRegion_data();
void boundingRegion();
void itemTransform_parentChild();
void itemTransform_siblings();
void itemTransform_unrelated();
void opacity_data();
void opacity();
void opacity2();
void opacityZeroUpdates();
void itemStacksBehindParent();
void nestedClipping();
void nestedClippingTransforms();
void sceneTransformCache();
void tabChangesFocus();
void tabChangesFocus_data();
void cacheMode();
void cacheMode2();
void updateCachedItemAfterMove();
void deviceTransform_data();
void deviceTransform();
void update();
void setTransformProperties_data();
void setTransformProperties();
void itemUsesExtendedStyleOption();
void itemSendsGeometryChanges();
void moveItem();
void moveLineItem();
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 panelWithFocusItems();
void activate();
void setActivePanelOnInactiveScene();
void activationOnShowHide();
void deactivateInactivePanel();
void moveWhileDeleting();
void ensureDirtySceneTransform();
void focusScope();
void focusScope2();
void focusScopeItemChangedWhileScopeDoesntHaveFocus();
void stackBefore();
void sceneModality();
void panelModality();
void mixedModality();
void modality_hover();
void modality_mouseGrabber();
void modality_clickFocus();
void modality_keyEvents();
void itemIsInFront();
void scenePosChange();
#if QT_CONFIG(shortcut)
void textItem_shortcuts();
#endif
void scroll();
void focusHandling_data();
void focusHandling();
void touchEventPropagation_data();
void touchEventPropagation();
void touchEventTransformation_data();
void touchEventTransformation();
void deviceCoordinateCache_simpleRotations();
void resolvePaletteForItemChildren();
// task specific tests below me
void task141694_textItemEnsureVisible();
void task128696_textItemEnsureMovable();
void ensureUpdateOnTextItem();
void task177918_lineItemUndetected();
void task240400_clickOnTextItem_data();
void task240400_clickOnTextItem();
void task243707_addChildBeforeParent();
void task197802_childrenVisibility();
void QTBUG_4233_updateCachedWithSceneRect();
void QTBUG_5418_textItemSetDefaultColor();
void QTBUG_6738_missingUpdateWithSetParent();
void QTBUG_7714_fullUpdateDiscardingOpacityUpdate2();
void QT_2653_fullUpdateDiscardingOpacityUpdate();
void QT_2649_focusScope();
void sortItemsWhileAdding();
void doNotMarkFullUpdateIfNotInScene();
void itemDiesDuringDraggingOperation();
void QTBUG_12112_focusItem();
void QTBUG_13473_sceneposchange();
void QTBUG_16374_crashInDestructor();
void QTBUG_20699_focusScopeCrash();
void QTBUG_30990_rightClickSelection();
void QTBUG_21618_untransformable_sceneTransform();
private:
GraphicsItems paintedItems;
QPointingDevice *m_touchDevice = QTest::createTouchDevice();
};
void tst_QGraphicsItem::cleanup()
{
QVERIFY(QApplication::topLevelWidgets().isEmpty());
}
template <class I>
static inline I *createBlackShapeItem()
{
auto result = new I;
result->setPen(QPen(Qt::black, 0));
return result;
}
void tst_QGraphicsItem::construction()
{
for (int i = 0; i < 7; ++i) {
QGraphicsItem *item = nullptr;
switch (i) {
case 0:
item = createBlackShapeItem<QGraphicsEllipseItem>();
QCOMPARE(item->type(), int(QGraphicsEllipseItem::Type));
QCOMPARE(qgraphicsitem_cast<QGraphicsEllipseItem *>(item), item);
QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
QCOMPARE(item->flags(), 0);
break;
case 1:
item = createBlackShapeItem<QGraphicsLineItem>();
QCOMPARE(int(item->type()), int(QGraphicsLineItem::Type));
QCOMPARE(qgraphicsitem_cast<QGraphicsLineItem *>(item), item);
QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
QCOMPARE(item->flags(), 0);
break;
case 2:
item = createBlackShapeItem<QGraphicsPathItem>();
QCOMPARE(int(item->type()), int(QGraphicsPathItem::Type));
QCOMPARE(qgraphicsitem_cast<QGraphicsPathItem *>(item), item);
QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
QCOMPARE(item->flags(), 0);
break;
case 3:
item = new QGraphicsPixmapItem;
QCOMPARE(int(item->type()), int(QGraphicsPixmapItem::Type));
QCOMPARE(qgraphicsitem_cast<QGraphicsPixmapItem *>(item), item);
QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
QCOMPARE(item->flags(), 0);
break;
case 4:
item = createBlackShapeItem<QGraphicsPolygonItem>();
QCOMPARE(int(item->type()), int(QGraphicsPolygonItem::Type));
QCOMPARE(qgraphicsitem_cast<QGraphicsPolygonItem *>(item), item);
QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
QCOMPARE(item->flags(), 0);
break;
case 5:
item = createBlackShapeItem<QGraphicsRectItem>();
QCOMPARE(int(item->type()), int(QGraphicsRectItem::Type));
QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), item);
QCOMPARE(qgraphicsitem_cast<QGraphicsLineItem *>(item), nullptr);
QCOMPARE(item->flags(), 0);
break;
case 6:
item = new QGraphicsTextItem;
QCOMPARE(int(item->type()), int(QGraphicsTextItem::Type));
QCOMPARE(qgraphicsitem_cast<QGraphicsTextItem *>(item), item);
QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), nullptr);
// 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!");
}
QCOMPARE(item->scene(), nullptr);
QCOMPARE(item->parentItem(), nullptr);
QVERIFY(item->childItems().isEmpty());
QVERIFY(item->isVisible());
QVERIFY(item->isEnabled());
QVERIFY(!item->isSelected());
QCOMPARE(item->acceptedMouseButtons(), Qt::MouseButtons(0x1f));
if (item->type() == QGraphicsTextItem::Type)
QVERIFY(item->acceptHoverEvents());
else
QVERIFY(!item->acceptHoverEvents());
QVERIFY(!item->hasFocus());
QCOMPARE(item->pos(), QPointF());
QCOMPARE(item->transform(), QTransform());
QCOMPARE(item->sceneTransform(), QTransform());
QCOMPARE(item->zValue(), qreal(0));
QCOMPARE(item->sceneBoundingRect(), QRectF());
QCOMPARE(item->shape(), QPainterPath());
QVERIFY(!item->contains(QPointF(0, 0)));
QVERIFY(!item->collidesWithItem(nullptr));
QVERIFY(item->collidesWithItem(item));
QVERIFY(!item->collidesWithPath(QPainterPath()));
QVERIFY(!item->isAncestorOf(nullptr));
QVERIFY(!item->isAncestorOf(item));
QCOMPARE(item->data(0), QVariant());
delete item;
}
}
class BoundingRectItem : public QGraphicsRectItem
{
public:
BoundingRectItem(QGraphicsItem *parent = nullptr)
: QGraphicsRectItem(0, 0, parent ? 200 : 100, parent ? 200 : 100,
parent)
{
setPen(QPen(Qt::black, 0));
}
QRectF boundingRect() const override
{
QRectF tmp = QGraphicsRectItem::boundingRect();
const auto children = childItems();
for (QGraphicsItem *child : children)
tmp |= child->boundingRect(); // <- might be pure virtual
return tmp;
}
};
void tst_QGraphicsItem::constructionWithParent()
{
// This test causes a crash if item1 calls item2's pure virtuals before the
// object has been constructed.
QGraphicsItem *item0 = new BoundingRectItem;
QGraphicsItem *item1 = new BoundingRectItem;
QGraphicsScene scene;
scene.addItem(item0);
scene.addItem(item1);
QGraphicsItem *item2 = new BoundingRectItem(item1);
QCOMPARE(item1->childItems(), GraphicsItemsList{item2});
QCOMPARE(item1->boundingRect(), QRectF(0, 0, 200, 200));
item2->setParentItem(item0);
QCOMPARE(item0->childItems(), GraphicsItemsList{item2});
QCOMPARE(item0->boundingRect(), QRectF(0, 0, 200, 200));
}
static int itemDeleted = 0;
class Item : public QGraphicsRectItem
{
public:
~Item()
{ ++itemDeleted; }
};
void tst_QGraphicsItem::destruction()
{
QCOMPARE(itemDeleted, 0);
{
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
QCOMPARE(child->parentItem(), parent);
delete parent;
QCOMPARE(itemDeleted, 1);
}
{
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
QCOMPARE(parent->childItems().size(), 1);
delete child;
QCOMPARE(parent->childItems().size(), 0);
delete parent;
QCOMPARE(itemDeleted, 2);
}
{
QGraphicsScene scene;
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
QCOMPARE(child->parentItem(), nullptr);
child->setParentItem(parent);
QCOMPARE(child->parentItem(), parent);
scene.addItem(parent);
QCOMPARE(child->parentItem(), parent);
delete parent;
QCOMPARE(itemDeleted, 3);
}
{
QGraphicsScene scene;
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
scene.addItem(parent);
QCOMPARE(child->scene(), &scene);
QCOMPARE(parent->childItems().size(), 1);
delete child;
QCOMPARE(parent->childItems().size(), 0);
delete parent;
QCOMPARE(itemDeleted, 4);
}
{
QGraphicsScene scene;
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
scene.addItem(parent);
QCOMPARE(child->scene(), &scene);
scene.removeItem(parent);
QCOMPARE(child->scene(), nullptr);
delete parent;
QCOMPARE(itemDeleted, 5);
}
{
QGraphicsScene scene;
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
QCOMPARE(child->scene(), nullptr);
QCOMPARE(parent->scene(), nullptr);
scene.addItem(parent);
QCOMPARE(child->scene(), &scene);
scene.removeItem(child);
QCOMPARE(child->scene(), nullptr);
QCOMPARE(parent->scene(), &scene);
QCOMPARE(child->parentItem(), nullptr);
QVERIFY(parent->childItems().isEmpty());
delete parent;
QCOMPARE(itemDeleted, 5);
delete child;
QCOMPARE(itemDeleted, 6);
}
{
QGraphicsScene scene;
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
scene.addItem(parent);
scene.removeItem(child);
scene.removeItem(parent);
delete child;
delete parent;
QCOMPARE(itemDeleted, 7);
}
{
QGraphicsScene scene;
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
scene.addItem(parent);
QGraphicsScene scene2;
scene2.addItem(parent);
delete parent;
QCOMPARE(itemDeleted, 8);
}
{
QGraphicsScene scene;
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
scene.addItem(parent);
QCOMPARE(child->scene(), &scene);
QGraphicsScene scene2;
scene2.addItem(parent);
QCOMPARE(child->scene(), &scene2);
scene.addItem(parent);
QCOMPARE(child->scene(), &scene);
scene2.addItem(parent);
QCOMPARE(child->scene(), &scene2);
delete parent;
QCOMPARE(itemDeleted, 9);
}
{
QGraphicsScene scene;
QGraphicsItem *parent = new QGraphicsRectItem;
Item *child = new Item;
child->setParentItem(parent);
scene.addItem(parent);
QCOMPARE(child->scene(), &scene);
QGraphicsScene scene2;
scene2.addItem(child);
QCOMPARE(child->scene(), &scene2);
delete parent;
QCOMPARE(itemDeleted, 9);
delete child;
QCOMPARE(itemDeleted, 10);
}
{
QGraphicsScene scene;
QGraphicsItem *root = new QGraphicsRectItem;
QGraphicsItem *parent = root;
QGraphicsItem *middleItem = nullptr;
for (int i = 0; i < 99; ++i) {
Item *child = new Item;
child->setParentItem(parent);
parent = child;
if (i == 50)
middleItem = parent;
}
scene.addItem(root);
QCOMPARE(scene.items().size(), 100);
QGraphicsScene scene2;
scene2.addItem(middleItem);
delete middleItem;
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);
Q_UNUSED(child3);
delete child1;
child2->setParentItem(nullptr);
delete child2;
}
void tst_QGraphicsItem::scene()
{
QGraphicsRectItem *item = new QGraphicsRectItem;
QCOMPARE(item->scene(), nullptr);
QGraphicsScene scene;
scene.addItem(item);
QCOMPARE(item->scene(), &scene);
QGraphicsScene scene2;
scene2.addItem(item);
QCOMPARE(item->scene(), &scene2);
scene2.removeItem(item);
QCOMPARE(item->scene(), nullptr);
delete item;
}
void tst_QGraphicsItem::parentItem()
{
QGraphicsRectItem item;
QCOMPARE(item.parentItem(), nullptr);
QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(), &item);
QCOMPARE(item2->parentItem(), &item);
item2->setParentItem(&item);
QCOMPARE(item2->parentItem(), &item);
item2->setParentItem(nullptr);
QCOMPARE(item2->parentItem(), nullptr);
delete item2;
}
void tst_QGraphicsItem::setParentItem()
{
QGraphicsScene scene;
const QScopedPointer<QGraphicsItem> item(scene.addRect(QRectF(0, 0, 10, 10)));
QCOMPARE(item->scene(), &scene);
const QScopedPointer<QGraphicsRectItem> child(new QGraphicsRectItem);
QCOMPARE(child->scene(), nullptr);
// This implicitly adds the item to the parent's scene
child->setParentItem(item.data());
QCOMPARE(child->scene(), &scene);
// This just makes it a toplevel
child->setParentItem(nullptr);
QCOMPARE(child->scene(), &scene);
// Add the child back to the parent, then remove the parent from the scene
child->setParentItem(item.data());
scene.removeItem(item.data());
QCOMPARE(child->scene(), nullptr);
}
void tst_QGraphicsItem::children()
{
QGraphicsRectItem item;
QVERIFY(item.childItems().isEmpty());
QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(), &item);
QCOMPARE(item.childItems().size(), 1);
QCOMPARE(item.childItems().constFirst(), item2);
QVERIFY(item2->childItems().isEmpty());
delete item2;
QVERIFY(item.childItems().isEmpty());
}
void tst_QGraphicsItem::flags()
{
QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20));
QCOMPARE(item->flags(), 0);
QGraphicsScene scene;
QEvent activate(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &activate);
scene.addItem(item);
{
// Focus
item->setFlag(QGraphicsItem::ItemIsFocusable, false);
QVERIFY(!item->hasFocus());
item->setFocus();
QVERIFY(!item->hasFocus());
item->setFlag(QGraphicsItem::ItemIsFocusable, true);
QVERIFY(!item->hasFocus());
item->setFocus();
QVERIFY(item->hasFocus());
QVERIFY(scene.hasFocus());
item->setFlag(QGraphicsItem::ItemIsFocusable, false);
QVERIFY(!item->hasFocus());
QVERIFY(scene.hasFocus());
}
{
// Selectable
item->setFlag(QGraphicsItem::ItemIsSelectable, false);
QVERIFY(!item->isSelected());
item->setSelected(true);
QVERIFY(!item->isSelected());
item->setFlag(QGraphicsItem::ItemIsSelectable, true);
QVERIFY(!item->isSelected());
item->setSelected(true);
QVERIFY(item->isSelected());
item->setFlag(QGraphicsItem::ItemIsSelectable, false);
QVERIFY(!item->isSelected());
}
{
// Movable
item->setFlag(QGraphicsItem::ItemIsMovable, false);
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setScenePos(QPointF(0, 0));
event.setButton(Qt::LeftButton);
event.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(scene.mouseGrabberItem(), nullptr); // mouse grabber is reset
QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove);
event2.setScenePos(QPointF(10, 10));
event2.setButton(Qt::LeftButton);
event2.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event2);
QCOMPARE(item->pos(), QPointF());
QGraphicsSceneMouseEvent event3(QEvent::GraphicsSceneMouseRelease);
event3.setScenePos(QPointF(10, 10));
event3.setButtons({});
QCoreApplication::sendEvent(&scene, &event3);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
item->setFlag(QGraphicsItem::ItemIsMovable, true);
QGraphicsSceneMouseEvent event4(QEvent::GraphicsSceneMousePress);
event4.setScenePos(QPointF(0, 0));
event4.setButton(Qt::LeftButton);
event4.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event4);
QCOMPARE(scene.mouseGrabberItem(), item);
QGraphicsSceneMouseEvent event5(QEvent::GraphicsSceneMouseMove);
event5.setScenePos(QPointF(10, 10));
event5.setButton(Qt::LeftButton);
event5.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event5);
QCOMPARE(item->pos(), QPointF(10, 10));
}
{
const QScopedPointer<QGraphicsItem> clippingParent(new QGraphicsRectItem);
clippingParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
const QScopedPointer<QGraphicsItem> nonClippingParent(new QGraphicsRectItem);
nonClippingParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
QGraphicsItem* child = new QGraphicsRectItem(nonClippingParent.data());
QVERIFY(!child->isClipped());
child->setParentItem(clippingParent.data());
QVERIFY(child->isClipped());
child->setParentItem(nonClippingParent.data());
QVERIFY(!child->isClipped());
}
}
class ImhTester : public QGraphicsItem
{
QRectF boundingRect() const override { return QRectF(); }
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
};
void tst_QGraphicsItem::inputMethodHints()
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QSKIP("Window activation is not supported");
ImhTester *item = new ImhTester;
item->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true);
item->setFlag(QGraphicsItem::ItemIsFocusable, true);
QCOMPARE(item->inputMethodHints(), Qt::ImhNone);
ImhTester *item2 = new ImhTester;
item2->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true);
item2->setFlag(QGraphicsItem::ItemIsFocusable, true);
Qt::InputMethodHints imHints = item2->inputMethodHints();
imHints |= Qt::ImhHiddenText;
item2->setInputMethodHints(imHints);
QGraphicsScene scene;
scene.addItem(item);
scene.addItem(item2);
QGraphicsView view(&scene);
QApplicationPrivate::setActiveWindow(&view);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QVERIFY(QTest::qWaitForWindowActive(&view));
item->setFocus();
QTRY_VERIFY(item->hasFocus());
QCOMPARE(view.inputMethodHints(), item->inputMethodHints());
item2->setFocus();
QTRY_VERIFY(item2->hasFocus());
QCOMPARE(view.inputMethodHints(), item2->inputMethodHints());
item->setFlag(QGraphicsItem::ItemAcceptsInputMethod, false);
item->setFocus();
QTRY_VERIFY(item->hasFocus());
//Focus has changed but the new item doesn't accept input method, no hints.
QCOMPARE(view.inputMethodHints(), 0);
item2->setFocus();
QTRY_VERIFY(item2->hasFocus());
QCOMPARE(view.inputMethodHints(), item2->inputMethodHints());
imHints = item2->inputMethodHints();
imHints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
item2->setInputMethodHints(imHints);
QCOMPARE(view.inputMethodHints(), item2->inputMethodHints());
QGraphicsProxyWidget *widget = new QGraphicsProxyWidget;
QLineEdit *edit = new QLineEdit;
edit->setEchoMode(QLineEdit::Password);
scene.addItem(widget);
widget->setFocus();
QTRY_VERIFY(widget->hasFocus());
//No widget on the proxy, so no hints
QCOMPARE(view.inputMethodHints(), 0);
widget->setWidget(edit);
//View should match with the line edit
QCOMPARE(view.inputMethodHints(), edit->inputMethodHints());
}
void tst_QGraphicsItem::toolTip()
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QSKIP("Window activation is not supported");
QString toolTip = "Qt rocks!";
QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
item->setPen(QPen(Qt::red, 1));
item->setBrush(QBrush(Qt::blue));
QVERIFY(item->toolTip().isEmpty());
item->setToolTip(toolTip);
QCOMPARE(item->toolTip(), toolTip);
QGraphicsScene scene;
scene.addItem(item);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.setFixedSize(200, 200);
view.show();
QApplicationPrivate::setActiveWindow(&view);
QVERIFY(QTest::qWaitForWindowExposed(&view));
QVERIFY(QTest::qWaitForWindowActive(&view));
{
QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(),
view.viewport()->mapToGlobal(view.viewport()->rect().topLeft()));
QCoreApplication::sendEvent(view.viewport(), &helpEvent);
QTest::qWait(250);
bool foundView = false;
bool foundTipLabel = false;
const auto topLevels = QApplication::topLevelWidgets();
for (auto widget : topLevels) {
if (widget == &view)
foundView = true;
if (widget->inherits("QTipLabel"))
foundTipLabel = true;
}
QVERIFY(foundView);
QVERIFY(!foundTipLabel);
}
{
QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().center(),
view.viewport()->mapToGlobal(view.viewport()->rect().center()));
QCoreApplication::sendEvent(view.viewport(), &helpEvent);
QTest::qWait(250);
bool foundView = false;
bool foundTipLabel = false;
const auto topLevels = QApplication::topLevelWidgets();
for (auto widget : topLevels) {
if (widget == &view)
foundView = true;
if (widget->inherits("QTipLabel"))
foundTipLabel = true;
}
QVERIFY(foundView);
QVERIFY(foundTipLabel);
}
{
QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(),
view.viewport()->mapToGlobal(view.viewport()->rect().topLeft()));
QCoreApplication::sendEvent(view.viewport(), &helpEvent);
QTest::qWait(1000);
bool foundView = false;
bool foundTipLabel = false;
const auto topLevels = QApplication::topLevelWidgets();
for (auto widget : topLevels) {
if (widget == &view)
foundView = true;
if (widget->inherits("QTipLabel") && widget->isVisible())
foundTipLabel = true;
}
QVERIFY(foundView);
QVERIFY(!foundTipLabel);
}
}
void tst_QGraphicsItem::visible()
{
QGraphicsItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20));
item->setFlag(QGraphicsItem::ItemIsMovable);
QVERIFY(item->isVisible());
item->setVisible(false);
QVERIFY(!item->isVisible());
item->setVisible(true);
QVERIFY(item->isVisible());
QGraphicsScene scene;
QEvent activate(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &activate);
scene.addItem(item);
QVERIFY(item->isVisible());
QCOMPARE(scene.items(QPointF(0, 0)).value(0, nullptr), item);
item->setVisible(false);
QVERIFY(scene.items(QPointF(0, 0)).isEmpty());
item->setVisible(true);
QCOMPARE(scene.items(QPointF(0, 0)).value(0, nullptr), item);
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setButton(Qt::LeftButton);
event.setScenePos(QPointF(0, 0));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(scene.mouseGrabberItem(), item);
item->setVisible(false);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
item->setVisible(true);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
item->setFlag(QGraphicsItem::ItemIsFocusable);
item->setFocus();
QVERIFY(item->hasFocus());
item->setVisible(false);
QVERIFY(!item->hasFocus());
item->setVisible(true);
QVERIFY(!item->hasFocus());
}
void tst_QGraphicsItem::isVisibleTo()
{
QGraphicsScene scene;
QGraphicsItem *parent = scene.addRect(QRectF(0, 0, 100, 100));
QGraphicsItem *child = scene.addRect(QRectF(25, 25, 50, 50));
QGraphicsItem *grandChild = scene.addRect(QRectF(50, 50, 50, 50));
QGraphicsItem *stranger = scene.addRect(100, 100, 100, 100);
child->setParentItem(parent);
grandChild->setParentItem(child);
QVERIFY(grandChild->isVisible());
QVERIFY(grandChild->isVisibleTo(grandChild));
QVERIFY(grandChild->isVisibleTo(child));
QVERIFY(grandChild->isVisibleTo(parent));
QVERIFY(grandChild->isVisibleTo(nullptr));
QVERIFY(child->isVisible());
QVERIFY(child->isVisibleTo(child));
QVERIFY(child->isVisibleTo(parent));
QVERIFY(child->isVisibleTo(nullptr));
QVERIFY(parent->isVisible());
QVERIFY(parent->isVisibleTo(parent));
QVERIFY(parent->isVisibleTo(nullptr));
QVERIFY(!parent->isVisibleTo(child));
QVERIFY(!child->isVisibleTo(grandChild));
QVERIFY(!grandChild->isVisibleTo(stranger));
QVERIFY(!child->isVisibleTo(stranger));
QVERIFY(!parent->isVisibleTo(stranger));
QVERIFY(!stranger->isVisibleTo(grandChild));
QVERIFY(!stranger->isVisibleTo(child));
QVERIFY(!stranger->isVisibleTo(parent));
// Case 1: only parent is explicitly hidden
parent->hide();
QVERIFY(!grandChild->isVisible());
QVERIFY(grandChild->isVisibleTo(grandChild));
QVERIFY(grandChild->isVisibleTo(child));
QVERIFY(grandChild->isVisibleTo(parent));
QVERIFY(!grandChild->isVisibleTo(nullptr));
QVERIFY(!child->isVisible());
QVERIFY(child->isVisibleTo(child));
QVERIFY(child->isVisibleTo(parent));
QVERIFY(!child->isVisibleTo(nullptr));
QVERIFY(!parent->isVisible());
QVERIFY(!parent->isVisibleTo(parent));
QVERIFY(!parent->isVisibleTo(nullptr));
QVERIFY(!parent->isVisibleTo(child));
QVERIFY(!child->isVisibleTo(grandChild));
QVERIFY(!grandChild->isVisibleTo(stranger));
QVERIFY(!child->isVisibleTo(stranger));
QVERIFY(!parent->isVisibleTo(stranger));
QVERIFY(!stranger->isVisibleTo(grandChild));
QVERIFY(!stranger->isVisibleTo(child));
QVERIFY(!stranger->isVisibleTo(parent));
// Case 2: only child is hidden
parent->show();
child->hide();
QVERIFY(!grandChild->isVisible());
QVERIFY(grandChild->isVisibleTo(grandChild));
QVERIFY(grandChild->isVisibleTo(child));
QVERIFY(!grandChild->isVisibleTo(parent));
QVERIFY(!grandChild->isVisibleTo(nullptr));
QVERIFY(!child->isVisible());
QVERIFY(!child->isVisibleTo(child));
QVERIFY(!child->isVisibleTo(parent));
QVERIFY(!child->isVisibleTo(nullptr));
QVERIFY(parent->isVisible());
QVERIFY(parent->isVisibleTo(parent));
QVERIFY(parent->isVisibleTo(nullptr));
QVERIFY(!parent->isVisibleTo(child));
QVERIFY(!child->isVisibleTo(grandChild));
QVERIFY(!grandChild->isVisibleTo(stranger));
QVERIFY(!child->isVisibleTo(stranger));
QVERIFY(!parent->isVisibleTo(stranger));
QVERIFY(!stranger->isVisibleTo(grandChild));
QVERIFY(!stranger->isVisibleTo(child));
QVERIFY(!stranger->isVisibleTo(parent));
// Case 3: only grand child is hidden
child->show();
grandChild->hide();
QVERIFY(!grandChild->isVisible());
QVERIFY(!grandChild->isVisibleTo(grandChild));
QVERIFY(!grandChild->isVisibleTo(child));
QVERIFY(!grandChild->isVisibleTo(parent));
QVERIFY(!grandChild->isVisibleTo(nullptr));
QVERIFY(child->isVisible());
QVERIFY(child->isVisibleTo(child));
QVERIFY(child->isVisibleTo(parent));
QVERIFY(child->isVisibleTo(nullptr));
QVERIFY(parent->isVisible());
QVERIFY(parent->isVisibleTo(parent));
QVERIFY(parent->isVisibleTo(nullptr));
QVERIFY(!parent->isVisibleTo(child));
QVERIFY(!child->isVisibleTo(grandChild));
QVERIFY(!grandChild->isVisibleTo(stranger));
QVERIFY(!child->isVisibleTo(stranger));
QVERIFY(!parent->isVisibleTo(stranger));
QVERIFY(!stranger->isVisibleTo(grandChild));
QVERIFY(!stranger->isVisibleTo(child));
QVERIFY(!stranger->isVisibleTo(parent));
}
void tst_QGraphicsItem::explicitlyVisible()
{
QGraphicsScene scene;
QGraphicsItem *parent = scene.addRect(QRectF(0, 0, 100, 100));
QGraphicsItem *child = scene.addRect(QRectF(25, 25, 50, 50));
child->setParentItem(parent);
QVERIFY(parent->isVisible());
QVERIFY(child->isVisible());
parent->hide();
QVERIFY(!parent->isVisible());
QVERIFY(!child->isVisible());
parent->show();
child->hide();
QVERIFY(parent->isVisible());
QVERIFY(!child->isVisible());
parent->hide();
QVERIFY(!parent->isVisible());
QVERIFY(!child->isVisible());
parent->show();
QVERIFY(parent->isVisible());
QVERIFY(!child->isVisible()); // <- explicitly hidden
child->show();
QVERIFY(child->isVisible());
parent->hide();
QVERIFY(!parent->isVisible());
QVERIFY(!child->isVisible()); // <- explicit show doesn't work
parent->show();
QVERIFY(parent->isVisible());
QVERIFY(child->isVisible()); // <- no longer explicitly hidden
// ------------------- Reparenting ------------------------------
QGraphicsItem *parent2 = scene.addRect(-50, -50, 200, 200);
QVERIFY(parent2->isVisible());
// Reparent implicitly hidden item to a visible parent.
parent->hide();
QVERIFY(!parent->isVisible());
QVERIFY(!child->isVisible());
child->setParentItem(parent2);
QVERIFY(parent2->isVisible());
QVERIFY(child->isVisible());
// Reparent implicitly hidden item to a hidden parent.
child->setParentItem(parent);
parent2->hide();
child->setParentItem(parent2);
QVERIFY(!parent2->isVisible());
QVERIFY(!child->isVisible());
// Reparent explicitly hidden item to a visible parent.
child->hide();
parent->show();
child->setParentItem(parent);
QVERIFY(parent->isVisible());
QVERIFY(!child->isVisible());
// Reparent explicitly hidden item to a hidden parent.
child->setParentItem(parent2);
QVERIFY(!parent2->isVisible());
QVERIFY(!child->isVisible());
// Reparent explicitly hidden item to a visible parent.
parent->show();
child->setParentItem(parent);
QVERIFY(parent->isVisible());
QVERIFY(!child->isVisible());
// Reparent visible item to a hidden parent.
child->show();
parent2->hide();
child->setParentItem(parent2);
QVERIFY(!parent2->isVisible());
QVERIFY(!child->isVisible());
parent2->show();
QVERIFY(parent2->isVisible());
QVERIFY(child->isVisible());
// Reparent implicitly hidden child to root.
parent2->hide();
QVERIFY(!child->isVisible());
child->setParentItem(nullptr);
QVERIFY(child->isVisible());
// Reparent an explicitly hidden child to root.
child->hide();
child->setParentItem(parent2);
parent2->show();
QVERIFY(!child->isVisible());
child->setParentItem(nullptr);
QVERIFY(!child->isVisible());
}
void tst_QGraphicsItem::enabled()
{
QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20));
item->setFlag(QGraphicsItem::ItemIsMovable);
QVERIFY(item->isEnabled());
item->setEnabled(false);
QVERIFY(!item->isEnabled());
item->setEnabled(true);
QVERIFY(item->isEnabled());
item->setEnabled(false);
item->setFlag(QGraphicsItem::ItemIsFocusable);
QGraphicsScene scene;
QEvent activate(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &activate);
scene.addItem(item);
item->setFocus();
QVERIFY(!item->hasFocus());
item->setEnabled(true);
item->setFocus();
QVERIFY(item->hasFocus());
item->setEnabled(false);
QVERIFY(!item->hasFocus());
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setButton(Qt::LeftButton);
event.setScenePos(QPointF(0, 0));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
item->setEnabled(true);
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(scene.mouseGrabberItem(), item);
item->setEnabled(false);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
}
void tst_QGraphicsItem::explicitlyEnabled()
{
QGraphicsScene scene;
QGraphicsItem *parent = scene.addRect(QRectF(0, 0, 100, 100));
QGraphicsItem *child = scene.addRect(QRectF(25, 25, 50, 50));
child->setParentItem(parent);
QVERIFY(parent->isEnabled());
QVERIFY(child->isEnabled());
parent->setEnabled(false);
QVERIFY(!parent->isEnabled());
QVERIFY(!child->isEnabled());
parent->setEnabled(true);
child->setEnabled(false);
QVERIFY(parent->isEnabled());
QVERIFY(!child->isEnabled());
parent->setEnabled(false);
QVERIFY(!parent->isEnabled());
QVERIFY(!child->isEnabled());
parent->setEnabled(true);
QVERIFY(parent->isEnabled());
QVERIFY(!child->isEnabled()); // <- explicitly disabled
child->setEnabled(true);
QVERIFY(child->isEnabled());
parent->setEnabled(false);
QVERIFY(!parent->isEnabled());
QVERIFY(!child->isEnabled()); // <- explicit enabled doesn't work
parent->setEnabled(true);
QVERIFY(parent->isEnabled());
QVERIFY(child->isEnabled()); // <- no longer explicitly disabled
// ------------------- Reparenting ------------------------------
QGraphicsItem *parent2 = scene.addRect(-50, -50, 200, 200);
QVERIFY(parent2->isEnabled());
// Reparent implicitly hidden item to a enabled parent.
parent->setEnabled(false);
QVERIFY(!parent->isEnabled());
QVERIFY(!child->isEnabled());
child->setParentItem(parent2);
QVERIFY(parent2->isEnabled());
QVERIFY(child->isEnabled());
// Reparent implicitly hidden item to a hidden parent.
child->setParentItem(parent);
parent2->setEnabled(false);
child->setParentItem(parent2);
QVERIFY(!parent2->isEnabled());
QVERIFY(!child->isEnabled());
// Reparent explicitly hidden item to a enabled parent.
child->setEnabled(false);
parent->setEnabled(true);
child->setParentItem(parent);
QVERIFY(parent->isEnabled());
QVERIFY(!child->isEnabled());
// Reparent explicitly hidden item to a hidden parent.
child->setParentItem(parent2);
QVERIFY(!parent2->isEnabled());
QVERIFY(!child->isEnabled());
// Reparent explicitly hidden item to a enabled parent.
parent->setEnabled(true);
child->setParentItem(parent);
QVERIFY(parent->isEnabled());
QVERIFY(!child->isEnabled());
// Reparent enabled item to a hidden parent.
child->setEnabled(true);
parent2->setEnabled(false);
child->setParentItem(parent2);
QVERIFY(!parent2->isEnabled());
QVERIFY(!child->isEnabled());
parent2->setEnabled(true);
QVERIFY(parent2->isEnabled());
QVERIFY(child->isEnabled());
// Reparent implicitly hidden child to root.
parent2->setEnabled(false);
QVERIFY(!child->isEnabled());
child->setParentItem(nullptr);
QVERIFY(child->isEnabled());
// Reparent an explicitly hidden child to root.
child->setEnabled(false);
child->setParentItem(parent2);
parent2->setEnabled(true);
QVERIFY(!child->isEnabled());
child->setParentItem(nullptr);
QVERIFY(!child->isEnabled());
}
class SelectChangeItem : public QGraphicsRectItem
{
public:
SelectChangeItem() : QGraphicsRectItem(-50, -50, 100, 100) { setBrush(Qt::blue); }
QList<bool> values;
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
{
if (change == ItemSelectedChange)
values << value.toBool();
return QGraphicsRectItem::itemChange(change, value);
}
};
void tst_QGraphicsItem::selected()
{
SelectChangeItem *item = new SelectChangeItem;
item->setFlag(QGraphicsItem::ItemIsSelectable);
QVERIFY(!item->isSelected());
QVERIFY(item->values.isEmpty());
item->setSelected(true);
QCOMPARE(item->values.size(), 1);
QCOMPARE(item->values.constLast(), true);
QVERIFY(item->isSelected());
item->setSelected(false);
QCOMPARE(item->values.size(), 2);
QCOMPARE(item->values.constLast(), false);
QVERIFY(!item->isSelected());
item->setSelected(true);
QCOMPARE(item->values.size(), 3);
item->setEnabled(false);
QCOMPARE(item->values.size(), 4);
QCOMPARE(item->values.constLast(), false);
QVERIFY(!item->isSelected());
item->setEnabled(true);
QCOMPARE(item->values.size(), 4);
item->setSelected(true);
QCOMPARE(item->values.size(), 5);
QCOMPARE(item->values.constLast(), true);
QVERIFY(item->isSelected());
item->setVisible(false);
QCOMPARE(item->values.size(), 6);
QCOMPARE(item->values.constLast(), false);
QVERIFY(!item->isSelected());
item->setVisible(true);
QCOMPARE(item->values.size(), 6);
item->setSelected(true);
QCOMPARE(item->values.size(), 7);
QCOMPARE(item->values.constLast(), true);
QVERIFY(item->isSelected());
QGraphicsScene scene(-100, -100, 200, 200);
scene.addItem(item);
QCOMPARE(scene.selectedItems(), GraphicsItemsList{item});
item->setSelected(false);
QVERIFY(scene.selectedItems().isEmpty());
item->setSelected(true);
QCOMPARE(scene.selectedItems(), GraphicsItemsList{item});
item->setSelected(false);
QVERIFY(scene.selectedItems().isEmpty());
// Interactive selection
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.setFixedSize(250, 250);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCoreApplication::processEvents();
QCoreApplication::processEvents();
scene.clearSelection();
QCOMPARE(item->values.size(), 10);
QCOMPARE(item->values.constLast(), false);
QVERIFY(!item->isSelected());
// Click inside and check that it's selected
QTest::mouseMove(view.viewport());
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item->scenePos()));
QCOMPARE(item->values.size(), 11);
QCOMPARE(item->values.constLast(), true);
QVERIFY(item->isSelected());
// Click outside and check that it's not selected
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item->scenePos() + QPointF(item->boundingRect().width(), item->boundingRect().height())));
QCOMPARE(item->values.size(), 12);
QCOMPARE(item->values.constLast(), false);
QVERIFY(!item->isSelected());
SelectChangeItem *item2 = new SelectChangeItem;
item2->setFlag(QGraphicsItem::ItemIsSelectable);
item2->setPos(100, 0);
scene.addItem(item2);
// Click inside and check that it's selected
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item->scenePos()));
QCOMPARE(item->values.size(), 13);
QCOMPARE(item->values.constLast(), true);
QVERIFY(item->isSelected());
// Click inside item2 and check that it's selected, and item is not
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item2->scenePos()));
QCOMPARE(item->values.size(), 14);
QCOMPARE(item->values.constLast(), false);
QVERIFY(!item->isSelected());
QCOMPARE(item2->values.size(), 1);
QCOMPARE(item2->values.constLast(), true);
QVERIFY(item2->isSelected());
}
void tst_QGraphicsItem::selected2()
{
// Selecting an item, then moving another previously caused a crash.
QGraphicsScene scene;
QGraphicsItem *line1 = scene.addRect(QRectF(0, 0, 100, 100));
line1->setPos(-105, 0);
line1->setFlag(QGraphicsItem::ItemIsSelectable);
QGraphicsItem *line2 = scene.addRect(QRectF(0, 0, 100, 100));
line2->setFlag(QGraphicsItem::ItemIsMovable);
line1->setSelected(true);
{
QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress);
mousePress.setScenePos(QPointF(50, 50));
mousePress.setButton(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &mousePress);
QVERIFY(mousePress.isAccepted());
}
{
QGraphicsSceneMouseEvent mouseMove(QEvent::GraphicsSceneMouseMove);
mouseMove.setScenePos(QPointF(60, 60));
mouseMove.setButton(Qt::LeftButton);
mouseMove.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &mouseMove);
QVERIFY(mouseMove.isAccepted());
}
}
void tst_QGraphicsItem::selected_group()
{
QGraphicsScene scene;
QGraphicsItem *item1 = scene.addRect(QRectF());
QGraphicsItem *item2 = scene.addRect(QRectF());
item1->setFlag(QGraphicsItem::ItemIsSelectable);
item2->setFlag(QGraphicsItem::ItemIsSelectable);
scene.addRect(QRectF())->setParentItem(item1);
QGraphicsItem *leaf = scene.addRect(QRectF());
leaf->setFlag(QGraphicsItem::ItemIsSelectable);
leaf->setParentItem(item2);
QGraphicsItemGroup *group = scene.createItemGroup(GraphicsItemsList{item1, item2});
QCOMPARE(group->scene(), &scene);
group->setFlag(QGraphicsItem::ItemIsSelectable);
const auto items = scene.items();
for (QGraphicsItem *item : items) {
if (item == group)
QVERIFY(!item->group());
else
QCOMPARE(item->group(), group);
}
QVERIFY(group->handlesChildEvents());
QVERIFY(!group->isSelected());
group->setSelected(false);
QVERIFY(!group->isSelected());
group->setSelected(true);
QVERIFY(group->isSelected());
const auto itemIsSelected = [](const QGraphicsItem *item) { return item->isSelected(); };
QVERIFY(std::all_of(items.cbegin(), items.cend(), itemIsSelected));
group->setSelected(false);
QVERIFY(!group->isSelected());
QVERIFY(std::none_of(items.cbegin(), items.cend(), itemIsSelected));
leaf->setSelected(true);
QVERIFY(std::all_of(items.cbegin(), items.cend(), itemIsSelected));
leaf->setSelected(false);
QVERIFY(std::none_of(items.cbegin(), items.cend(), itemIsSelected));
leaf->setSelected(true);
QGraphicsScene scene2;
scene2.addItem(item1);
QVERIFY(!item1->isSelected());
QVERIFY(item2->isSelected());
}
void tst_QGraphicsItem::selected_textItem()
{
QGraphicsScene scene;
QGraphicsTextItem *text = scene.addText(QLatin1String("Text"));
text->setFlag(QGraphicsItem::ItemIsSelectable);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QTRY_VERIFY(!text->isSelected());
QTest::mouseClick(view.viewport(), Qt::LeftButton, {},
view.mapFromScene(text->mapToScene(0, 0)));
QTRY_VERIFY(text->isSelected());
text->setSelected(false);
text->setTextInteractionFlags(Qt::TextEditorInteraction);
QTest::mouseClick(view.viewport(), Qt::LeftButton, {},
view.mapFromScene(text->mapToScene(0, 0)));
QTRY_VERIFY(text->isSelected());
}
void tst_QGraphicsItem::selected_multi()
{
// Test multiselection behavior
QGraphicsScene scene;
// Create two disjoint items
QGraphicsItem *item1 = scene.addRect(QRectF(-10, -10, 20, 20));
QGraphicsItem *item2 = scene.addRect(QRectF(-10, -10, 20, 20));
item1->setPos(-15, 0);
item2->setPos(15, 20);
// Make both items selectable
item1->setFlag(QGraphicsItem::ItemIsSelectable);
item2->setFlag(QGraphicsItem::ItemIsSelectable);
// Create and show a view
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
view.fitInView(scene.sceneRect());
QVERIFY(QTest::qWaitForWindowExposed(&view));
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QVERIFY(QTest::qWaitForWindowActive(&view));
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
// Click on item1
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item1->scenePos()));
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
// Click on item2
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item2->scenePos()));
QVERIFY(item2->isSelected());
QVERIFY(!item1->isSelected());
// Ctrl-click on item1
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos()));
QVERIFY(item2->isSelected());
QVERIFY(item1->isSelected());
// Ctrl-click on item1 again
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos()));
QVERIFY(item2->isSelected());
QVERIFY(!item1->isSelected());
// Ctrl-click on item2
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item2->scenePos()));
QVERIFY(!item2->isSelected());
QVERIFY(!item1->isSelected());
// Click on item1
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item1->scenePos()));
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
// Click on scene
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(0, 0));
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
// Click on item1
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item1->scenePos()));
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
// Ctrl-click on scene
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(0, 0));
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
// Click on scene
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(0, 0));
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
// Click on item1
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item1->scenePos()));
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
// Press on item2
QTest::mousePress(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item2->scenePos()));
QVERIFY(!item1->isSelected());
QVERIFY(item2->isSelected());
// Release on item2
QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item2->scenePos()));
QVERIFY(!item1->isSelected());
QVERIFY(item2->isSelected());
// Click on item1
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item1->scenePos()));
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
// Ctrl-click on item1
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos()));
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
// Ctrl-press on item1
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos()));
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
{
// Ctrl-move on item1
const QPoint item1Point = view.mapFromScene(item1->scenePos()) + QPoint(1, 0);
QMouseEvent event(QEvent::MouseMove, item1Point, view.viewport()->mapToGlobal(item1Point), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier);
QCoreApplication::sendEvent(view.viewport(), &event);
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
}
// Release on item1
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos()));
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
item1->setFlag(QGraphicsItem::ItemIsMovable);
item1->setSelected(false);
// Ctrl-press on item1
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos()));
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
{
// Ctrl-move on item1
const QPoint item1Point = view.mapFromScene(item1->scenePos()) + QPoint(1, 0);
QMouseEvent event(QEvent::MouseMove, item1Point, view.viewport()->mapToGlobal(item1Point), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier);
QCoreApplication::sendEvent(view.viewport(), &event);
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
}
// Release on item1
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos()));
QVERIFY(item1->isSelected());
QVERIFY(!item2->isSelected());
}
void tst_QGraphicsItem::acceptedMouseButtons()
{
QGraphicsScene scene;
QGraphicsRectItem *item1 = scene.addRect(QRectF(-10, -10, 20, 20));
QGraphicsRectItem *item2 = scene.addRect(QRectF(-10, -10, 20, 20));
item2->setZValue(1);
item1->setFlag(QGraphicsItem::ItemIsMovable);
item2->setFlag(QGraphicsItem::ItemIsMovable);
QCOMPARE(item1->acceptedMouseButtons(), Qt::MouseButtons(0x1f));
QCOMPARE(item2->acceptedMouseButtons(), Qt::MouseButtons(0x1f));
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setButton(Qt::LeftButton);
event.setScenePos(QPointF(0, 0));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(scene.mouseGrabberItem(), item2);
item2->setAcceptedMouseButtons({ });
QCOMPARE(scene.mouseGrabberItem(), nullptr);
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(scene.mouseGrabberItem(), item1);
}
class HoverItem : public QGraphicsRectItem
{
public:
HoverItem(const QRectF &rect) : QGraphicsRectItem(rect) { }
int hoverInCount = 0;
int hoverMoveCount = 0;
int hoverOutCount = 0;
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *) override
{ ++hoverInCount; }
void hoverMoveEvent(QGraphicsSceneHoverEvent *) override
{ ++hoverMoveCount; }
void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override
{ ++hoverOutCount; }
};
void tst_QGraphicsItem::acceptHoverEvents()
{
QGraphicsScene scene;
HoverItem *item1 = new HoverItem(QRectF(-10, -10, 20, 20));
HoverItem *item2 = new HoverItem(QRectF(-5, -5, 10, 10));
scene.addItem(item1);
scene.addItem(item2);
item2->setZValue(1);
QVERIFY(!item1->acceptHoverEvents());
QVERIFY(!item2->acceptHoverEvents());
item1->setAcceptHoverEvents(true);
item2->setAcceptHoverEvents(true);
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
event.setScenePos(QPointF(-100, -100));
QCoreApplication::sendEvent(&scene, &event);
event.setScenePos(QPointF(-2.5, -2.5));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item1->hoverInCount, 0);
QCOMPARE(item2->hoverInCount, 1);
item1->setAcceptHoverEvents(false);
item2->setAcceptHoverEvents(false);
event.setScenePos(QPointF(-100, -100));
QCoreApplication::sendEvent(&scene, &event);
event.setScenePos(QPointF(-2.5, -2.5));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item1->hoverInCount, 0);
QCOMPARE(item2->hoverInCount, 1);
item1->setAcceptHoverEvents(true);
item2->setAcceptHoverEvents(false);
event.setScenePos(QPointF(-100, -100));
QCoreApplication::sendEvent(&scene, &event);
event.setScenePos(QPointF(-2.5, -2.5));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item1->hoverInCount, 1);
QCOMPARE(item2->hoverInCount, 1);
}
void tst_QGraphicsItem::childAcceptsHoverEvents()
{
QGraphicsScene scene;
HoverItem *item1 = new HoverItem(QRectF(-10, -10, 20, 20));
HoverItem *item2 = new HoverItem(QRectF(-5, -5, 10, 10));
scene.addItem(item1);
scene.addItem(item2);
item2->setParentItem(item1);
item2->setAcceptHoverEvents(true);
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
event.setScenePos(QPointF(-100, -100));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item2->hoverInCount, 0);
QCOMPARE(item2->hoverMoveCount, 0);
QCOMPARE(item2->hoverOutCount, 0);
QCOMPARE(item1->hoverInCount, 0);
QCOMPARE(item1->hoverMoveCount, 0);
QCOMPARE(item1->hoverOutCount, 0);
event.setScenePos(QPointF(-2.5, -2.5));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item2->hoverInCount, 1);
QCOMPARE(item2->hoverMoveCount, 1);
QCOMPARE(item2->hoverOutCount, 0);
QCOMPARE(item1->hoverInCount, 0);
QCOMPARE(item1->hoverMoveCount, 0);
QCOMPARE(item1->hoverOutCount, 0);
event.setScenePos(QPointF(0, 0));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item2->hoverInCount, 1);
QCOMPARE(item2->hoverMoveCount, 2);
QCOMPARE(item2->hoverOutCount, 0);
QCOMPARE(item1->hoverInCount, 0);
QCOMPARE(item1->hoverMoveCount, 0);
QCOMPARE(item1->hoverOutCount, 0);
event.setScenePos(QPointF(-7, -7));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item2->hoverInCount, 1);
QCOMPARE(item2->hoverMoveCount, 2);
QCOMPARE(item2->hoverOutCount, 1);
QCOMPARE(item1->hoverInCount, 0);
QCOMPARE(item1->hoverMoveCount, 0);
QCOMPARE(item1->hoverOutCount, 0);
event.setScenePos(QPointF(0, 0));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item2->hoverInCount, 2);
QCOMPARE(item2->hoverMoveCount, 3);
QCOMPARE(item2->hoverOutCount, 1);
QCOMPARE(item1->hoverInCount, 0);
QCOMPARE(item1->hoverMoveCount, 0);
QCOMPARE(item1->hoverOutCount, 0);
HoverItem *item0 = new HoverItem(QRectF(-20, -20, 20, 20));
scene.addItem(item0);
item1->setParentItem(item0);
item0->setAcceptHoverEvents(true);
event.setScenePos(QPointF(-100, -100));
QCoreApplication::sendEvent(&scene, &event);
event.setScenePos(QPointF(-15, -15));
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(item2->hoverInCount, 2);
QCOMPARE(item2->hoverMoveCount, 3);
QCOMPARE(item2->hoverOutCount, 2);
QCOMPARE(item1->hoverInCount, 0);
QCOMPARE(item1->hoverMoveCount, 0);
QCOMPARE(item1->hoverOutCount, 0);
QCOMPARE(item0->hoverInCount, 1);
QCOMPARE(item0->hoverMoveCount, 1);
QCOMPARE(item0->hoverOutCount, 0);
}
void tst_QGraphicsItem::hasFocus()
{
QGraphicsLineItem *line = new QGraphicsLineItem;
QVERIFY(!line->hasFocus());
line->setFocus();
QVERIFY(!line->hasFocus());
QGraphicsScene scene;
QEvent activate(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &activate);
scene.addItem(line);
line->setFocus();
QVERIFY(!line->hasFocus());
line->setFlag(QGraphicsItem::ItemIsFocusable);
line->setFocus();
QVERIFY(line->hasFocus());
QGraphicsScene scene2;
QCoreApplication::sendEvent(&scene2, &activate);
scene2.addItem(line);
QVERIFY(!line->hasFocus());
QCOMPARE(scene.focusItem(), nullptr);
QCOMPARE(scene2.focusItem(), nullptr);
line->setFocus();
QVERIFY(line->hasFocus());
line->clearFocus();
QVERIFY(!line->hasFocus());
QGraphicsLineItem *line2 = new QGraphicsLineItem;
line2->setFlag(QGraphicsItem::ItemIsFocusable);
scene2.addItem(line2);
line2->setFocus();
QVERIFY(!line->hasFocus());
QVERIFY(line2->hasFocus());
line->setFocus();
QVERIFY(line->hasFocus());
QVERIFY(!line2->hasFocus());
}
void tst_QGraphicsItem::pos()
{
QGraphicsItem *child = new QGraphicsLineItem;
QGraphicsItem *parent = new QGraphicsLineItem;
QCOMPARE(child->pos(), QPointF());
QCOMPARE(parent->pos(), QPointF());
child->setParentItem(parent);
child->setPos(10, 10);
QCOMPARE(child->pos(), QPointF(10, 10));
parent->setPos(10, 10);
QCOMPARE(parent->pos(), QPointF(10, 10));
QCOMPARE(child->pos(), QPointF(10, 10));
delete child;
delete parent;
}
void tst_QGraphicsItem::scenePos()
{
QGraphicsItem *child = new QGraphicsLineItem;
QGraphicsItem *parent = new QGraphicsLineItem;
QCOMPARE(child->scenePos(), QPointF());
QCOMPARE(parent->scenePos(), QPointF());
child->setParentItem(parent);
child->setPos(10, 10);
QCOMPARE(child->scenePos(), QPointF(10, 10));
parent->setPos(10, 10);
QCOMPARE(parent->scenePos(), QPointF(10, 10));
QCOMPARE(child->scenePos(), QPointF(20, 20));
parent->setPos(20, 20);
QCOMPARE(parent->scenePos(), QPointF(20, 20));
QCOMPARE(child->scenePos(), QPointF(30, 30));
delete child;
delete parent;
}
void tst_QGraphicsItem::matrix()
{
QGraphicsLineItem line;
QCOMPARE(line.transform(), QTransform());
line.setTransform(QTransform().rotate(90));
QCOMPARE(line.transform(), QTransform().rotate(90));
line.setTransform(QTransform().rotate(90));
QCOMPARE(line.transform(), QTransform().rotate(90));
line.setTransform(QTransform().rotate(90), true);
QCOMPARE(line.transform(), QTransform().rotate(180));
line.setTransform(QTransform().rotate(-90), true);
QCOMPARE(line.transform(), QTransform().rotate(90));
line.resetTransform();
QCOMPARE(line.transform(), QTransform());
line.setTransform(QTransform().rotate(90), true);
QCOMPARE(line.transform(), QTransform().rotate(90));
line.setTransform(QTransform().rotate(90), true);
QCOMPARE(line.transform(), QTransform().rotate(90).rotate(90));
line.resetTransform();
line.setTransform(QTransform::fromScale(2, 4), true);
QCOMPARE(line.transform(), QTransform::fromScale(2, 4));
line.setTransform(QTransform::fromScale(2, 4), true);
QCOMPARE(line.transform(), QTransform::fromScale(2, 4).scale(2, 4));
line.resetTransform();
line.setTransform(QTransform().shear(2, 4), true);
QCOMPARE(line.transform(), QTransform().shear(2, 4));
line.setTransform(QTransform().shear(2, 4), true);
QCOMPARE(line.transform(), QTransform().shear(2, 4).shear(2, 4));
line.resetTransform();
line.setTransform(QTransform::fromTranslate(10, 10), true);
QCOMPARE(line.transform(), QTransform::fromTranslate(10, 10));
line.setTransform(QTransform::fromTranslate(10, 10), true);
QCOMPARE(line.transform(), QTransform::fromTranslate(10, 10).translate(10, 10));
line.resetTransform();
}
void tst_QGraphicsItem::sceneTransform()
{
QGraphicsLineItem *parent = new QGraphicsLineItem;
QGraphicsLineItem *child = new QGraphicsLineItem(QLineF(), parent);
QCOMPARE(parent->sceneTransform(), QTransform());
QCOMPARE(child->sceneTransform(), QTransform());
parent->setTransform(QTransform::fromTranslate(10, 10), true);
QCOMPARE(parent->sceneTransform(), QTransform().translate(10, 10));
QCOMPARE(child->sceneTransform(), QTransform().translate(10, 10));
child->setTransform(QTransform::fromTranslate(10, 10), true);
QCOMPARE(parent->sceneTransform(), QTransform().translate(10, 10));
QCOMPARE(child->sceneTransform(), QTransform().translate(20, 20));
parent->setTransform(QTransform().rotate(90), true);
QCOMPARE(parent->sceneTransform(), QTransform().translate(10, 10).rotate(90));
QCOMPARE(child->sceneTransform(), QTransform().translate(10, 10).rotate(90).translate(10, 10));
delete child;
delete parent;
}
void tst_QGraphicsItem::setTransform()
{
QGraphicsScene scene;
QSignalSpy spy(&scene, &QGraphicsScene::changed);
QRectF unrotatedRect(-12, -34, 56, 78);
QGraphicsRectItem item(unrotatedRect, nullptr);
item.setPen(QPen(Qt::black, 0));
scene.addItem(&item);
scene.update(scene.sceneRect());
QCoreApplication::processEvents();
QCOMPARE(spy.size(), 1);
item.setTransform(QTransform().rotate(qreal(12.34)));
QRectF rotatedRect = scene.sceneRect();
QVERIFY(unrotatedRect != rotatedRect);
scene.update(scene.sceneRect());
QCoreApplication::processEvents();
QCOMPARE(spy.size(), 2);
item.setTransform(QTransform());
scene.update(scene.sceneRect());
QCoreApplication::processEvents();
QCOMPARE(spy.size(), 3);
QList<QRectF> rlist = qvariant_cast<QList<QRectF> >(spy.last().at(0));
QCOMPARE(rlist.size(), 2);
// From item.setMatrix() (clearing rotated rect), from scene.update() (updating scene rect),
// squashed into one
QCOMPARE(rlist.at(0), rotatedRect);
QCOMPARE(rlist.at(1), unrotatedRect); // From post-update (update current state)
}
static GraphicsItems _paintedItems;
class PainterItem : public QGraphicsItem
{
protected:
QRectF boundingRect() const override
{ return QRectF(-10, -10, 20, 20); }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
{
_paintedItems << this;
painter->fillRect(boundingRect(), Qt::red);
}
};
void tst_QGraphicsItem::zValue()
{
Q_CHECK_PAINTEVENTS
QGraphicsScene scene;
QGraphicsItem *item1 = new PainterItem;
QGraphicsItem *item2 = new PainterItem;
QGraphicsItem *item3 = new PainterItem;
QGraphicsItem *item4 = new PainterItem;
scene.addItem(item1);
scene.addItem(item2);
scene.addItem(item3);
scene.addItem(item4);
item2->setZValue(-3);
item4->setZValue(-2);
item1->setZValue(-1);
item3->setZValue(0);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QApplication::processEvents();
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());
}
void tst_QGraphicsItem::shape()
{
QGraphicsLineItem line(QLineF(-10, -10, 20, 20));
line.setPen(QPen(Qt::black, 0));
// We unfortunately need this hack as QPainterPathStroker will set a width of 1.0
// if we pass a value of 0.0 to QPainterPathStroker::setWidth()
const qreal penWidthZero = qreal(0.00000001);
QPainterPathStroker ps;
ps.setWidth(penWidthZero);
QPainterPath path(line.line().p1());
path.lineTo(line.line().p2());
QPainterPath p = ps.createStroke(path);
p.addPath(path);
QCOMPARE(line.shape(), p);
QPen linePen;
linePen.setWidthF(5.0);
linePen.setCapStyle(Qt::RoundCap);
line.setPen(linePen);
ps.setCapStyle(line.pen().capStyle());
ps.setWidth(line.pen().widthF());
p = ps.createStroke(path);
p.addPath(path);
QCOMPARE(line.shape(), p);
linePen.setCapStyle(Qt::FlatCap);
line.setPen(linePen);
ps.setCapStyle(line.pen().capStyle());
p = ps.createStroke(path);
p.addPath(path);
QCOMPARE(line.shape(), p);
linePen.setCapStyle(Qt::SquareCap);
line.setPen(linePen);
ps.setCapStyle(line.pen().capStyle());
p = ps.createStroke(path);
p.addPath(path);
QCOMPARE(line.shape(), p);
QGraphicsRectItem rect(QRectF(-10, -10, 20, 20));
rect.setPen(QPen(Qt::black, 0));
QPainterPathStroker ps1;
ps1.setWidth(penWidthZero);
path = QPainterPath();
path.addRect(rect.rect());
p = ps1.createStroke(path);
p.addPath(path);
QCOMPARE(rect.shape(), p);
QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20));
ellipse.setPen(QPen(Qt::black, 0));
QPainterPathStroker ps2;
ps2.setWidth(ellipse.pen().widthF() <= 0.0 ? penWidthZero : ellipse.pen().widthF());
path = QPainterPath();
path.addEllipse(ellipse.rect());
p = ps2.createStroke(path);
p.addPath(path);
QCOMPARE(ellipse.shape(), p);
QPainterPathStroker ps3;
ps3.setWidth(penWidthZero);
p = ps3.createStroke(path);
p.addPath(path);
QGraphicsPathItem pathItem(path);
pathItem.setPen(QPen(Qt::black, 0));
QCOMPARE(pathItem.shape(), p);
QRegion region(QRect(0, 0, 300, 200));
region = region.subtracted(QRect(50, 50, 200, 100));
QImage image(300, 200, QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QPainter painter(&image);
painter.setClipRegion(region);
painter.fillRect(0, 0, 300, 200, Qt::green);
painter.end();
QPixmap pixmap = QPixmap::fromImage(image);
QGraphicsPixmapItem pixmapItem(pixmap);
path = QPainterPath();
path.addRegion(region);
{
QBitmap bitmap(300, 200);
bitmap.clear();
QPainter painter(&bitmap);
painter.setClipRegion(region);
painter.fillRect(0, 0, 300, 200, Qt::color1);
painter.end();
QBitmap bitmap2(300, 200);
bitmap2.clear();
painter.begin(&bitmap2);
painter.setClipPath(pixmapItem.shape());
painter.fillRect(0, 0, 300, 200, Qt::color1);
painter.end();
QCOMPARE(bitmap.toImage(), bitmap2.toImage());
}
QPolygonF poly;
poly << QPointF(0, 0) << QPointF(10, 0) << QPointF(0, 10);
QGraphicsPolygonItem polygon(poly);
polygon.setPen(QPen(Qt::black, 0));
path = QPainterPath();
path.addPolygon(poly);
QPainterPathStroker ps4;
ps4.setWidth(penWidthZero);
p = ps4.createStroke(path);
p.addPath(path);
QCOMPARE(polygon.shape(), p);
}
void tst_QGraphicsItem::contains()
{
if (sizeof(qreal) != sizeof(double))
QSKIP("Skipped due to rounding errors");
// Rect
QGraphicsRectItem rect(QRectF(-10, -10, 20, 20));
QVERIFY(!rect.contains(QPointF(-11, -10)));
QVERIFY(rect.contains(QPointF(-10, -10)));
QVERIFY(!rect.contains(QPointF(-11, 0)));
QVERIFY(rect.contains(QPointF(-10, 0)));
QVERIFY(rect.contains(QPointF(0, -10)));
QVERIFY(rect.contains(QPointF(0, 0)));
QVERIFY(rect.contains(QPointF(9, 9)));
// Ellipse
QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20));
QVERIFY(!ellipse.contains(QPointF(-10, -10)));
QVERIFY(ellipse.contains(QPointF(-9, 0)));
QVERIFY(ellipse.contains(QPointF(0, -9)));
QVERIFY(ellipse.contains(QPointF(0, 0)));
QVERIFY(!ellipse.contains(QPointF(9, 9)));
// Line
QGraphicsLineItem line(QLineF(-10, -10, 20, 20));
QVERIFY(!line.contains(QPointF(-10, 0)));
QVERIFY(!line.contains(QPointF(0, -10)));
QVERIFY(!line.contains(QPointF(10, 0)));
QVERIFY(!line.contains(QPointF(0, 10)));
QVERIFY(line.contains(QPointF(0, 0)));
QVERIFY(line.contains(QPointF(-9, -9)));
QVERIFY(line.contains(QPointF(9, 9)));
// Polygon
QGraphicsPolygonItem polygon(QPolygonF()
<< QPointF(0, 0)
<< QPointF(10, 0)
<< QPointF(0, 10));
QVERIFY(polygon.contains(QPointF(1, 1)));
QVERIFY(polygon.contains(QPointF(4, 4)));
QVERIFY(polygon.contains(QPointF(1, 4)));
QVERIFY(polygon.contains(QPointF(4, 1)));
QVERIFY(!polygon.contains(QPointF(8, 8)));
QVERIFY(polygon.contains(QPointF(1, 8)));
QVERIFY(polygon.contains(QPointF(8, 1)));
}
void tst_QGraphicsItem::collidesWith_item()
{
// Rectangle
QGraphicsRectItem rect(QRectF(-10, -10, 20, 20));
QGraphicsRectItem rect2(QRectF(-10, -10, 20, 20));
QVERIFY(rect.collidesWithItem(&rect2));
QVERIFY(rect2.collidesWithItem(&rect));
rect2.setPos(21, 21);
QVERIFY(!rect.collidesWithItem(&rect2));
QVERIFY(!rect2.collidesWithItem(&rect));
rect2.setPos(-21, -21);
QVERIFY(!rect.collidesWithItem(&rect2));
QVERIFY(!rect2.collidesWithItem(&rect));
rect2.setPos(-17, -17);
QVERIFY(rect.collidesWithItem(&rect2));
QVERIFY(rect2.collidesWithItem(&rect));
QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20));
QGraphicsEllipseItem ellipse2(QRectF(-10, -10, 20, 20));
QVERIFY(ellipse.collidesWithItem(&ellipse2));
QVERIFY(ellipse2.collidesWithItem(&ellipse));
ellipse2.setPos(21, 21);
QVERIFY(!ellipse.collidesWithItem(&ellipse2));
QVERIFY(!ellipse2.collidesWithItem(&ellipse));
ellipse2.setPos(-21, -21);
QVERIFY(!ellipse.collidesWithItem(&ellipse2));
QVERIFY(!ellipse2.collidesWithItem(&ellipse));
ellipse2.setPos(-17, -17);
QVERIFY(!ellipse.collidesWithItem(&ellipse2));
QVERIFY(!ellipse2.collidesWithItem(&ellipse));
{
QGraphicsScene scene;
QGraphicsRectItem rect(20, 20, 100, 100, nullptr);
scene.addItem(&rect);
QGraphicsRectItem rect2(40, 40, 50, 50, nullptr);
scene.addItem(&rect2);
rect2.setZValue(1);
QGraphicsLineItem line(0, 0, 200, 200, nullptr);
scene.addItem(&line);
line.setZValue(2);
QCOMPARE(scene.items().size(), 3);
QList<QGraphicsItem *> col1 = rect.collidingItems();
QCOMPARE(col1.size(), 2);
QCOMPARE(col1.constFirst(), &line);
QCOMPARE(col1.constLast(), &rect2);
QList<QGraphicsItem *> col2 = rect2.collidingItems();
QCOMPARE(col2.size(), 2);
QCOMPARE(col2.constFirst(), &line);
QCOMPARE(col2.constLast(), &rect);
QList<QGraphicsItem *> col3 = line.collidingItems();
QCOMPARE(col3.size(), 2);
QCOMPARE(col3.constFirst(), &rect2);
QCOMPARE(col3.constLast(), &rect);
}
}
void tst_QGraphicsItem::collidesWith_path_data()
{
QTest::addColumn<QPointF>("pos");
QTest::addColumn<QTransform>("transform");
QTest::addColumn<QPainterPath>("shape");
QTest::addColumn<bool>("rectCollides");
QTest::addColumn<bool>("ellipseCollides");
QTest::newRow("nothing") << QPointF(0, 0) << QTransform() << QPainterPath() << false << false;
QPainterPath rect;
rect.addRect(0, 0, 20, 20);
QTest::newRow("rect1") << QPointF(0, 0) << QTransform() << rect << true << true;
QTest::newRow("rect2") << QPointF(0, 0) << QTransform::fromTranslate(21, 21) << rect << false << false;
QTest::newRow("rect3") << QPointF(21, 21) << QTransform() << rect << false << false;
}
void tst_QGraphicsItem::collidesWith_path()
{
QFETCH(QPointF, pos);
QFETCH(QTransform, transform);
QFETCH(QPainterPath, shape);
QFETCH(bool, rectCollides);
QFETCH(bool, ellipseCollides);
QGraphicsRectItem rect(QRectF(0, 0, 20, 20));
QGraphicsEllipseItem ellipse(QRectF(0, 0, 20, 20));
rect.setPos(pos);
rect.setTransform(transform);
ellipse.setPos(pos);
ellipse.setTransform(transform);
QPainterPath mappedShape = rect.sceneTransform().inverted().map(shape);
if (rectCollides)
QVERIFY(rect.collidesWithPath(mappedShape));
else
QVERIFY(!rect.collidesWithPath(mappedShape));
if (ellipseCollides)
QVERIFY(ellipse.collidesWithPath(mappedShape));
else
QVERIFY(!ellipse.collidesWithPath(mappedShape));
}
void tst_QGraphicsItem::collidesWithItemWithClip()
{
QGraphicsScene scene;
QGraphicsEllipseItem *ellipse = scene.addEllipse(0, 0, 100, 100);
ellipse->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
QGraphicsEllipseItem *ellipse2 = scene.addEllipse(0, 0, 10, 10);
ellipse2->setParentItem(ellipse);
QGraphicsEllipseItem *ellipse3 = scene.addEllipse(0, 0, 10, 10);
ellipse3->setParentItem(ellipse);
QGraphicsEllipseItem *ellipse5 = scene.addEllipse(50, 50, 10, 10);
ellipse5->setParentItem(ellipse);
QGraphicsEllipseItem *ellipse4 = scene.addEllipse(0, 0, 10, 10);
QVERIFY(ellipse2->collidesWithItem(ellipse3));
QVERIFY(ellipse3->collidesWithItem(ellipse2));
QVERIFY(!ellipse2->collidesWithItem(ellipse));
QVERIFY(!ellipse->collidesWithItem(ellipse2));
QVERIFY(!ellipse4->collidesWithItem(ellipse));
QVERIFY(!ellipse4->collidesWithItem(ellipse2));
QVERIFY(!ellipse4->collidesWithItem(ellipse3));
QVERIFY(!ellipse->collidesWithItem(ellipse4));
QVERIFY(!ellipse2->collidesWithItem(ellipse4));
QVERIFY(!ellipse3->collidesWithItem(ellipse4));
QVERIFY(ellipse->collidesWithItem(ellipse5));
QVERIFY(ellipse5->collidesWithItem(ellipse));
}
class MyItem : public QGraphicsEllipseItem
{
public:
bool isObscuredBy(const QGraphicsItem *item) const override
{
auto myItem = qgraphicsitem_cast<const MyItem *>(item);
if (!myItem)
return QGraphicsEllipseItem::isObscuredBy(item);
if (item->zValue() <= zValue())
return false;
QRectF r = rect();
QPointF topMid = (r.topRight() + r.topLeft()) / 2;
QPointF botMid = (r.bottomRight() + r.bottomLeft()) / 2;
QPointF leftMid = (r.topLeft() + r.bottomLeft()) / 2;
QPointF rightMid = (r.topRight() + r.bottomRight()) / 2;
QPainterPath mappedShape = item->mapToItem(this, item->opaqueArea());
return mappedShape.contains(topMid) && mappedShape.contains(botMid)
&& mappedShape.contains(leftMid) && mappedShape.contains(rightMid);
}
QPainterPath opaqueArea() const override
{
return shape();
}
enum { Type = UserType + 1 };
int type() const override { return Type; }
};
void tst_QGraphicsItem::isObscuredBy()
{
QGraphicsScene scene;
MyItem myitem1, myitem2;
myitem1.setRect(QRectF(50, 50, 40, 200));
myitem1.setTransform(QTransform().rotate(67), true);
myitem2.setRect(QRectF(25, 25, 20, 20));
myitem2.setZValue(-1.0);
scene.addItem(&myitem1);
scene.addItem(&myitem2);
QVERIFY(!myitem2.isObscuredBy(&myitem1));
QVERIFY(!myitem1.isObscuredBy(&myitem2));
myitem2.setRect(QRectF(-50, 85, 20, 20));
QVERIFY(myitem2.isObscuredBy(&myitem1));
QVERIFY(!myitem1.isObscuredBy(&myitem2));
myitem2.setRect(QRectF(-30, 70, 20, 20));
QVERIFY(!myitem2.isObscuredBy(&myitem1));
QVERIFY(!myitem1.isObscuredBy(&myitem2));
QGraphicsRectItem rect1, rect2;
rect1.setRect(QRectF(-40, -40, 50, 50));
rect1.setBrush(QBrush(Qt::red));
rect2.setRect(QRectF(-30, -20, 20, 20));
rect2.setZValue(-1.0);
rect2.setBrush(QBrush(Qt::blue));
QVERIFY(rect2.isObscuredBy(&rect1));
QVERIFY(!rect1.isObscuredBy(&rect2));
rect2.setPos(QPointF(-20, -25));
QVERIFY(!rect2.isObscuredBy(&rect1));
QVERIFY(!rect1.isObscuredBy(&rect2));
rect2.setPos(QPointF(-100, -100));
QVERIFY(!rect2.isObscuredBy(&rect1));
QVERIFY(!rect1.isObscuredBy(&rect2));
}
class OpaqueItem : public QGraphicsRectItem
{
protected:
QPainterPath opaqueArea() const override
{
return shape();
}
};
void tst_QGraphicsItem::isObscured()
{
if (sizeof(qreal) != sizeof(double))
QSKIP("Skipped due to rounding errors");
OpaqueItem *item1 = new OpaqueItem;
item1->setRect(0, 0, 100, 100);
item1->setZValue(0);
OpaqueItem *item2 = new OpaqueItem;
item2->setZValue(1);
item2->setRect(0, 0, 100, 100);
QGraphicsScene scene;
scene.addItem(item1);
scene.addItem(item2);
QVERIFY(item1->isObscured());
QVERIFY(item1->isObscuredBy(item2));
QVERIFY(item1->isObscured(QRectF(0, 0, 50, 50)));
QVERIFY(item1->isObscured(QRectF(50, 0, 50, 50)));
QVERIFY(item1->isObscured(QRectF(50, 50, 50, 50)));
QVERIFY(item1->isObscured(QRectF(0, 50, 50, 50)));
QVERIFY(item1->isObscured(0, 0, 50, 50));
QVERIFY(item1->isObscured(50, 0, 50, 50));
QVERIFY(item1->isObscured(50, 50, 50, 50));
QVERIFY(item1->isObscured(0, 50, 50, 50));
QVERIFY(!item2->isObscured());
QVERIFY(!item2->isObscuredBy(item1));
QVERIFY(!item2->isObscured(QRectF(0, 0, 50, 50)));
QVERIFY(!item2->isObscured(QRectF(50, 0, 50, 50)));
QVERIFY(!item2->isObscured(QRectF(50, 50, 50, 50)));
QVERIFY(!item2->isObscured(QRectF(0, 50, 50, 50)));
QVERIFY(!item2->isObscured(0, 0, 50, 50));
QVERIFY(!item2->isObscured(50, 0, 50, 50));
QVERIFY(!item2->isObscured(50, 50, 50, 50));
QVERIFY(!item2->isObscured(0, 50, 50, 50));
item2->moveBy(50, 0);
QVERIFY(!item1->isObscured());
QVERIFY(!item1->isObscuredBy(item2));
QVERIFY(!item1->isObscured(QRectF(0, 0, 50, 50)));
QVERIFY(item1->isObscured(QRectF(50, 0, 50, 50)));
QVERIFY(item1->isObscured(QRectF(50, 50, 50, 50)));
QVERIFY(!item1->isObscured(QRectF(0, 50, 50, 50)));
QVERIFY(!item1->isObscured(0, 0, 50, 50));
QVERIFY(item1->isObscured(50, 0, 50, 50));
QVERIFY(item1->isObscured(50, 50, 50, 50));
QVERIFY(!item1->isObscured(0, 50, 50, 50));
QVERIFY(!item2->isObscured());
QVERIFY(!item2->isObscuredBy(item1));
QVERIFY(!item2->isObscured(QRectF(0, 0, 50, 50)));
QVERIFY(!item2->isObscured(QRectF(50, 0, 50, 50)));
QVERIFY(!item2->isObscured(QRectF(50, 50, 50, 50)));
QVERIFY(!item2->isObscured(QRectF(0, 50, 50, 50)));
QVERIFY(!item2->isObscured(0, 0, 50, 50));
QVERIFY(!item2->isObscured(50, 0, 50, 50));
QVERIFY(!item2->isObscured(50, 50, 50, 50));
QVERIFY(!item2->isObscured(0, 50, 50, 50));
}
void tst_QGraphicsItem::mapFromToParent()
{
QPainterPath path1;
path1.addRect(0, 0, 200, 200);
QPainterPath path2;
path2.addRect(0, 0, 100, 100);
QPainterPath path3;
path3.addRect(0, 0, 50, 50);
QPainterPath path4;
path4.addRect(0, 0, 25, 25);
QGraphicsItem *item1 = new QGraphicsPathItem(path1);
QGraphicsItem *item2 = new QGraphicsPathItem(path2, item1);
QGraphicsItem *item3 = new QGraphicsPathItem(path3, item2);
QGraphicsItem *item4 = new QGraphicsPathItem(path4, item3);
item1->setPos(10, 10);
item2->setPos(10, 10);
item3->setPos(10, 10);
item4->setPos(10, 10);
for (int i = 0; i < 4; ++i) {
QTransform transform;
transform.rotate(i * 90);
transform.translate(i * 100, -i * 100);
transform.scale(2, 4);
item1->setTransform(transform);
QCOMPARE(item1->mapToParent(QPointF(0, 0)), item1->pos() + transform.map(QPointF(0, 0)));
QCOMPARE(item2->mapToParent(QPointF(0, 0)), item2->pos());
QCOMPARE(item3->mapToParent(QPointF(0, 0)), item3->pos());
QCOMPARE(item4->mapToParent(QPointF(0, 0)), item4->pos());
QCOMPARE(item1->mapToParent(QPointF(10, -10)), item1->pos() + transform.map(QPointF(10, -10)));
QCOMPARE(item2->mapToParent(QPointF(10, -10)), item2->pos() + QPointF(10, -10));
QCOMPARE(item3->mapToParent(QPointF(10, -10)), item3->pos() + QPointF(10, -10));
QCOMPARE(item4->mapToParent(QPointF(10, -10)), item4->pos() + QPointF(10, -10));
QCOMPARE(item1->mapToParent(QPointF(-10, 10)), item1->pos() + transform.map(QPointF(-10, 10)));
QCOMPARE(item2->mapToParent(QPointF(-10, 10)), item2->pos() + QPointF(-10, 10));
QCOMPARE(item3->mapToParent(QPointF(-10, 10)), item3->pos() + QPointF(-10, 10));
QCOMPARE(item4->mapToParent(QPointF(-10, 10)), item4->pos() + QPointF(-10, 10));
QCOMPARE(item1->mapFromParent(item1->pos()), transform.inverted().map(QPointF(0, 0)));
QCOMPARE(item2->mapFromParent(item2->pos()), QPointF(0, 0));
QCOMPARE(item3->mapFromParent(item3->pos()), QPointF(0, 0));
QCOMPARE(item4->mapFromParent(item4->pos()), QPointF(0, 0));
QCOMPARE(item1->mapFromParent(item1->pos() + QPointF(10, -10)),
transform.inverted().map(QPointF(10, -10)));
QCOMPARE(item2->mapFromParent(item2->pos() + QPointF(10, -10)), QPointF(10, -10));
QCOMPARE(item3->mapFromParent(item3->pos() + QPointF(10, -10)), QPointF(10, -10));
QCOMPARE(item4->mapFromParent(item4->pos() + QPointF(10, -10)), QPointF(10, -10));
QCOMPARE(item1->mapFromParent(item1->pos() + QPointF(-10, 10)),
transform.inverted().map(QPointF(-10, 10)));
QCOMPARE(item2->mapFromParent(item2->pos() + QPointF(-10, 10)), QPointF(-10, 10));
QCOMPARE(item3->mapFromParent(item3->pos() + QPointF(-10, 10)), QPointF(-10, 10));
QCOMPARE(item4->mapFromParent(item4->pos() + QPointF(-10, 10)), QPointF(-10, 10));
}
delete item1;
}
void tst_QGraphicsItem::mapFromToScene()
{
QGraphicsItem *item1 = new QGraphicsPathItem(QPainterPath());
QGraphicsItem *item2 = new QGraphicsPathItem(QPainterPath(), item1);
QGraphicsItem *item3 = new QGraphicsPathItem(QPainterPath(), item2);
QGraphicsItem *item4 = new QGraphicsPathItem(QPainterPath(), item3);
item1->setPos(100, 100);
item2->setPos(100, 100);
item3->setPos(100, 100);
item4->setPos(100, 100);
QCOMPARE(item1->pos(), QPointF(100, 100));
QCOMPARE(item2->pos(), QPointF(100, 100));
QCOMPARE(item3->pos(), QPointF(100, 100));
QCOMPARE(item4->pos(), QPointF(100, 100));
QCOMPARE(item1->pos(), item1->mapToParent(0, 0));
QCOMPARE(item2->pos(), item2->mapToParent(0, 0));
QCOMPARE(item3->pos(), item3->mapToParent(0, 0));
QCOMPARE(item4->pos(), item4->mapToParent(0, 0));
QCOMPARE(item1->mapToParent(10, 10), QPointF(110, 110));
QCOMPARE(item2->mapToParent(10, 10), QPointF(110, 110));
QCOMPARE(item3->mapToParent(10, 10), QPointF(110, 110));
QCOMPARE(item4->mapToParent(10, 10), QPointF(110, 110));
QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100));
QCOMPARE(item2->mapToScene(0, 0), QPointF(200, 200));
QCOMPARE(item3->mapToScene(0, 0), QPointF(300, 300));
QCOMPARE(item4->mapToScene(0, 0), QPointF(400, 400));
QCOMPARE(item1->mapToScene(10, 0), QPointF(110, 100));
QCOMPARE(item2->mapToScene(10, 0), QPointF(210, 200));
QCOMPARE(item3->mapToScene(10, 0), QPointF(310, 300));
QCOMPARE(item4->mapToScene(10, 0), QPointF(410, 400));
QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0));
QCOMPARE(item2->mapFromScene(200, 200), QPointF(0, 0));
QCOMPARE(item3->mapFromScene(300, 300), QPointF(0, 0));
QCOMPARE(item4->mapFromScene(400, 400), QPointF(0, 0));
QCOMPARE(item1->mapFromScene(110, 100), QPointF(10, 0));
QCOMPARE(item2->mapFromScene(210, 200), QPointF(10, 0));
QCOMPARE(item3->mapFromScene(310, 300), QPointF(10, 0));
QCOMPARE(item4->mapFromScene(410, 400), QPointF(10, 0));
// Rotate item1 90 degrees clockwise
QTransform transform; transform.rotate(90);
item1->setTransform(transform);
QCOMPARE(item1->pos(), item1->mapToParent(0, 0));
QCOMPARE(item2->pos(), item2->mapToParent(0, 0));
QCOMPARE(item3->pos(), item3->mapToParent(0, 0));
QCOMPARE(item4->pos(), item4->mapToParent(0, 0));
QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110));
QCOMPARE(item2->mapToParent(10, 0), QPointF(110, 100));
QCOMPARE(item3->mapToParent(10, 0), QPointF(110, 100));
QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100));
QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100));
QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200));
QCOMPARE(item3->mapToScene(0, 0), QPointF(-100, 300));
QCOMPARE(item4->mapToScene(0, 0), QPointF(-200, 400));
QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110));
QCOMPARE(item2->mapToScene(10, 0), QPointF(0, 210));
QCOMPARE(item3->mapToScene(10, 0), QPointF(-100, 310));
QCOMPARE(item4->mapToScene(10, 0), QPointF(-200, 410));
QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0));
QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0));
QCOMPARE(item3->mapFromScene(-100, 300), QPointF(0, 0));
QCOMPARE(item4->mapFromScene(-200, 400), QPointF(0, 0));
QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0));
QCOMPARE(item2->mapFromScene(0, 210), QPointF(10, 0));
QCOMPARE(item3->mapFromScene(-100, 310), QPointF(10, 0));
QCOMPARE(item4->mapFromScene(-200, 410), QPointF(10, 0));
// Rotate item2 90 degrees clockwise
item2->setTransform(transform);
QCOMPARE(item1->pos(), item1->mapToParent(0, 0));
QCOMPARE(item2->pos(), item2->mapToParent(0, 0));
QCOMPARE(item3->pos(), item3->mapToParent(0, 0));
QCOMPARE(item4->pos(), item4->mapToParent(0, 0));
QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110));
QCOMPARE(item2->mapToParent(10, 0), QPointF(100, 110));
QCOMPARE(item3->mapToParent(10, 0), QPointF(110, 100));
QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100));
QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100));
QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200));
QCOMPARE(item3->mapToScene(0, 0), QPointF(-100, 100));
QCOMPARE(item4->mapToScene(0, 0), QPointF(-200, 0));
QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110));
QCOMPARE(item2->mapToScene(10, 0), QPointF(-10, 200));
QCOMPARE(item3->mapToScene(10, 0), QPointF(-110, 100));
QCOMPARE(item4->mapToScene(10, 0), QPointF(-210, 0));
QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0));
QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0));
QCOMPARE(item3->mapFromScene(-100, 100), QPointF(0, 0));
QCOMPARE(item4->mapFromScene(-200, 0), QPointF(0, 0));
QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0));
QCOMPARE(item2->mapFromScene(-10, 200), QPointF(10, 0));
QCOMPARE(item3->mapFromScene(-110, 100), QPointF(10, 0));
QCOMPARE(item4->mapFromScene(-210, 0), QPointF(10, 0));
// Translate item3 50 points, then rotate 90 degrees counterclockwise
QTransform transform2;
transform2.translate(50, 0);
transform2.rotate(-90);
item3->setTransform(transform2);
QCOMPARE(item1->pos(), item1->mapToParent(0, 0));
QCOMPARE(item2->pos(), item2->mapToParent(0, 0));
QCOMPARE(item3->pos(), item3->mapToParent(0, 0) - QPointF(50, 0));
QCOMPARE(item4->pos(), item4->mapToParent(0, 0));
QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110));
QCOMPARE(item2->mapToParent(10, 0), QPointF(100, 110));
QCOMPARE(item3->mapToParent(10, 0), QPointF(150, 90));
QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100));
QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100));
QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200));
QCOMPARE(item3->mapToScene(0, 0), QPointF(-150, 100));
QCOMPARE(item4->mapToScene(0, 0), QPointF(-250, 200));
QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110));
QCOMPARE(item2->mapToScene(10, 0), QPointF(-10, 200));
QCOMPARE(item3->mapToScene(10, 0), QPointF(-150, 110));
QCOMPARE(item4->mapToScene(10, 0), QPointF(-250, 210));
QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0));
QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0));
QCOMPARE(item3->mapFromScene(-150, 100), QPointF(0, 0));
QCOMPARE(item4->mapFromScene(-250, 200), QPointF(0, 0));
QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0));
QCOMPARE(item2->mapFromScene(-10, 200), QPointF(10, 0));
QCOMPARE(item3->mapFromScene(-150, 110), QPointF(10, 0));
QCOMPARE(item4->mapFromScene(-250, 210), QPointF(10, 0));
delete item1;
}
void tst_QGraphicsItem::mapFromToItem()
{
QGraphicsItem *item1 = new QGraphicsPathItem;
QGraphicsItem *item2 = new QGraphicsPathItem;
QGraphicsItem *item3 = new QGraphicsPathItem;
QGraphicsItem *item4 = new QGraphicsPathItem;
item1->setPos(-100, -100);
item2->setPos(100, -100);
item3->setPos(100, 100);
item4->setPos(-100, 100);
QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(200, 0));
QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(0, 200));
QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(-200, 0));
QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(0, -200));
QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(0, 200));
QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(-200, 0));
QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(0, -200));
QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(200, 0));
QTransform transform;
transform.translate(100, 100);
item1->setTransform(transform);
QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(100, -100));
QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(0, 200));
QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(-200, 0));
QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(100, -100));
QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(-100, 100));
QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(-100, 100));
QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(0, -200));
QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(200, 0));
transform.rotate(90);
item1->setTransform(transform);
item2->setTransform(transform);
item3->setTransform(transform);
item4->setTransform(transform);
QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(0, -200));
QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(200, 0));
QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(0, 200));
QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(-200, 0));
QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(200, 0));
QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(0, 200));
QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(-200, 0));
QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(0, -200));
QCOMPARE(item1->mapFromItem(item2, 10, -5), QPointF(10, -205));
QCOMPARE(item2->mapFromItem(item3, 10, -5), QPointF(210, -5));
QCOMPARE(item3->mapFromItem(item4, 10, -5), QPointF(10, 195));
QCOMPARE(item4->mapFromItem(item1, 10, -5), QPointF(-190, -5));
QCOMPARE(item1->mapFromItem(item4, 10, -5), QPointF(210, -5));
QCOMPARE(item2->mapFromItem(item1, 10, -5), QPointF(10, 195));
QCOMPARE(item3->mapFromItem(item2, 10, -5), QPointF(-190, -5));
QCOMPARE(item4->mapFromItem(item3, 10, -5), QPointF(10, -205));
QCOMPARE(item1->mapFromItem(nullptr, 10, -5), item1->mapFromScene(10, -5));
QCOMPARE(item2->mapFromItem(nullptr, 10, -5), item2->mapFromScene(10, -5));
QCOMPARE(item3->mapFromItem(nullptr, 10, -5), item3->mapFromScene(10, -5));
QCOMPARE(item4->mapFromItem(nullptr, 10, -5), item4->mapFromScene(10, -5));
QCOMPARE(item1->mapToItem(nullptr, 10, -5), item1->mapToScene(10, -5));
QCOMPARE(item2->mapToItem(nullptr, 10, -5), item2->mapToScene(10, -5));
QCOMPARE(item3->mapToItem(nullptr, 10, -5), item3->mapToScene(10, -5));
QCOMPARE(item4->mapToItem(nullptr, 10, -5), item4->mapToScene(10, -5));
delete item1;
delete item2;
delete item3;
delete item4;
}
void tst_QGraphicsItem::mapRectFromToParent_data()
{
QTest::addColumn<bool>("parent");
QTest::addColumn<QPointF>("parentPos");
QTest::addColumn<QTransform>("parentTransform");
QTest::addColumn<QPointF>("pos");
QTest::addColumn<QTransform>("transform");
QTest::addColumn<QRectF>("inputRect");
QTest::addColumn<QRectF>("outputRect");
QTest::newRow("nil") << false << QPointF() << QTransform() << QPointF() << QTransform() << QRectF() << QRectF();
QTest::newRow("simple") << false << QPointF() << QTransform() << QPointF() << QTransform()
<< QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10);
QTest::newRow("simple w/parent") << true
<< QPointF() << QTransform()
<< QPointF() << QTransform()
<< QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10);
QTest::newRow("simple w/parent parentPos") << true
<< QPointF(50, 50) << QTransform()
<< QPointF() << QTransform()
<< QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10);
QTest::newRow("simple w/parent parentPos parentRotation") << true
<< QPointF(50, 50) << QTransform().rotate(45)
<< QPointF() << QTransform()
<< QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10);
QTest::newRow("pos w/parent") << true
<< QPointF() << QTransform()
<< QPointF(50, 50) << QTransform()
<< QRectF(0, 0, 10, 10) << QRectF(50, 50, 10, 10);
QTest::newRow("rotation w/parent") << true
<< QPointF() << QTransform()
<< QPointF() << QTransform().rotate(90)
<< QRectF(0, 0, 10, 10) << QRectF(-10, 0, 10, 10);
QTest::newRow("pos rotation w/parent") << true
<< QPointF() << QTransform()
<< QPointF(50, 50) << QTransform().rotate(90)
<< QRectF(0, 0, 10, 10) << QRectF(40, 50, 10, 10);
QTest::newRow("pos rotation w/parent parentPos parentRotation") << true
<< QPointF(-170, -190) << QTransform().rotate(90)
<< QPointF(50, 50) << QTransform().rotate(90)
<< QRectF(0, 0, 10, 10) << QRectF(40, 50, 10, 10);
}
void tst_QGraphicsItem::mapRectFromToParent()
{
QFETCH(bool, parent);
QFETCH(QPointF, parentPos);
QFETCH(QTransform, parentTransform);
QFETCH(QPointF, pos);
QFETCH(QTransform, transform);
QFETCH(QRectF, inputRect);
QFETCH(QRectF, outputRect);
QGraphicsRectItem *rect = new QGraphicsRectItem;
rect->setPos(pos);
rect->setTransform(transform);
if (parent) {
QGraphicsRectItem *rectParent = new QGraphicsRectItem;
rect->setParentItem(rectParent);
rectParent->setPos(parentPos);
rectParent->setTransform(parentTransform);
}
// Make sure we use non-destructive transform operations (e.g., 90 degree
// rotations).
QCOMPARE(rect->mapRectToParent(inputRect), outputRect);
QCOMPARE(rect->mapRectFromParent(outputRect), inputRect);
QCOMPARE(rect->itemTransform(rect->parentItem()).mapRect(inputRect), outputRect);
QCOMPARE(rect->mapToParent(inputRect).boundingRect(), outputRect);
QCOMPARE(rect->mapToParent(QPolygonF(inputRect)).boundingRect(), outputRect);
QCOMPARE(rect->mapFromParent(outputRect).boundingRect(), inputRect);
QCOMPARE(rect->mapFromParent(QPolygonF(outputRect)).boundingRect(), inputRect);
QPainterPath inputPath;
inputPath.addRect(inputRect);
QPainterPath outputPath;
outputPath.addRect(outputRect);
QCOMPARE(rect->mapToParent(inputPath).boundingRect(), outputPath.boundingRect());
QCOMPARE(rect->mapFromParent(outputPath).boundingRect(), inputPath.boundingRect());
}
void tst_QGraphicsItem::isAncestorOf()
{
QGraphicsItem *grandPa = new QGraphicsRectItem;
QGraphicsItem *parent = new QGraphicsRectItem;
QGraphicsItem *child = new QGraphicsRectItem;
QVERIFY(!parent->isAncestorOf(nullptr));
QVERIFY(!child->isAncestorOf(nullptr));
QVERIFY(!parent->isAncestorOf(child));
QVERIFY(!child->isAncestorOf(parent));
QVERIFY(!parent->isAncestorOf(parent));
child->setParentItem(parent);
parent->setParentItem(grandPa);
QVERIFY(parent->isAncestorOf(child));
QVERIFY(grandPa->isAncestorOf(parent));
QVERIFY(grandPa->isAncestorOf(child));
QVERIFY(!child->isAncestorOf(parent));
QVERIFY(!parent->isAncestorOf(grandPa));
QVERIFY(!child->isAncestorOf(grandPa));
QVERIFY(!child->isAncestorOf(child));
QVERIFY(!parent->isAncestorOf(parent));
QVERIFY(!grandPa->isAncestorOf(grandPa));
parent->setParentItem(nullptr);
delete child;
delete parent;
delete grandPa;
}
void tst_QGraphicsItem::commonAncestorItem()
{
QGraphicsRectItem ancestorItem;
QGraphicsItem *ancestor = &ancestorItem;
QGraphicsItem *grandMa = new QGraphicsRectItem;
QGraphicsItem *grandPa = new QGraphicsRectItem;
QGraphicsItem *brotherInLaw = new QGraphicsRectItem;
QGraphicsItem *cousin = new QGraphicsRectItem;
QGraphicsItem *husband = new QGraphicsRectItem;
QGraphicsItem *child = new QGraphicsRectItem;
QGraphicsItem *wife = new QGraphicsRectItem;
child->setParentItem(husband);
husband->setParentItem(grandPa);
brotherInLaw->setParentItem(grandPa);
cousin->setParentItem(brotherInLaw);
wife->setParentItem(grandMa);
grandMa->setParentItem(ancestor);
grandPa->setParentItem(ancestor);
QCOMPARE(grandMa->commonAncestorItem(grandMa), grandMa);
QCOMPARE(grandMa->commonAncestorItem(nullptr), nullptr);
QCOMPARE(grandMa->commonAncestorItem(grandPa), ancestor);
QCOMPARE(grandPa->commonAncestorItem(grandMa), ancestor);
QCOMPARE(grandPa->commonAncestorItem(husband), grandPa);
QCOMPARE(grandPa->commonAncestorItem(wife), ancestor);
QCOMPARE(grandMa->commonAncestorItem(husband), ancestor);
QCOMPARE(grandMa->commonAncestorItem(wife), grandMa);
QCOMPARE(wife->commonAncestorItem(grandMa), grandMa);
QCOMPARE(child->commonAncestorItem(cousin), grandPa);
QCOMPARE(cousin->commonAncestorItem(child), grandPa);
QCOMPARE(wife->commonAncestorItem(child), ancestor);
QCOMPARE(child->commonAncestorItem(wife), ancestor);
}
void tst_QGraphicsItem::data()
{
QGraphicsTextItem text;
QCOMPARE(text.data(0), QVariant());
text.setData(0, "TextItem");
QCOMPARE(text.data(0), QVariant(QString("TextItem")));
text.setData(0, QVariant());
QCOMPARE(text.data(0), QVariant());
}
void tst_QGraphicsItem::type()
{
QCOMPARE(int(QGraphicsItem::Type), 1);
QCOMPARE(int(QGraphicsPathItem::Type), 2);
QCOMPARE(int(QGraphicsRectItem::Type), 3);
QCOMPARE(int(QGraphicsEllipseItem::Type), 4);
QCOMPARE(int(QGraphicsPolygonItem::Type), 5);
QCOMPARE(int(QGraphicsLineItem::Type), 6);
QCOMPARE(int(QGraphicsPixmapItem::Type), 7);
QCOMPARE(int(QGraphicsTextItem::Type), 8);
QCOMPARE(QGraphicsPathItem().type(), 2);
QCOMPARE(QGraphicsRectItem().type(), 3);
QCOMPARE(QGraphicsEllipseItem().type(), 4);
QCOMPARE(QGraphicsPolygonItem().type(), 5);
QCOMPARE(QGraphicsLineItem().type(), 6);
QCOMPARE(QGraphicsPixmapItem().type(), 7);
QCOMPARE(QGraphicsTextItem().type(), 8);
}
void tst_QGraphicsItem::graphicsitem_cast()
{
QGraphicsPathItem pathItem;
const QGraphicsPathItem *pPathItem = &pathItem;
QGraphicsRectItem rectItem;
const QGraphicsRectItem *pRectItem = &rectItem;
QGraphicsEllipseItem ellipseItem;
const QGraphicsEllipseItem *pEllipseItem = &ellipseItem;
QGraphicsPolygonItem polygonItem;
const QGraphicsPolygonItem *pPolygonItem = &polygonItem;
QGraphicsLineItem lineItem;
const QGraphicsLineItem *pLineItem = &lineItem;
QGraphicsPixmapItem pixmapItem;
const QGraphicsPixmapItem *pPixmapItem = &pixmapItem;
QGraphicsTextItem textItem;
const QGraphicsTextItem *pTextItem = &textItem;
QVERIFY(qgraphicsitem_cast<QGraphicsPathItem *>(&pathItem));
//QVERIFY(qgraphicsitem_cast<QAbstractGraphicsPathItem *>(&pathItem));
QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&pathItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pPathItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsPathItem *>(pPathItem));
QVERIFY(qgraphicsitem_cast<QGraphicsRectItem *>(&rectItem));
QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&rectItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pRectItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsRectItem *>(pRectItem));
QVERIFY(qgraphicsitem_cast<QGraphicsEllipseItem *>(&ellipseItem));
QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&ellipseItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pEllipseItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsEllipseItem *>(pEllipseItem));
QVERIFY(qgraphicsitem_cast<QGraphicsPolygonItem *>(&polygonItem));
//QVERIFY(qgraphicsitem_cast<QAbstractGraphicsPathItem *>(&polygonItem));
QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&polygonItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pPolygonItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsPolygonItem *>(pPolygonItem));
QVERIFY(qgraphicsitem_cast<QGraphicsLineItem *>(&lineItem));
QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&lineItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pLineItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsLineItem *>(pLineItem));
QVERIFY(qgraphicsitem_cast<QGraphicsPixmapItem *>(&pixmapItem));
QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&pixmapItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pPixmapItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsPixmapItem *>(pPixmapItem));
QVERIFY(qgraphicsitem_cast<QGraphicsTextItem *>(&textItem));
QVERIFY(qgraphicsitem_cast<QGraphicsItem *>(&textItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsItem *>(pTextItem));
QVERIFY(qgraphicsitem_cast<const QGraphicsTextItem *>(pTextItem));
// and some casts that _should_ fail:
QVERIFY(!qgraphicsitem_cast<QGraphicsEllipseItem *>(&pathItem));
QVERIFY(!qgraphicsitem_cast<const QGraphicsTextItem *>(pPolygonItem));
// and this shouldn't crash
QGraphicsItem *ptr = nullptr;
QVERIFY(!qgraphicsitem_cast<QGraphicsTextItem *>(ptr));
QVERIFY(!qgraphicsitem_cast<QGraphicsItem *>(ptr));
}
void tst_QGraphicsItem::hoverEventsGenerateRepaints()
{
Q_CHECK_PAINTEVENTS
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QVERIFY(QTest::qWaitForWindowActive(&view));
QCoreApplication::processEvents(); // Process all queued paint events
EventTester *tester = new EventTester;
scene.addItem(tester);
tester->setAcceptHoverEvents(true);
QTRY_COMPARE(tester->repaints, 1);
// Send a hover enter event
QGraphicsSceneHoverEvent hoverEnterEvent(QEvent::GraphicsSceneHoverEnter);
hoverEnterEvent.setScenePos(QPointF(0, 0));
hoverEnterEvent.setPos(QPointF(0, 0));
QCoreApplication::sendEvent(&scene, &hoverEnterEvent);
// Check that we get a repaint
int npaints = tester->repaints;
QCoreApplication::processEvents();
QCoreApplication::processEvents();
QCOMPARE(tester->events.size(), 2); // enter + move
QCOMPARE(tester->repaints, npaints + 1);
QCOMPARE(tester->events.constLast(), QEvent::GraphicsSceneHoverMove);
// Send a hover move event
QGraphicsSceneHoverEvent hoverMoveEvent(QEvent::GraphicsSceneHoverMove);
hoverMoveEvent.setScenePos(QPointF(0, 0));
hoverMoveEvent.setPos(QPointF(0, 0));
QCoreApplication::sendEvent(&scene, &hoverMoveEvent);
// Check that we don't get a repaint
QCoreApplication::processEvents();
QCoreApplication::processEvents();
QCOMPARE(tester->events.size(), 3);
QCOMPARE(tester->repaints, npaints + 1);
QCOMPARE(tester->events.constLast(), QEvent::GraphicsSceneHoverMove);
// Send a hover leave event
QGraphicsSceneHoverEvent hoverLeaveEvent(QEvent::GraphicsSceneHoverLeave);
hoverLeaveEvent.setScenePos(QPointF(-100, -100));
hoverLeaveEvent.setPos(QPointF(0, 0));
QCoreApplication::sendEvent(&scene, &hoverLeaveEvent);
// Check that we get a repaint
QCoreApplication::processEvents();
QCoreApplication::processEvents();
QCOMPARE(tester->events.size(), 4);
QCOMPARE(tester->repaints, npaints + 2);
QCOMPARE(tester->events.constLast(), QEvent::GraphicsSceneHoverLeave);
}
void tst_QGraphicsItem::boundingRects_data()
{
QTest::addColumn<AbstractGraphicsShapeItemPtr>("item");
QTest::addColumn<QRectF>("boundingRect");
QRectF rect(0, 0, 100, 100);
QPainterPath path;
path.addRect(rect);
QRectF adjustedRect(-0.5, -0.5, 101, 101);
QTest::newRow("path") << AbstractGraphicsShapeItemPtr(new QGraphicsPathItem(path)) << adjustedRect;
QTest::newRow("rect") << AbstractGraphicsShapeItemPtr(new QGraphicsRectItem(rect)) << adjustedRect;
QTest::newRow("ellipse") << AbstractGraphicsShapeItemPtr(new QGraphicsEllipseItem(rect)) << adjustedRect;
QTest::newRow("polygon") << AbstractGraphicsShapeItemPtr(new QGraphicsPolygonItem(rect)) << adjustedRect;
}
void tst_QGraphicsItem::boundingRects()
{
QFETCH(AbstractGraphicsShapeItemPtr, item);
QFETCH(QRectF, boundingRect);
item->setPen(QPen(Qt::black, 1));
QCOMPARE(item->boundingRect(), boundingRect);
}
void tst_QGraphicsItem::boundingRects2()
{
QGraphicsPixmapItem pixmap(QPixmap::fromImage(QImage(100, 100, QImage::Format_ARGB32_Premultiplied)));
QCOMPARE(pixmap.boundingRect(), QRectF(0, 0, 100, 100));
QGraphicsLineItem line(0, 0, 100, 0);
line.setPen(QPen(Qt::black, 1));
QCOMPARE(line.boundingRect(), QRectF(-0.5, -0.5, 101, 1));
}
void tst_QGraphicsItem::sceneBoundingRect()
{
QGraphicsScene scene;
QGraphicsRectItem *item = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0));
item->setPos(100, 100);
QCOMPARE(item->boundingRect(), QRectF(0, 0, 100, 100));
QCOMPARE(item->sceneBoundingRect(), QRectF(100, 100, 100, 100));
item->setTransform(QTransform().rotate(90), true);
QCOMPARE(item->boundingRect(), QRectF(0, 0, 100, 100));
QCOMPARE(item->sceneBoundingRect(), QRectF(0, 100, 100, 100));
}
void tst_QGraphicsItem::childrenBoundingRect()
{
QGraphicsScene scene;
QGraphicsRectItem *parent = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0));
QGraphicsRectItem *child = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0));
child->setParentItem(parent);
parent->setPos(100, 100);
child->setPos(100, 100);
QCOMPARE(parent->boundingRect(), QRectF(0, 0, 100, 100));
QCOMPARE(child->boundingRect(), QRectF(0, 0, 100, 100));
QCOMPARE(child->childrenBoundingRect(), QRectF());
QCOMPARE(parent->childrenBoundingRect(), QRectF(100, 100, 100, 100));
QGraphicsRectItem *child2 = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0));
child2->setParentItem(parent);
child2->setPos(-100, -100);
QCOMPARE(parent->childrenBoundingRect(), QRectF(-100, -100, 300, 300));
QGraphicsRectItem *childChild = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0));
childChild->setParentItem(child);
childChild->setPos(500, 500);
child->setTransform(QTransform().rotate(90), true);
scene.addPolygon(parent->mapToScene(parent->boundingRect() | parent->childrenBoundingRect()))->setPen(QPen(Qt::red));;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QTRY_COMPARE(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);
rect->setPen(QPen(Qt::black, 0));
rect2->setPen(QPen(Qt::black, 0));
rect3->setPen(QPen(Qt::black, 0));
rect4->setPen(QPen(Qt::black, 0));
rect5->setPen(QPen(Qt::black, 0));
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->setTransform(QTransform().rotate(45), true);
rect2->setTransform(QTransform().rotate(-45), true);
rect3->setTransform(QTransform().rotate(45), true);
rect4->setTransform(QTransform().rotate(-45), true);
rect5->setTransform(QTransform().rotate(45), true);
subTreeRect = rect->childrenBoundingRect();
QCOMPARE(rect->childrenBoundingRect(), QRectF(-100, 75, 275, 250));
}
void tst_QGraphicsItem::childrenBoundingRect2()
{
QGraphicsItemGroup box;
QGraphicsLineItem l1(0, 0, 100, 0, &box);
QGraphicsLineItem l2(100, 0, 100, 100, &box);
QGraphicsLineItem l3(0, 0, 0, 100, &box);
// Make sure lines (zero with/height) are included in the childrenBoundingRect.
l1.setPen(QPen(Qt::black, 0));
l2.setPen(QPen(Qt::black, 0));
l3.setPen(QPen(Qt::black, 0));
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);
rect->setPen(QPen(Qt::black, 0));
rect2->setPen(QPen(Qt::black, 0));
rect3->setPen(QPen(Qt::black, 0));
rect4->setPen(QPen(Qt::black, 0));
rect5->setPen(QPen(Qt::black, 0));
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::childrenBoundingRect4()
{
QGraphicsScene scene;
QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 10, 10));
QGraphicsRectItem *rect2 = scene.addRect(QRectF(0, 0, 20, 20));
QGraphicsRectItem *rect3 = scene.addRect(QRectF(0, 0, 30, 30));
rect2->setParentItem(rect);
rect3->setParentItem(rect);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
// Try to mess up the cached bounding rect.
rect->childrenBoundingRect();
rect2->childrenBoundingRect();
rect3->setOpacity(0.0);
rect3->setParentItem(rect2);
QCOMPARE(rect->childrenBoundingRect(), rect3->boundingRect());
QCOMPARE(rect2->childrenBoundingRect(), rect3->boundingRect());
}
void tst_QGraphicsItem::childrenBoundingRect5()
{
QGraphicsScene scene;
QGraphicsRectItem *parent = scene.addRect(QRectF(0, 0, 100, 100));
QGraphicsRectItem *child = scene.addRect(QRectF(0, 0, 100, 100));
child->setParentItem(parent);
parent->setPen(QPen(Qt::black, 0));
child->setPen(QPen(Qt::black, 0));
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
// Try to mess up the cached bounding rect.
QRectF expectedChildrenBoundingRect = parent->boundingRect();
QCOMPARE(parent->childrenBoundingRect(), expectedChildrenBoundingRect);
// Apply some effects.
QGraphicsDropShadowEffect *dropShadow = new QGraphicsDropShadowEffect;
dropShadow->setOffset(25, 25);
child->setGraphicsEffect(dropShadow);
parent->setGraphicsEffect(new QGraphicsOpacityEffect);
QVERIFY(parent->childrenBoundingRect() != expectedChildrenBoundingRect);
expectedChildrenBoundingRect |= dropShadow->boundingRect();
QCOMPARE(parent->childrenBoundingRect(), expectedChildrenBoundingRect);
}
void tst_QGraphicsItem::group()
{
QGraphicsScene scene;
QGraphicsRectItem *parent = scene.addRect(QRectF(0, 0, 50, 50), QPen(Qt::black, 0), QBrush(Qt::green));
QGraphicsRectItem *child = scene.addRect(QRectF(0, 0, 50, 50), QPen(Qt::black, 0), QBrush(Qt::blue));
QGraphicsRectItem *parent2 = scene.addRect(QRectF(0, 0, 50, 50), QPen(Qt::black, 0), QBrush(Qt::red));
parent2->setPos(-50, 50);
child->setTransform(QTransform().rotate(45), true);
child->setParentItem(parent);
parent->setPos(25, 25);
child->setPos(25, 25);
QCOMPARE(parent->group(), nullptr);
QCOMPARE(parent2->group(), nullptr);
QCOMPARE(child->group(), nullptr);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QGraphicsItemGroup *group = new QGraphicsItemGroup;
group->setSelected(true);
scene.addItem(group);
QRectF parentSceneBoundingRect = parent->sceneBoundingRect();
group->addToGroup(parent);
QCOMPARE(parent->group(), group);
QCOMPARE(parent->sceneBoundingRect(), parentSceneBoundingRect);
QCOMPARE(parent->parentItem(), group);
QCOMPARE(group->childItems().size(), 1);
QCOMPARE(scene.items().size(), 4);
QCOMPARE(scene.items(group->sceneBoundingRect()).size(), 3);
QRectF parent2SceneBoundingRect = parent2->sceneBoundingRect();
group->addToGroup(parent2);
QCOMPARE(parent2->group(), group);
QCOMPARE(parent2->sceneBoundingRect(), parent2SceneBoundingRect);
QCOMPARE(parent2->parentItem(), group);
QCOMPARE(group->childItems().size(), 2);
QCOMPARE(scene.items().size(), 4);
QCOMPARE(scene.items(group->sceneBoundingRect()).size(), 4);
GraphicsItems newItems;
newItems.reserve(100);
for (int i = 0; i < 100; ++i) {
QGraphicsItem *item = scene.addRect(QRectF(-25, -25, 50, 50), QPen(Qt::black, 0),
QBrush(QColor(QRandomGenerator::global()->bounded(255), QRandomGenerator::global()->bounded(255),
QRandomGenerator::global()->bounded(255), QRandomGenerator::global()->bounded(255))));
newItems << item;
item->setPos(-1000 + QRandomGenerator::global()->bounded(2000),
-1000 + QRandomGenerator::global()->bounded(2000));
item->setTransform(QTransform().rotate(QRandomGenerator::global()->bounded(90)), true);
}
view.fitInView(scene.itemsBoundingRect());
for (QGraphicsItem *item : std::as_const(newItems)) {
group->addToGroup(item);
QCOMPARE(item->group(), group);
}
}
void tst_QGraphicsItem::setGroup()
{
QGraphicsItemGroup group1;
QGraphicsItemGroup group2;
const QScopedPointer<QGraphicsRectItem> rect(new QGraphicsRectItem);
QCOMPARE(rect->group(), nullptr);
QCOMPARE(rect->parentItem(), nullptr);
rect->setGroup(&group1);
QCOMPARE(rect->group(), &group1);
QCOMPARE(rect->parentItem(), &group1);
rect->setGroup(&group2);
QCOMPARE(rect->group(), &group2);
QCOMPARE(rect->parentItem(), &group2);
rect->setGroup(nullptr);
QCOMPARE(rect->group(), nullptr);
QCOMPARE(rect->parentItem(), nullptr);
}
void tst_QGraphicsItem::setGroup2()
{
QGraphicsScene scene;
QGraphicsItemGroup group;
scene.addItem(&group);
QGraphicsRectItem *rect = scene.addRect(50,50,50,50,Qt::NoPen,Qt::black);
rect->setTransformOriginPoint(50,50);
rect->setRotation(45);
rect->setScale(1.5);
rect->setTransform(QTransform::fromTranslate(20,20), true);
group.setTransform(QTransform::fromTranslate(-30, -40), true);
group.setRotation(180);
group.setScale(0.5);
QTransform oldSceneTransform = rect->sceneTransform();
rect->setGroup(&group);
QCOMPARE(rect->sceneTransform(), oldSceneTransform);
group.setRotation(20);
group.setScale(2);
rect->setRotation(90);
rect->setScale(0.8);
oldSceneTransform = rect->sceneTransform();
rect->setGroup(nullptr);
qFuzzyCompare(rect->sceneTransform(), oldSceneTransform);
}
void tst_QGraphicsItem::nestedGroups()
{
QGraphicsItemGroup *group1 = new QGraphicsItemGroup;
QGraphicsItemGroup *group2 = new QGraphicsItemGroup;
QGraphicsRectItem *rect = new QGraphicsRectItem;
QGraphicsRectItem *rect2 = new QGraphicsRectItem;
rect2->setParentItem(rect);
group1->addToGroup(rect);
QCOMPARE(rect->group(), group1);
QCOMPARE(rect2->group(), group1);
group2->addToGroup(group1);
QCOMPARE(rect->group(), group1);
QCOMPARE(rect2->group(), group1);
QCOMPARE(group1->group(), group2);
QCOMPARE(group2->group(), nullptr);
QGraphicsScene scene;
scene.addItem(group1);
QCOMPARE(rect->group(), group1);
QCOMPARE(rect2->group(), group1);
QCOMPARE(group1->group(), nullptr);
QVERIFY(group2->childItems().isEmpty());
delete group2;
}
void tst_QGraphicsItem::warpChildrenIntoGroup()
{
QGraphicsScene scene;
QGraphicsRectItem *parentRectItem = scene.addRect(QRectF(0, 0, 100, 100));
QGraphicsRectItem *childRectItem = scene.addRect(QRectF(0, 0, 100, 100));
parentRectItem->setTransform(QTransform().rotate(90), true);
childRectItem->setPos(-50, -25);
childRectItem->setParentItem(parentRectItem);
QCOMPARE(childRectItem->mapToScene(50, 0), QPointF(25, 0));
QCOMPARE(childRectItem->scenePos(), QPointF(25, -50));
QGraphicsRectItem *parentOfGroup = scene.addRect(QRectF(0, 0, 100, 100));
parentOfGroup->setPos(-200, -200);
parentOfGroup->setTransform(QTransform::fromScale(2, 2), true);
QGraphicsItemGroup *group = new QGraphicsItemGroup;
group->setPos(50, 50);
group->setParentItem(parentOfGroup);
QCOMPARE(group->scenePos(), QPointF(-100, -100));
group->addToGroup(childRectItem);
QCOMPARE(childRectItem->mapToScene(50, 0), QPointF(25, 0));
QCOMPARE(childRectItem->scenePos(), QPointF(25, -50));
}
void tst_QGraphicsItem::removeFromGroup()
{
QGraphicsScene scene;
QGraphicsRectItem *rect1 = scene.addRect(QRectF(-100, -100, 200, 200));
QGraphicsRectItem *rect2 = scene.addRect(QRectF(100, 100, 200, 200));
rect1->setFlag(QGraphicsItem::ItemIsSelectable);
rect2->setFlag(QGraphicsItem::ItemIsSelectable);
rect1->setSelected(true);
rect2->setSelected(true);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QCoreApplication::processEvents(); // index items
QCoreApplication::processEvents(); // emit changed
QGraphicsItemGroup *group = scene.createItemGroup(scene.selectedItems());
QVERIFY(group);
QCOMPARE(group->childItems().size(), 2);
QCoreApplication::processEvents(); // index items
QCoreApplication::processEvents(); // emit changed
scene.destroyItemGroup(group); // calls removeFromGroup.
QCoreApplication::processEvents(); // index items
QCoreApplication::processEvents(); // emit changed
QCOMPARE(scene.items().size(), 2);
QVERIFY(!rect1->group());
QVERIFY(!rect2->group());
}
class ChildEventTester : public QGraphicsRectItem
{
public:
using QGraphicsRectItem::QGraphicsRectItem;
int counter = 0;
protected:
void focusInEvent(QFocusEvent *event) override
{ ++counter; QGraphicsRectItem::focusInEvent(event); }
void mousePressEvent(QGraphicsSceneMouseEvent *) override
{ ++counter; }
void mouseMoveEvent(QGraphicsSceneMouseEvent *) override
{ ++counter; }
void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override
{ ++counter; }
};
void tst_QGraphicsItem::handlesChildEvents()
{
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(blue);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
// Pull out the items, closest item first
QList<QGraphicsItem *> items = scene.items(scene.itemsBoundingRect());
QCOMPARE(items.at(0), yellow);
QCOMPARE(items.at(1), gray);
QCOMPARE(items.at(2), green);
QCOMPARE(items.at(3), red);
QCOMPARE(items.at(4), 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(blue->mapToScene(5, 5));
pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos()));
releaseEvent.setButton(Qt::LeftButton);
releaseEvent.setScenePos(blue->mapToScene(5, 5));
releaseEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos()));
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::sendEvent(&scene, &releaseEvent);
QCOMPARE(blue->counter, 2);
// Send events to a level1 item
pressEvent.setScenePos(red->mapToScene(5, 5));
pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos()));
releaseEvent.setScenePos(red->mapToScene(5, 5));
releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos()));
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::sendEvent(&scene, &releaseEvent);
QCOMPARE(blue->counter, 2);
QCOMPARE(red->counter, 2);
// Send events to a level2 item
pressEvent.setScenePos(green->mapToScene(5, 5));
pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos()));
releaseEvent.setScenePos(green->mapToScene(5, 5));
releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos()));
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::sendEvent(&scene, &releaseEvent);
QCOMPARE(blue->counter, 2);
QCOMPARE(red->counter, 2);
QCOMPARE(green->counter, 2);
blue->setHandlesChildEvents(true);
// Send events to a level1 item
pressEvent.setScenePos(red->mapToScene(5, 5));
pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos()));
releaseEvent.setScenePos(red->mapToScene(5, 5));
releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos()));
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::sendEvent(&scene, &releaseEvent);
QCOMPARE(blue->counter, 4);
QCOMPARE(red->counter, 2);
// Send events to a level2 item
pressEvent.setScenePos(green->mapToScene(5, 5));
pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos()));
releaseEvent.setScenePos(green->mapToScene(5, 5));
releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos()));
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::sendEvent(&scene, &releaseEvent);
QCOMPARE(blue->counter, 6);
QCOMPARE(red->counter, 2);
QCOMPARE(green->counter, 2);
blue->setHandlesChildEvents(false);
// Send events to a level1 item
pressEvent.setScenePos(red->mapToScene(5, 5));
pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos()));
releaseEvent.setScenePos(red->mapToScene(5, 5));
releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos()));
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::sendEvent(&scene, &releaseEvent);
QCOMPARE(blue->counter, 6);
QCOMPARE(red->counter, 4);
// Send events to a level2 item
pressEvent.setScenePos(green->mapToScene(5, 5));
pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos()));
releaseEvent.setScenePos(green->mapToScene(5, 5));
releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos()));
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::sendEvent(&scene, &releaseEvent);
QCOMPARE(blue->counter, 6);
QCOMPARE(red->counter, 4);
QCOMPARE(green->counter, 4);
}
void tst_QGraphicsItem::handlesChildEvents2()
{
ChildEventTester *root = new ChildEventTester(QRectF(0, 0, 10, 10));
root->setHandlesChildEvents(true);
QVERIFY(root->handlesChildEvents());
ChildEventTester *child = new ChildEventTester(QRectF(0, 0, 10, 10), root);
QVERIFY(!child->handlesChildEvents());
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->handlesChildEvents());
QVERIFY(!child3->handlesChildEvents());
QVERIFY(!child4->handlesChildEvents());
QGraphicsScene scene;
scene.addItem(root);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QApplication::processEvents();
QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(5, 5),
view.viewport()->mapToGlobal(view.mapFromScene(5, 5)), Qt::LeftButton, {}, {});
QCoreApplication::sendEvent(view.viewport(), &event);
QTRY_COMPARE(root->counter, 1);
}
void tst_QGraphicsItem::handlesChildEvents3()
{
QGraphicsScene scene;
QEvent activate(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &activate);
ChildEventTester *group2 = new ChildEventTester(QRectF(), nullptr);
ChildEventTester *group1 = new ChildEventTester(QRectF(), group2);
ChildEventTester *leaf = new ChildEventTester(QRectF(), group1);
scene.addItem(group2);
leaf->setFlag(QGraphicsItem::ItemIsFocusable);
group1->setFlag(QGraphicsItem::ItemIsFocusable);
group1->setHandlesChildEvents(true);
group2->setFlag(QGraphicsItem::ItemIsFocusable);
group2->setHandlesChildEvents(true);
leaf->setFocus();
QVERIFY(leaf->hasFocus()); // group2 stole the event, but leaf still got focus
QVERIFY(!group1->hasFocus());
QVERIFY(!group2->hasFocus());
QCOMPARE(leaf->counter, 0);
QCOMPARE(group1->counter, 0);
QCOMPARE(group2->counter, 1);
group1->setFocus();
QVERIFY(group1->hasFocus()); // group2 stole the event, but group1 still got focus
QVERIFY(!leaf->hasFocus());
QVERIFY(!group2->hasFocus());
QCOMPARE(leaf->counter, 0);
QCOMPARE(group1->counter, 0);
QCOMPARE(group2->counter, 2);
group2->setFocus();
QVERIFY(group2->hasFocus()); // group2 stole the event, and now group2 also has focus
QVERIFY(!leaf->hasFocus());
QVERIFY(!group1->hasFocus());
QCOMPARE(leaf->counter, 0);
QCOMPARE(group1->counter, 0);
QCOMPARE(group2->counter, 3);
}
class ChildEventFilterTester : public ChildEventTester
{
public:
ChildEventFilterTester(const QRectF &rect, QGraphicsItem *parent = nullptr)
: ChildEventTester(rect, parent)
{ }
QEvent::Type filter = QEvent::None;
protected:
bool sceneEventFilter(QGraphicsItem *item, QEvent *event) override
{
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.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
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()));
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::sendEvent(&scene, &releaseEvent);
QTRY_COMPARE(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
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::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
QCoreApplication::sendEvent(&scene, &pressEvent);
QCoreApplication::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.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QApplication::processEvents();
QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(5, 5),
view.viewport()->mapToGlobal(view.mapFromScene(5, 5)), Qt::LeftButton, {}, {});
QCoreApplication::sendEvent(view.viewport(), &event);
QTRY_COMPARE(root->counter, 1);
QCOMPARE(child->counter, 0);
QCOMPARE(child2->counter, 0);
QCOMPARE(child3->counter, 0);
QCOMPARE(child4->counter, 0);
}
class CustomItem : public QGraphicsItem
{
public:
QRectF boundingRect() const override
{ return QRectF(-110, -110, 220, 220); }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
{
for (int x = -100; x <= 100; x += 25)
painter->drawLine(x, -100, x, 100);
for (int y = -100; y <= 100; y += 25)
painter->drawLine(-100, y, 100, y);
QFont font = painter->font();
font.setPointSize(4);
painter->setFont(font);
for (int x = -100; x < 100; x += 25) {
const QString prefix = QString::number(x) + QLatin1Char('x');
for (int y = -100; y < 100; y += 25)
painter->drawText(QRectF(x, y, 25, 25), Qt::AlignCenter, prefix + QString::number(y));
}
}
};
void tst_QGraphicsItem::ensureVisible()
{
QGraphicsScene scene;
scene.setSceneRect(-200, -200, 400, 400);
QGraphicsItem *item = new CustomItem;
scene.addItem(item);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.setFixedSize(300, 300);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
for (int i = 0; i < 25; ++i) {
view.scale(qreal(1.06), qreal(1.06));
QApplication::processEvents();
}
item->ensureVisible(-100, -100, 25, 25);
for (int x = -100; x < 100; x += 25) {
for (int y = -100; y < 100; y += 25) {
int xmargin = QRandomGenerator::global()->bounded(75);
int ymargin = QRandomGenerator::global()->bounded(75);
item->ensureVisible(x, y, 25, 25, xmargin, ymargin);
QApplication::processEvents();
QPolygonF viewScenePoly;
viewScenePoly << view.mapToScene(view.rect().topLeft())
<< view.mapToScene(view.rect().topRight())
<< view.mapToScene(view.rect().bottomRight())
<< view.mapToScene(view.rect().bottomLeft());
QVERIFY(scene.items(viewScenePoly).contains(item));
QPainterPath path;
path.addPolygon(viewScenePoly);
QVERIFY(path.contains(item->mapToScene(x + 12, y + 12)));
QPolygonF viewScenePolyMinusMargins;
viewScenePolyMinusMargins << view.mapToScene(view.rect().topLeft() + QPoint(xmargin, ymargin))
<< view.mapToScene(view.rect().topRight() + QPoint(-xmargin, ymargin))
<< view.mapToScene(view.rect().bottomRight() + QPoint(-xmargin, -ymargin))
<< view.mapToScene(view.rect().bottomLeft() + QPoint(xmargin, -ymargin));
QPainterPath path2;
path2.addPolygon(viewScenePolyMinusMargins);
QVERIFY(path2.contains(item->mapToScene(x + 12, y + 12)));
}
}
item->ensureVisible(100, 100, 25, 25);
QTest::qWait(25);
}
#ifndef QT_NO_CURSOR
void tst_QGraphicsItem::cursor()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
view.showFullScreen();
QVERIFY(QTest::qWaitForWindowExposed(&view));
const Qt::CursorShape viewportShape = view.viewport()->cursor().shape();
QGraphicsRectItem *item1 = scene.addRect(QRectF(-100, 0, 50, 50));
QGraphicsRectItem *item2 = scene.addRect(QRectF(50, 0, 50, 50));
QVERIFY(!item1->hasCursor());
QVERIFY(!item2->hasCursor());
item1->setCursor(Qt::IBeamCursor);
item2->setCursor(Qt::PointingHandCursor);
QVERIFY(item1->hasCursor());
QVERIFY(item2->hasCursor());
item1->setCursor(QCursor());
item2->setCursor(QCursor());
QVERIFY(item1->hasCursor());
QVERIFY(item2->hasCursor());
item1->unsetCursor();
item2->unsetCursor();
QVERIFY(!item1->hasCursor());
QVERIFY(!item2->hasCursor());
item1->setCursor(Qt::IBeamCursor);
item2->setCursor(Qt::PointingHandCursor);
QPoint viewCenter = view.rect().center();
QPoint item1Center = view.mapFromScene(item1->sceneBoundingRect().center());
QPoint item2Center = view.mapFromScene(item2->sceneBoundingRect().center());
{
QMouseEvent event(QEvent::MouseMove, viewCenter, view.viewport()->mapToGlobal(viewCenter), Qt::NoButton, {}, {});
QCoreApplication::sendEvent(view.viewport(), &event);
}
QCOMPARE(view.viewport()->cursor().shape(), viewportShape);
{
QMouseEvent event(QEvent::MouseMove, item1Center, view.viewport()->mapToGlobal(item1Center), Qt::NoButton, {}, {});
QCoreApplication::sendEvent(view.viewport(), &event);
}
QCOMPARE(view.viewport()->cursor().shape(), item1->cursor().shape());
{
QMouseEvent event(QEvent::MouseMove, item2Center, view.viewport()->mapToGlobal(item2Center), Qt::NoButton, {}, {});
QCoreApplication::sendEvent(view.viewport(), &event);
}
QCOMPARE(view.viewport()->cursor().shape(), item2->cursor().shape());
{
QMouseEvent event(QEvent::MouseMove, viewCenter, view.viewport()->mapToGlobal(viewCenter), Qt::NoButton, {}, {});
QCoreApplication::sendEvent(view.viewport(), &event);
}
QCOMPARE(view.viewport()->cursor().shape(), viewportShape);
item1->setEnabled(false);
{
QMouseEvent event(QEvent::MouseMove, item1Center, view.viewport()->mapToGlobal(item1Center), Qt::NoButton, {}, {});
QCoreApplication::sendEvent(view.viewport(), &event);
}
QCOMPARE(view.viewport()->cursor().shape(), viewportShape);
}
#endif
/*
void tst_QGraphicsItem::textControlGetterSetter()
{
QGraphicsTextItem *item = new QGraphicsTextItem;
QCOMPARE(item->textControl()->parent(), item);
QPointer<QWidgetTextControl> control = item->textControl();
delete item;
QVERIFY(!control);
item = new QGraphicsTextItem;
QPointer<QWidgetTextControl> oldControl = control;
control = new QWidgetTextControl;
item->setTextControl(control);
QCOMPARE(item->textControl(), control);
QVERIFY(!control->parent());
QVERIFY(!oldControl);
// set some text to give it a size, to test that
// setTextControl (re)connects signals
const QRectF oldBoundingRect = item->boundingRect();
QVERIFY(oldBoundingRect.isValid());
item->setPlainText("Some text");
item->adjustSize();
QVERIFY(item->boundingRect().isValid());
QVERIFY(item->boundingRect() != oldBoundingRect);
// test that on setting a control the item size
// is adjusted
oldControl = control;
control = new QWidgetTextControl;
control->setPlainText("foo!");
item->setTextControl(control);
QCOMPARE(item->boundingRect().size(), control->document()->documentLayout()->documentSize());
QVERIFY(oldControl);
delete oldControl;
delete item;
QVERIFY(control);
delete control;
}
*/
void tst_QGraphicsItem::defaultItemTest_QGraphicsLineItem()
{
QGraphicsLineItem item;
QCOMPARE(item.line(), QLineF());
QCOMPARE(item.pen(), QPen());
QCOMPARE(item.shape(), QPainterPath());
item.setPen(QPen(Qt::black, 1));
QCOMPARE(item.pen(), QPen(Qt::black, 1));
item.setLine(QLineF(0, 0, 10, 0));
QCOMPARE(item.line(), QLineF(0, 0, 10, 0));
QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 11, 1));
QCOMPARE(item.shape().elementCount(), 11);
QPainterPath path;
path.moveTo(0, -0.5);
path.lineTo(10, -0.5);
path.lineTo(10.5, -0.5);
path.lineTo(10.5, 0.5);
path.lineTo(10, 0.5);
path.lineTo(0, 0.5);
path.lineTo(-0.5, 0.5);
path.lineTo(-0.5, -0.5);
path.lineTo(0, -0.5);
path.lineTo(0, 0);
path.lineTo(10, 0);
path.closeSubpath();
for (int i = 0; i < 11; ++i)
QCOMPARE(QPointF(item.shape().elementAt(i)), QPointF(path.elementAt(i)));
}
void tst_QGraphicsItem::defaultItemTest_QGraphicsPixmapItem()
{
QGraphicsPixmapItem item;
QVERIFY(item.pixmap().isNull());
QCOMPARE(item.offset(), QPointF());
QCOMPARE(item.transformationMode(), Qt::FastTransformation);
QPixmap pixmap(300, 200);
pixmap.fill(Qt::red);
item.setPixmap(pixmap);
QCOMPARE(item.pixmap(), pixmap);
item.setTransformationMode(Qt::FastTransformation);
QCOMPARE(item.transformationMode(), Qt::FastTransformation);
item.setTransformationMode(Qt::SmoothTransformation);
QCOMPARE(item.transformationMode(), Qt::SmoothTransformation);
item.setOffset(-15, -15);
QCOMPARE(item.offset(), QPointF(-15, -15));
item.setOffset(QPointF(-10, -10));
QCOMPARE(item.offset(), QPointF(-10, -10));
QCOMPARE(item.boundingRect(), QRectF(-10, -10, 300, 200));
}
void tst_QGraphicsItem::defaultItemTest_QGraphicsTextItem()
{
QGraphicsTextItem *text = new QGraphicsTextItem;
QVERIFY(!text->openExternalLinks());
QVERIFY(text->textCursor().isNull());
QCOMPARE(text->defaultTextColor(), QPalette().color(QPalette::Text));
QVERIFY(text->document() != nullptr);
QCOMPARE(text->font(), QApplication::font());
QCOMPARE(text->textInteractionFlags(), Qt::TextInteractionFlags(Qt::NoTextInteraction));
QCOMPARE(text->textWidth(), -1.0);
QCOMPARE(text->toPlainText(), QString(""));
QGraphicsScene scene;
scene.addItem(text);
text->setPlainText("Hello world");
text->setFlag(QGraphicsItem::ItemIsMovable);
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setScenePos(QPointF(1, 1));
event.setButton(Qt::LeftButton);
event.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event);
QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove);
event2.setScenePos(QPointF(11, 11));
event2.setButton(Qt::LeftButton);
event2.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event2);
}
QCOMPARE(text->pos(), QPointF(10, 10));
text->setTextInteractionFlags(Qt::NoTextInteraction);
QVERIFY(!(text->flags() & QGraphicsItem::ItemAcceptsInputMethod));
text->setTextInteractionFlags(Qt::TextEditorInteraction);
QCOMPARE(text->textInteractionFlags(), Qt::TextInteractionFlags(Qt::TextEditorInteraction));
QVERIFY(text->flags() & QGraphicsItem::ItemAcceptsInputMethod);
{
QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove);
event2.setScenePos(QPointF(21, 21));
event2.setButton(Qt::LeftButton);
event2.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event2);
}
QCOMPARE(text->pos(), QPointF(20, 20)); // clicked on edge, item moved
}
void tst_QGraphicsItem::defaultItemTest_QGraphicsEllipseItem()
{
QGraphicsEllipseItem item;
item.setPen(QPen(Qt::black, 0));
QVERIFY(item.rect().isNull());
QVERIFY(item.boundingRect().isNull());
QVERIFY(item.shape().isEmpty());
QCOMPARE(item.spanAngle(), 360 * 16);
QCOMPARE(item.startAngle(), 0);
item.setRect(0, 0, 100, 100);
QCOMPARE(item.boundingRect(), QRectF(0, 0, 100, 100));
item.setSpanAngle(90 * 16);
// for some reason, the bounding rect has very few significant digits
// (i.e. it's likely that floats are being used inside it), so we
// must force the conversion from qreals to float or these tests will fail
QCOMPARE(float(item.boundingRect().left()), 50.0f);
QVERIFY(qFuzzyIsNull(float(item.boundingRect().top())));
QCOMPARE(float(item.boundingRect().width()), 50.0f);
QCOMPARE(float(item.boundingRect().height()), 50.0f);
item.setPen(QPen(Qt::black, 1));
QCOMPARE(item.boundingRect(), QRectF(49.5, -0.5, 51, 51));
item.setSpanAngle(180 * 16);
QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 101, 51));
item.setSpanAngle(360 * 16);
QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 101, 101));
}
class ItemChangeTester : public QGraphicsRectItem
{
public:
ItemChangeTester(QGraphicsItem *parent = nullptr) : QGraphicsRectItem(parent)
{
setFlag(ItemSendsGeometryChanges);
clear();
}
void clear()
{
itemChangeReturnValue = QVariant();
itemSceneChangeTargetScene = nullptr;
changes.clear();
values.clear();
oldValues.clear();
}
QVariant itemChangeReturnValue;
QGraphicsScene *itemSceneChangeTargetScene;
QList<GraphicsItemChange> changes;
QVariantList values;
QVariantList oldValues;
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
{
changes << change;
values << value;
switch (change) {
case QGraphicsItem::ItemPositionChange:
oldValues << pos();
break;
case QGraphicsItem::ItemPositionHasChanged:
break;
case QGraphicsItem::ItemTransformChange: {
QVariant variant;
variant.setValue(transform());
oldValues << variant;
}
break;
case QGraphicsItem::ItemTransformHasChanged:
break;
case QGraphicsItem::ItemVisibleChange:
oldValues << isVisible();
break;
case QGraphicsItem::ItemVisibleHasChanged:
break;
case QGraphicsItem::ItemEnabledChange:
oldValues << isEnabled();
break;
case QGraphicsItem::ItemEnabledHasChanged:
break;
case QGraphicsItem::ItemSelectedChange:
oldValues << isSelected();
break;
case QGraphicsItem::ItemSelectedHasChanged:
break;
case QGraphicsItem::ItemParentChange:
oldValues << QVariant::fromValue<void *>(parentItem());
break;
case QGraphicsItem::ItemParentHasChanged:
break;
case QGraphicsItem::ItemChildAddedChange:
oldValues << childItems().size();
break;
case QGraphicsItem::ItemChildRemovedChange:
oldValues << childItems().size();
break;
case QGraphicsItem::ItemSceneChange:
oldValues << QVariant::fromValue<QGraphicsScene *>(scene());
if (itemSceneChangeTargetScene
&& qvariant_cast<QGraphicsScene *>(value)
&& itemSceneChangeTargetScene != qvariant_cast<QGraphicsScene *>(value)) {
return QVariant::fromValue<QGraphicsScene *>(itemSceneChangeTargetScene);
}
return value;
case QGraphicsItem::ItemSceneHasChanged:
break;
case QGraphicsItem::ItemCursorChange:
#ifndef QT_NO_CURSOR
oldValues << cursor();
#endif
break;
case QGraphicsItem::ItemCursorHasChanged:
break;
case QGraphicsItem::ItemToolTipChange:
oldValues << toolTip();
break;
case QGraphicsItem::ItemToolTipHasChanged:
break;
case QGraphicsItem::ItemFlagsChange:
oldValues << quint32(flags());
break;
case QGraphicsItem::ItemFlagsHaveChanged:
break;
case QGraphicsItem::ItemZValueChange:
oldValues << zValue();
break;
case QGraphicsItem::ItemZValueHasChanged:
break;
case QGraphicsItem::ItemOpacityChange:
oldValues << opacity();
break;
case QGraphicsItem::ItemOpacityHasChanged:
break;
case QGraphicsItem::ItemScenePositionHasChanged:
break;
case QGraphicsItem::ItemRotationChange:
oldValues << rotation();
break;
case QGraphicsItem::ItemRotationHasChanged:
break;
case QGraphicsItem::ItemScaleChange:
oldValues << scale();
break;
case QGraphicsItem::ItemScaleHasChanged:
break;
case QGraphicsItem::ItemTransformOriginPointChange:
oldValues << transformOriginPoint();
break;
case QGraphicsItem::ItemTransformOriginPointHasChanged:
break;
}
return itemChangeReturnValue.isValid() ? itemChangeReturnValue : value;
}
};
void tst_QGraphicsItem::itemChange()
{
ItemChangeTester tester;
tester.itemSceneChangeTargetScene = nullptr;
ItemChangeTester testerHelper;
QVERIFY(tester.changes.isEmpty());
QVERIFY(tester.values.isEmpty());
int changeCount = 0;
{
// ItemEnabledChange
tester.itemChangeReturnValue = true;
tester.setEnabled(false);
++changeCount;
++changeCount; // HasChanged
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemEnabledChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemEnabledHasChanged);
QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(false));
QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(true));
QCOMPARE(tester.oldValues.constLast(), QVariant(true));
QCOMPARE(tester.isEnabled(), true);
}
{
// ItemTransformChange / ItemTransformHasChanged
tester.itemChangeReturnValue.setValue(QTransform().rotate(90));
tester.setTransform(QTransform::fromTranslate(50, 0), true);
++changeCount; // notification sent too
++changeCount;
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemTransformChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemTransformHasChanged);
QCOMPARE(qvariant_cast<QTransform>(tester.values.at(tester.values.size() - 2)),
QTransform().translate(50, 0));
QCOMPARE(qvariant_cast<QTransform>(tester.values.at(tester.values.size() - 1)),
QTransform().rotate(90));
QVariant variant;
variant.setValue(QTransform());
QCOMPARE(tester.oldValues.constLast(), variant);
QCOMPARE(tester.transform(), QTransform().rotate(90));
}
{
// ItemPositionChange / ItemPositionHasChanged
tester.itemChangeReturnValue = QPointF(42, 0);
tester.setPos(0, 42);
++changeCount; // notification sent too
++changeCount;
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemPositionChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemPositionHasChanged);
QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(QPointF(0, 42)));
QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(QPointF(42, 0)));
QCOMPARE(tester.oldValues.constLast(), QVariant(QPointF()));
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.constLast(), QVariant(qreal(0.0)));
QCOMPARE(tester.zValue(), qreal(2.0));
}
{
// ItemRotationChange / ItemRotationHasChanged
tester.itemChangeReturnValue = qreal(15.0);
tester.setRotation(10.0);
++changeCount; // notification sent too
++changeCount;
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemRotationChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemRotationHasChanged);
QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(10.0)));
QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(15.0)));
QCOMPARE(tester.oldValues.constLast(), QVariant(qreal(0.0)));
QCOMPARE(tester.rotation(), qreal(15.0));
}
{
// ItemScaleChange / ItemScaleHasChanged
tester.itemChangeReturnValue = qreal(2.0);
tester.setScale(1.5);
++changeCount; // notification sent too
++changeCount;
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemScaleChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemScaleHasChanged);
QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(1.5)));
QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(2.0)));
QCOMPARE(tester.oldValues.constLast(), QVariant(qreal(1.0)));
QCOMPARE(tester.scale(), qreal(2.0));
}
{
// ItemTransformOriginPointChange / ItemTransformOriginPointHasChanged
tester.itemChangeReturnValue = QPointF(2.0, 2.0);
tester.setTransformOriginPoint(1.0, 1.0);
++changeCount; // notification sent too
++changeCount;
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemTransformOriginPointChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemTransformOriginPointHasChanged);
QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(QPointF(1.0, 1.0)));
QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(QPointF(2.0, 2.0)));
QCOMPARE(tester.oldValues.constLast(), QVariant(QPointF(0.0, 0.0)));
QCOMPARE(tester.transformOriginPoint(), QPointF(2.0, 2.0));
}
{
// ItemFlagsChange
tester.itemChangeReturnValue = QGraphicsItem::ItemIsSelectable;
tester.setFlag(QGraphicsItem::ItemIsSelectable, false);
QCOMPARE(tester.changes.size(), changeCount); // No change
tester.setFlag(QGraphicsItem::ItemIsSelectable, true);
++changeCount;
++changeCount; // ItemFlagsHasChanged
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);
QVariant expectedFlags = QVariant::fromValue<quint32>(QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges));
QCOMPARE(tester.values.at(tester.values.size() - 2), expectedFlags);
QCOMPARE(tester.values.at(tester.values.size() - 1),
QVariant::fromValue<quint32>(quint32(QGraphicsItem::ItemIsSelectable)));
}
{
// ItemSelectedChange
tester.setSelected(false);
QCOMPARE(tester.changes.size(), changeCount); // No change :-)
tester.itemChangeReturnValue = true;
tester.setSelected(true);
++changeCount;
++changeCount; // ItemSelectedHasChanged
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSelectedChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSelectedHasChanged);
QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(true));
QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(true));
QCOMPARE(tester.oldValues.constLast(), QVariant(false));
QCOMPARE(tester.isSelected(), true);
tester.itemChangeReturnValue = false;
tester.setSelected(true);
// the value hasn't changed to the itemChange return value
// bacause itemChange is never called (true -> true is a noop).
QCOMPARE(tester.isSelected(), true);
}
{
// ItemVisibleChange
tester.itemChangeReturnValue = false;
QVERIFY(tester.isVisible());
tester.setVisible(false);
++changeCount; // ItemVisibleChange
++changeCount; // ItemSelectedChange
++changeCount; // ItemSelectedHasChanged
++changeCount; // ItemVisibleHasChanged
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 4), QGraphicsItem::ItemVisibleChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 3), QGraphicsItem::ItemSelectedChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSelectedHasChanged);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemVisibleHasChanged);
QCOMPARE(tester.values.at(tester.values.size() - 4), QVariant(false));
QCOMPARE(tester.values.at(tester.values.size() - 3), QVariant(false));
QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(false));
QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(false));
QCOMPARE(tester.isVisible(), false);
}
{
// ItemParentChange
tester.itemChangeReturnValue.setValue(static_cast<QGraphicsItem *>(nullptr));
tester.setParentItem(&testerHelper);
QCOMPARE(tester.changes.size(), ++changeCount);
QCOMPARE(tester.changes.constLast(), QGraphicsItem::ItemParentChange);
QCOMPARE(qvariant_cast<QGraphicsItem *>(tester.values.last()), &testerHelper);
QCOMPARE(qvariant_cast<QGraphicsItem *>(tester.oldValues.last()), nullptr);
QCOMPARE(tester.parentItem(), nullptr);
}
{
// ItemOpacityChange
tester.itemChangeReturnValue = 1.0;
tester.setOpacity(0.7);
QCOMPARE(tester.changes.size(), ++changeCount);
QCOMPARE(tester.changes.constLast(), QGraphicsItem::ItemOpacityChange);
QVERIFY(qFuzzyCompare(qreal(tester.values.last().toDouble()), qreal(0.7)));
QCOMPARE(tester.oldValues.last().toDouble(), double(1.0));
QCOMPARE(tester.opacity(), qreal(1.0));
tester.itemChangeReturnValue = 0.7;
tester.setOpacity(0.7);
++changeCount; // ItemOpacityChange
++changeCount; // ItemOpacityHasChanged
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemOpacityChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemOpacityHasChanged);
QCOMPARE(tester.opacity(), qreal(0.7));
}
{
// ItemChildAddedChange
tester.itemChangeReturnValue.clear();
testerHelper.setParentItem(&tester);
QCOMPARE(tester.changes.size(), ++changeCount);
QCOMPARE(tester.changes.constLast(), QGraphicsItem::ItemChildAddedChange);
QCOMPARE(qvariant_cast<QGraphicsItem *>(tester.values.last()), &testerHelper);
}
{
// ItemChildRemovedChange 1
testerHelper.setParentItem(nullptr);
QCOMPARE(tester.changes.size(), ++changeCount);
QCOMPARE(tester.changes.constLast(), QGraphicsItem::ItemChildRemovedChange);
QCOMPARE(qvariant_cast<QGraphicsItem *>(tester.values.last()), &testerHelper);
// ItemChildRemovedChange 1
ItemChangeTester *test = new ItemChangeTester;
test->itemSceneChangeTargetScene = nullptr;
int count = 0;
QGraphicsScene *scene = new QGraphicsScene;
scene->addItem(test);
count = test->changes.size();
//We test here the fact that when a child is deleted the parent receive only one ItemChildRemovedChange
QGraphicsRectItem *child = new QGraphicsRectItem(test);
//We received ItemChildAddedChange
QCOMPARE(test->changes.size(), ++count);
QCOMPARE(test->changes.constLast(), QGraphicsItem::ItemChildAddedChange);
delete child;
child = nullptr;
QCOMPARE(test->changes.size(), ++count);
QCOMPARE(test->changes.constLast(), QGraphicsItem::ItemChildRemovedChange);
ItemChangeTester *childTester = new ItemChangeTester(test);
//Changes contains all sceneHasChanged and so on, we don't want to test that
int childCount = childTester->changes.size();
//We received ItemChildAddedChange
QCOMPARE(test->changes.size(), ++count);
child = new QGraphicsRectItem(childTester);
//We received ItemChildAddedChange
QCOMPARE(childTester->changes.size(), ++childCount);
QCOMPARE(childTester->changes.constLast(), QGraphicsItem::ItemChildAddedChange);
//Delete the child of the top level with all its children
delete childTester;
//Only one removal
QCOMPARE(test->changes.size(), ++count);
QCOMPARE(test->changes.constLast(), QGraphicsItem::ItemChildRemovedChange);
delete scene;
}
{
// ItemChildRemovedChange 2
ItemChangeTester parent;
ItemChangeTester *child = new ItemChangeTester;
child->setParentItem(&parent);
QCOMPARE(parent.changes.constLast(), QGraphicsItem::ItemChildAddedChange);
QCOMPARE(qvariant_cast<QGraphicsItem *>(parent.values.last()), child);
delete child;
QCOMPARE(parent.changes.constLast(), QGraphicsItem::ItemChildRemovedChange);
QCOMPARE(qvariant_cast<QGraphicsItem *>(parent.values.last()), child);
}
{
// !!! Note: If this test crashes because of double-deletion, there's
// a bug somewhere in QGraphicsScene or QGraphicsItem.
// ItemSceneChange
QGraphicsScene scene;
QGraphicsScene scene2;
scene.addItem(&tester);
++changeCount; // ItemSceneChange (scene)
++changeCount; // ItemSceneHasChanged (scene)
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.scene(), &scene);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged);
// Item's old value was 0
// Item's current value is scene
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.last()), nullptr);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.last()), &scene);
scene2.addItem(&tester);
++changeCount; // ItemSceneChange (0) was: (scene)
++changeCount; // ItemSceneHasChanged (0)
++changeCount; // ItemSceneChange (scene2) was: (0)
++changeCount; // ItemSceneHasChanged (scene2)
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.scene(), &scene2);
QCOMPARE(tester.changes.at(tester.changes.size() - 4), QGraphicsItem::ItemSceneChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 3), QGraphicsItem::ItemSceneHasChanged);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged);
// Item's last old value was scene
// Item's last current value is 0
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.at(tester.oldValues.size() - 2)), &scene);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.at(tester.oldValues.size() - 1)), nullptr);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 4)), nullptr);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 3)), nullptr);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), &scene2);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), &scene2);
// Item's last old value was 0
// Item's last current value is scene2
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.last()), nullptr);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.last()), &scene2);
scene2.removeItem(&tester);
++changeCount; // ItemSceneChange (0) was: (scene2)
++changeCount; // ItemSceneHasChanged (0)
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.scene(), nullptr);
QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange);
QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged);
// Item's last old value was scene2
// Item's last current value is 0
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.oldValues.last()), &scene2);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), nullptr);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), nullptr);
tester.itemSceneChangeTargetScene = &scene;
scene2.addItem(&tester);
++changeCount; // ItemSceneChange (scene2) was: (0)
++changeCount; // ItemSceneChange (scene) was: (0)
++changeCount; // ItemSceneHasChanged (scene)
QCOMPARE(tester.values.size(), changeCount);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 3)), &scene2);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), &scene);
QCOMPARE(qvariant_cast<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), &scene);
QCOMPARE(tester.scene(), &scene);
tester.itemSceneChangeTargetScene = nullptr;
tester.itemChangeReturnValue = QVariant();
scene.removeItem(&tester);
++changeCount; // ItemSceneChange
++changeCount; // ItemSceneHasChanged
QCOMPARE(tester.scene(), nullptr);
}
{
// ItemToolTipChange/ItemToolTipHasChanged
const QString toolTip(QLatin1String("I'm soo cool"));
const QString overridenToolTip(QLatin1String("No, you are not soo cool"));
tester.itemChangeReturnValue = overridenToolTip;
tester.setToolTip(toolTip);
++changeCount; // ItemToolTipChange
++changeCount; // ItemToolTipHasChanged
QCOMPARE(tester.changes.size(), changeCount);
QCOMPARE(tester.changes.at(changeCount - 2), QGraphicsItem::ItemToolTipChange);
QCOMPARE(tester.values.at(changeCount - 2).toString(), toolTip);
QCOMPARE(tester.changes.at(changeCount - 1), QGraphicsItem::ItemToolTipHasChanged);
QCOMPARE(tester.values.at(changeCount - 1).toString(), overridenToolTip);
QCOMPARE(tester.toolTip(), overridenToolTip);
tester.itemChangeReturnValue = QVariant();
}
}
class EventFilterTesterItem : public QGraphicsLineItem
{
public:
QList<QEvent::Type> filteredEvents;
GraphicsItems filteredEventReceivers;
QList<QEvent::Type> receivedEvents;
bool handlesSceneEvents = false;
protected:
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
{
filteredEvents << event->type();
filteredEventReceivers << watched;
return handlesSceneEvents;
}
bool sceneEvent(QEvent *event) override
{
return QGraphicsLineItem::sceneEvent(event);
}
};
void tst_QGraphicsItem::sceneEventFilter()
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QSKIP("Window activation is not supported");
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QApplicationPrivate::setActiveWindow(&view);
QVERIFY(QTest::qWaitForWindowExposed(&view));
QVERIFY(QTest::qWaitForWindowActive(&view));
QGraphicsTextItem *text1 = scene.addText(QLatin1String("Text1"));
QGraphicsTextItem *text2 = scene.addText(QLatin1String("Text2"));
QGraphicsTextItem *text3 = scene.addText(QLatin1String("Text3"));
text1->setFlag(QGraphicsItem::ItemIsFocusable);
text2->setFlag(QGraphicsItem::ItemIsFocusable);
text3->setFlag(QGraphicsItem::ItemIsFocusable);
EventFilterTesterItem *tester = new EventFilterTesterItem;
scene.addItem(tester);
QTRY_VERIFY(!text1->hasFocus());
text1->installSceneEventFilter(tester);
text1->setFocus();
QTRY_VERIFY(text1->hasFocus());
QCOMPARE(tester->filteredEvents.size(), 1);
QCOMPARE(tester->filteredEvents.at(0), QEvent::FocusIn);
QCOMPARE(tester->filteredEventReceivers.at(0), static_cast<QGraphicsItem *>(text1));
text2->installSceneEventFilter(tester);
text3->installSceneEventFilter(tester);
text2->setFocus();
text3->setFocus();
QCOMPARE(tester->filteredEvents.size(), 5);
QCOMPARE(tester->filteredEvents.at(1), QEvent::FocusOut);
QCOMPARE(tester->filteredEventReceivers.at(1), static_cast<QGraphicsItem *>(text1));
QCOMPARE(tester->filteredEvents.at(2), QEvent::FocusIn);
QCOMPARE(tester->filteredEventReceivers.at(2), static_cast<QGraphicsItem *>(text2));
QCOMPARE(tester->filteredEvents.at(3), QEvent::FocusOut);
QCOMPARE(tester->filteredEventReceivers.at(3), static_cast<QGraphicsItem *>(text2));
QCOMPARE(tester->filteredEvents.at(4), QEvent::FocusIn);
QCOMPARE(tester->filteredEventReceivers.at(4), static_cast<QGraphicsItem *>(text3));
text1->removeSceneEventFilter(tester);
text1->setFocus();
QCOMPARE(tester->filteredEvents.size(), 6);
QCOMPARE(tester->filteredEvents.at(5), QEvent::FocusOut);
QCOMPARE(tester->filteredEventReceivers.at(5), static_cast<QGraphicsItem *>(text3));
tester->handlesSceneEvents = true;
text2->setFocus();
QCOMPARE(tester->filteredEvents.size(), 7);
QCOMPARE(tester->filteredEvents.at(6), QEvent::FocusIn);
QCOMPARE(tester->filteredEventReceivers.at(6), static_cast<QGraphicsItem *>(text2));
QVERIFY(text2->hasFocus());
//Let check if the items are correctly removed from the sceneEventFilters array
//to avoid stale pointers.
QGraphicsView gv;
QGraphicsScene *anotherScene = new QGraphicsScene;
QGraphicsTextItem *ti = anotherScene->addText("This is a test #1");
ti->moveBy(50, 50);
QGraphicsTextItem *ti2 = anotherScene->addText("This is a test #2");
QGraphicsTextItem *ti3 = anotherScene->addText("This is a test #3");
gv.setScene(anotherScene);
gv.show();
QVERIFY(QTest::qWaitForWindowExposed(&gv));
ti->installSceneEventFilter(ti2);
ti3->installSceneEventFilter(ti);
delete ti2;
//we souldn't crash
QTest::mouseMove(gv.viewport(), gv.mapFromScene(ti->scenePos()));
QTest::qWait(30);
delete ti;
}
class GeometryChanger : public QGraphicsRectItem
{
public:
explicit GeometryChanger(QRectF r) : QGraphicsRectItem(r) {}
void changeGeometry()
{ prepareGeometryChange(); }
};
void tst_QGraphicsItem::prepareGeometryChange()
{
{
QGraphicsScene scene;
const QRectF rect(0, 0, 100, 100);
GeometryChanger item(rect);
scene.addItem(&item);
QVERIFY(scene.items(rect).contains(&item));
item.changeGeometry();
QVERIFY(scene.items(rect).contains(&item));
}
}
class PaintTester : public QGraphicsRectItem
{
public:
PaintTester() { setRect(QRectF(10, 10, 20, 20));}
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *w) override
{
widget = w;
painted++;
}
QWidget *widget = nullptr;
int painted = 0;
};
void tst_QGraphicsItem::paint()
{
QGraphicsScene scene;
PaintTester paintTester;
scene.addItem(&paintTester);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QApplication::processEvents();
#ifdef Q_OS_WIN32
//we try to switch the desktop: if it fails, we skip the test
if (::SwitchDesktop( ::GetThreadDesktop( ::GetCurrentThreadId() ) ) == 0) {
QSKIP("The Graphics View doesn't get the paint events");
}
#endif
QTRY_COMPARE(paintTester.widget, view.viewport());
view.hide();
QGraphicsScene scene2;
QGraphicsView view2(&scene2);
view2.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view2.show();
QVERIFY(QTest::qWaitForWindowExposed(&view2));
QCoreApplication::processEvents(); // Process all queued paint events
PaintTester tester2;
scene2.addItem(&tester2);
//First show at least one paint
QCOMPARE(tester2.painted, 0);
QTRY_VERIFY(tester2.painted > 0);
int painted = tester2.painted;
//nominal case, update call paint
tester2.update();
QTRY_COMPARE(tester2.painted, painted + 1);
painted = tester2.painted;
//we remove the item from the scene, number of updates is still the same
tester2.update();
scene2.removeItem(&tester2);
QTRY_COMPARE(tester2.painted, painted);
//We re-add the item, the number of paint should increase
scene2.addItem(&tester2);
tester2.update();
QTRY_COMPARE(tester2.painted, painted + 1);
}
class HarakiriItem : public QGraphicsRectItem
{
public:
HarakiriItem(int harakiriPoint)
: QGraphicsRectItem(QRectF(0, 0, 100, 100)), harakiri(harakiriPoint)
{ dead = false; }
static bool dead;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
QGraphicsRectItem::paint(painter, option, widget);
if (harakiri == 0) {
// delete unsupported since 4.5
/*
dead = true;
delete this;
*/
}
}
void advance(int n) override
{
if (harakiri == 1 && n == 0) {
// delete unsupported
/*
dead = true;
delete this;
*/
}
if (harakiri == 2 && n == 1) {
dead = true;
delete this;
}
}
protected:
#ifndef QT_NO_CONTEXTMENU
void contextMenuEvent(QGraphicsSceneContextMenuEvent *) override
{
if (harakiri == 3) {
dead = true;
delete this;
}
}
#endif // QT_NO_CONTEXTMENU
void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override
{
// ??
QGraphicsRectItem::dragEnterEvent(event);
}
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) override
{
// ??
QGraphicsRectItem::dragLeaveEvent(event);
}
void dragMoveEvent(QGraphicsSceneDragDropEvent *event) override
{
// ??
QGraphicsRectItem::dragMoveEvent(event);
}
void dropEvent(QGraphicsSceneDragDropEvent *event) override
{
// ??
QGraphicsRectItem::dropEvent(event);
}
void focusInEvent(QFocusEvent *) override
{
if (harakiri == 4) {
dead = true;
delete this;
}
}
void focusOutEvent(QFocusEvent *) override
{
if (harakiri == 5) {
dead = true;
delete this;
}
}
void hoverEnterEvent(QGraphicsSceneHoverEvent *) override
{
if (harakiri == 6) {
dead = true;
delete this;
}
}
void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override
{
if (harakiri == 7) {
dead = true;
delete this;
}
}
void hoverMoveEvent(QGraphicsSceneHoverEvent *) override
{
if (harakiri == 8) {
dead = true;
delete this;
}
}
void inputMethodEvent(QInputMethodEvent *event) override
{
// ??
QGraphicsRectItem::inputMethodEvent(event);
}
QVariant inputMethodQuery(Qt::InputMethodQuery query) const override
{
// ??
return QGraphicsRectItem::inputMethodQuery(query);
}
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
{
// deletion not supported
return QGraphicsRectItem::itemChange(change, value);
}
void keyPressEvent(QKeyEvent *) override
{
if (harakiri == 9) {
dead = true;
delete this;
}
}
void keyReleaseEvent(QKeyEvent *) override
{
if (harakiri == 10) {
dead = true;
delete this;
}
}
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override
{
if (harakiri == 11) {
dead = true;
delete this;
}
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *) override
{
if (harakiri == 12) {
dead = true;
delete this;
}
}
void mousePressEvent(QGraphicsSceneMouseEvent *) override
{
if (harakiri == 13) {
dead = true;
delete this;
}
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override
{
if (harakiri == 14) {
dead = true;
delete this;
}
}
bool sceneEvent(QEvent *event) override
{
// deletion not supported
return QGraphicsRectItem::sceneEvent(event);
}
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
{
// deletion not supported
return QGraphicsRectItem::sceneEventFilter(watched, event);
}
void wheelEvent(QGraphicsSceneWheelEvent *) override
{
if (harakiri == 16) {
dead = true;
delete this;
}
}
private:
int harakiri;
};
bool HarakiriItem::dead;
void tst_QGraphicsItem::deleteItemInEventHandlers()
{
for (int i = 0; i < 17; ++i) {
QGraphicsScene scene;
HarakiriItem *item = new HarakiriItem(i);
item->setAcceptHoverEvents(true);
item->setFlag(QGraphicsItem::ItemIsFocusable);
scene.addItem(item);
item->installSceneEventFilter(item); // <- ehey!
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QCoreApplication::processEvents();
QCoreApplication::processEvents();
if (!HarakiriItem::dead)
scene.advance();
#ifndef QT_NO_CONTEXTMENU
if (!HarakiriItem::dead) {
auto viewPos = view.mapFromScene(item->scenePos());
QContextMenuEvent event(QContextMenuEvent::Other, viewPos,
view.mapToGlobal(viewPos));
QCoreApplication::sendEvent(view.viewport(), &event);
}
#endif // QT_NO_CONTEXTMENU
if (!HarakiriItem::dead)
QTest::mouseMove(view.viewport(), view.mapFromScene(item->scenePos()));
if (!HarakiriItem::dead)
QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item->scenePos()));
if (!HarakiriItem::dead)
QTest::mouseDClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(item->scenePos()));
if (!HarakiriItem::dead)
QTest::mouseClick(view.viewport(), Qt::RightButton, {}, view.mapFromScene(item->scenePos()));
if (!HarakiriItem::dead)
QTest::mouseMove(view.viewport(), view.mapFromScene(item->scenePos() + QPointF(20, -20)));
if (!HarakiriItem::dead)
item->setFocus();
if (!HarakiriItem::dead)
item->clearFocus();
if (!HarakiriItem::dead)
item->setFocus();
if (!HarakiriItem::dead)
QTest::keyPress(view.viewport(), Qt::Key_A);
if (!HarakiriItem::dead)
QTest::keyRelease(view.viewport(), Qt::Key_A);
if (!HarakiriItem::dead)
QTest::keyPress(view.viewport(), Qt::Key_A);
if (!HarakiriItem::dead)
QTest::keyRelease(view.viewport(), Qt::Key_A);
}
}
class ItemPaintsOutsideShape : public QGraphicsItem
{
public:
QRectF boundingRect() const override
{
return QRectF(0, 0, 100, 100);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
{
painter->fillRect(-50, -50, 200, 200, Qt::red);
painter->fillRect(0, 0, 100, 100, Qt::blue);
}
};
void tst_QGraphicsItem::itemClipsToShape()
{
QGraphicsItem *clippedItem = new ItemPaintsOutsideShape;
clippedItem->setFlag(QGraphicsItem::ItemClipsToShape);
QGraphicsItem *unclippedItem = new ItemPaintsOutsideShape;
unclippedItem->setPos(200, 0);
QGraphicsScene scene(-50, -50, 400, 200);
scene.addItem(clippedItem);
scene.addItem(unclippedItem);
QImage image(400, 200, QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
scene.render(&painter);
painter.end();
QCOMPARE(image.pixel(45, 100), QRgb(0));
QCOMPARE(image.pixel(100, 45), QRgb(0));
QCOMPARE(image.pixel(155, 100), QRgb(0));
QCOMPARE(image.pixel(45, 155), QRgb(0));
QCOMPARE(image.pixel(55, 100), QColor(Qt::blue).rgba());
QCOMPARE(image.pixel(100, 55), QColor(Qt::blue).rgba());
QCOMPARE(image.pixel(145, 100), QColor(Qt::blue).rgba());
QCOMPARE(image.pixel(55, 145), QColor(Qt::blue).rgba());
QCOMPARE(image.pixel(245, 100), QColor(Qt::red).rgba());
QCOMPARE(image.pixel(300, 45), QColor(Qt::red).rgba());
QCOMPARE(image.pixel(355, 100), QColor(Qt::red).rgba());
QCOMPARE(image.pixel(245, 155), QColor(Qt::red).rgba());
QCOMPARE(image.pixel(255, 100), QColor(Qt::blue).rgba());
QCOMPARE(image.pixel(300, 55), QColor(Qt::blue).rgba());
QCOMPARE(image.pixel(345, 100), QColor(Qt::blue).rgba());
QCOMPARE(image.pixel(255, 145), QColor(Qt::blue).rgba());
}
void tst_QGraphicsItem::itemClipsChildrenToShape()
{
QGraphicsScene scene;
QGraphicsItem *rect = scene.addRect(0, 0, 50, 50, QPen(Qt::NoPen), QBrush(Qt::yellow));
QGraphicsItem *ellipse = scene.addEllipse(0, 0, 100, 100, QPen(Qt::NoPen), QBrush(Qt::green));
ellipse->setParentItem(rect);
QGraphicsItem *clippedEllipse = scene.addEllipse(0, 0, 50, 50, QPen(Qt::NoPen), QBrush(Qt::blue));
clippedEllipse->setParentItem(ellipse);
QGraphicsItem *clippedEllipse2 = scene.addEllipse(0, 0, 25, 25, QPen(Qt::NoPen), QBrush(Qt::red));
clippedEllipse2->setParentItem(clippedEllipse);
QGraphicsItem *clippedEllipse3 = scene.addEllipse(50, 50, 25, 25, QPen(Qt::NoPen), QBrush(Qt::red));
clippedEllipse3->setParentItem(clippedEllipse);
QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape));
ellipse->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
QVERIFY((ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape));
QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
scene.render(&painter);
painter.end();
QCOMPARE(image.pixel(16, 16), QColor(255, 0, 0).rgba());
QCOMPARE(image.pixel(32, 32), QColor(0, 0, 255).rgba());
QCOMPARE(image.pixel(50, 50), QColor(0, 255, 0).rgba());
QCOMPARE(image.pixel(12, 12), QColor(255, 255, 0).rgba());
QCOMPARE(image.pixel(60, 60), QColor(255, 0, 0).rgba());
}
void tst_QGraphicsItem::itemClipsChildrenToShape2()
{
QGraphicsRectItem *parent = new QGraphicsRectItem(QRectF(0, 0, 10, 10));
QGraphicsEllipseItem *child1 = new QGraphicsEllipseItem(QRectF(50, 50, 100, 100));
QGraphicsRectItem *child2 = new QGraphicsRectItem(QRectF(15, 15, 80, 80));
child1->setParentItem(parent);
child1->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
child2->setParentItem(child1);
parent->setBrush(Qt::blue);
child1->setBrush(Qt::green);
child2->setBrush(Qt::red);
QGraphicsScene scene;
scene.addItem(parent);
QCOMPARE(scene.items(QPointF(5, 5)).value(0, nullptr), parent);
QVERIFY(scene.items(QPointF(15, 5)).isEmpty());
QVERIFY(scene.items(QPointF(5, 15)).isEmpty());
QVERIFY(scene.items(QPointF(60, 60)).isEmpty());
QVERIFY(scene.items(QPointF(140, 60)).isEmpty());
QVERIFY(scene.items(QPointF(60, 140)).isEmpty());
QVERIFY(scene.items(QPointF(140, 140)).isEmpty());
QCOMPARE(scene.items(QPointF(75, 75)).value(0, nullptr), child2);
QCOMPARE(scene.items(QPointF(75, 100)).value(0, nullptr), child1);
QCOMPARE(scene.items(QPointF(100, 75)).value(0, nullptr), child1);
QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
image.fill(0);
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));
QCOMPARE(image.pixel(40, 40), QRgb(0));
QCOMPARE(image.pixel(90, 40), QRgb(0));
QCOMPARE(image.pixel(40, 90), QRgb(0));
QCOMPARE(image.pixel(95, 95), QRgb(0));
QCOMPARE(image.pixel(50, 70), QColor(0, 255, 0).rgba());
QCOMPARE(image.pixel(70, 50), QColor(0, 255, 0).rgba());
QCOMPARE(image.pixel(50, 60), QColor(255, 0, 0).rgba());
QCOMPARE(image.pixel(60, 50), QColor(255, 0, 0).rgba());
}
void tst_QGraphicsItem::itemClipsChildrenToShape3()
{
// Construct a scene with nested children, each 50 pixels offset from the elder.
// Set a top-level clipping flag
QGraphicsScene scene;
QGraphicsRectItem *parent = scene.addRect( 0, 0, 150, 150 );
QGraphicsRectItem *child = scene.addRect( 0, 0, 150, 150 );
QGraphicsRectItem *grandchild = scene.addRect( 0, 0, 150, 150 );
child->setParentItem(parent);
grandchild->setParentItem(child);
child->setPos( 50, 50 );
grandchild->setPos( 50, 50 );
parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
QCOMPARE(scene.items(QPointF(25, 25)).value(0, nullptr), parent);
QCOMPARE(scene.items(QPointF(75, 75)).value(0, nullptr), child);
QCOMPARE(scene.items(QPointF(125, 125)).value(0, nullptr), grandchild);
QVERIFY(scene.items(QPointF(175, 175)).isEmpty());
// Move child to fully overlap the parent. The grandchild should
// now occupy two-thirds of the scene
child->prepareGeometryChange();
child->setPos( 0, 0 );
QCOMPARE(scene.items(QPointF(25, 25)).value(0, nullptr), child);
QCOMPARE(scene.items(QPointF(75, 75)).value(0, nullptr), grandchild);
QCOMPARE(scene.items(QPointF(125, 125)).value(0, nullptr), grandchild);
QVERIFY(scene.items(QPointF(175, 175)).isEmpty());
}
class MyProxyWidget : public QGraphicsProxyWidget
{
public:
using QGraphicsProxyWidget::QGraphicsProxyWidget;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
QGraphicsProxyWidget::paint(painter, option, widget);
painted = true;
}
bool painted = false;
};
void tst_QGraphicsItem::itemClipsChildrenToShape4()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
QGraphicsWidget * outerWidget = new QGraphicsWidget();
outerWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
MyProxyWidget * innerWidget = new MyProxyWidget(outerWidget);
QLabel * label = new QLabel();
label->setText("Welcome back my friends to the show that never ends...");
innerWidget->setWidget(label);
view.resize(300, 300);
scene.addItem(outerWidget);
outerWidget->resize( 200, 100 );
scene.addEllipse( 100, 100, 100, 50 ); // <-- this is important to trigger the right codepath*
//now the label is shown
outerWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false );
QApplicationPrivate::setActiveWindow(&view);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QTRY_COMPARE(QApplication::activeWindow(), &view);
QTRY_COMPARE(innerWidget->painted, true);
}
//#define DEBUG_ITEM_CLIPS_CHILDREN_TO_SHAPE_5
static inline void renderSceneToImage(QGraphicsScene *scene, QImage *image, const QString &filename)
{
image->fill(0);
QPainter painter(image);
scene->render(&painter);
painter.end();
#ifdef DEBUG_ITEM_CLIPS_CHILDREN_TO_SHAPE_5
image->save(filename);
#else
Q_UNUSED(filename);
#endif
}
void tst_QGraphicsItem::itemClipsChildrenToShape5()
{
class ParentItem : public QGraphicsRectItem
{
public:
using QGraphicsRectItem::QGraphicsRectItem;
QPainterPath shape() const override
{
QPainterPath path;
path.addRect(50, 50, 200, 200);
return path;
}
};
ParentItem *parent = new ParentItem(0, 0, 300, 300);
parent->setBrush(Qt::blue);
parent->setOpacity(0.5);
const QRegion parentRegion(0, 0, 300, 300);
const QRegion clippedParentRegion = parentRegion & QRect(50, 50, 200, 200);
QRegion childRegion;
QRegion grandChildRegion;
QGraphicsRectItem *topLeftChild = new QGraphicsRectItem(0, 0, 100, 100);
topLeftChild->setBrush(Qt::red);
topLeftChild->setParentItem(parent);
childRegion += QRect(0, 0, 100, 100);
QGraphicsRectItem *topRightChild = new QGraphicsRectItem(0, 0, 100, 100);
topRightChild->setBrush(Qt::red);
topRightChild->setParentItem(parent);
topRightChild->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
topRightChild->setPos(200, 0);
childRegion += QRect(200, 0, 100, 100);
QGraphicsRectItem *topRightGrandChild = new QGraphicsRectItem(0, 0, 100, 100);
topRightGrandChild->setBrush(Qt::green);
topRightGrandChild->setParentItem(topRightChild);
topRightGrandChild->setPos(-40, 40);
grandChildRegion += QRect(200 - 40, 0 + 40, 100, 100) & QRect(200, 0, 100, 100);
QGraphicsRectItem *bottomLeftChild = new QGraphicsRectItem(0, 0, 100, 100);
bottomLeftChild->setBrush(Qt::red);
bottomLeftChild->setParentItem(parent);
bottomLeftChild->setFlag(QGraphicsItem::ItemClipsToShape);
bottomLeftChild->setPos(0, 200);
childRegion += QRect(0, 200, 100, 100);
QGraphicsRectItem *bottomLeftGrandChild = new QGraphicsRectItem(0, 0, 160, 160);
bottomLeftGrandChild->setBrush(Qt::green);
bottomLeftGrandChild->setParentItem(bottomLeftChild);
bottomLeftGrandChild->setFlag(QGraphicsItem::ItemClipsToShape);
bottomLeftGrandChild->setPos(0, -60);
grandChildRegion += QRect(0, 200 - 60, 160, 160);
QGraphicsRectItem *bottomRightChild = new QGraphicsRectItem(0, 0, 100, 100);
bottomRightChild->setBrush(Qt::red);
bottomRightChild->setParentItem(parent);
bottomRightChild->setPos(200, 200);
childRegion += QRect(200, 200, 100, 100);
const QPoint controlPoints[17] = {
{5, 5}, {95, 5}, {205, 5}, {295, 5},
{5, 95}, {95, 95}, {205, 95}, {295, 95},
{150, 150},
{5, 205}, {95, 205}, {205, 205}, {295, 205},
{5, 295}, {95, 295}, {205, 295}, {295, 295},
};
const QRegion clippedChildRegion = childRegion & QRect(50, 50, 200, 200);
const QRegion clippedGrandChildRegion = grandChildRegion & QRect(50, 50, 200, 200);
QGraphicsScene scene;
scene.addItem(parent);
QImage sceneImage(300, 300, QImage::Format_ARGB32);
#define VERIFY_CONTROL_POINTS(pRegion, cRegion, gRegion) \
for (const QPoint &controlPoint : controlPoints) { \
QRgb pixel = sceneImage.pixel(controlPoint.x(), controlPoint.y()); \
if (pRegion.contains(controlPoint)) \
QVERIFY(qBlue(pixel) != 0); \
else \
QVERIFY(qBlue(pixel) == 0); \
if (cRegion.contains(controlPoint)) \
QVERIFY(qRed(pixel) != 0); \
else \
QVERIFY(qRed(pixel) == 0); \
if (gRegion.contains(controlPoint)) \
QVERIFY(qGreen(pixel) != 0); \
else \
QVERIFY(qGreen(pixel) == 0); \
}
const QList<QGraphicsItem *> children = parent->childItems();
const int childrenCount = children.size();
for (int i = 0; i < 5; ++i) {
QString clipString;
QString childString;
switch (i) {
case 0:
// All children stacked in front.
childString = QLatin1String("ChildrenInFront.png");
for (QGraphicsItem *child : children)
child->setFlag(QGraphicsItem::ItemStacksBehindParent, false);
break;
case 1:
// All children stacked behind.
childString = QLatin1String("ChildrenBehind.png");
for (QGraphicsItem *child : children)
child->setFlag(QGraphicsItem::ItemStacksBehindParent, true);
break;
case 2:
// First half of the children behind, second half in front.
childString = QLatin1String("FirstHalfBehind_SecondHalfInFront.png");
for (int j = 0; j < childrenCount; ++j) {
QGraphicsItem *child = children.at(j);
child->setFlag(QGraphicsItem::ItemStacksBehindParent, (j < childrenCount / 2));
}
break;
case 3:
// First half of the children in front, second half behind.
childString = QLatin1String("FirstHalfInFront_SecondHalfBehind.png");
for (int j = 0; j < childrenCount; ++j) {
QGraphicsItem *child = children.at(j);
child->setFlag(QGraphicsItem::ItemStacksBehindParent, (j >= childrenCount / 2));
}
break;
case 4:
// Child2 and child4 behind, rest in front.
childString = QLatin1String("Child2And4Behind_RestInFront.png");
for (int j = 0; j < childrenCount; ++j) {
QGraphicsItem *child = children.at(j);
if (j == 1 || j == 3)
child->setFlag(QGraphicsItem::ItemStacksBehindParent, true);
else
child->setFlag(QGraphicsItem::ItemStacksBehindParent, false);
}
break;
default:
qFatal("internal error");
}
// Nothing is clipped.
parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
parent->setFlag(QGraphicsItem::ItemClipsToShape, false);
clipString = QLatin1String("nothingClipped_");
renderSceneToImage(&scene, &sceneImage, clipString + childString);
VERIFY_CONTROL_POINTS(parentRegion, childRegion, grandChildRegion);
// Parent clips children to shape.
parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
clipString = QLatin1String("parentClipsChildrenToShape_");
renderSceneToImage(&scene, &sceneImage, clipString + childString);
VERIFY_CONTROL_POINTS(parentRegion, clippedChildRegion, clippedGrandChildRegion);
// Parent clips itself and children to shape.
parent->setFlag(QGraphicsItem::ItemClipsToShape);
clipString = QLatin1String("parentClipsItselfAndChildrenToShape_");
renderSceneToImage(&scene, &sceneImage, clipString + childString);
VERIFY_CONTROL_POINTS(clippedParentRegion, clippedChildRegion, clippedGrandChildRegion);
// Parent clips itself to shape.
parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
clipString = QLatin1String("parentClipsItselfToShape_");
renderSceneToImage(&scene, &sceneImage, clipString + childString);
VERIFY_CONTROL_POINTS(clippedParentRegion, childRegion, grandChildRegion);
}
}
void tst_QGraphicsItem::itemClipsTextChildToShape()
{
// Construct a scene with a rect that clips its children, with one text
// child that has text that exceeds the size of the rect.
QGraphicsScene scene;
QGraphicsItem *rect = scene.addRect(0, 0, 50, 50, QPen(Qt::black), Qt::black);
rect->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
QGraphicsTextItem *text = new QGraphicsTextItem("This is a long sentence that's wider than 50 pixels.");
text->setParentItem(rect);
// Render this scene to a transparent image.
QRectF sr = scene.itemsBoundingRect();
QImage image(sr.size().toSize(), QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QPainter painter(&image);
scene.render(&painter);
// Erase the area immediately underneath the rect.
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.fillRect(rect->sceneBoundingRect().translated(-sr.topLeft()).adjusted(-0.5, -0.5, 0.5, 0.5),
Qt::transparent);
painter.end();
// Check that you get a truly transparent image back (i.e., the text was
// clipped away, so there should be no trails left after erasing only the
// rect's area).
QImage emptyImage(scene.itemsBoundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied);
emptyImage.fill(0);
QCOMPARE(image, emptyImage);
}
void tst_QGraphicsItem::itemClippingDiscovery()
{
// A simple scene with an ellipse parent and two rect children, one a
// child of the other.
QGraphicsScene scene;
QGraphicsEllipseItem *clipItem = scene.addEllipse(0, 0, 100, 100);
QGraphicsRectItem *leftRectItem = scene.addRect(0, 0, 50, 100);
QGraphicsRectItem *rightRectItem = scene.addRect(50, 0, 50, 100);
leftRectItem->setParentItem(clipItem);
rightRectItem->setParentItem(clipItem);
// The rects item are both visible at these points.
QCOMPARE(scene.items(QPointF(10, 10)).value(0, nullptr), leftRectItem);
QCOMPARE(scene.items(QPointF(90, 90)).value(0, nullptr), rightRectItem);
// The ellipse clips the rects now.
clipItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
// The rect items are no longer visible at these points.
QVERIFY(scene.items(QPointF(10, 10)).isEmpty());
if (sizeof(qreal) != sizeof(double))
QSKIP("This fails due to internal rounding errors");
QVERIFY(scene.items(QPointF(90, 90)).isEmpty());
}
class ItemCountsBoundingRectCalls : public QGraphicsRectItem
{
public:
using QGraphicsRectItem::QGraphicsRectItem;
QRectF boundingRect () const override
{
++boundingRectCalls;
return QGraphicsRectItem::boundingRect();
}
mutable int boundingRectCalls = 0;
};
void tst_QGraphicsItem::itemContainsChildrenInShape()
{
ItemCountsBoundingRectCalls *parent = new ItemCountsBoundingRectCalls(QRectF(0,0, 10, 10));
ItemCountsBoundingRectCalls *childOutsideShape = new ItemCountsBoundingRectCalls(QRectF(0,0, 10, 10), parent);
childOutsideShape->setPos(20,0);
QGraphicsScene scene;
scene.setItemIndexMethod(QGraphicsScene::NoIndex);
scene.addItem(parent);
QCOMPARE(parent->boundingRectCalls, childOutsideShape->boundingRectCalls);
int oldParentBoundingRectCalls = parent->boundingRectCalls;
int oldChildBoundingRectCalls = childOutsideShape->boundingRectCalls;
// First test that both items are searched if no optimization flags are set
QGraphicsItem* item = scene.items(QPointF(25, 5)).value(0, nullptr);
QCOMPARE(item, childOutsideShape);
QVERIFY(parent->boundingRectCalls > oldParentBoundingRectCalls);
QVERIFY(childOutsideShape->boundingRectCalls > oldChildBoundingRectCalls);
QCOMPARE(parent->boundingRectCalls, childOutsideShape->boundingRectCalls);
oldParentBoundingRectCalls = parent->boundingRectCalls;
oldChildBoundingRectCalls = childOutsideShape->boundingRectCalls;
// Repeat the test to make sure that no caching/indexing is in effect
item = scene.items(QPointF(25, 5)).value(0, nullptr);
QCOMPARE(item, childOutsideShape);
QVERIFY(parent->boundingRectCalls > oldParentBoundingRectCalls);
QVERIFY(childOutsideShape->boundingRectCalls > oldChildBoundingRectCalls);
QCOMPARE(parent->boundingRectCalls, childOutsideShape->boundingRectCalls);
oldParentBoundingRectCalls = parent->boundingRectCalls;
oldChildBoundingRectCalls = childOutsideShape->boundingRectCalls;
// Set the optimization flag and make sure that the child is not returned
// and that the child's boundingRect() method is never called.
parent->setFlag(QGraphicsItem::ItemContainsChildrenInShape);
item = scene.items(QPointF(25, 5)).value(0, nullptr);
QVERIFY(!(item));
QVERIFY(parent->boundingRectCalls > oldParentBoundingRectCalls);
QCOMPARE(childOutsideShape->boundingRectCalls, oldChildBoundingRectCalls);
QVERIFY(parent->boundingRectCalls > childOutsideShape->boundingRectCalls);
}
void tst_QGraphicsItem::itemContainsChildrenInShape2()
{
//The tested flag behaves almost identically to ItemClipsChildrenToShape
//in terms of optimizations but does not enforce the clip.
//This test makes sure there is no clip.
QGraphicsScene scene;
QGraphicsItem *rect = scene.addRect(0, 0, 50, 50, QPen(Qt::NoPen), QBrush(Qt::yellow));
QGraphicsItem *ellipse = scene.addEllipse(0, 0, 100, 100, QPen(Qt::NoPen), QBrush(Qt::green));
ellipse->setParentItem(rect);
QGraphicsItem *clippedEllipse = scene.addEllipse(0, 0, 50, 50, QPen(Qt::NoPen), QBrush(Qt::blue));
clippedEllipse->setParentItem(ellipse);
QGraphicsItem *clippedEllipse2 = scene.addEllipse(0, 0, 25, 25, QPen(Qt::NoPen), QBrush(Qt::red));
clippedEllipse2->setParentItem(clippedEllipse);
QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape));
QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemContainsChildrenInShape));
ellipse->setFlags(QGraphicsItem::ItemContainsChildrenInShape);
QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape));
QVERIFY((ellipse->flags() & QGraphicsItem::ItemContainsChildrenInShape));
QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
scene.render(&painter);
painter.end();
QCOMPARE(image.pixel(2, 2), QColor(Qt::yellow).rgba());
QCOMPARE(image.pixel(12, 12), QColor(Qt::red).rgba());
QCOMPARE(image.pixel(2, 25), QColor(Qt::blue).rgba());
QCOMPARE(image.pixel(2, 50), QColor(Qt::green).rgba());
}
void tst_QGraphicsItem::ancestorFlags()
{
QGraphicsItem *level1 = new QGraphicsRectItem;
QGraphicsItem *level21 = new QGraphicsRectItem;
level21->setParentItem(level1);
QGraphicsItem *level22 = new QGraphicsRectItem;
level22->setParentItem(level1);
QGraphicsItem *level31 = new QGraphicsRectItem;
level31->setParentItem(level21);
QGraphicsItem *level32 = new QGraphicsRectItem;
level32->setParentItem(level21);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
// HandlesChildEvents: 1) Root level sets a flag
level1->setHandlesChildEvents(true);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// HandlesChildEvents: 2) Root level set it again
level1->setHandlesChildEvents(true);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// HandlesChildEvents: 3) Root level unsets a flag
level1->setHandlesChildEvents(false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
// HandlesChildEvents: 4) Child item sets a flag
level21->setHandlesChildEvents(true);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// HandlesChildEvents: 5) Parent item sets a flag
level1->setHandlesChildEvents(true);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// HandlesChildEvents: 6) Child item unsets a flag
level21->setHandlesChildEvents(false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// HandlesChildEvents: 7) Parent item unsets a flag
level21->setHandlesChildEvents(true);
level1->setHandlesChildEvents(false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// Reparent the child to root
level21->setParentItem(nullptr);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// Reparent the child to level1 again.
level1->setHandlesChildEvents(true);
level21->setParentItem(level1);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// Reparenting level31 back to level1.
level31->setParentItem(level1);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// Reparenting level31 back to level21.
level31->setParentItem(nullptr);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
level31->setParentItem(level21);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// Level1 doesn't handle child events
level1->setHandlesChildEvents(false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 1);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 1);
// Nobody handles child events
level21->setHandlesChildEvents(false);
for (int i = 0; i < 3; ++i) {
QGraphicsItem::GraphicsItemFlag flag;
int ancestorFlag;
switch (i) {
case(0):
flag = QGraphicsItem::ItemClipsChildrenToShape;
ancestorFlag = QGraphicsItemPrivate::AncestorClipsChildren;
break;
case(1):
flag = QGraphicsItem::ItemIgnoresTransformations;
ancestorFlag = QGraphicsItemPrivate::AncestorIgnoresTransformations;
break;
case(2):
flag = QGraphicsItem::ItemContainsChildrenInShape;
ancestorFlag = QGraphicsItemPrivate::AncestorContainsChildren;
break;
default:
qFatal("Unknown ancestor flag, please fix!");
}
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
// HandlesChildEvents: 1) Root level sets a flag
level1->setFlag(flag, true);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// HandlesChildEvents: 2) Root level set it again
level1->setFlag(flag, true);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// HandlesChildEvents: 3) Root level unsets a flag
level1->setFlag(flag, false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
// HandlesChildEvents: 4) Child item sets a flag
level21->setFlag(flag, true);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// HandlesChildEvents: 5) Parent item sets a flag
level1->setFlag(flag, true);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// HandlesChildEvents: 6) Child item unsets a flag
level21->setFlag(flag, false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// HandlesChildEvents: 7) Parent item unsets a flag
level21->setFlag(flag, true);
level1->setFlag(flag, false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// Reparent the child to root
level21->setParentItem(nullptr);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// Reparent the child to level1 again.
level1->setFlag(flag, true);
level21->setParentItem(level1);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// Reparenting level31 back to level1.
level31->setParentItem(level1);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// Reparenting level31 back to level21.
level31->setParentItem(nullptr);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
level31->setParentItem(level21);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// Level1 doesn't handle child events
level1->setFlag(flag, false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag);
QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag);
// Nobody handles child events
level21->setFlag(flag, false);
QCOMPARE(int(level1->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level21->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level22->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level31->d_ptr->ancestorFlags), 0);
QCOMPARE(int(level32->d_ptr->ancestorFlags), 0);
}
delete level1;
}
void tst_QGraphicsItem::untransformable()
{
auto item1 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100));
item1->setZValue(1);
item1->setFlag(QGraphicsItem::ItemIgnoresTransformations);
item1->setTransform(QTransform().rotate(45), true);
item1->setBrush(Qt::red);
auto item2 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100));
item2->setParentItem(item1);
item2->setTransform(QTransform().rotate(45), true);
item2->setPos(100, 0);
item2->setBrush(Qt::green);
auto item3 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100));
item3->setParentItem(item2);
item3->setPos(100, 0);
item3->setBrush(Qt::blue);
QGraphicsScene scene(-500, -500, 1000, 1000);
scene.addItem(item1);
QWidget topLevel;
QGraphicsView view(&scene,&topLevel);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.resize(300, 300);
topLevel.show();
view.scale(8, 8);
view.centerOn(0, 0);
// Painting with the DiagCrossPattern is really slow on Mac
// when zoomed out. (The test times out). Task to fix is 155567.
#if !defined(Q_OS_MAC) || 1
view.setBackgroundBrush(QBrush(Qt::black, Qt::DiagCrossPattern));
#endif
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
for (int i = 0; i < 10; ++i) {
QPoint center = view.viewport()->rect().center();
QCOMPARE(view.itemAt(center), item1);
QCOMPARE(view.itemAt(center - QPoint(40, 0)), item1);
QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item1);
QCOMPARE(view.itemAt(center - QPoint(0, 40)), item1);
QCOMPARE(view.itemAt(center - QPoint(0, -40)), item1);
center += QPoint(70, 70);
QCOMPARE(view.itemAt(center - QPoint(40, 0)), item2);
QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item2);
QCOMPARE(view.itemAt(center - QPoint(0, 40)), item2);
QCOMPARE(view.itemAt(center - QPoint(0, -40)), item2);
center += QPoint(0, 100);
QCOMPARE(view.itemAt(center - QPoint(40, 0)), item3);
QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item3);
QCOMPARE(view.itemAt(center - QPoint(0, 40)), item3);
QCOMPARE(view.itemAt(center - QPoint(0, -40)), item3);
view.scale(0.5, 0.5);
view.rotate(13);
view.shear(qreal(0.01), qreal(0.01));
view.translate(10, 10);
}
}
#ifndef QT_NO_CONTEXTMENU
class ContextMenuItem : public QGraphicsRectItem
{
public:
using QGraphicsRectItem::QGraphicsRectItem;
bool ignoreEvent = true;
bool gotEvent = false;
bool eventWasAccepted = false;
protected:
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override
{
gotEvent = true;
eventWasAccepted = event->isAccepted();
if (ignoreEvent)
event->ignore();
}
};
void tst_QGraphicsItem::contextMenuEventPropagation()
{
ContextMenuItem *bottomItem = new ContextMenuItem;
bottomItem->setRect(0, 0, 100, 100);
ContextMenuItem *topItem = new ContextMenuItem;
topItem->setParentItem(bottomItem);
topItem->setRect(0, 0, 100, 100);
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.setAlignment(Qt::AlignLeft | Qt::AlignTop);
view.show();
view.resize(200, 200);
QVERIFY(QTest::qWaitForWindowExposed(&view));
QContextMenuEvent event(QContextMenuEvent::Mouse, QPoint(10, 10),
view.viewport()->mapToGlobal(QPoint(10, 10)));
event.ignore();
QCoreApplication::sendEvent(view.viewport(), &event);
QVERIFY(!event.isAccepted());
scene.addItem(bottomItem);
topItem->ignoreEvent = true;
bottomItem->ignoreEvent = true;
QCoreApplication::sendEvent(view.viewport(), &event);
QVERIFY(!event.isAccepted());
QCOMPARE(topItem->gotEvent, true);
QCOMPARE(topItem->eventWasAccepted, true);
QCOMPARE(bottomItem->gotEvent, true);
QCOMPARE(bottomItem->eventWasAccepted, true);
topItem->ignoreEvent = false;
topItem->gotEvent = false;
bottomItem->gotEvent = false;
QCoreApplication::sendEvent(view.viewport(), &event);
QVERIFY(event.isAccepted());
QCOMPARE(topItem->gotEvent, true);
QCOMPARE(bottomItem->gotEvent, false);
QCOMPARE(topItem->eventWasAccepted, true);
}
#endif // QT_NO_CONTEXTMENU
void tst_QGraphicsItem::itemIsMovable()
{
QGraphicsRectItem *rect = new QGraphicsRectItem(-50, -50, 100, 100);
rect->setFlag(QGraphicsItem::ItemIsMovable);
QGraphicsScene scene;
scene.addItem(rect);
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setButton(Qt::LeftButton);
event.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event);
}
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
event.setButton(Qt::LeftButton);
event.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event);
}
QCOMPARE(rect->pos(), QPointF(0, 0));
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
event.setButtons(Qt::LeftButton);
event.setScenePos(QPointF(10, 10));
QCoreApplication::sendEvent(&scene, &event);
}
QCOMPARE(rect->pos(), QPointF(10, 10));
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
event.setButtons(Qt::RightButton);
event.setScenePos(QPointF(20, 20));
QCoreApplication::sendEvent(&scene, &event);
}
QCOMPARE(rect->pos(), QPointF(10, 10));
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove);
event.setButtons(Qt::LeftButton);
event.setScenePos(QPointF(30, 30));
QCoreApplication::sendEvent(&scene, &event);
}
QCOMPARE(rect->pos(), QPointF(30, 30));
}
class ItemAddScene : public QGraphicsScene
{
Q_OBJECT
public:
ItemAddScene()
{
QTimer::singleShot(500, this, &ItemAddScene::newTextItem);
}
public slots:
void newTextItem()
{
// Add a text item
QGraphicsItem *item = addText("This item will not ensure that it's visible");
item->setPos(.0, .0);
item->show();
}
};
void tst_QGraphicsItem::task141694_textItemEnsureVisible()
{
ItemAddScene scene;
scene.setSceneRect(-1000, -1000, 2000, 2000);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.setFixedSize(200, 200);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
view.ensureVisible(-1000, -1000, 5, 5);
int hscroll = view.horizontalScrollBar()->value();
int vscroll = view.verticalScrollBar()->value();
QTest::qWait(10);
// This should not cause the view to scroll
QTRY_COMPARE(view.horizontalScrollBar()->value(), hscroll);
QCOMPARE(view.verticalScrollBar()->value(), vscroll);
}
void tst_QGraphicsItem::task128696_textItemEnsureMovable()
{
QGraphicsTextItem *item = new QGraphicsTextItem;
item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
item->setTextInteractionFlags(Qt::TextEditorInteraction);
item->setPlainText("abc de\nf ghi\n j k l");
QGraphicsScene scene;
scene.setSceneRect(-100, -100, 200, 200);
scene.addItem(item);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.setFixedSize(200, 200);
view.show();
QGraphicsSceneMouseEvent event1(QEvent::GraphicsSceneMousePress);
event1.setScenePos(QPointF(0, 0));
event1.setButton(Qt::LeftButton);
event1.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event1);
QCOMPARE(scene.mouseGrabberItem(), item);
QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove);
event2.setScenePos(QPointF(10, 10));
event2.setButton(Qt::LeftButton);
event2.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event2);
QCOMPARE(item->pos(), QPointF(10, 10));
}
void tst_QGraphicsItem::task177918_lineItemUndetected()
{
QGraphicsScene scene;
QGraphicsLineItem *line = scene.addLine(10, 10, 10, 10);
QCOMPARE(line->boundingRect(), QRectF(10, 10, 0, 0));
const QRectF rect(9, 9, 2, 2);
QVERIFY(!scene.items(rect, Qt::IntersectsItemShape).isEmpty());
QVERIFY(!scene.items(rect, Qt::ContainsItemShape).isEmpty());
QVERIFY(!scene.items(rect, Qt::IntersectsItemBoundingRect).isEmpty());
QVERIFY(!scene.items(rect, Qt::ContainsItemBoundingRect).isEmpty());
}
void tst_QGraphicsItem::task240400_clickOnTextItem_data()
{
using Flags = QGraphicsItem::GraphicsItemFlags;
QTest::addColumn<Flags>("flags");
QTest::addColumn<Qt::TextInteractionFlags>("textFlags");
QTest::newRow("editor, noflags")
<< Flags{}
<< Qt::TextInteractionFlags(Qt::TextEditorInteraction);
QTest::newRow("editor, movable")
<< Flags(QGraphicsItem::ItemIsMovable)
<< Qt::TextInteractionFlags(Qt::TextEditorInteraction);
QTest::newRow("editor, selectable")
<< Flags(QGraphicsItem::ItemIsSelectable)
<< Qt::TextInteractionFlags(Qt::TextEditorInteraction);
QTest::newRow("editor, movable | selectable")
<< Flags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable)
<< Qt::TextInteractionFlags(Qt::TextEditorInteraction);
QTest::newRow("noninteractive, noflags")
<< Flags{} << Qt::TextInteractionFlags(Qt::NoTextInteraction);
QTest::newRow("noninteractive, movable")
<< Flags(QGraphicsItem::ItemIsMovable) << Qt::TextInteractionFlags(Qt::NoTextInteraction);
QTest::newRow("noninteractive, selectable")
<< Flags(QGraphicsItem::ItemIsSelectable) << Qt::TextInteractionFlags(Qt::NoTextInteraction);
QTest::newRow("noninteractive, movable | selectable")
<< Flags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable)
<< Qt::TextInteractionFlags(Qt::NoTextInteraction);
}
void tst_QGraphicsItem::task240400_clickOnTextItem()
{
QFETCH(QGraphicsItem::GraphicsItemFlags, flags);
QFETCH(Qt::TextInteractionFlags, textFlags);
QGraphicsScene scene;
QEvent activate(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &activate);
QGraphicsTextItem *item = scene.addText("Hello");
item->setFlags(flags);
item->setTextInteractionFlags(textFlags);
const bool focusable = item->flags().testFlag(QGraphicsItem::ItemIsFocusable);
QVERIFY(textFlags ? focusable : !focusable);
int column = item->textCursor().columnNumber();
QCOMPARE(column, 0);
QVERIFY(!item->hasFocus());
// Click in the top-left corner of the item
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setScenePos(item->sceneBoundingRect().topLeft() + QPointF(0.1, 0.1));
event.setButton(Qt::LeftButton);
event.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event);
}
if (flags || textFlags)
QCOMPARE(scene.mouseGrabberItem(), item);
else
QCOMPARE(scene.mouseGrabberItem(), nullptr);
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
event.setScenePos(item->sceneBoundingRect().topLeft() + QPointF(0.1, 0.1));
event.setButton(Qt::LeftButton);
event.setButtons({});
QCoreApplication::sendEvent(&scene, &event);
}
if (textFlags)
QVERIFY(item->hasFocus());
else
QVERIFY(!item->hasFocus());
QVERIFY(!scene.mouseGrabberItem());
bool selectable = flags.testFlag(QGraphicsItem::ItemIsSelectable);
QVERIFY(selectable ? item->isSelected() : !item->isSelected());
// Now click in the middle and check that the cursor moved.
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
event.setScenePos(item->sceneBoundingRect().center());
event.setButton(Qt::LeftButton);
event.setButtons(Qt::LeftButton);
QCoreApplication::sendEvent(&scene, &event);
}
if (flags || textFlags)
QCOMPARE(scene.mouseGrabberItem(), item);
else
QCOMPARE(scene.mouseGrabberItem(), nullptr);
{
QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease);
event.setScenePos(item->sceneBoundingRect().center());
event.setButton(Qt::LeftButton);
event.setButtons({});
QCoreApplication::sendEvent(&scene, &event);
}
if (textFlags)
QVERIFY(item->hasFocus());
else
QVERIFY(!item->hasFocus());
QVERIFY(!scene.mouseGrabberItem());
QVERIFY(selectable ? item->isSelected() : !item->isSelected());
//
if (textFlags.testFlag(Qt::TextEditorInteraction))
QVERIFY(item->textCursor().columnNumber() > column);
else
QCOMPARE(item->textCursor().columnNumber(), 0);
}
class TextItem : public QGraphicsSimpleTextItem
{
public:
using QGraphicsSimpleTextItem::QGraphicsSimpleTextItem;
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) override
{
updates++;
QGraphicsSimpleTextItem::paint(painter, option, widget);
}
int updates = 0;
};
void tst_QGraphicsItem::ensureUpdateOnTextItem()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCoreApplication::processEvents(); // Process all queued paint events
TextItem *text1 = new TextItem(QLatin1String("123"));
scene.addItem(text1);
QTRY_COMPARE(text1->updates,1);
//same bouding rect but we have to update
text1->setText(QLatin1String("321"));
QTRY_COMPARE(text1->updates,2);
}
void tst_QGraphicsItem::task243707_addChildBeforeParent()
{
// Task reports that adding the child before the parent leads to an
// inconsistent internal state that can cause a crash. This test shows
// one such crash.
QGraphicsScene scene;
QGraphicsWidget *widget = new QGraphicsWidget;
QGraphicsWidget *widget2 = new QGraphicsWidget(widget);
scene.addItem(widget2);
QVERIFY(!widget2->parentItem());
scene.addItem(widget);
QVERIFY(!widget->commonAncestorItem(widget2));
QVERIFY(!widget2->commonAncestorItem(widget));
}
void tst_QGraphicsItem::task197802_childrenVisibility()
{
QGraphicsScene scene;
QGraphicsRectItem item(QRectF(0,0,20,20));
QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(0,0,10,10), &item);
scene.addItem(&item);
//freshly created: both visible
QVERIFY(item.isVisible());
QVERIFY(item2->isVisible());
//hide child: parent visible, child not
item2->hide();
QVERIFY(item.isVisible());
QVERIFY(!item2->isVisible());
//hide parent: parent and child invisible
item.hide();
QVERIFY(!item.isVisible());
QVERIFY(!item2->isVisible());
//ask to show the child: parent and child invisible anyways
item2->show();
QVERIFY(!item.isVisible());
QVERIFY(!item2->isVisible());
//show the parent: both parent and child visible
item.show();
QVERIFY(item.isVisible());
QVERIFY(item2->isVisible());
delete item2;
}
void tst_QGraphicsItem::boundingRegion_data()
{
QTest::addColumn<QLineF>("line");
QTest::addColumn<qreal>("granularity");
QTest::addColumn<QTransform>("transform");
QTest::addColumn<QRegion>("expectedRegion");
QTest::newRow("(0, 0, 10, 10) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 10) << qreal(0.0) << QTransform()
<< QRegion(QRect(0, 0, 10, 10));
QTest::newRow("(0, 0, 10, 0) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 0) << qreal(0.0) << QTransform()
<< QRegion(QRect(0, 0, 10, 1));
QTest::newRow("(0, 0, 10, 0) | 0.5 | identity | {(0, 0, 10, 1)}") << QLineF(0, 0, 10, 0) << qreal(0.5) << QTransform()
<< QRegion(QRect(0, 0, 10, 1));
QTest::newRow("(0, 0, 10, 0) | 1.0 | identity | {(0, 0, 10, 1)}") << QLineF(0, 0, 10, 0) << qreal(1.0) << QTransform()
<< QRegion(QRect(0, 0, 10, 1));
QTest::newRow("(0, 0, 0, 10) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 0, 10) << qreal(0.0) << QTransform()
<< QRegion(QRect(0, 0, 1, 10));
QTest::newRow("(0, 0, 0, 10) | 0.5 | identity | {(0, 0, 1, 10)}") << QLineF(0, 0, 0, 10) << qreal(0.5) << QTransform()
<< QRegion(QRect(0, 0, 1, 10));
QTest::newRow("(0, 0, 0, 10) | 1.0 | identity | {(0, 0, 1, 10)}") << QLineF(0, 0, 0, 10) << qreal(1.0) << QTransform()
<< QRegion(QRect(0, 0, 1, 10));
}
void tst_QGraphicsItem::boundingRegion()
{
QFETCH(QLineF, line);
QFETCH(qreal, granularity);
QFETCH(QTransform, transform);
QFETCH(QRegion, expectedRegion);
QGraphicsLineItem item(line);
item.setPen(QPen(Qt::black, 0));
QCOMPARE(item.boundingRegionGranularity(), qreal(0.0));
item.setBoundingRegionGranularity(granularity);
QCOMPARE(item.boundingRegionGranularity(), granularity);
QCOMPARE(item.boundingRegion(transform), expectedRegion);
}
void tst_QGraphicsItem::itemTransform_parentChild()
{
QGraphicsScene scene;
QGraphicsItem *parent = scene.addRect(0, 0, 100, 100);
QGraphicsItem *child = scene.addRect(0, 0, 100, 100);
child->setParentItem(parent);
child->setPos(10, 10);
child->setTransform(QTransform::fromScale(2, 2), true);
child->setTransform(QTransform().rotate(90), true);
QCOMPARE(child->itemTransform(parent).map(QPointF(10, 10)), QPointF(-10, 30));
QCOMPARE(parent->itemTransform(child).map(QPointF(-10, 30)), QPointF(10, 10));
}
void tst_QGraphicsItem::itemTransform_siblings()
{
QGraphicsScene scene;
QGraphicsItem *parent = scene.addRect(0, 0, 100, 100);
QGraphicsItem *brother = scene.addRect(0, 0, 100, 100);
QGraphicsItem *sister = scene.addRect(0, 0, 100, 100);
parent->setTransform(QTransform::fromScale(10, 5), true);
parent->setTransform(QTransform().rotate(-180), true);
parent->setTransform(QTransform().shear(2, 3), true);
brother->setParentItem(parent);
sister->setParentItem(parent);
brother->setPos(10, 10);
brother->setTransform(QTransform::fromScale(2, 2), true);
brother->setTransform(QTransform().rotate(90), true);
sister->setPos(10, 10);
sister->setTransform(QTransform::fromScale(2, 2), true);
sister->setTransform(QTransform().rotate(90), true);
QCOMPARE(brother->itemTransform(sister).map(QPointF(10, 10)), QPointF(10, 10));
QCOMPARE(sister->itemTransform(brother).map(QPointF(10, 10)), QPointF(10, 10));
}
void tst_QGraphicsItem::itemTransform_unrelated()
{
QGraphicsScene scene;
QGraphicsItem *stranger1 = scene.addRect(0, 0, 100, 100);
QGraphicsItem *stranger2 = scene.addRect(0, 0, 100, 100);
stranger1->setPos(10, 10);
stranger1->setTransform(QTransform::fromScale(2, 2), true);
stranger1->setTransform(QTransform().rotate(90), true);
stranger2->setPos(10, 10);
stranger2->setTransform(QTransform::fromScale(2, 2), true);
stranger2->setTransform(QTransform().rotate(90), true);
QCOMPARE(stranger1->itemTransform(stranger2).map(QPointF(10, 10)), QPointF(10, 10));
QCOMPARE(stranger2->itemTransform(stranger1).map(QPointF(10, 10)), QPointF(10, 10));
}
void tst_QGraphicsItem::opacity_data()
{
QTest::addColumn<qreal>("p_opacity");
QTest::addColumn<int>("p_opacityFlags");
QTest::addColumn<qreal>("c1_opacity");
QTest::addColumn<int>("c1_opacityFlags");
QTest::addColumn<qreal>("c2_opacity");
QTest::addColumn<int>("c2_opacityFlags");
QTest::addColumn<qreal>("p_effectiveOpacity");
QTest::addColumn<qreal>("c1_effectiveOpacity");
QTest::addColumn<qreal>("c2_effectiveOpacity");
QTest::addColumn<qreal>("c3_effectiveOpacity");
// Modify the opacity and see how it propagates
QTest::newRow("A: 1.0 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0
<< qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
QTest::newRow("B: 0.5 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0
<< qreal(0.5) << qreal(0.5) << qreal(0.5) << qreal(0.5);
QTest::newRow("C: 0.5 0 0.1 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(0.1) << 0 << qreal(1.0) << 0
<< qreal(0.5) << qreal(0.05) << qreal(0.05) << qreal(0.05);
QTest::newRow("D: 0.0 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0
<< qreal(0.0) << qreal(0.0) << qreal(0.0) << qreal(0.0);
// Parent doesn't propagate to children - now modify the opacity and see how it propagates
int flags = QGraphicsItem::ItemDoesntPropagateOpacityToChildren;
QTest::newRow("E: 1.0 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << flags << qreal(1.0) << 0 << qreal(1.0) << 0
<< qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
QTest::newRow("F: 0.5 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << flags << qreal(1.0) << 0 << qreal(1.0) << 0
<< qreal(0.5) << qreal(1.0) << qreal(1.0) << qreal(1.0);
QTest::newRow("G: 0.5 2 0.1 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << flags << qreal(0.1) << 0 << qreal(1.0) << 0
<< qreal(0.5) << qreal(0.1) << qreal(0.1) << qreal(0.1);
QTest::newRow("H: 0.0 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << flags << qreal(1.0) << 0 << qreal(1.0) << 0
<< qreal(0.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
// Child ignores parent - now modify the opacity and see how it propagates
flags = QGraphicsItem::ItemIgnoresParentOpacity;
QTest::newRow("I: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 << qreal(1.0) << flags << qreal(1.0) << 0
<< qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
QTest::newRow("J: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(0.5) << flags << qreal(0.5) << 0
<< qreal(0.5) << qreal(0.5) << qreal(0.25) << qreal(0.25);
QTest::newRow("K: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.2) << 0 << qreal(0.2) << flags << qreal(0.2) << 0
<< qreal(0.2) << qreal(0.2) << qreal(0.04) << qreal(0.04);
QTest::newRow("L: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << 0 << qreal(0.0) << flags << qreal(0.0) << 0
<< qreal(0.0) << qreal(0.0) << qreal(0.0) << qreal(0.0);
// Child ignores parent and doesn't propagate - now modify the opacity and see how it propagates
flags = QGraphicsItem::ItemIgnoresParentOpacity | QGraphicsItem::ItemDoesntPropagateOpacityToChildren;
QTest::newRow("M: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 // p
<< qreal(1.0) << flags // c1 (no prop)
<< qreal(1.0) << 0 // c2
<< qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0);
QTest::newRow("M: 0.5 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p
<< qreal(1.0) << flags // c1 (no prop)
<< qreal(1.0) << 0 // c2
<< qreal(0.5) << qreal(1.0) << qreal(1.0) << qreal(1.0);
QTest::newRow("M: 0.5 0 0.5 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p
<< qreal(0.5) << flags // c1 (no prop)
<< qreal(1.0) << 0 // c2
<< qreal(0.5) << qreal(0.5) << qreal(1.0) << qreal(1.0);
QTest::newRow("M: 0.5 0 0.5 1 0.5 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p
<< qreal(0.5) << flags // c1 (no prop)
<< qreal(0.5) << 0 // c2
<< qreal(0.5) << qreal(0.5) << qreal(0.5) << qreal(0.5);
QTest::newRow("M: 1.0 0 0.5 1 0.5 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 // p
<< qreal(0.5) << flags // c1 (no prop)
<< qreal(0.5) << 0 // c2
<< qreal(1.0) << qreal(0.5) << qreal(0.5) << qreal(0.5);
}
void tst_QGraphicsItem::opacity()
{
QFETCH(qreal, p_opacity);
QFETCH(int, p_opacityFlags);
QFETCH(qreal, p_effectiveOpacity);
QFETCH(qreal, c1_opacity);
QFETCH(int, c1_opacityFlags);
QFETCH(qreal, c1_effectiveOpacity);
QFETCH(qreal, c2_opacity);
QFETCH(int, c2_opacityFlags);
QFETCH(qreal, c2_effectiveOpacity);
QFETCH(qreal, c3_effectiveOpacity);
const QScopedPointer<QGraphicsRectItem> p(new QGraphicsRectItem);
QGraphicsRectItem *c1 = new QGraphicsRectItem(p.data());
QGraphicsRectItem *c2 = new QGraphicsRectItem(c1);
QGraphicsRectItem *c3 = new QGraphicsRectItem(c2);
QCOMPARE(p->opacity(), qreal(1.0));
QCOMPARE(p->effectiveOpacity(), qreal(1.0));
int opacityMask = QGraphicsItem::ItemIgnoresParentOpacity | QGraphicsItem::ItemDoesntPropagateOpacityToChildren;
QVERIFY(!(p->flags() & opacityMask));
p->setOpacity(p_opacity);
c1->setOpacity(c1_opacity);
c2->setOpacity(c2_opacity);
p->setFlags(QGraphicsItem::GraphicsItemFlags(p->flags() | p_opacityFlags));
c1->setFlags(QGraphicsItem::GraphicsItemFlags(c1->flags() | c1_opacityFlags));
c2->setFlags(QGraphicsItem::GraphicsItemFlags(c2->flags() | c2_opacityFlags));
QCOMPARE(int(p->flags() & opacityMask), p_opacityFlags);
QCOMPARE(int(c1->flags() & opacityMask), c1_opacityFlags);
QCOMPARE(int(c2->flags() & opacityMask), c2_opacityFlags);
QCOMPARE(p->opacity(), p_opacity);
QCOMPARE(p->effectiveOpacity(), p_effectiveOpacity);
QCOMPARE(c1->effectiveOpacity(), c1_effectiveOpacity);
QCOMPARE(c2->effectiveOpacity(), c2_effectiveOpacity);
QCOMPARE(c3->effectiveOpacity(), c3_effectiveOpacity);
}
void tst_QGraphicsItem::opacity2()
{
EventTester *parent = new EventTester;
EventTester *child = new EventTester(parent);
EventTester *grandChild = new EventTester(child);
QGraphicsScene scene;
scene.addItem(parent);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QVERIFY(QTest::qWaitForWindowActive(&view));
QCoreApplication::processEvents(); // Process all queued paint events
QTRY_VERIFY(view.repaints >= 1);
#define RESET_REPAINT_COUNTERS \
parent->repaints = 0; \
child->repaints = 0; \
grandChild->repaints = 0; \
view.repaints = 0;
RESET_REPAINT_COUNTERS
child->setOpacity(0.0);
QTRY_COMPARE(view.repaints, 1);
QCOMPARE(parent->repaints, 1);
QCOMPARE(child->repaints, 0);
QCOMPARE(grandChild->repaints, 0);
RESET_REPAINT_COUNTERS
child->setOpacity(1.0);
QTRY_COMPARE(view.repaints, 1);
QCOMPARE(parent->repaints, 1);
QCOMPARE(child->repaints, 1);
QCOMPARE(grandChild->repaints, 1);
RESET_REPAINT_COUNTERS
parent->setOpacity(0.0);
QTRY_COMPARE(view.repaints, 1);
QCOMPARE(parent->repaints, 0);
QCOMPARE(child->repaints, 0);
QCOMPARE(grandChild->repaints, 0);
RESET_REPAINT_COUNTERS
parent->setOpacity(1.0);
QTRY_COMPARE(view.repaints, 1);
QCOMPARE(parent->repaints, 1);
QCOMPARE(child->repaints, 1);
QCOMPARE(grandChild->repaints, 1);
grandChild->setFlag(QGraphicsItem::ItemIgnoresParentOpacity);
RESET_REPAINT_COUNTERS
child->setOpacity(0.0);
QTRY_COMPARE(view.repaints, 1);
QCOMPARE(parent->repaints, 1);
QCOMPARE(child->repaints, 0);
QCOMPARE(grandChild->repaints, 1);
RESET_REPAINT_COUNTERS
child->setOpacity(0.0); // Already 0.0; no change.
QTest::qWait(10);
QCOMPARE(view.repaints, 0);
QCOMPARE(parent->repaints, 0);
QCOMPARE(child->repaints, 0);
QCOMPARE(grandChild->repaints, 0);
}
void tst_QGraphicsItem::opacityZeroUpdates()
{
EventTester *parent = new EventTester;
EventTester *child = new EventTester(parent);
child->setPos(10, 10);
QGraphicsScene scene;
scene.addItem(parent);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QCoreApplication::processEvents(); // Process all queued paint events
QTRY_VERIFY(view.repaints > 0);
view.reset();
parent->setOpacity(0.0);
QTRY_COMPARE(view.repaints, 1);
// transforming items bounding rect to view coordinates
const QRect childDeviceBoundingRect = child->deviceTransform(view.viewportTransform())
.mapRect(child->boundingRect()).toRect();
const QRect parentDeviceBoundingRect = parent->deviceTransform(view.viewportTransform())
.mapRect(parent->boundingRect()).toRect();
QRegion expectedRegion = parentDeviceBoundingRect.adjusted(-2, -2, 2, 2);
expectedRegion += childDeviceBoundingRect.adjusted(-2, -2, 2, 2);
COMPARE_REGIONS(view.paintedRegion, expectedRegion);
}
class StacksBehindParentHelper : public QGraphicsRectItem
{
public:
StacksBehindParentHelper(GraphicsItems *paintedItems, const QRectF &rect, QGraphicsItem *parent = nullptr)
: QGraphicsRectItem(rect, parent), m_paintedItems(paintedItems)
{ }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
QGraphicsRectItem::paint(painter, option, widget);
m_paintedItems->append(this);
}
private:
GraphicsItems *m_paintedItems;
};
void tst_QGraphicsItem::itemStacksBehindParent()
{
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);
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");
grandChild111->setData(0, "grandChild111");
child12->setData(0, "child12");
grandChild121->setData(0, "grandChild121");
parent2->setData(0, "parent2");
child21->setData(0, "child21");
grandChild211->setData(0, "grandChild211");
child22->setData(0, "child22");
grandChild221->setData(0, "grandChild221");
// Disambiguate siblings
parent1->setZValue(1);
child11->setZValue(1);
child21->setZValue(1);
QGraphicsScene scene;
scene.addItem(parent1);
scene.addItem(parent2);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QTRY_VERIFY(!paintedItems.isEmpty());
paintedItems.clear();
view.viewport()->update();
QApplication::processEvents();
QRectF rect(0, 0, 100, 100);
const GraphicsItemsList expected1{grandChild111, child11, grandChild121, child12, parent1,
grandChild211, child21, grandChild221, child22, parent2};
QTRY_COMPARE(scene.items(rect), expected1);
const GraphicsItems expected2{parent2, child22, grandChild221, child21, grandChild211,
parent1, child12, grandChild121, child11, grandChild111};
QTRY_COMPARE(paintedItems, expected2);
child11->setFlag(QGraphicsItem::ItemStacksBehindParent);
scene.update();
paintedItems.clear();
const GraphicsItemsList expected3{grandChild121, child12, parent1, grandChild111, child11,
grandChild211, child21, grandChild221, child22, parent2};
QTRY_COMPARE(scene.items(rect), expected3);
const GraphicsItems expected4{parent2, child22, grandChild221, child21, grandChild211, child11, grandChild111,
parent1, child12, grandChild121};
QTRY_COMPARE(paintedItems, expected4);
child12->setFlag(QGraphicsItem::ItemStacksBehindParent);
paintedItems.clear();
scene.update();
const GraphicsItemsList expected5{parent1, grandChild111, child11, grandChild121, child12,
grandChild211, child21, grandChild221, child22, parent2};
QTRY_COMPARE(scene.items(rect), expected5);
const GraphicsItems expected6{parent2, child22, grandChild221, child21, grandChild211,
child12, grandChild121, child11, grandChild111, parent1};
QTRY_COMPARE(paintedItems, expected6);
}
class ClippingAndTransformsScene : public QGraphicsScene
{
Q_OBJECT
public:
GraphicsItems drawnItems;
protected:
void drawItems(QPainter *painter, int numItems, QGraphicsItem *items[],
const QStyleOptionGraphicsItem options[], QWidget *widget = nullptr) override
{
drawnItems.clear();
for (int i = 0; i < numItems; ++i)
drawnItems << items[i];
QGraphicsScene::drawItems(painter, numItems, items, options, widget);
}
};
void tst_QGraphicsItem::nestedClipping()
{
ClippingAndTransformsScene scene;
scene.setSceneRect(-50, -50, 200, 200);
QGraphicsRectItem *root = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
root->setBrush(QColor(0, 0, 255));
root->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
QGraphicsRectItem *l1 = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
l1->setParentItem(root);
l1->setPos(-50, 0);
l1->setBrush(QColor(255, 0, 0));
l1->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
QGraphicsEllipseItem *l2 = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100));
l2->setParentItem(l1);
l2->setPos(50, 50);
l2->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
l2->setBrush(QColor(255, 255, 0));
QGraphicsRectItem *l3 = new QGraphicsRectItem(QRectF(0, 0, 25, 25));
l3->setParentItem(l2);
l3->setBrush(QColor(0, 255, 0));
l3->setPos(50 - 12, -12);
scene.addItem(root);
root->setData(0, "root");
l1->setData(0, "l1");
l2->setData(0, "l2");
l3->setData(0, "l3");
QGraphicsView view(&scene);
view.setOptimizationFlag(QGraphicsView::IndirectPainting);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
GraphicsItems expected{root, l1, l2, l3};
QTRY_COMPARE(scene.drawnItems, expected);
QImage image(200, 200, QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QPainter painter(&image);
scene.render(&painter);
painter.end();
// Check transparent areas
QCOMPARE(image.pixel(100, 25), qRgba(0, 0, 0, 0));
QCOMPARE(image.pixel(100, 175), qRgba(0, 0, 0, 0));
QCOMPARE(image.pixel(25, 100), qRgba(0, 0, 0, 0));
QCOMPARE(image.pixel(175, 100), qRgba(0, 0, 0, 0));
QCOMPARE(image.pixel(70, 80), qRgba(255, 0, 0, 255));
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
// Enable this to compare if the test starts failing.
image.save("nestedClipping_reference.png");
#endif
}
class TransformDebugItem : public QGraphicsRectItem
{
public:
TransformDebugItem()
: QGraphicsRectItem(QRectF(-10, -10, 20, 20))
{
setPen(QPen(Qt::black, 0));
setBrush(QColor(QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256)));
}
QTransform x;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget = nullptr) override
{
x = painter->worldTransform();
QGraphicsRectItem::paint(painter, option, widget);
}
};
void tst_QGraphicsItem::nestedClippingTransforms()
{
TransformDebugItem *rootClipper = new TransformDebugItem;
rootClipper->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
TransformDebugItem *child = new TransformDebugItem;
child->setParentItem(rootClipper);
child->setPos(2, 2);
TransformDebugItem *grandChildClipper = new TransformDebugItem;
grandChildClipper->setParentItem(child);
grandChildClipper->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
grandChildClipper->setPos(4, 4);
TransformDebugItem *greatGrandChild = new TransformDebugItem;
greatGrandChild->setPos(2, 2);
greatGrandChild->setParentItem(grandChildClipper);
TransformDebugItem *grandChildClipper2 = new TransformDebugItem;
grandChildClipper2->setParentItem(child);
grandChildClipper2->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
grandChildClipper2->setPos(8, 8);
TransformDebugItem *greatGrandChild2 = new TransformDebugItem;
greatGrandChild2->setPos(2, 2);
greatGrandChild2->setParentItem(grandChildClipper2);
TransformDebugItem *grandChildClipper3 = new TransformDebugItem;
grandChildClipper3->setParentItem(child);
grandChildClipper3->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
grandChildClipper3->setPos(12, 12);
TransformDebugItem *greatGrandChild3 = new TransformDebugItem;
greatGrandChild3->setPos(2, 2);
greatGrandChild3->setParentItem(grandChildClipper3);
QGraphicsScene scene;
scene.addItem(rootClipper);
QImage image(scene.itemsBoundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QPainter p(&image);
scene.render(&p);
p.end();
QCOMPARE(rootClipper->x, QTransform(1, 0, 0, 0, 1, 0, 10, 10, 1));
QCOMPARE(child->x, QTransform(1, 0, 0, 0, 1, 0, 12, 12, 1));
QCOMPARE(grandChildClipper->x, QTransform(1, 0, 0, 0, 1, 0, 16, 16, 1));
QCOMPARE(greatGrandChild->x, QTransform(1, 0, 0, 0, 1, 0, 18, 18, 1));
QCOMPARE(grandChildClipper2->x, QTransform(1, 0, 0, 0, 1, 0, 20, 20, 1));
QCOMPARE(greatGrandChild2->x, QTransform(1, 0, 0, 0, 1, 0, 22, 22, 1));
QCOMPARE(grandChildClipper3->x, QTransform(1, 0, 0, 0, 1, 0, 24, 24, 1));
QCOMPARE(greatGrandChild3->x, QTransform(1, 0, 0, 0, 1, 0, 26, 26, 1));
}
void tst_QGraphicsItem::sceneTransformCache()
{
// Test that an item's scene transform is updated correctly when the
// parent is transformed.
QGraphicsScene scene;
const QScopedPointer<QGraphicsRectItem> rect(scene.addRect(0, 0, 100, 100));
rect->setPen(QPen(Qt::black, 0));
QGraphicsRectItem *rect2 = scene.addRect(0, 0, 100, 100);
rect2->setPen(QPen(Qt::black, 0));
rect2->setParentItem(rect.data());
rect2->setTransform(QTransform().rotate(90), true);
rect->setTransform(QTransform::fromTranslate(0, 50), true);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
rect->setTransform(QTransform::fromTranslate(0, 100), true);
QTransform x;
x.translate(0, 150);
x.rotate(90);
QCOMPARE(rect2->sceneTransform(), x);
scene.removeItem(rect.data());
//Crazy use case : rect4 child of rect3 so the transformation of rect4 will be cached.Good!
//We remove rect4 from the scene, then the validTransform bit flag is set to 0 and the index of the cache
//add to the freeTransformSlots. The problem was that sceneTransformIndex was not set to -1 so if a new item arrive
//with a child (rect6) that will be cached then it will take the freeSlot (ex rect4) and put it his transform. But if rect4 is
//added back to the scene then it will set the transform to his old sceneTransformIndex value that will erase the new
//value of rect6 so rect6 transform will be wrong.
QGraphicsRectItem *rect3 = scene.addRect(0, 0, 100, 100);
QGraphicsRectItem *rect4 = scene.addRect(0, 0, 100, 100);
rect3->setPos(QPointF(10,10));
rect3->setPen(QPen(Qt::black, 0));
rect4->setParentItem(rect3);
rect4->setPos(QPointF(10,10));
rect4->setPen(QPen(Qt::black, 0));
QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(20,20));
scene.removeItem(rect4);
//rect4 transform is local only
QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(10,10));
QGraphicsRectItem *rect5 = scene.addRect(0, 0, 100, 100);
QGraphicsRectItem *rect6 = scene.addRect(0, 0, 100, 100);
rect5->setPos(QPointF(20,20));
rect5->setPen(QPen(Qt::black, 0));
rect6->setParentItem(rect5);
rect6->setPos(QPointF(10,10));
rect6->setPen(QPen(Qt::black, 0));
//test if rect6 transform is ok
QCOMPARE(rect6->mapToScene(rect6->boundingRect().topLeft()), QPointF(30,30));
scene.addItem(rect4);
QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(10,10));
//test if rect6 transform is still correct
QCOMPARE(rect6->mapToScene(rect6->boundingRect().topLeft()), QPointF(30,30));
}
void tst_QGraphicsItem::tabChangesFocus_data()
{
QTest::addColumn<bool>("tabChangesFocus");
QTest::newRow("tab changes focus") << true;
QTest::newRow("tab doesn't change focus") << false;
}
void tst_QGraphicsItem::tabChangesFocus()
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QSKIP("Window activation is not supported");
QFETCH(bool, tabChangesFocus);
QGraphicsScene scene;
QGraphicsTextItem *item = scene.addText("Hello");
item->setTabChangesFocus(tabChangesFocus);
item->setTextInteractionFlags(Qt::TextEditorInteraction);
item->setFocus();
QDial *dial1 = new QDial;
QGraphicsView *view = new QGraphicsView(&scene);
QDial *dial2 = new QDial;
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(dial1);
layout->addWidget(view);
layout->addWidget(dial2);
QWidget widget;
widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
widget.setLayout(layout);
widget.show();
view->window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&widget));
QTRY_VERIFY(scene.isActive());
dial1->setFocus();
QTRY_VERIFY(dial1->hasFocus());
QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
QTRY_VERIFY(view->hasFocus());
QTRY_VERIFY(item->hasFocus());
QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
if (tabChangesFocus) {
QTRY_VERIFY(!view->hasFocus());
QTRY_VERIFY(!item->hasFocus());
QTRY_VERIFY(dial2->hasFocus());
} else {
QTRY_VERIFY(view->hasFocus());
QTRY_VERIFY(item->hasFocus());
QCOMPARE(item->toPlainText(), QString("\tHello"));
}
}
void tst_QGraphicsItem::cacheMode()
{
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
QSKIP("Wayland: This fails. Figure out why.");
QGraphicsScene scene(0, 0, 100, 100);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.resize(150, 150);
view.show();
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QVERIFY(QTest::qWaitForWindowActive(&view));
QCoreApplication::processEvents(); // Process all queued paint events
EventTester *tester = new EventTester;
EventTester *testerChild = new EventTester;
testerChild->setParentItem(tester);
EventTester *testerChild2 = new EventTester;
testerChild2->setParentItem(testerChild);
testerChild2->setFlag(QGraphicsItem::ItemIgnoresTransformations);
scene.addItem(tester);
for (int i = 0; i < 2; ++i) {
// No visual change.
QTRY_COMPARE(tester->repaints, 1);
QCOMPARE(testerChild->repaints, 1);
QCOMPARE(testerChild2->repaints, 1);
tester->setCacheMode(QGraphicsItem::NoCache);
testerChild->setCacheMode(QGraphicsItem::NoCache);
testerChild2->setCacheMode(QGraphicsItem::NoCache);
QTest::qWait(25);
QTRY_COMPARE(tester->repaints, 1);
QCOMPARE(testerChild->repaints, 1);
QCOMPARE(testerChild2->repaints, 1);
tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
testerChild->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
testerChild2->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
QTest::qWait(25);
}
// The first move causes a repaint as the item is painted into its pixmap.
// (Only occurs if the item has previously been painted without cache).
tester->setPos(10, 10);
testerChild->setPos(10, 10);
testerChild2->setPos(10, 10);
QTRY_COMPARE(tester->repaints, 2);
QCOMPARE(testerChild->repaints, 2);
QCOMPARE(testerChild2->repaints, 2);
// Consecutive moves should not repaint.
tester->setPos(20, 20);
testerChild->setPos(20, 20);
testerChild2->setPos(20, 20);
QTest::qWait(250);
QCOMPARE(tester->repaints, 2);
QCOMPARE(testerChild->repaints, 2);
QCOMPARE(testerChild2->repaints, 2);
// Translating does not result in a repaint.
tester->setTransform(QTransform::fromTranslate(10, 10), true);
QTest::qWait(25);
QCOMPARE(tester->repaints, 2);
QCOMPARE(testerChild->repaints, 2);
QCOMPARE(testerChild2->repaints, 2);
// Rotating results in a repaint.
tester->setTransform(QTransform().rotate(45), true);
QTRY_COMPARE(tester->repaints, 3);
QCOMPARE(testerChild->repaints, 3);
QCOMPARE(testerChild2->repaints, 2);
// Change to ItemCoordinateCache (triggers repaint).
tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); // autosize
testerChild->setCacheMode(QGraphicsItem::ItemCoordinateCache); // autosize
testerChild2->setCacheMode(QGraphicsItem::ItemCoordinateCache); // autosize
QTRY_COMPARE(tester->repaints, 4);
QCOMPARE(testerChild->repaints, 4);
QCOMPARE(testerChild2->repaints, 3);
// Rotating items with ItemCoordinateCache doesn't cause a repaint.
tester->setTransform(QTransform().rotate(22), true);
testerChild->setTransform(QTransform().rotate(22), true);
testerChild2->setTransform(QTransform().rotate(22), true);
QTest::qWait(25);
QCOMPARE(tester->repaints, 4);
QCOMPARE(testerChild->repaints, 4);
QCOMPARE(testerChild2->repaints, 3);
tester->resetTransform();
testerChild->resetTransform();
testerChild2->resetTransform();
// Explicit update causes a repaint.
tester->update(0, 0, 5, 5);
QTRY_COMPARE(tester->repaints, 5);
QCOMPARE(testerChild->repaints, 4);
QCOMPARE(testerChild2->repaints, 3);
// Updating outside the item's bounds does not cause a repaint.
tester->update(10, 10, 5, 5);
QTest::qWait(25);
QCOMPARE(tester->repaints, 5);
QCOMPARE(testerChild->repaints, 4);
QCOMPARE(testerChild2->repaints, 3);
// Resizing an item should cause a repaint of that item. (because of
// autosize).
tester->setGeometry(QRectF(-15, -15, 30, 30));
QTRY_COMPARE(tester->repaints, 6);
QCOMPARE(testerChild->repaints, 4);
QCOMPARE(testerChild2->repaints, 3);
// Set fixed size.
tester->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(30, 30));
testerChild->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(30, 30));
testerChild2->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(30, 30));
QTRY_COMPARE(tester->repaints, 7);
QCOMPARE(testerChild->repaints, 5);
QCOMPARE(testerChild2->repaints, 4);
// Resizing the item should cause a repaint.
testerChild->setGeometry(QRectF(-15, -15, 30, 30));
QTRY_COMPARE(testerChild->repaints, 6);
QCOMPARE(tester->repaints, 7);
QCOMPARE(testerChild2->repaints, 4);
// Scaling the view does not cause a repaint.
view.scale(0.7, 0.7);
QTest::qWait(25);
QCOMPARE(tester->repaints, 7);
QCOMPARE(testerChild->repaints, 6);
QCOMPARE(testerChild2->repaints, 4);
// Switch to device coordinate cache.
tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
testerChild->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
testerChild2->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
QTRY_COMPARE(tester->repaints, 8);
QCOMPARE(testerChild->repaints, 7);
QCOMPARE(testerChild2->repaints, 5);
// Scaling the view back should cause repaints for two of the items.
view.setTransform(QTransform());
QTRY_COMPARE(tester->repaints, 9);
QCOMPARE(testerChild->repaints, 8);
QCOMPARE(testerChild2->repaints, 5);
// Rotating the base item (perspective) should repaint two items.
tester->setTransform(QTransform().rotate(10, Qt::XAxis));
QTRY_COMPARE(tester->repaints, 10);
QCOMPARE(testerChild->repaints, 9);
QCOMPARE(testerChild2->repaints, 5);
// Moving the middle item should cause a repaint even if it's a move,
// because the parent is rotated with a perspective.
testerChild->setPos(1, 1);
QTRY_COMPARE(tester->repaints, 11);
QTRY_COMPARE(testerChild->repaints, 10);
QCOMPARE(testerChild2->repaints, 5);
tester->resetTransform();
// Make a huge item
tester->setGeometry(QRectF(-4000, -4000, 8000, 8000));
QTRY_COMPARE(tester->repaints, 12);
QTRY_COMPARE(testerChild->repaints, 11);
QCOMPARE(testerChild2->repaints, 5);
// Move the large item - will cause a repaint as the
// cache is clipped.
tester->setPos(5, 0);
QTRY_COMPARE(tester->repaints, 13);
QTRY_COMPARE(testerChild->repaints, 11);
QCOMPARE(testerChild2->repaints, 5);
// Hiding and showing should invalidate the cache
tester->hide();
QTest::qWait(25);
tester->show();
QTRY_COMPARE(tester->repaints, 14);
QTRY_COMPARE(testerChild->repaints, 12);
QTRY_COMPARE(testerChild2->repaints, 6);
}
void tst_QGraphicsItem::cacheMode2()
{
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
QSKIP("Wayland: This fails. Figure out why.");
QGraphicsScene scene(0, 0, 100, 100);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.resize(150, 150);
view.show();
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QVERIFY(QTest::qWaitForWindowActive(&view));
QCoreApplication::processEvents(); // Process all queued paint events
EventTester *tester = new EventTester;
scene.addItem(tester);
QTRY_COMPARE(tester->repaints, 1);
// Switching from NoCache to NoCache (no repaint)
tester->setCacheMode(QGraphicsItem::NoCache);
QTest::qWait(50);
QCOMPARE(tester->repaints, 1);
// Switching from NoCache to DeviceCoordinateCache (no repaint)
tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
QTest::qWait(50);
QCOMPARE(tester->repaints, 1);
// Switching from DeviceCoordinateCache to DeviceCoordinateCache (no repaint)
tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
QTest::qWait(50);
QCOMPARE(tester->repaints, 1);
// Switching from DeviceCoordinateCache to NoCache (no repaint)
tester->setCacheMode(QGraphicsItem::NoCache);
QTest::qWait(50);
QCOMPARE(tester->repaints, 1);
// Switching from NoCache to ItemCoordinateCache (repaint)
tester->setCacheMode(QGraphicsItem::ItemCoordinateCache);
QTRY_COMPARE(tester->repaints, 2);
// Switching from ItemCoordinateCache to ItemCoordinateCache (no repaint)
tester->setCacheMode(QGraphicsItem::ItemCoordinateCache);
QTest::qWait(50);
QCOMPARE(tester->repaints, 2);
// Switching from ItemCoordinateCache to ItemCoordinateCache with different size (repaint)
tester->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(100, 100));
QTRY_COMPARE(tester->repaints, 3);
// Switching from ItemCoordinateCache to NoCache (repaint)
tester->setCacheMode(QGraphicsItem::NoCache);
QTRY_COMPARE(tester->repaints, 4);
// Switching from DeviceCoordinateCache to ItemCoordinateCache (repaint)
tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
QTest::qWait(50);
QCOMPARE(tester->repaints, 4);
tester->setCacheMode(QGraphicsItem::ItemCoordinateCache);
QTRY_COMPARE(tester->repaints, 5);
// Switching from ItemCoordinateCache to DeviceCoordinateCache (repaint)
tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
QTRY_COMPARE(tester->repaints, 6);
}
void tst_QGraphicsItem::updateCachedItemAfterMove()
{
// A simple item that uses ItemCoordinateCache
EventTester *tester = new EventTester;
tester->setCacheMode(QGraphicsItem::ItemCoordinateCache);
// Add to a scene, show in a view, ensure it's painted and reset its
// repaint counter.
QGraphicsScene scene;
scene.addItem(tester);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QTRY_VERIFY(tester->repaints > 0);
tester->repaints = 0;
// Move the item, should not cause repaints
tester->setPos(10, 0);
QTest::qWait(12);
QCOMPARE(tester->repaints, 0);
// Move then update, should cause one repaint
tester->setPos(20, 0);
tester->update();
QTRY_COMPARE(tester->repaints, 1);
// Hiding the item doesn't cause a repaint
tester->hide();
QTest::qWait(12);
QCOMPARE(tester->repaints, 1);
// Moving a hidden item doesn't cause a repaint
tester->setPos(30, 0);
tester->update();
QTest::qWait(12);
QCOMPARE(tester->repaints, 1);
}
class Track : public QGraphicsRectItem
{
public:
Track(const QRectF &rect)
: QGraphicsRectItem(rect)
{
setAcceptHoverEvents(true);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override
{
QGraphicsRectItem::paint(painter, option, widget);
const QString text = QString::number(p.x()) + QLatin1Char('x') + QString::number(p.y())
+ QLatin1Char('\n') + QString::number(sp.x()) + QLatin1Char('x') + QString::number(sp.y());
painter->drawText(boundingRect(), Qt::AlignCenter, text);
}
protected:
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override
{
p = event->pos();
sp = event->widget()->mapFromGlobal(event->screenPos());
update();
}
private:
QPointF p;
QPoint sp;
};
void tst_QGraphicsItem::deviceTransform_data()
{
QTest::addColumn<bool>("untransformable1");
QTest::addColumn<bool>("untransformable2");
QTest::addColumn<bool>("untransformable3");
QTest::addColumn<qreal>("rotation1");
QTest::addColumn<qreal>("rotation2");
QTest::addColumn<qreal>("rotation3");
QTest::addColumn<QTransform>("deviceX");
QTest::addColumn<QPointF>("mapResult1");
QTest::addColumn<QPointF>("mapResult2");
QTest::addColumn<QPointF>("mapResult3");
QTest::newRow("nil") << false << false << false
<< qreal(0.0) << qreal(0.0) << qreal(0.0)
<< QTransform()
<< QPointF(150, 150) << QPointF(250, 250) << QPointF(350, 350);
QTest::newRow("deviceX rot 90") << false << false << false
<< qreal(0.0) << qreal(0.0) << qreal(0.0)
<< QTransform().rotate(90)
<< QPointF(-150, 150) << QPointF(-250, 250) << QPointF(-350, 350);
QTest::newRow("deviceX rot 90 100") << true << false << false
<< qreal(0.0) << qreal(0.0) << qreal(0.0)
<< QTransform().rotate(90)
<< QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350);
QTest::newRow("deviceX rot 90 010") << false << true << false
<< qreal(0.0) << qreal(0.0) << qreal(0.0)
<< QTransform().rotate(90)
<< QPointF(-150, 150) << QPointF(-150, 250) << QPointF(-50, 350);
QTest::newRow("deviceX rot 90 001") << false << false << true
<< qreal(0.0) << qreal(0.0) << qreal(0.0)
<< QTransform().rotate(90)
<< QPointF(-150, 150) << QPointF(-250, 250) << QPointF(-250, 350);
QTest::newRow("deviceX rot 90 111") << true << true << true
<< qreal(0.0) << qreal(0.0) << qreal(0.0)
<< QTransform().rotate(90)
<< QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350);
QTest::newRow("deviceX rot 90 101") << true << false << true
<< qreal(0.0) << qreal(0.0) << qreal(0.0)
<< QTransform().rotate(90)
<< QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350);
}
void tst_QGraphicsItem::deviceTransform()
{
QFETCH(bool, untransformable1);
QFETCH(bool, untransformable2);
QFETCH(bool, untransformable3);
QFETCH(qreal, rotation1);
QFETCH(qreal, rotation2);
QFETCH(qreal, rotation3);
QFETCH(QTransform, deviceX);
QFETCH(QPointF, mapResult1);
QFETCH(QPointF, mapResult2);
QFETCH(QPointF, mapResult3);
QGraphicsScene scene;
Track *rect1 = new Track(QRectF(0, 0, 100, 100));
Track *rect2 = new Track(QRectF(0, 0, 100, 100));
Track *rect3 = new Track(QRectF(0, 0, 100, 100));
rect2->setParentItem(rect1);
rect3->setParentItem(rect2);
rect1->setPos(100, 100);
rect2->setPos(100, 100);
rect3->setPos(100, 100);
rect1->setTransform(QTransform().rotate(rotation1), true);
rect2->setTransform(QTransform().rotate(rotation2), true);
rect3->setTransform(QTransform().rotate(rotation3), true);
rect1->setFlag(QGraphicsItem::ItemIgnoresTransformations, untransformable1);
rect2->setFlag(QGraphicsItem::ItemIgnoresTransformations, untransformable2);
rect3->setFlag(QGraphicsItem::ItemIgnoresTransformations, untransformable3);
rect1->setBrush(Qt::red);
rect2->setBrush(Qt::green);
rect3->setBrush(Qt::blue);
scene.addItem(rect1);
QCOMPARE(rect1->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult1);
QCOMPARE(rect2->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult2);
QCOMPARE(rect3->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult3);
}
void tst_QGraphicsItem::update()
{
QGraphicsScene scene;
scene.setSceneRect(-100, -100, 200, 200);
QWidget topLevel;
topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
MyGraphicsView view(&scene,&topLevel);
topLevel.resize(300, 300);
topLevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
EventTester *item = new EventTester;
scene.addItem(item);
QTRY_VERIFY(item->repaints > 0); // Wait for painting
item->repaints = 0;
item->update(); // Item marked as dirty
scene.update(); // Entire scene marked as dirty
QTRY_COMPARE(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();
QTRY_COMPARE(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();
QTRY_COMPARE(item->repaints, 1);
QTRY_COMPARE(view.repaints, 1);
QRect itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform())
.mapRect(item->boundingRect()).toAlignedRect();
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)
QCoreApplication::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.
QCoreApplication::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();
QCoreApplication::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();
QCoreApplication::processEvents();
view.reset();
const QPointF originalPos = item->pos();
item->setPos(5000, 5000);
QCoreApplication::processEvents();
QCOMPARE(item->repaints, 0);
QCOMPARE(view.repaints, 0);
QCoreApplication::processEvents();
item->setPos(originalPos);
QCoreApplication::processEvents();
QCOMPARE(item->repaints, 0);
QCOMPARE(view.repaints, 0);
item->show();
QCoreApplication::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);
QTRY_VERIFY(view.repaints > 1 && item->repaints > 1);
item->repaints = 0;
view.reset();
item->setPos(originalPos);
QTRY_COMPARE(item->repaints, 1);
QCOMPARE(view.repaints, 1);
COMPARE_REGIONS(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);
itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform())
.mapRect(item->boundingRect()).toAlignedRect();
expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2);
view.reset();
item->repaints = 0;
parent->setTransform(QTransform::fromTranslate(-400, 0), true);
QCoreApplication::processEvents();
QCOMPARE(item->repaints, 0);
QCOMPARE(view.repaints, 1);
QCOMPARE(view.paintedRegion, expectedRegion);
view.reset();
item->repaints = 0;
parent->setTransform(QTransform::fromTranslate(400, 0), true);
QCoreApplication::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:
using QGraphicsRectItem::QGraphicsRectItem;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override
{
++repaints;
if (startTrack) {
//Doesn't use the extended style option so the exposed rect is the boundingRect
if (!(flags() & QGraphicsItem::ItemUsesExtendedStyleOption)) {
QCOMPARE(option->exposedRect, boundingRect());
} else {
QVERIFY(option->exposedRect != QRect());
QVERIFY(option->exposedRect != boundingRect());
}
}
QGraphicsRectItem::paint(painter, option, widget);
}
bool startTrack = false;
int repaints = 0;
};
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);
QWidget topLevel;
topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
topLevel.resize(200, 200);
QGraphicsView view(&scene, &topLevel);
topLevel.setWindowFlags(Qt::X11BypassWindowManagerHint);
rect->startTrack = false;
topLevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
QCoreApplication::processEvents(); // Process all queued paint events
QTRY_VERIFY(rect->repaints > 0);
rect->repaints = 0;
rect->startTrack = true;
rect->update(10, 10, 10, 10);
QTRY_COMPARE(rect->repaints, 1);
rect->repaints = 0;
rect->startTrack = false;
rect->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
QVERIFY((rect->flags() & QGraphicsItem::ItemUsesExtendedStyleOption));
QTRY_COMPARE(rect->repaints, 1);
rect->repaints = 0;
rect->startTrack = true;
rect->update(10, 10, 10, 10);
QTest::qWait(60);
// MyStyleOptionTester does not receive a paint event. Why not?
}
void tst_QGraphicsItem::itemSendsGeometryChanges()
{
ItemChangeTester item;
item.setFlags({ });
item.clear();
QTransform x = QTransform().rotate(45);
QPointF pos(10, 10);
qreal o(0.5);
qreal r(10.0);
qreal s(1.5);
QPointF origin(1.0, 1.0);
item.setTransform(x);
item.setPos(pos);
item.setRotation(r);
item.setScale(s);
item.setTransformOriginPoint(origin);
QCOMPARE(item.transform(), x);
QCOMPARE(item.pos(), pos);
QCOMPARE(item.rotation(), r);
QCOMPARE(item.scale(), s);
QCOMPARE(item.transformOriginPoint(), origin);
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);
item.setRotation(0.0);
item.setScale(1.0);
item.setTransformOriginPoint(0.0, 0.0);
QCOMPARE(item.changes.size(), 14); // rotation + scale + origin
QCOMPARE(item.rotation(), qreal(0.0));
QCOMPARE(item.scale(), qreal(1.0));
QCOMPARE(item.transformOriginPoint(), QPointF(0.0, 0.0));
const QList<QGraphicsItem::GraphicsItemChange> expected {
QGraphicsItem::ItemOpacityChange,
QGraphicsItem::ItemOpacityHasChanged,
QGraphicsItem::ItemFlagsChange,
QGraphicsItem::ItemFlagsHaveChanged,
QGraphicsItem::ItemTransformChange,
QGraphicsItem::ItemTransformHasChanged,
QGraphicsItem::ItemPositionChange,
QGraphicsItem::ItemPositionHasChanged,
QGraphicsItem::ItemRotationChange,
QGraphicsItem::ItemRotationHasChanged,
QGraphicsItem::ItemScaleChange,
QGraphicsItem::ItemScaleHasChanged,
QGraphicsItem::ItemTransformOriginPointChange,
QGraphicsItem::ItemTransformOriginPointHasChanged
};
QCOMPARE(item.changes, expected);
}
// Make sure we update moved items correctly.
void tst_QGraphicsItem::moveItem()
{
QGraphicsScene scene;
scene.setSceneRect(-50, -50, 200, 200);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCoreApplication::processEvents(); // Process all queued paint events
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();
RESET_COUNTERS
scene.addItem(parent);
QTRY_COMPARE(view.repaints, 1);
RESET_COUNTERS
// Item's boundingRect: (-10, -10, 20, 20).
QRect parentDeviceBoundingRect = parent->deviceTransform(view.viewportTransform())
.mapRect(parent->boundingRect()).toAlignedRect()
.adjusted(-2, -2, 2, 2); // Adjusted for antialiasing.
parent->setPos(20, 20);
QCoreApplication::processEvents();
QCOMPARE(parent->repaints, 1);
QCOMPARE(view.repaints, 1);
QRegion expectedParentRegion = parentDeviceBoundingRect; // old position
parentDeviceBoundingRect.translate(20, 20);
expectedParentRegion += parentDeviceBoundingRect; // new position
COMPARE_REGIONS(view.paintedRegion, expectedParentRegion);
RESET_COUNTERS
child->setPos(20, 20);
QCoreApplication::processEvents();
QCOMPARE(parent->repaints, 1);
QCOMPARE(child->repaints, 1);
QCOMPARE(view.repaints, 1);
const QRegion expectedChildRegion = expectedParentRegion.translated(20, 20);
COMPARE_REGIONS(view.paintedRegion, expectedChildRegion);
RESET_COUNTERS
grandChild->setPos(20, 20);
QCoreApplication::processEvents();
QCOMPARE(parent->repaints, 1);
QCOMPARE(child->repaints, 1);
QCOMPARE(grandChild->repaints, 1);
QCOMPARE(view.repaints, 1);
const QRegion expectedGrandChildRegion = expectedParentRegion.translated(40, 40);
COMPARE_REGIONS(view.paintedRegion, expectedGrandChildRegion);
RESET_COUNTERS
parent->setTransform(QTransform::fromTranslate(20, 20), true);
QCoreApplication::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);
COMPARE_REGIONS(view.paintedRegion, expectedParentRegion);
}
void tst_QGraphicsItem::moveLineItem()
{
QGraphicsScene scene;
scene.setSceneRect(0, 0, 200, 200);
QGraphicsLineItem *item = new QGraphicsLineItem(0, 0, 100, 0);
item->setPos(50, 50);
scene.addItem(item);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCoreApplication::processEvents(); // Process all queued paint events
view.reset();
QRectF brect = item->boundingRect();
// Do same adjustments as in qgraphicsscene.cpp
if (qFuzzyIsNull(brect.width()))
brect.adjust(qreal(-0.00001), 0, qreal(0.00001), 0);
if (qFuzzyIsNull(brect.height()))
brect.adjust(0, qreal(-0.00001), 0, qreal(0.00001));
const QRect itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform())
.mapRect(brect).toAlignedRect();
QRegion expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2); // antialiasing
// Make sure the calculated region is correct.
item->update();
QTRY_COMPARE(view.paintedRegion, expectedRegion);
view.reset();
// Old position: (50, 50)
item->setPos(50, 100);
expectedRegion += expectedRegion.translated(0, 50);
QTRY_COMPARE(view.paintedRegion, expectedRegion);
}
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()
{
if (qGuiApp->styleHints()->showIsFullScreen())
QSKIP("Skipped because Platform is auto maximizing");
_paintedItems.clear();
QGraphicsScene scene;
QGraphicsItem *grid[100][100];
for (int x = 0; x < 100; ++x) {
const QString prefix = QString::number(x) + QLatin1Char('x');
for (int y = 0; y < 100; ++y) {
PainterItem *item = new PainterItem;
item->setPos(x * 25, y * 25);
item->setData(0, prefix + QString::number(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.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
// Use Qt::Tool as fully decorated windows have a minimum width of 160 on Windows.
view.setWindowFlags(view.windowFlags() | Qt::Tool);
view.setResizeAnchor(QGraphicsView::NoAnchor);
view.setTransformationAnchor(QGraphicsView::NoAnchor);
view.resize(120, 100);
view.setFrameStyle(0);
view.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QVERIFY(QTest::qWaitForWindowExposed(&view));
QTRY_VERIFY(_paintedItems.size() > 0);
_paintedItems.clear();
view.viewport()->update();
const GraphicsItems expected{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};
QTRY_COMPARE(_paintedItems, expected);
}
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.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QVERIFY(QTest::qWaitForWindowExposed(&view));
QTRY_VERIFY(!_paintedItems.isEmpty());
_paintedItems.clear();
view.viewport()->update();
QTRY_COMPARE(_paintedItems, GraphicsItems{item2});
}
void tst_QGraphicsItem::hitTestUntransformableItem()
{
QGraphicsScene scene;
scene.setSceneRect(-100, -100, 200, 200);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
// 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);
QList<QGraphicsItem *> items = scene.items(QPointF(80, 80));
QCOMPARE(items.size(), 1);
QCOMPARE(items.at(0), 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);
QWidget toplevel;
toplevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
QGraphicsView view(&scene, &toplevel);
toplevel.resize(300, 300);
toplevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&toplevel));
QCoreApplication::processEvents(); // Process all queued paint events
// 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);
item1->brush = Qt::red;
EventTester *item2 = new EventTester;
item2->br = itemBoundingRect;
item2->setFlag(QGraphicsItem::ItemIgnoresTransformations);
item2->setParentItem(item1);
item2->setPos(200, 200);
item2->brush = Qt::green;
EventTester *item3 = new EventTester;
item3->br = itemBoundingRect;
item3->setParentItem(item2);
item3->setPos(80, 80);
item3->brush = Qt::blue;
scene.addItem(item1);
QTRY_COMPARE(item2->repaints, 1);
QCOMPARE(item3->repaints, 1);
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);
// Make sure all visible items are repainted.
QTRY_COMPARE(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), item3);
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<QGraphicsItem *>(item3));
}
void tst_QGraphicsItem::focusProxy()
{
QGraphicsScene scene;
QEvent activate(QEvent::WindowActivate);
QCoreApplication::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: "
<< static_cast<const void*>(item) << " is already in the focus proxy chain" << Qt::flush;
QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData());
item2->setFocusProxy(item); // fails
QCOMPARE(item->focusProxy(), item2);
QCOMPARE(item2->focusProxy(), nullptr);
// 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(), item2);
QCOMPARE(item2->focusProxy(), nullptr);
// Reset the focus proxy
item->setFocusProxy(nullptr);
QCOMPARE(item->focusProxy(), nullptr);
QVERIFY(!item->hasFocus());
QVERIFY(item2->hasFocus());
// Test deletion
item->setFocusProxy(item2);
QCOMPARE(item->focusProxy(), item2);
delete item2;
QCOMPARE(item->focusProxy(), nullptr);
// 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(), nullptr);
QCOMPARE(item3->focusProxy(), item2);
delete item2;
QCOMPARE(item->focusProxy(), nullptr);
QCOMPARE(item3->focusProxy(), nullptr);
}
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(), 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);
QCoreApplication::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(), text2);
text2->setParentItem(text);
QCOMPARE(text->focusItem(), text2);
QCOMPARE(text2->focusItem(), 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(), nullptr);
QCOMPARE(text2->focusItem(), nullptr);
text2->setFocus();
QCOMPARE(text->focusItem(), text2);
QCOMPARE(text2->focusItem(), text2);
scene.addItem(text);
// Hiding and showing text should pass focus to text2.
QCOMPARE(text->focusItem(), 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(), rect3);
QCOMPARE(rect2->focusItem(), rect3);
QCOMPARE(rect3->focusItem(), rect3);
rect->setParentItem(text2);
QCOMPARE(text->focusItem(), rect3);
QCOMPARE(text2->focusItem(), rect3);
QCOMPARE(rect->focusItem(), rect3);
QCOMPARE(rect2->focusItem(), rect3);
QCOMPARE(rect3->focusItem(), rect3);
QVERIFY(!rect->hasFocus());
QVERIFY(!rect2->hasFocus());
QVERIFY(rect3->hasFocus());
delete rect2;
QCOMPARE(text->focusItem(), nullptr);
QCOMPARE(text2->focusItem(), nullptr);
QCOMPARE(rect->focusItem(), nullptr);
}
void tst_QGraphicsItem::focusProxyDeletion()
{
QGraphicsRectItem *rect = new QGraphicsRectItem;
QGraphicsRectItem *rect2 = new QGraphicsRectItem;
rect->setFocusProxy(rect2);
QCOMPARE(rect->focusProxy(), rect2);
delete rect2;
QCOMPARE(rect->focusProxy(), nullptr);
rect2 = new QGraphicsRectItem;
rect->setFocusProxy(rect2);
QGraphicsItem **danglingFocusProxyRef = &rect->d_ptr->focusProxy;
delete rect; // don't crash
QVERIFY(!rect2->d_ptr->focusProxyRefs.contains(danglingFocusProxyRef));
rect = new QGraphicsRectItem;
rect->setFocusProxy(rect2);
QGraphicsScene *scene = new QGraphicsScene;
scene->addItem(rect);
scene->addItem(rect2);
delete rect2;
QCOMPARE(rect->focusProxy(), nullptr);
rect2 = new QGraphicsRectItem;
QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::setFocusProxy: focus proxy must be in same scene");
rect->setFocusProxy(rect2);
QCOMPARE(rect->focusProxy(), nullptr);
scene->addItem(rect2);
rect->setFocusProxy(rect2);
QCOMPARE(rect->focusProxy(), rect2);
delete rect; // don't crash
rect = new QGraphicsRectItem;
rect2 = new QGraphicsRectItem;
rect->setFocusProxy(rect2);
QCOMPARE(rect->focusProxy(), 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;
// Ensure the effect is uninstalled when deleting it
item = new QGraphicsRectItem(0, 0, 10, 10);
blurEffect = new QGraphicsBlurEffect;
item->setGraphicsEffect(blurEffect);
delete blurEffect;
QVERIFY(!item->graphicsEffect());
// Ensure the existing effect is uninstalled and deleted when setting a null effect
blurEffect = new QGraphicsBlurEffect;
item->setGraphicsEffect(blurEffect);
item->setGraphicsEffect(nullptr);
QVERIFY(!item->graphicsEffect());
QVERIFY(!blurEffect);
delete item;
}
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);
QCoreApplication::sendEvent(&scene, &activate);
// No previous activation, so the scene is active.
QVERIFY(scene.isActive());
QCOMPARE(scene.activePanel(), 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(nullptr);
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
QCoreApplication::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
QCoreApplication::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
QCoreApplication::sendEvent(&scene, &deactivate);
QVERIFY(!panel1->isActive());
QCOMPARE(spy_deactivate_panel1.count(), 2);
// Reactivate the scene
QCoreApplication::sendEvent(&scene, &activate);
QVERIFY(panel1->isActive());
QCOMPARE(spy_activate_panel1.count(), 3);
// Deactivate the scene
QCoreApplication::sendEvent(&scene, &deactivate);
QVERIFY(!panel1->isActive());
QVERIFY(!scene.activePanel());
scene.setActivePanel(nullptr);
// Reactivate the scene
QCoreApplication::sendEvent(&scene, &activate);
QVERIFY(!panel1->isActive());
}
void tst_QGraphicsItem::panelWithFocusItems()
{
for (int i = 0; i < 2; ++i)
{
QGraphicsScene scene;
QEvent activate(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &activate);
bool widget = (i == 1);
auto parentPanel = widget
? static_cast<QGraphicsItem *>(new QGraphicsWidget)
: static_cast<QGraphicsItem *>(new QGraphicsRectItem);
auto parentPanelFocusItem = widget
? static_cast<QGraphicsItem *>(new QGraphicsWidget)
: static_cast<QGraphicsItem *>(new QGraphicsRectItem);
auto parentPanelFocusItemSibling = widget
? static_cast<QGraphicsItem *>(new QGraphicsWidget)
: static_cast<QGraphicsItem *>(new QGraphicsRectItem);
parentPanel->setFlag(QGraphicsItem::ItemIsPanel);
parentPanelFocusItem->setFlag(QGraphicsItem::ItemIsFocusable);
parentPanelFocusItemSibling->setFlag(QGraphicsItem::ItemIsFocusable);
if (widget) {
static_cast<QGraphicsWidget *>(parentPanelFocusItem)->setFocusPolicy(Qt::StrongFocus);
static_cast<QGraphicsWidget *>(parentPanelFocusItemSibling)->setFocusPolicy(Qt::StrongFocus);
}
parentPanelFocusItem->setParentItem(parentPanel);
parentPanelFocusItemSibling->setParentItem(parentPanel);
parentPanelFocusItem->setFocus();
scene.addItem(parentPanel);
QVERIFY(parentPanel->isActive());
QVERIFY(parentPanelFocusItem->hasFocus());
QCOMPARE(parentPanel->focusItem(), parentPanelFocusItem);
QCOMPARE(parentPanelFocusItem->focusItem(), parentPanelFocusItem);
auto childPanel = widget
? static_cast<QGraphicsItem *>(new QGraphicsWidget)
: static_cast<QGraphicsItem *>(new QGraphicsRectItem);
auto childPanelFocusItem = widget
? static_cast<QGraphicsItem *>(new QGraphicsWidget)
: static_cast<QGraphicsItem *>(new QGraphicsRectItem);
auto grandChildPanelFocusItem = widget
? static_cast<QGraphicsItem *>(new QGraphicsWidget)
: static_cast<QGraphicsItem *>(new QGraphicsRectItem);
auto grandChildPanelFocusItem2 = widget
? static_cast<QGraphicsItem *>(new QGraphicsWidget)
: static_cast<QGraphicsItem *>(new QGraphicsRectItem);
childPanel->setFlag(QGraphicsItem::ItemIsPanel);
childPanelFocusItem->setFlag(QGraphicsItem::ItemIsFocusable);
grandChildPanelFocusItem->setFlag(QGraphicsItem::ItemIsFocusable);
grandChildPanelFocusItem2->setFlag(QGraphicsItem::ItemIsFocusable);
if (widget) {
static_cast<QGraphicsWidget *>(childPanelFocusItem)->setFocusPolicy(Qt::StrongFocus);
static_cast<QGraphicsWidget *>(grandChildPanelFocusItem)->setFocusPolicy(Qt::StrongFocus);
static_cast<QGraphicsWidget *>(grandChildPanelFocusItem2)->setFocusPolicy(Qt::StrongFocus);
}
grandChildPanelFocusItem->setParentItem(childPanelFocusItem);
grandChildPanelFocusItem2->setParentItem(childPanelFocusItem);
childPanelFocusItem->setParentItem(childPanel);
grandChildPanelFocusItem->setFocus();
QVERIFY(!grandChildPanelFocusItem->hasFocus());
QCOMPARE(childPanel->focusItem(), grandChildPanelFocusItem);
QCOMPARE(childPanelFocusItem->focusItem(), grandChildPanelFocusItem);
QCOMPARE(grandChildPanelFocusItem->focusItem(), grandChildPanelFocusItem);
childPanel->setParentItem(parentPanel);
QVERIFY(!parentPanel->isActive());
QVERIFY(!parentPanelFocusItem->hasFocus());
QCOMPARE(parentPanel->focusItem(), parentPanelFocusItem);
QCOMPARE(parentPanelFocusItem->focusItem(), parentPanelFocusItem);
QVERIFY(childPanel->isActive());
QVERIFY(!childPanelFocusItem->hasFocus());
QVERIFY(grandChildPanelFocusItem->hasFocus());
QCOMPARE(childPanel->focusItem(), grandChildPanelFocusItem);
QCOMPARE(childPanelFocusItem->focusItem(), grandChildPanelFocusItem);
childPanel->hide();
QCOMPARE(childPanel->focusItem(), grandChildPanelFocusItem);
QVERIFY(!childPanel->focusItem()->hasFocus());
QVERIFY(parentPanel->isActive());
QVERIFY(parentPanelFocusItem->hasFocus());
QCOMPARE(parentPanel->focusItem(), parentPanelFocusItem);
QCOMPARE(parentPanelFocusItem->focusItem(), parentPanelFocusItem);
QCOMPARE(grandChildPanelFocusItem->focusItem(), grandChildPanelFocusItem);
childPanel->show();
QVERIFY(childPanel->isActive());
QVERIFY(grandChildPanelFocusItem->hasFocus());
QCOMPARE(childPanel->focusItem(), grandChildPanelFocusItem);
QCOMPARE(childPanelFocusItem->focusItem(), grandChildPanelFocusItem);
QCOMPARE(grandChildPanelFocusItem->focusItem(), grandChildPanelFocusItem);
childPanel->hide();
QVERIFY(parentPanel->isActive());
QVERIFY(parentPanelFocusItem->hasFocus());
QCOMPARE(parentPanel->focusItem(), parentPanelFocusItem);
QCOMPARE(parentPanelFocusItem->focusItem(), parentPanelFocusItem);
}
}
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);
QCoreApplication::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(), rect);
QGraphicsRectItem *rect2 = new QGraphicsRectItem;
scene.addItem(rect2);
QVERIFY(rect->isActive());
QCOMPARE(scene.activePanel(), 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);
QCoreApplication::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(), nullptr);
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(), rect3);
scene.removeItem(rect4);
rect6->setActive(true);
scene.addItem(rect4);
QVERIFY(rect4->isActive());
QVERIFY(rect5->isActive());
QVERIFY(rect6->isActive());
QCOMPARE(scene.activePanel(), 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(), nullptr);
// Controlling auto-activation when the scene changes activation.
rect4->setActive(true);
QCoreApplication::sendEvent(&scene, &deactivate);
QVERIFY(!scene.isActive());
QVERIFY(!rect4->isActive());
rect4->setActive(false);
QCoreApplication::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(), nullptr);
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);
QCoreApplication::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:
using QGraphicsRectItem::QGraphicsRectItem;
~MoveWhileDying()
{
const auto children = childItems();
for (QGraphicsItem *c : children) {
const auto grandChildren = c->childItems();
for (QGraphicsItem *cc : grandChildren)
cc->moveBy(10, 10);
c->moveBy(10, 10);
}
if (QGraphicsItem *p = parentItem())
p->moveBy(10, 10);
}
};
void tst_QGraphicsItem::deactivateInactivePanel()
{
QGraphicsScene scene;
QGraphicsItem *panel1 = scene.addRect(QRectF(0, 0, 10, 10));
panel1->setFlag(QGraphicsItem::ItemIsPanel);
QGraphicsItem *panel2 = scene.addRect(QRectF(0, 0, 10, 10));
panel2->setFlag(QGraphicsItem::ItemIsPanel);
QEvent event(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &event);
panel1->setActive(true);
QVERIFY(scene.isActive());
QVERIFY(panel1->isActive());
QVERIFY(!panel2->isActive());
QCOMPARE(scene.activePanel(), panel1);
panel2->setActive(true);
QVERIFY(panel2->isActive());
QVERIFY(!panel1->isActive());
QCOMPARE(scene.activePanel(), panel2);
panel2->setActive(false);
QVERIFY(panel1->isActive());
QVERIFY(!panel2->isActive());
QCOMPARE(scene.activePanel(), panel1);
panel2->setActive(false);
QVERIFY(panel1->isActive());
QVERIFY(!panel2->isActive());
QCOMPARE(scene.activePanel(), panel1);
}
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.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
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:
using QGraphicsWidget::QGraphicsWidget;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
{
painter->setBrush(brush);
painter->drawRect(boundingRect());
}
void move()
{
setPos(-100,-100);
topLevel->collidingItems(Qt::IntersectsItemBoundingRect);
}
public:
QGraphicsItem *topLevel = nullptr;
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.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
}
QVERIFY(QTest::qWaitForWindowExposed(&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(), 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(), scope3);
QCOMPARE(scope2->focusItem(), scope3);
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(), scope3);
QCOMPARE(scope2->focusItem(), scope3);
QCOMPARE(scope3->focusItem(), scope3);
QCOMPARE(scope1->focusScopeItem(), scope2);
QCOMPARE(scope2->focusScopeItem(), scope3);
QCOMPARE(scope3->focusScopeItem(), nullptr);
scene.addItem(scope1);
QEvent windowActivate(QEvent::WindowActivate);
qApp->sendEvent(&scene, &windowActivate);
scene.setFocus();
QCOMPARE(scope1->focusItem(), scope3);
QCOMPARE(scope2->focusItem(), scope3);
QCOMPARE(scope3->focusItem(), scope3);
QCOMPARE(scope1->focusScopeItem(), scope2);
QCOMPARE(scope2->focusScopeItem(), scope3);
QCOMPARE(scope3->focusScopeItem(), nullptr);
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(scope1->hasFocus());
scope2->setFocus();
QVERIFY(scope2->hasFocus());
scope3->setFocus();
QVERIFY(scope3->hasFocus());
// clearFocus() on a focus scope will remove focus from its children.
scope1->clearFocus();
QVERIFY(!scope1->hasFocus());
QVERIFY(!scope2->hasFocus());
QVERIFY(!scope3->hasFocus());
scope1->setFocus();
QVERIFY(!scope1->hasFocus());
QVERIFY(!scope2->hasFocus());
QVERIFY(scope3->hasFocus());
scope2->clearFocus();
QVERIFY(scope1->hasFocus());
QVERIFY(!scope2->hasFocus());
QVERIFY(!scope3->hasFocus());
scope2->setFocus();
QVERIFY(!scope1->hasFocus());
QVERIFY(!scope2->hasFocus());
QVERIFY(scope3->hasFocus());
// Focus cleared while a parent doesn't have focus remains cleared
// when the parent regains focus.
scope1->clearFocus();
scope3->clearFocus();
QVERIFY(!scope1->hasFocus());
QVERIFY(!scope2->hasFocus());
QVERIFY(!scope3->hasFocus());
scope1->setFocus();
QVERIFY(!scope1->hasFocus());
QVERIFY(scope2->hasFocus());
QVERIFY(!scope3->hasFocus());
scope3->setFocus();
QVERIFY(!scope1->hasFocus());
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(), rect5);
QVERIFY(rect5->hasFocus());
rect4->setParentItem(nullptr);
QVERIFY(rect5->hasFocus());
QCOMPARE(scope3->focusScopeItem(), nullptr);
QCOMPARE(scope3->focusItem(), nullptr);
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(), scopeB);
}
void tst_QGraphicsItem::focusScope2()
{
QGraphicsRectItem *child1 = new QGraphicsRectItem;
child1->setFlags(QGraphicsItem::ItemIsFocusable);
child1->setFocus();
QCOMPARE(child1->focusItem(), child1);
QGraphicsRectItem *child2 = new QGraphicsRectItem;
child2->setFlags(QGraphicsItem::ItemIsFocusable);
QGraphicsRectItem *rootFocusScope = new QGraphicsRectItem;
rootFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
rootFocusScope->setFocus();
QCOMPARE(rootFocusScope->focusItem(), rootFocusScope);
child1->setParentItem(rootFocusScope);
child2->setParentItem(rootFocusScope);
QCOMPARE(rootFocusScope->focusScopeItem(), child1);
QCOMPARE(rootFocusScope->focusItem(), child1);
QGraphicsRectItem *siblingChild1 = new QGraphicsRectItem;
siblingChild1->setFlags(QGraphicsItem::ItemIsFocusable);
siblingChild1->setFocus();
QGraphicsRectItem *siblingChild2 = new QGraphicsRectItem;
siblingChild2->setFlags(QGraphicsItem::ItemIsFocusable);
QGraphicsRectItem *siblingFocusScope = new QGraphicsRectItem;
siblingFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
siblingChild1->setParentItem(siblingFocusScope);
siblingChild2->setParentItem(siblingFocusScope);
QCOMPARE(siblingFocusScope->focusScopeItem(), siblingChild1);
QCOMPARE(siblingFocusScope->focusItem(), nullptr);
QGraphicsItem *root = new QGraphicsRectItem;
rootFocusScope->setParentItem(root);
siblingFocusScope->setParentItem(root);
QCOMPARE(root->focusItem(), child1);
QGraphicsScene scene;
scene.addItem(root);
QEvent activate(QEvent::WindowActivate);
qApp->sendEvent(&scene, &activate);
scene.setFocus();
QCOMPARE(scene.focusItem(), child1);
// You cannot set focus on a descendant of a focus scope directly;
// this will only change the scope's focus scope item pointer. If
// you want to give true input focus, you must set it directly on
// the scope itself
siblingChild2->setFocus();
QVERIFY(!siblingChild2->hasFocus());
QVERIFY(!siblingChild2->focusItem());
QCOMPARE(siblingFocusScope->focusScopeItem(), siblingChild2);
QCOMPARE(siblingFocusScope->focusItem(), nullptr);
// Set focus on the scope; focus is forwarded to the focus scope item.
siblingFocusScope->setFocus();
QVERIFY(siblingChild2->hasFocus());
QVERIFY(siblingChild2->focusItem());
QCOMPARE(siblingFocusScope->focusScopeItem(), siblingChild2);
QCOMPARE(siblingFocusScope->focusItem(), siblingChild2);
}
class FocusScopeItemPrivate;
class FocusScopeItem : public QGraphicsItem
{
Q_DECLARE_PRIVATE(FocusScopeItem)
public:
FocusScopeItem(QGraphicsItem *parent = nullptr);
QRectF boundingRect() const override { return QRectF(); }
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override { }
int focusScopeChanged = 0;
FocusScopeItemPrivate *d_ptr;
};
class FocusScopeItemPrivate : QGraphicsItemPrivate
{
Q_DECLARE_PUBLIC(FocusScopeItem)
public:
void focusScopeItemChange(bool) override
{ ++q_func()->focusScopeChanged; }
};
FocusScopeItem::FocusScopeItem(QGraphicsItem *parent)
: QGraphicsItem(*new FocusScopeItemPrivate, parent)
{
setFlag(ItemIsFocusable);
}
void tst_QGraphicsItem::focusScopeItemChangedWhileScopeDoesntHaveFocus()
{
QGraphicsRectItem rect;
rect.setFlags(QGraphicsItem::ItemIsFocusScope | QGraphicsItem::ItemIsFocusable);
FocusScopeItem *child1 = new FocusScopeItem(&rect);
FocusScopeItem *child2 = new FocusScopeItem(&rect);
QCOMPARE(rect.focusScopeItem(), nullptr);
QCOMPARE(child1->focusScopeChanged, 0);
QCOMPARE(child2->focusScopeChanged, 0);
child1->setFocus();
QCOMPARE(rect.focusScopeItem(), child1);
QCOMPARE(child1->focusScopeChanged, 1);
QCOMPARE(child2->focusScopeChanged, 0);
child2->setFocus();
QCOMPARE(rect.focusScopeItem(), child2);
QCOMPARE(child1->focusScopeChanged, 2);
QCOMPARE(child2->focusScopeChanged, 1);
child1->setFocus();
QCOMPARE(rect.focusScopeItem(), child1);
QCOMPARE(child1->focusScopeChanged, 3);
QCOMPARE(child2->focusScopeChanged, 2);
child1->clearFocus();
QCOMPARE(rect.focusScopeItem(), nullptr);
QCOMPARE(child1->focusScopeChanged, 4);
QCOMPARE(child2->focusScopeChanged, 2);
}
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);
const GraphicsItemsList expected1234{child1, child2, child3, child4};
QCOMPARE(parent.childItems(), expected1234);
child1->setData(0, "child1");
child2->setData(0, "child2");
child3->setData(0, "child3");
child4->setData(0, "child4");
// Remove and append
child2->setParentItem(nullptr);
child2->setParentItem(&parent);
const GraphicsItemsList expected1342{child1, child3, child4, child2};
QCOMPARE(parent.childItems(), expected1342);
// Move child2 before child1
child2->stackBefore(child1); // 2134
const GraphicsItemsList expected2134{child2, child1, child3, child4};
QCOMPARE(parent.childItems(), expected2134);
child2->stackBefore(child2); // 2134
QCOMPARE(parent.childItems(), expected2134);
child1->setZValue(1); // 2341
const GraphicsItemsList expected2341{child2, child3, child4, child1};
QCOMPARE(parent.childItems(), expected2341);
child1->stackBefore(child2); // 2341
QCOMPARE(parent.childItems(), expected2341);
child1->setZValue(0); // 1234
QCOMPARE(parent.childItems(), expected1234);
child4->stackBefore(child1); // 4123
const GraphicsItemsList expected4123{child4, child1, child2, child3};
QCOMPARE(parent.childItems(), expected4123);
child4->setZValue(1); // 1234 (4123)
QCOMPARE(parent.childItems(), expected1234);
child3->stackBefore(child1); // 3124 (4312)
const GraphicsItemsList expected3124{child3, child1, child2, child4};
QCOMPARE(parent.childItems(), expected3124);
child4->setZValue(0); // 4312
const GraphicsItemsList expected4312{child4, child3, child1, child2};
QCOMPARE(parent.childItems(), expected4312);
// Make them all toplevels
child1->setParentItem(nullptr);
child2->setParentItem(nullptr);
child3->setParentItem(nullptr);
child4->setParentItem(nullptr);
QGraphicsScene scene;
scene.addItem(child1);
scene.addItem(child2);
scene.addItem(child3);
scene.addItem(child4);
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected1234);
// Remove and append
scene.removeItem(child2);
scene.addItem(child2);
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected1342);
// Move child2 before child1
child2->stackBefore(child1); // 2134
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected2134);
child2->stackBefore(child2); // 2134
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected2134);
child1->setZValue(1); // 2341
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected2341);
child1->stackBefore(child2); // 2341
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected2341);
child1->setZValue(0); // 1234
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected1234);
child4->stackBefore(child1); // 4123
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected4123);
child4->setZValue(1); // 1234 (4123)
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected1234);
child3->stackBefore(child1); // 3124 (4312)
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected3124);
child4->setZValue(0); // 4312
QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), expected4312);
}
void tst_QGraphicsItem::QTBUG_4233_updateCachedWithSceneRect()
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QSKIP("Window activation is not supported");
EventTester *tester = new EventTester;
tester->setCacheMode(QGraphicsItem::ItemCoordinateCache);
QGraphicsScene scene;
scene.addItem(tester);
scene.setSceneRect(-100, -100, 200, 200); // contains the tester item
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QVERIFY(QTest::qWaitForWindowActive(&view));
QCOMPARE(QApplication::activeWindow(), &view);
QTRY_COMPARE(tester->repaints, 1);
scene.update(); // triggers "updateAll" optimization
QCoreApplication::processEvents();
QCoreApplication::processEvents(); // in 4.6 only one processEvents is necessary
QCOMPARE(tester->repaints, 1);
scene.update(); // triggers "updateAll" optimization
tester->update();
QCoreApplication::processEvents();
QCoreApplication::processEvents(); // in 4.6 only one processEvents is necessary
QCOMPARE(tester->repaints, 2);
}
void tst_QGraphicsItem::sceneModality()
{
// 1) Test mouse events (delivery/propagation/redirection)
// 2) Test hover events (incl. leave on block, enter on unblock)
// 3) Test cursor stuff (incl. unset on block, set on unblock)
// 4) Test clickfocus
// 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock)
// 6) ### modality for non-panels is unsupported for now
QGraphicsScene scene;
QGraphicsRectItem *bottomItem = scene.addRect(-150, -100, 300, 200);
bottomItem->setFlag(QGraphicsItem::ItemIsFocusable);
bottomItem->setBrush(Qt::yellow);
QGraphicsRectItem *leftParent = scene.addRect(-50, -50, 100, 100);
leftParent->setFlag(QGraphicsItem::ItemIsPanel);
leftParent->setBrush(Qt::blue);
QGraphicsRectItem *leftChild = scene.addRect(-25, -25, 50, 50);
leftChild->setFlag(QGraphicsItem::ItemIsPanel);
leftChild->setBrush(Qt::green);
leftChild->setParentItem(leftParent);
QGraphicsRectItem *rightParent = scene.addRect(-50, -50, 100, 100);
rightParent->setFlag(QGraphicsItem::ItemIsPanel);
rightParent->setBrush(Qt::red);
QGraphicsRectItem *rightChild = scene.addRect(-25, -25, 50, 50);
rightChild->setFlag(QGraphicsItem::ItemIsPanel);
rightChild->setBrush(Qt::gray);
rightChild->setParentItem(rightParent);
leftParent->setPos(-75, 0);
rightParent->setPos(75, 0);
bottomItem->setData(0, "bottomItem");
leftParent->setData(0, "leftParent");
leftChild->setData(0, "leftChild");
rightParent->setData(0, "rightParent");
rightChild->setData(0, "rightChild");
scene.setSceneRect(scene.itemsBoundingRect().adjusted(-50, -50, 50, 50));
EventSpy2 leftParentSpy(&scene, leftParent);
EventSpy2 leftChildSpy(&scene, leftChild);
EventSpy2 rightParentSpy(&scene, rightParent);
EventSpy2 rightChildSpy(&scene, rightChild);
EventSpy2 bottomItemSpy(&scene, bottomItem);
// Scene modality, also test multiple scene modal items
leftChild->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); // not a panel
// Click inside left child
sendMouseClick(&scene, leftChild->scenePos(), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
// Click on left parent, event goes to modal child
sendMouseClick(&scene, leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 2);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
// Click on all other items and outside the items
sendMouseClick(&scene, rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 3);
sendMouseClick(&scene, rightChild->scenePos(), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 4);
sendMouseClick(&scene, bottomItem->scenePos(), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 5);
sendMouseClick(&scene, QPointF(10000, 10000), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 6);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
leftChildSpy.counts.clear();
rightChildSpy.counts.clear();
leftParentSpy.counts.clear();
rightParentSpy.counts.clear();
bottomItemSpy.counts.clear();
leftChild->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 1);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowUnblocked], 0);
// Left parent enters scene modality.
leftParent->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
// Click inside left child.
sendMouseClick(&scene, leftChild->scenePos(), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // panel stops propagation
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0);
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
// Click on left parent.
sendMouseClick(&scene, leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0);
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0);
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0);
// Click on all other items and outside the items
sendMouseClick(&scene, rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton);
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 2);
sendMouseClick(&scene, rightChild->scenePos(), Qt::LeftButton);
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 3);
sendMouseClick(&scene, bottomItem->scenePos(), Qt::LeftButton);
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 4);
sendMouseClick(&scene, QPointF(10000, 10000), Qt::LeftButton);
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 5);
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0);
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0);
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0);
leftChildSpy.counts.clear();
rightChildSpy.counts.clear();
leftParentSpy.counts.clear();
rightParentSpy.counts.clear();
bottomItemSpy.counts.clear();
// Now both left parent and child are scene modal. Left parent is blocked.
leftChild->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
// Click inside left child
sendMouseClick(&scene, leftChild->scenePos(), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
// Click on left parent, event goes to modal child
sendMouseClick(&scene, leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 2);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
// Click on all other items and outside the items
sendMouseClick(&scene, rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 3);
sendMouseClick(&scene, rightChild->scenePos(), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 4);
sendMouseClick(&scene, bottomItem->scenePos(), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 5);
sendMouseClick(&scene, QPointF(10000, 10000), Qt::LeftButton);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 6);
QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab
QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked
leftChildSpy.counts.clear();
rightChildSpy.counts.clear();
leftParentSpy.counts.clear();
rightParentSpy.counts.clear();
bottomItemSpy.counts.clear();
// Right child enters scene modality, only left child is blocked.
rightChild->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
}
void tst_QGraphicsItem::panelModality()
{
// 1) Test mouse events (delivery/propagation/redirection)
// 2) Test hover events (incl. leave on block, enter on unblock)
// 3) Test cursor stuff (incl. unset on block, set on unblock)
// 4) Test clickfocus
// 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock)
// 6) ### modality for non-panels is unsupported for now
QGraphicsScene scene;
QGraphicsRectItem *bottomItem = scene.addRect(-150, -100, 300, 200);
bottomItem->setFlag(QGraphicsItem::ItemIsFocusable);
bottomItem->setBrush(Qt::yellow);
QGraphicsRectItem *leftParent = scene.addRect(-50, -50, 100, 100);
leftParent->setFlag(QGraphicsItem::ItemIsPanel);
leftParent->setBrush(Qt::blue);
QGraphicsRectItem *leftChild = scene.addRect(-25, -25, 50, 50);
leftChild->setFlag(QGraphicsItem::ItemIsPanel);
leftChild->setBrush(Qt::green);
leftChild->setParentItem(leftParent);
QGraphicsRectItem *rightParent = scene.addRect(-50, -50, 100, 100);
rightParent->setFlag(QGraphicsItem::ItemIsPanel);
rightParent->setBrush(Qt::red);
QGraphicsRectItem *rightChild = scene.addRect(-25, -25, 50, 50);
rightChild->setFlag(QGraphicsItem::ItemIsPanel);
rightChild->setBrush(Qt::gray);
rightChild->setParentItem(rightParent);
leftParent->setPos(-75, 0);
rightParent->setPos(75, 0);
bottomItem->setData(0, "bottomItem");
leftParent->setData(0, "leftParent");
leftChild->setData(0, "leftChild");
rightParent->setData(0, "rightParent");
rightChild->setData(0, "rightChild");
scene.setSceneRect(scene.itemsBoundingRect().adjusted(-50, -50, 50, 50));
EventSpy2 leftParentSpy(&scene, leftParent);
EventSpy2 leftChildSpy(&scene, leftChild);
EventSpy2 rightParentSpy(&scene, rightParent);
EventSpy2 rightChildSpy(&scene, rightChild);
EventSpy2 bottomItemSpy(&scene, bottomItem);
// Left Child enters panel modality, only left parent is blocked.
leftChild->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
leftChild->setPanelModality(QGraphicsItem::NonModal);
leftChildSpy.counts.clear();
rightChildSpy.counts.clear();
leftParentSpy.counts.clear();
rightParentSpy.counts.clear();
bottomItemSpy.counts.clear();
// Left parent enter panel modality, nothing is blocked.
leftParent->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
// Left child enters panel modality, left parent is blocked again.
leftChild->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0);
leftChildSpy.counts.clear();
rightChildSpy.counts.clear();
leftParentSpy.counts.clear();
rightParentSpy.counts.clear();
bottomItemSpy.counts.clear();
leftChild->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1);
leftParent->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(bottomItemSpy.counts[QEvent::WindowUnblocked], 0);
leftChildSpy.counts.clear();
rightChildSpy.counts.clear();
leftParentSpy.counts.clear();
rightParentSpy.counts.clear();
bottomItemSpy.counts.clear();
// Left and right child enter panel modality, both parents are blocked.
rightChild->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
leftChild->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
}
void tst_QGraphicsItem::mixedModality()
{
// 1) Test mouse events (delivery/propagation/redirection)
// 2) Test hover events (incl. leave on block, enter on unblock)
// 3) Test cursor stuff (incl. unset on block, set on unblock)
// 4) Test clickfocus
// 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock)
// 6) ### modality for non-panels is unsupported for now
QGraphicsScene scene;
QGraphicsRectItem *bottomItem = scene.addRect(-150, -100, 300, 200);
bottomItem->setFlag(QGraphicsItem::ItemIsFocusable);
bottomItem->setBrush(Qt::yellow);
QGraphicsRectItem *leftParent = scene.addRect(-50, -50, 100, 100);
leftParent->setFlag(QGraphicsItem::ItemIsPanel);
leftParent->setBrush(Qt::blue);
QGraphicsRectItem *leftChild = scene.addRect(-25, -25, 50, 50);
leftChild->setFlag(QGraphicsItem::ItemIsPanel);
leftChild->setBrush(Qt::green);
leftChild->setParentItem(leftParent);
QGraphicsRectItem *rightParent = scene.addRect(-50, -50, 100, 100);
rightParent->setFlag(QGraphicsItem::ItemIsPanel);
rightParent->setBrush(Qt::red);
QGraphicsRectItem *rightChild = scene.addRect(-25, -25, 50, 50);
rightChild->setFlag(QGraphicsItem::ItemIsPanel);
rightChild->setBrush(Qt::gray);
rightChild->setParentItem(rightParent);
leftParent->setPos(-75, 0);
rightParent->setPos(75, 0);
bottomItem->setData(0, "bottomItem");
leftParent->setData(0, "leftParent");
leftChild->setData(0, "leftChild");
rightParent->setData(0, "rightParent");
rightChild->setData(0, "rightChild");
scene.setSceneRect(scene.itemsBoundingRect().adjusted(-50, -50, 50, 50));
EventSpy2 leftParentSpy(&scene, leftParent);
EventSpy2 leftChildSpy(&scene, leftChild);
EventSpy2 rightParentSpy(&scene, rightParent);
EventSpy2 rightChildSpy(&scene, rightChild);
EventSpy2 bottomItemSpy(&scene, bottomItem);
// Left Child enters panel modality, only left parent is blocked.
leftChild->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0);
// Left parent enters scene modality, which blocks everything except the child.
leftParent->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
// Right child enters panel modality (changes nothing).
rightChild->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
// Left parent leaves modality. Right child is unblocked.
leftParent->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0);
QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
// Right child "upgrades" its modality to scene modal. Left child is blocked.
// Right parent is unaffected.
rightChild->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
// "downgrade" right child back to panel modal, left child is unblocked
rightChild->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 1);
QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0);
QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1);
QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0);
}
void tst_QGraphicsItem::modality_hover()
{
QGraphicsScene scene;
QGraphicsRectItem *rect1 = scene.addRect(-50, -50, 100, 100);
rect1->setFlag(QGraphicsItem::ItemIsPanel);
rect1->setAcceptHoverEvents(true);
rect1->setData(0, "rect1");
QGraphicsRectItem *rect2 = scene.addRect(-50, -50, 100, 100);
rect2->setParentItem(rect1);
rect2->setFlag(QGraphicsItem::ItemIsPanel);
rect2->setAcceptHoverEvents(true);
rect2->setPos(50, 50);
rect2->setPanelModality(QGraphicsItem::SceneModal);
rect2->setData(0, "rect2");
EventSpy2 rect1Spy(&scene, rect1);
EventSpy2 rect2Spy(&scene, rect2);
sendMouseMove(&scene, QPointF(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0);
sendMouseMove(&scene, QPointF(75, 75));
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 1);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 1);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 0);
sendMouseMove(&scene, QPointF(-25, -25));
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0);
rect2->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1);
sendMouseMove(&scene, QPointF(75, 75));
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 2);
rect2->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
// changing modality causes a spurious GraphicsSceneHoveMove, even though the mouse didn't
// actually move
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3);
sendMouseMove(&scene, QPointF(-25, -25));
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1);
rect2->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2);
rect2->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 2);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2);
}
void tst_QGraphicsItem::modality_mouseGrabber()
{
QGraphicsScene scene;
QGraphicsRectItem *rect1 = scene.addRect(-50, -50, 100, 100);
rect1->setFlag(QGraphicsItem::ItemIsPanel);
rect1->setFlag(QGraphicsItem::ItemIsMovable);
rect1->setData(0, "rect1");
QGraphicsRectItem *rect2 = scene.addRect(-50, -50, 100, 100);
rect2->setParentItem(rect1);
rect2->setFlag(QGraphicsItem::ItemIsPanel);
rect2->setFlag(QGraphicsItem::ItemIsMovable);
rect2->setPos(50, 50);
rect2->setData(0, "rect2");
EventSpy2 rect1Spy(&scene, rect1);
EventSpy2 rect2Spy(&scene, rect2);
{
// pressing mouse on rect1 starts implicit grab
sendMousePress(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(scene.mouseGrabberItem(), rect1);
// grab lost when rect1 is modally shadowed
rect2->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
// releasing goes nowhere
sendMouseRelease(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
// pressing mouse on rect1 starts implicit grab on rect2 (since it is modal)
sendMouseClick(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
rect2->setPanelModality(QGraphicsItem::NonModal);
// pressing mouse on rect1 starts implicit grab
sendMousePress(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 2);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), rect1);
// grab lost to rect2 when rect1 is modally shadowed
rect2->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
// rect1 does *not* re-grab when rect2 is no longer modal
rect2->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
// release goes nowhere
sendMouseRelease(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
}
{
// repeat the test using PanelModal
rect2->setPanelModality(QGraphicsItem::NonModal);
rect1Spy.counts.clear();
rect2Spy.counts.clear();
// pressing mouse on rect1 starts implicit grab
sendMousePress(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(scene.mouseGrabberItem(), rect1);
// grab lost when rect1 is modally shadowed
rect2->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
// releasing goes nowhere
sendMouseRelease(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
// pressing mouse on rect1 starts implicit grab on rect2 (since it is modal)
sendMouseClick(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
rect2->setPanelModality(QGraphicsItem::NonModal);
// pressing mouse on rect1 starts implicit grab
sendMousePress(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 2);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), rect1);
// grab lost to rect2 when rect1 is modally shadowed
rect2->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
// rect1 does *not* re-grab when rect2 is no longer modal
rect2->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
// release goes nowhere
sendMouseRelease(&scene, QPoint(-25, -25));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
}
{
// repeat the PanelModal tests, but this time the mouse events will be on a non-modal item,
// meaning normal grabbing should work
rect2->setPanelModality(QGraphicsItem::NonModal);
rect1Spy.counts.clear();
rect2Spy.counts.clear();
QGraphicsRectItem *rect3 = scene.addRect(-50, -50, 100, 100);
rect3->setFlag(QGraphicsItem::ItemIsPanel);
rect3->setFlag(QGraphicsItem::ItemIsMovable);
rect3->setPos(150, 50);
rect3->setData(0, "rect3");
EventSpy2 rect3Spy(&scene, rect3);
// pressing mouse on rect3 starts implicit grab
sendMousePress(&scene, QPoint(150, 50));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect3Spy.counts[QEvent::GraphicsSceneMousePress], 1);
QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(scene.mouseGrabberItem(), rect3);
// grab is *not* lost when rect1 is modally shadowed by rect2
rect2->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(scene.mouseGrabberItem(), rect3);
// releasing goes to rect3
sendMouseRelease(&scene, QPoint(150, 50));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1);
QCOMPARE(rect3Spy.counts[QEvent::GraphicsSceneMouseRelease], 1);
QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
rect2->setPanelModality(QGraphicsItem::NonModal);
// pressing mouse on rect3 starts implicit grab
sendMousePress(&scene, QPoint(150, 50));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), rect3);
// grab is not lost
rect2->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), rect3);
// grab stays on rect3
rect2->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1);
QCOMPARE(scene.mouseGrabberItem(), rect3);
// release goes to rect3
sendMouseRelease(&scene, QPoint(150, 50));
QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0);
QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0);
QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2);
QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 2);
QCOMPARE(scene.mouseGrabberItem(), nullptr);
}
}
void tst_QGraphicsItem::modality_clickFocus()
{
QGraphicsScene scene;
QGraphicsRectItem *rect1 = scene.addRect(-50, -50, 100, 100);
rect1->setFlag(QGraphicsItem::ItemIsPanel);
rect1->setFlag(QGraphicsItem::ItemIsFocusable);
rect1->setData(0, "rect1");
QGraphicsRectItem *rect2 = scene.addRect(-50, -50, 100, 100);
rect2->setParentItem(rect1);
rect2->setFlag(QGraphicsItem::ItemIsPanel);
rect2->setFlag(QGraphicsItem::ItemIsFocusable);
rect2->setPos(50, 50);
rect2->setData(0, "rect2");
QEvent windowActivateEvent(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &windowActivateEvent);
EventSpy2 rect1Spy(&scene, rect1);
EventSpy2 rect2Spy(&scene, rect2);
// activate rect1, it should get focus
rect1->setActive(true);
QCOMPARE(scene.focusItem(), rect1);
// focus stays when rect2 becomes modal
rect2->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(scene.focusItem(), rect1);
QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 1);
QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0);
QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 0);
QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0);
// clicking on rect1 should not set it's focus item
rect1->clearFocus();
sendMouseClick(&scene, QPointF(-25, -25));
QCOMPARE(rect1->focusItem(), nullptr);
QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 1);
QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 1);
QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 0);
QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0);
// clicking on rect2 gives it focus
rect2->setActive(true);
sendMouseClick(&scene, QPointF(75, 75));
QCOMPARE(scene.focusItem(), rect2);
QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 1);
QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 1);
QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1);
QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0);
// clicking on rect1 does *not* give it focus
rect1->setActive(true);
rect1->clearFocus();
sendMouseClick(&scene, QPointF(-25, -25));
QCOMPARE(scene.focusItem(), nullptr);
QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 2);
QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 2);
QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1);
QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1);
// focus doesn't change when leaving modality either
rect2->setPanelModality(QGraphicsItem::NonModal);
QCOMPARE(scene.focusItem(), nullptr);
QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 2);
QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 2);
QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1);
QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1);
// click on rect1, it should get focus now
sendMouseClick(&scene, QPointF(-25, -25));
QCOMPARE(scene.focusItem(), rect1);
QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 3);
QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 2);
QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1);
QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1);
}
void tst_QGraphicsItem::modality_keyEvents()
{
QGraphicsScene scene;
QGraphicsRectItem *rect1 = scene.addRect(-50, -50, 100, 100);
rect1->setFlag(QGraphicsItem::ItemIsPanel);
rect1->setFlag(QGraphicsItem::ItemIsFocusable);
rect1->setData(0, "rect1");
QGraphicsRectItem *rect1child = scene.addRect(-10, -10, 20, 20);
rect1child->setParentItem(rect1);
rect1child->setFlag(QGraphicsItem::ItemIsFocusable);
rect1child->setData(0, "rect1child1");
QGraphicsRectItem *rect2 = scene.addRect(-50, -50, 100, 100);
rect2->setParentItem(rect1);
rect2->setFlag(QGraphicsItem::ItemIsPanel);
rect2->setFlag(QGraphicsItem::ItemIsFocusable);
rect2->setPos(50, 50);
rect2->setData(0, "rect2");
QGraphicsRectItem *rect2child = scene.addRect(-10, -10, 20, 20);
rect2child->setParentItem(rect2);
rect2child->setFlag(QGraphicsItem::ItemIsFocusable);
rect2child->setData(0, "rect2child1");
QEvent windowActivateEvent(QEvent::WindowActivate);
QCoreApplication::sendEvent(&scene, &windowActivateEvent);
EventSpy2 rect1Spy(&scene, rect1);
EventSpy2 rect1childSpy(&scene, rect1child);
EventSpy2 rect2Spy(&scene, rect2);
EventSpy2 rect2childSpy(&scene, rect2child);
// activate rect1 and give it rect1child focus
rect1->setActive(true);
rect1child->setFocus();
QCOMPARE(scene.focusItem(), rect1child);
// focus stays on rect1child when rect2 becomes modal
rect2->setPanelModality(QGraphicsItem::SceneModal);
QCOMPARE(scene.focusItem(), rect1child);
// but key events to rect1child should be neither delivered nor propagated
sendKeyClick(&scene, Qt::Key_A);
sendKeyClick(&scene, Qt::Key_S);
sendKeyClick(&scene, Qt::Key_D);
sendKeyClick(&scene, Qt::Key_F);
QCOMPARE(rect1childSpy.counts[QEvent::KeyPress], 0);
QCOMPARE(rect1childSpy.counts[QEvent::KeyRelease], 0);
QCOMPARE(rect1Spy.counts[QEvent::KeyPress], 0);
QCOMPARE(rect1Spy.counts[QEvent::KeyRelease], 0);
// change to panel modality, rect1child1 keeps focus
rect2->setPanelModality(QGraphicsItem::PanelModal);
QCOMPARE(scene.focusItem(), rect1child);
// still no key events
sendKeyClick(&scene, Qt::Key_J);
sendKeyClick(&scene, Qt::Key_K);
sendKeyClick(&scene, Qt::Key_L);
sendKeyClick(&scene, Qt::Key_Semicolon);
QCOMPARE(rect1childSpy.counts[QEvent::KeyPress], 0);
QCOMPARE(rect1childSpy.counts[QEvent::KeyRelease], 0);
QCOMPARE(rect1Spy.counts[QEvent::KeyPress], 0);
QCOMPARE(rect1Spy.counts[QEvent::KeyRelease], 0);
}
void tst_QGraphicsItem::itemIsInFront()
{
QGraphicsScene scene;
QGraphicsRectItem *rect1 = new QGraphicsRectItem;
rect1->setData(0, "rect1");
scene.addItem(rect1);
QGraphicsRectItem *rect1child1 = new QGraphicsRectItem(rect1);
rect1child1->setZValue(1);
rect1child1->setData(0, "rect1child1");
QGraphicsRectItem *rect1child2 = new QGraphicsRectItem(rect1);
rect1child2->setParentItem(rect1);
rect1child2->setData(0, "rect1child2");
QGraphicsRectItem *rect1child1_1 = new QGraphicsRectItem(rect1child1);
rect1child1_1->setData(0, "rect1child1_1");
QGraphicsRectItem *rect1child1_2 = new QGraphicsRectItem(rect1child1);
rect1child1_2->setFlag(QGraphicsItem::ItemStacksBehindParent);
rect1child1_2->setData(0, "rect1child1_2");
QGraphicsRectItem *rect2 = new QGraphicsRectItem;
rect2->setData(0, "rect2");
scene.addItem(rect2);
QGraphicsRectItem *rect2child1 = new QGraphicsRectItem(rect2);
rect2child1->setData(0, "rect2child1");
QCOMPARE(qt_closestItemFirst(rect1, rect1), false);
QCOMPARE(qt_closestItemFirst(rect1, rect2), false);
QCOMPARE(qt_closestItemFirst(rect1child1, rect2child1), false);
QCOMPARE(qt_closestItemFirst(rect1child1, rect1child2), true);
QCOMPARE(qt_closestItemFirst(rect1child1_1, rect1child2), true);
QCOMPARE(qt_closestItemFirst(rect1child1_1, rect1child1), true);
QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1child2), true);
QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1child1), false);
QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1), true);
QCOMPARE(qt_closestItemFirst(rect1child1_2, rect2), false);
QCOMPARE(qt_closestItemFirst(rect1child1_2, rect2child1), false);
}
using ScenePosChangeTester = ItemChangeTester;
void tst_QGraphicsItem::scenePosChange()
{
ScenePosChangeTester* root = new ScenePosChangeTester;
ScenePosChangeTester* child1 = new ScenePosChangeTester(root);
const QScopedPointer<ScenePosChangeTester> grandChild1(new ScenePosChangeTester(child1));
ScenePosChangeTester* child2 = new ScenePosChangeTester(root);
ScenePosChangeTester* grandChild2 = new ScenePosChangeTester(child2);
child1->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true);
grandChild2->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true);
QVERIFY(child1->flags() & QGraphicsItem::ItemSendsScenePositionChanges);
QVERIFY(grandChild2->flags() & QGraphicsItem::ItemSendsScenePositionChanges);
QGraphicsScene scene;
scene.addItem(root);
// ignore uninteresting changes
child1->clear();
child2->clear();
grandChild1->clear();
grandChild2->clear();
// move whole tree
root->moveBy(1.0, 1.0);
QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
// move subtree
child2->moveBy(1.0, 1.0);
QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2);
// reparent
grandChild2->setParentItem(child1);
child1->moveBy(1.0, 1.0);
QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2);
QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3);
// change flags
grandChild1->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true);
grandChild2->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, false);
QCoreApplication::processEvents(); // QGraphicsScenePrivate::_q_updateScenePosDescendants()
child1->moveBy(1.0, 1.0);
QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3);
QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3);
// remove
scene.removeItem(grandChild1.data());
delete grandChild2; grandChild2 = nullptr;
QCoreApplication::processEvents(); // QGraphicsScenePrivate::_q_updateScenePosDescendants()
root->moveBy(1.0, 1.0);
QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 4);
QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
root->setX(1);
QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 5);
QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
root->setY(1);
QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 6);
QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0);
}
#if QT_CONFIG(shortcut)
void tst_QGraphicsItem::textItem_shortcuts()
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QSKIP("Window activation is not supported");
QWidget w;
w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
auto l = new QVBoxLayout(&w);
QGraphicsScene scene;
QGraphicsView view(&scene);
l->addWidget(&view);
QPushButton b("Push Me");
l->addWidget(&b);
QGraphicsTextItem *item = scene.addText("Troll Text");
item->setFlag(QGraphicsItem::ItemIsFocusable);
item->setTextInteractionFlags(Qt::TextEditorInteraction);
w.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QVERIFY(QTest::qWaitForWindowExposed(&w));
item->setFocus();
QTRY_VERIFY(item->hasFocus());
QVERIFY(item->textCursor().selectedText().isEmpty());
// Shortcut should work (select all)
QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier);
QTRY_COMPARE(item->textCursor().selectedText(), item->toPlainText());
QTextCursor tc = item->textCursor();
tc.clearSelection();
item->setTextCursor(tc);
QVERIFY(item->textCursor().selectedText().isEmpty());
// Shortcut should also work if the text item has the focus and another widget
// has the same shortcut.
b.setShortcut(QKeySequence("CTRL+A"));
QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier);
QTRY_COMPARE(item->textCursor().selectedText(), item->toPlainText());
}
#endif // QT_CONFIG(shortcut)
void tst_QGraphicsItem::scroll()
{
// Create two overlapping rectangles in the scene:
// +-------+
// | | <- item1
// | +-------+
// | | |
// +---| | <- item2
// | |
// +-------+
EventTester *item1 = new EventTester;
item1->br = QRectF(0, 0, 200, 200);
item1->brush = Qt::red;
item1->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption);
EventTester *item2 = new EventTester;
item2->br = QRectF(0, 0, 200, 200);
item2->brush = Qt::blue;
item2->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption);
item2->setPos(100, 100);
QGraphicsScene scene(0, 0, 300, 300);
scene.addItem(item1);
scene.addItem(item2);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.setFrameStyle(0);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QTRY_VERIFY(view.repaints > 0);
view.reset();
item1->reset();
item2->reset();
const QRectF item1BoundingRect = item1->boundingRect();
const QRectF item2BoundingRect = item2->boundingRect();
// Scroll item1:
// Item1 should get full exposure
// Item2 should get exposure for the part that overlaps item1.
item1->scroll(0, -10);
QTRY_VERIFY(view.repaints > 0);
QCOMPARE(item1->lastExposedRect, item1BoundingRect);
QRectF expectedItem2Expose = item2BoundingRect;
// NB! Adjusted by 2 pixels for antialiasing
expectedItem2Expose &= item1->mapRectToItem(item2, item1BoundingRect.adjusted(-2, -2, 2, 2));
QCOMPARE(item2->lastExposedRect, expectedItem2Expose);
// Enable ItemCoordinateCache on item1.
view.reset();
item1->setCacheMode(QGraphicsItem::ItemCoordinateCache);
QTRY_VERIFY(view.repaints > 0);
view.reset();
item1->reset();
item2->reset();
// Scroll item1:
// Item1 should only get expose for the newly exposed area (accelerated scroll).
// Item2 should get exposure for the part that overlaps item1.
item1->scroll(0, -10, QRectF(50, 50, 100, 100));
QTRY_VERIFY(view.repaints > 0);
QCOMPARE(item1->lastExposedRect, QRectF(50, 140, 100, 10));
expectedItem2Expose = item2BoundingRect;
// NB! Adjusted by 2 pixels for antialiasing
expectedItem2Expose &= item1->mapRectToItem(item2, QRectF(50, 50, 100, 100).adjusted(-2, -2, 2, 2));
QCOMPARE(item2->lastExposedRect, expectedItem2Expose);
}
Q_DECLARE_METATYPE(QGraphicsItem::GraphicsItemFlag);
void tst_QGraphicsItem::focusHandling_data()
{
QTest::addColumn<QGraphicsItem::GraphicsItemFlag>("focusFlag");
QTest::addColumn<bool>("useStickyFocus");
QTest::addColumn<int>("expectedFocusItem"); // 0: none, 1: focusableUnder, 2: itemWithFocus
QTest::newRow("Focus goes through.")
<< static_cast<QGraphicsItem::GraphicsItemFlag>(0x0) << false << 1;
QTest::newRow("Focus goes through, even with sticky scene.")
<< static_cast<QGraphicsItem::GraphicsItemFlag>(0x0) << true << 1;
QTest::newRow("With ItemStopsClickFocusPropagation, we cannot focus the item beneath the flagged one (but can still focus-out).")
<< QGraphicsItem::ItemStopsClickFocusPropagation << false << 0;
QTest::newRow("With ItemStopsClickFocusPropagation, we cannot focus the item beneath the flagged one (and cannot focus-out if scene is sticky).")
<< QGraphicsItem::ItemStopsClickFocusPropagation << true << 2;
QTest::newRow("With ItemStopsFocusHandling, focus cannot be changed by presses.")
<< QGraphicsItem::ItemStopsFocusHandling << false << 2;
QTest::newRow("With ItemStopsFocusHandling, focus cannot be changed by presses (even if scene is sticky).")
<< QGraphicsItem::ItemStopsFocusHandling << true << 2;
}
void tst_QGraphicsItem::focusHandling()
{
QFETCH(QGraphicsItem::GraphicsItemFlag, focusFlag);
QFETCH(bool, useStickyFocus);
QFETCH(int, expectedFocusItem);
class MyItem : public QGraphicsRectItem
{
public:
MyItem() : QGraphicsRectItem(0, 0, 100, 100) {}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
{
painter->fillRect(boundingRect(), hasFocus() ? QBrush(Qt::red) : brush());
}
};
QGraphicsRectItem *noFocusOnTop = new MyItem;
noFocusOnTop->setFlag(QGraphicsItem::ItemIsFocusable, false);
noFocusOnTop->setBrush(Qt::yellow);
QGraphicsRectItem *focusableUnder = new MyItem;
focusableUnder->setBrush(Qt::blue);
focusableUnder->setFlag(QGraphicsItem::ItemIsFocusable);
focusableUnder->setPos(50, 50);
QGraphicsRectItem *itemWithFocus = new MyItem;
itemWithFocus->setBrush(Qt::black);
itemWithFocus->setFlag(QGraphicsItem::ItemIsFocusable);
itemWithFocus->setPos(250, 10);
QGraphicsScene scene(-50, -50, 400, 400);
scene.addItem(noFocusOnTop);
scene.addItem(focusableUnder);
scene.addItem(itemWithFocus);
scene.setStickyFocus(useStickyFocus);
noFocusOnTop->setFlag(focusFlag);
focusableUnder->stackBefore(noFocusOnTop);
itemWithFocus->setFocus();
QGraphicsView view(&scene);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QApplicationPrivate::setActiveWindow(&view);
QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
QVERIFY(itemWithFocus->hasFocus());
const QPointF mousePressPoint = noFocusOnTop->mapToScene(noFocusOnTop->boundingRect().center());
const QList<QGraphicsItem *> itemsAtMousePressPosition = scene.items(mousePressPoint);
QVERIFY(itemsAtMousePressPosition.contains(noFocusOnTop));
sendMousePress(&scene, mousePressPoint);
switch (expectedFocusItem) {
case 0:
QCOMPARE(scene.focusItem(), nullptr);
break;
case 1:
QCOMPARE(scene.focusItem(), focusableUnder);
break;
case 2:
QCOMPARE(scene.focusItem(), itemWithFocus);
break;
}
// Sanity check - manually setting the focus must work regardless of our
// focus handling flags:
focusableUnder->setFocus();
QCOMPARE(scene.focusItem(), focusableUnder);
}
class TouchEventTestee : public QGraphicsRectItem
{
public:
using TouchPoints = QList<QEventPoint>;
TouchEventTestee(const QSizeF &size = QSizeF(100, 100)) :
QGraphicsRectItem(QRectF(QPointF(), size))
{
setAcceptTouchEvents(true);
setFlag(QGraphicsItem::ItemIsFocusable, false);
}
TouchPoints touchBeginPoints() const { return m_touchBeginPoints; }
int touchBeginEventCount() const { return m_touchBeginPoints.size(); }
TouchPoints touchUpdatePoints() const { return m_touchUpdatePoints; }
int touchUpdateEventCount() const { return m_touchUpdatePoints.size(); }
protected:
bool sceneEvent(QEvent *ev) override
{
switch (ev->type()) {
case QEvent::TouchBegin:
m_touchBeginPoints.append(static_cast<const QTouchEvent *>(ev)->points().constFirst());
ev->accept();
return true;
case QEvent::TouchUpdate:
m_touchUpdatePoints.append(static_cast<const QTouchEvent *>(ev)->points().constFirst());
ev->accept();
return true;
default:
break;
}
return QGraphicsRectItem::sceneEvent(ev);
}
private:
TouchPoints m_touchBeginPoints;
TouchPoints m_touchUpdatePoints;
};
static QList<QEventPoint>
createTouchPoints(const QGraphicsView &view,
const QPointF &scenePos,
const QSizeF &ellipseDiameters,
QEventPoint::State state = QEventPoint::State::Pressed)
{
const QPointF screenPos = view.viewport()->mapToGlobal(view.mapFromScene(scenePos));
QEventPoint tp(0, state, scenePos, screenPos);
QMutableEventPoint::setState(tp, state);
QMutableEventPoint::setScenePosition(tp, scenePos);
QMutableEventPoint::setGlobalPosition(tp, screenPos);
QMutableEventPoint::setGlobalPressPosition(tp, screenPos);
QMutableEventPoint::setGlobalLastPosition(tp, screenPos);
QMutableEventPoint::setEllipseDiameters(tp, ellipseDiameters);
return QList<QEventPoint>() << tp;
}
static bool comparePointF(const QPointF &p1, const QPointF &p2)
{
return qFuzzyCompare(p1.x(), p2.x()) && qFuzzyCompare(p1.y(), p2.y());
}
static bool compareSizeF(const QSizeF &s1, const QSizeF &s2)
{
return qFuzzyCompare(s1.width(), s2.width()) && qFuzzyCompare(s1.height(), s2.height());
}
static QByteArray msgPointFComparisonFailed(const QPointF &p1, const QPointF &p2)
{
return QByteArray::number(p1.x()) + ", " + QByteArray::number(p1.y())
+ " != " + QByteArray::number(p2.x()) + ", " + QByteArray::number(p2.y());
}
static QByteArray msgSizeFComparisonFailed(const QSizeF &s1, const QSizeF &s2)
{
return QByteArray::number(s1.width()) + 'x' + QByteArray::number(s1.height())
+ " != " + QByteArray::number(s2.width()) + 'x' + QByteArray::number(s2.height());
}
#define COMPARE_POINTF(ACTUAL, EXPECTED) \
QVERIFY2(comparePointF(ACTUAL, EXPECTED), msgPointFComparisonFailed(ACTUAL, EXPECTED).constData())
#define COMPARE_SIZEF(ACTUAL, EXPECTED) \
QVERIFY2(compareSizeF(ACTUAL, EXPECTED), msgSizeFComparisonFailed(ACTUAL, EXPECTED).constData())
void tst_QGraphicsItem::touchEventPropagation_data()
{
QTest::addColumn<QGraphicsItem::GraphicsItemFlag>("flag");
QTest::addColumn<int>("expectedCount");
QTest::newRow("ItemIsPanel")
<< QGraphicsItem::ItemIsPanel << 0;
QTest::newRow("ItemStopsClickFocusPropagation")
<< QGraphicsItem::ItemStopsClickFocusPropagation << 1;
QTest::newRow("ItemStopsFocusHandling")
<< QGraphicsItem::ItemStopsFocusHandling << 1;
}
void tst_QGraphicsItem::touchEventPropagation()
{
QFETCH(QGraphicsItem::GraphicsItemFlag, flag);
QFETCH(int, expectedCount);
TouchEventTestee *touchEventReceiver = new TouchEventTestee;
QGraphicsItem *topMost = new QGraphicsRectItem(touchEventReceiver->boundingRect());
QGraphicsScene scene;
scene.addItem(topMost);
scene.addItem(touchEventReceiver);
topMost->setAcceptTouchEvents(true);
topMost->setZValue(FLT_MAX);
topMost->setFlag(QGraphicsItem::ItemIsFocusable, false);
topMost->setFlag(flag, true);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
+ QLatin1String(QTest::currentDataTag()));
view.setSceneRect(touchEventReceiver->boundingRect());
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QInputDevicePrivate::get(m_touchDevice)->setAvailableVirtualGeometry(view.screen()->geometry());
QCOMPARE(touchEventReceiver->touchBeginEventCount(), 0);
const QPointF scenePos = view.sceneRect().center();
sendMousePress(&scene, scenePos);
QMutableTouchEvent touchBegin(QEvent::TouchBegin, m_touchDevice, Qt::NoModifier,
createTouchPoints(view, scenePos, QSizeF(10, 10)));
touchBegin.setTarget(view.viewport());
qApp->sendEvent(&scene, &touchBegin);
QCOMPARE(touchEventReceiver->touchBeginEventCount(), expectedCount);
}
void tst_QGraphicsItem::touchEventTransformation_data()
{
QTest::addColumn<QGraphicsItem::GraphicsItemFlag>("flag");
QTest::addColumn<QTransform>("viewTransform");
QTest::addColumn<QPointF>("touchScenePos");
QTest::addColumn<QSizeF>("ellipseDiameters");
QTest::addColumn<QPointF>("expectedItemPos");
QTest::newRow("notransform")
<< QGraphicsItem::ItemIsSelectable << QTransform()
<< QPointF(150, 150) << QSizeF(7, 8) << QPointF(50, 50);
QTest::newRow("scaled")
<< QGraphicsItem::ItemIsSelectable << QTransform::fromScale(0.5, 0.5)
<< QPointF(150, 150) << QSizeF(7, 8) << QPointF(50, 50);
// QTBUG-66192: When the item ignores the downscaling transformation,
// it will receive the touch point at 25,25 instead of 50,50.
QTest::newRow("scaled/ItemIgnoresTransformations")
<< QGraphicsItem::ItemIgnoresTransformations << QTransform::fromScale(0.5, 0.5)
<< QPointF(150, 150) << QSizeF(7, 8) << QPointF(25, 25);
}
void tst_QGraphicsItem::touchEventTransformation()
{
QFETCH(QGraphicsItem::GraphicsItemFlag, flag);
QFETCH(QTransform, viewTransform);
QFETCH(QPointF, touchScenePos);
QFETCH(QSizeF, ellipseDiameters);
QFETCH(QPointF, expectedItemPos);
TouchEventTestee *touchEventReceiver = new TouchEventTestee;
QGraphicsScene scene;
scene.addItem(touchEventReceiver);
const QPointF itemPos(100, 100);
touchEventReceiver->setPos(itemPos);
touchEventReceiver->setFlag(flag, true);
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
+ QLatin1String(QTest::currentDataTag()));
view.setSceneRect(QRectF(QPointF(0, 0), QSizeF(300, 300)));
view.setTransform(viewTransform);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QInputDevicePrivate::get(m_touchDevice)->setAvailableVirtualGeometry(view.screen()->geometry());
QCOMPARE(touchEventReceiver->touchBeginEventCount(), 0);
QMutableTouchEvent touchBegin(QEvent::TouchBegin, m_touchDevice, Qt::NoModifier,
createTouchPoints(view, touchScenePos, ellipseDiameters));
touchBegin.setTarget(view.viewport());
QCoreApplication::sendEvent(&scene, &touchBegin);
QCOMPARE(touchEventReceiver->touchBeginEventCount(), 1);
const QEventPoint touchBeginPoint = touchEventReceiver->touchBeginPoints().constFirst();
COMPARE_POINTF(touchBeginPoint.scenePosition(), touchScenePos);
COMPARE_POINTF(touchBeginPoint.scenePressPosition(), touchScenePos);
COMPARE_POINTF(touchBeginPoint.sceneLastPosition(), touchScenePos);
COMPARE_POINTF(touchBeginPoint.position(), expectedItemPos);
COMPARE_SIZEF(touchBeginPoint.ellipseDiameters(), ellipseDiameters); // Must remain untransformed
QMutableTouchEvent touchUpdate(QEvent::TouchUpdate, m_touchDevice, Qt::NoModifier,
createTouchPoints(view, touchScenePos, ellipseDiameters, QEventPoint::State::Updated));
touchUpdate.setTarget(view.viewport());
QCoreApplication::sendEvent(&scene, &touchUpdate);
QCOMPARE(touchEventReceiver->touchUpdateEventCount(), 1);
const QEventPoint touchUpdatePoint = touchEventReceiver->touchUpdatePoints().constFirst();
COMPARE_POINTF(touchUpdatePoint.scenePosition(), touchScenePos);
COMPARE_POINTF(touchBeginPoint.scenePressPosition(), touchScenePos);
COMPARE_POINTF(touchUpdatePoint.position(), expectedItemPos);
COMPARE_SIZEF(touchUpdatePoint.ellipseDiameters(), ellipseDiameters); // Must remain untransformed
}
void tst_QGraphicsItem::deviceCoordinateCache_simpleRotations()
{
// Make sure we don't invalidate the cache when applying simple
// (90, 180, 270, 360) rotation transforms to the item.
QGraphicsRectItem *item = new QGraphicsRectItem(0, 0, 300, 200);
item->setBrush(Qt::red);
item->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
QGraphicsScene scene;
scene.setSceneRect(0, 0, 300, 200);
scene.addItem(item);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QTRY_VERIFY(view.repaints > 0);
QGraphicsItemCache *itemCache = QGraphicsItemPrivate::get(item)->extraItemCache();
QVERIFY(itemCache);
QPixmapCache::Key currentKey = itemCache->deviceData.value(view.viewport()).key;
// Trigger an update and verify that the cache is unchanged.
QPixmapCache::Key oldKey = currentKey;
view.reset();
view.viewport()->update();
QTRY_VERIFY(view.repaints > 0);
currentKey = itemCache->deviceData.value(view.viewport()).key;
QCOMPARE(currentKey, oldKey);
// Check 90, 180, 270 and 360 degree rotations.
for (int angle = 90; angle <= 360; angle += 90) {
// Rotate item and verify that the cache was invalidated.
oldKey = currentKey;
view.reset();
QTransform transform;
transform.translate(150, 100);
transform.rotate(angle);
transform.translate(-150, -100);
item->setTransform(transform);
QTRY_VERIFY(view.repaints > 0);
currentKey = itemCache->deviceData.value(view.viewport()).key;
QVERIFY(currentKey != oldKey);
// IMPORTANT PART:
// Trigger an update and verify that the cache is unchanged.
oldKey = currentKey;
view.reset();
view.viewport()->update();
QTRY_VERIFY(view.repaints > 0);
currentKey = itemCache->deviceData.value(view.viewport()).key;
QCOMPARE(currentKey, oldKey);
}
// 45 degree rotation.
oldKey = currentKey;
view.reset();
QTransform transform;
transform.translate(150, 100);
transform.rotate(45);
transform.translate(-150, -100);
item->setTransform(transform);
QTRY_VERIFY(view.repaints > 0);
currentKey = itemCache->deviceData.value(view.viewport()).key;
QVERIFY(currentKey != oldKey);
// Trigger an update and verify that the cache was invalidated.
// We should always invalidate the cache for non-trivial transforms.
oldKey = currentKey;
view.reset();
view.viewport()->update();
QTRY_VERIFY(view.repaints > 0);
currentKey = itemCache->deviceData.value(view.viewport()).key;
QVERIFY(currentKey != oldKey);
}
void tst_QGraphicsItem::QTBUG_5418_textItemSetDefaultColor()
{
struct Item : public QGraphicsTextItem
{
int painted;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *wid) override
{
painted++;
QGraphicsTextItem::paint(painter, opt, wid);
}
};
Item *i = new Item;
i->painted = 0;
i->setPlainText("I AM A TROLL");
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
scene.addItem(i);
QApplication::processEvents();
QTRY_VERIFY(i->painted);
QApplication::processEvents();
i->painted = 0;
QColor col(Qt::red);
i->setDefaultTextColor(col);
QApplication::processEvents();
QTRY_COMPARE(i->painted, 1); //check that changing the color force an update
i->painted = false;
QImage image(400, 200, QImage::Format_RGB32);
image.fill(0);
QPainter painter(&image);
scene.render(&painter);
painter.end();
QCOMPARE(i->painted, 1);
int numRedPixel = 0;
QRgb rgb = col.rgb();
for (int y = 0; y < image.height(); ++y) {
for (int x = 0; x < image.width(); ++x) {
// Because of antialiasing we allow a certain range of errors here.
QRgb pixel = image.pixel(x, y);
if (qAbs(int(pixel & 0xff) - int(rgb & 0xff)) +
qAbs(int((pixel & 0xff00) >> 8) - int((rgb & 0xff00) >> 8)) +
qAbs(int((pixel & 0xff0000) >> 16) - int((rgb & 0xff0000) >> 16)) <= 50) {
if (++numRedPixel >= 10) {
return;
}
}
}
}
QCOMPARE(numRedPixel, -1); //color not found, FAIL!
i->painted = 0;
i->setDefaultTextColor(col);
QApplication::processEvents();
QCOMPARE(i->painted, 0); //same color as before should not trigger an update (QTBUG-6242)
}
void tst_QGraphicsItem::QTBUG_6738_missingUpdateWithSetParent()
{
// In all 3 test cases below the reparented item should disappear
EventTester *parent = new EventTester;
EventTester *child = new EventTester(parent);
EventTester *child2 = new EventTester(parent);
EventTester *child3 = new EventTester(parent);
EventTester *child4 = new EventTester(parent);
child->setPos(10, 10);
child2->setPos(20, 20);
child3->setPos(30, 30);
child4->setPos(40, 40);
QGraphicsScene scene;
scene.addItem(parent);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
view.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCoreApplication::processEvents(); // Process all queued paint events
QTRY_VERIFY(view.repaints > 0);
// test case #1
view.reset();
child2->setVisible(false);
child2->setParentItem(child);
QTRY_COMPARE(view.repaints, 1);
// test case #2
view.reset();
child3->setOpacity(0.0);
child3->setParentItem(child);
QTRY_COMPARE(view.repaints, 1);
// test case #3
view.reset();
child4->setParentItem(child);
child4->setVisible(false);
QTRY_COMPARE(view.repaints, 1);
}
void tst_QGraphicsItem::QT_2653_fullUpdateDiscardingOpacityUpdate()
{
QGraphicsScene scene(0, 0, 200, 200);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
EventTester *parentGreen = new EventTester();
parentGreen->setGeometry(QRectF(20, 20, 100, 100));
parentGreen->brush = Qt::green;
EventTester *childYellow = new EventTester(parentGreen);
childYellow->setGeometry(QRectF(10, 10, 50, 50));
childYellow->brush = Qt::yellow;
scene.addItem(parentGreen);
childYellow->setOpacity(0.0);
parentGreen->setOpacity(0.0);
// set any of the flags below to trigger a fullUpdate to reproduce the bug:
// ItemIgnoresTransformations, ItemClipsChildrenToShape, ItemIsSelectable
parentGreen->setFlag(QGraphicsItem::ItemIgnoresTransformations);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QCoreApplication::processEvents(); // Process all queued paint events
view.reset();
parentGreen->setOpacity(1.0);
QTRY_COMPARE(view.repaints, 1);
view.reset();
childYellow->repaints = 0;
childYellow->setOpacity(1.0);
QTRY_COMPARE(view.repaints, 1);
QTRY_COMPARE(childYellow->repaints, 1);
}
void tst_QGraphicsItem::QTBUG_7714_fullUpdateDiscardingOpacityUpdate2()
{
QGraphicsScene scene(0, 0, 200, 200);
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
MyGraphicsView origView(&scene);
EventTester *parentGreen = new EventTester();
parentGreen->setGeometry(QRectF(20, 20, 100, 100));
parentGreen->brush = Qt::green;
EventTester *childYellow = new EventTester(parentGreen);
childYellow->setGeometry(QRectF(10, 10, 50, 50));
childYellow->brush = Qt::yellow;
scene.addItem(parentGreen);
origView.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&origView));
}
QVERIFY(QTest::qWaitForWindowExposed(&origView));
QCoreApplication::processEvents(); // Process all queued paint events
origView.setGeometry(origView.x() + origView.width() + 20, origView.y() + 20,
origView.width(), origView.height());
parentGreen->setFlag(QGraphicsItem::ItemIgnoresTransformations);
origView.reset();
childYellow->setOpacity(0.0);
QTRY_VERIFY(origView.repaints > 0);
view.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
QApplicationPrivate::setActiveWindow(&view);
QVERIFY(QTest::qWaitForWindowActive(&view));
}
QVERIFY(QTest::qWaitForWindowExposed(&view));
view.reset();
origView.reset();
childYellow->setOpacity(1.0);
QTRY_VERIFY(origView.repaints > 0);
QTRY_VERIFY(view.repaints > 0);
}
void tst_QGraphicsItem::QT_2649_focusScope()
{
QGraphicsScene *scene = new QGraphicsScene;
QGraphicsRectItem *subFocusItem = new QGraphicsRectItem;
subFocusItem->setFlags(QGraphicsItem::ItemIsFocusable);
subFocusItem->setFocus();
QCOMPARE(subFocusItem->focusItem(), subFocusItem);
QGraphicsRectItem *scope = new QGraphicsRectItem;
scope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope);
scope->setFocus();
subFocusItem->setParentItem(scope);
QCOMPARE(subFocusItem->focusItem(), subFocusItem);
QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
QCOMPARE(scope->focusItem(), subFocusItem);
QCOMPARE(scope->focusScopeItem(), subFocusItem);
QGraphicsRectItem *rootItem = new QGraphicsRectItem;
rootItem->setFlags(QGraphicsItem::ItemIsFocusable);
scope->setParentItem(rootItem);
QCOMPARE(rootItem->focusItem(), subFocusItem);
QCOMPARE(rootItem->focusScopeItem(), nullptr);
QCOMPARE(subFocusItem->focusItem(), subFocusItem);
QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
QCOMPARE(scope->focusItem(), subFocusItem);
QCOMPARE(scope->focusScopeItem(), subFocusItem);
scene->addItem(rootItem);
QEvent windowActivate(QEvent::WindowActivate);
qApp->sendEvent(scene, &windowActivate);
scene->setFocus();
QCOMPARE(rootItem->focusItem(), subFocusItem);
QCOMPARE(scope->focusItem(), subFocusItem);
QCOMPARE(subFocusItem->focusItem(), subFocusItem);
QCOMPARE(rootItem->focusScopeItem(), nullptr);
QCOMPARE(scope->focusScopeItem(), subFocusItem);
QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
QVERIFY(subFocusItem->hasFocus());
scope->hide();
QCOMPARE(rootItem->focusItem(), nullptr);
QCOMPARE(scope->focusItem(), nullptr);
QCOMPARE(subFocusItem->focusItem(), nullptr);
QCOMPARE(rootItem->focusScopeItem(), nullptr);
QCOMPARE(scope->focusScopeItem(), subFocusItem);
QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
QVERIFY(!subFocusItem->hasFocus());
scope->show();
QCOMPARE(rootItem->focusItem(), subFocusItem);
QCOMPARE(scope->focusItem(), subFocusItem);
QCOMPARE(subFocusItem->focusItem(), subFocusItem);
QCOMPARE(rootItem->focusScopeItem(), nullptr);
QCOMPARE(scope->focusScopeItem(), subFocusItem);
QCOMPARE(subFocusItem->focusScopeItem(), nullptr);
QVERIFY(subFocusItem->hasFocus());
// This should not crash
scope->hide();
delete scene;
}
class MyGraphicsItemWithItemChange : public QGraphicsWidget
{
public:
using QGraphicsWidget::QGraphicsWidget;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
{
if (change == QGraphicsItem::ItemSceneHasChanged) {
const auto views = scene()->views();
for (QGraphicsView *view : views) // We trigger a sort of unindexed items in the BSP
view->sceneRect();
}
return QGraphicsWidget::itemChange(change, value);
}
};
void tst_QGraphicsItem::sortItemsWhileAdding()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
QGraphicsWidget grandGrandParent;
grandGrandParent.resize(200, 200);
scene.addItem(&grandGrandParent);
QGraphicsWidget grandParent;
grandParent.resize(200, 200);
QGraphicsWidget parent(&grandParent);
parent.resize(200, 200);
MyGraphicsItemWithItemChange item(&parent);
grandParent.setParentItem(&grandGrandParent);
}
void tst_QGraphicsItem::doNotMarkFullUpdateIfNotInScene()
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
QSKIP("Window activation is not supported");
struct Item : public QGraphicsTextItem
{
int painted = 0;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *wid) override
{
painted++;
QGraphicsTextItem::paint(painter, opt, wid);
}
};
QGraphicsScene scene;
MyGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
Item *item = new Item;
item->setPlainText("Grandparent");
Item *item2 = new Item;
item2->setPlainText("parent");
Item *item3 = new Item;
item3->setPlainText("child");
QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect;
effect->setOpacity(0.5);
item2->setGraphicsEffect(effect);
item3->setParentItem(item2);
item2->setParentItem(item);
scene.addItem(item);
view.show();
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowExposed(view.windowHandle()));
QVERIFY(QTest::qWaitForWindowActive(view.windowHandle()));
QCoreApplication::processEvents(); // Process all queued paint events
view.activateWindow();
QTRY_VERIFY(view.isActiveWindow());
QTRY_VERIFY(view.repaints >= 1);
int count = view.repaints;
QTRY_COMPARE(item->painted, count);
// cached as graphics effects, not painted multiple times
QTRY_COMPARE(item2->painted, 1);
QTRY_COMPARE(item3->painted, 1);
item2->update();
QApplication::processEvents();
QTRY_COMPARE(item->painted, count + 1);
QTRY_COMPARE(item2->painted, 2);
QTRY_COMPARE(item3->painted, 2);
item2->update();
QApplication::processEvents();
QTRY_COMPARE(item->painted, count + 2);
QTRY_COMPARE(item2->painted, 3);
QTRY_COMPARE(item3->painted, 3);
}
void tst_QGraphicsItem::itemDiesDuringDraggingOperation()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
item->setFlag(QGraphicsItem::ItemIsMovable);
item->setAcceptDrops(true);
scene.addItem(item);
view.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
QCOMPARE(QApplication::activeWindow(), &view);
}
QVERIFY(QTest::qWaitForWindowExposed(&view));
QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter);
dragEnter.setScenePos(item->boundingRect().center());
QCoreApplication::sendEvent(&scene, &dragEnter);
QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove);
event.setScenePos(item->boundingRect().center());
QCoreApplication::sendEvent(&scene, &event);
QCOMPARE(QGraphicsScenePrivate::get(&scene)->dragDropItem, item);
delete item;
QVERIFY(!QGraphicsScenePrivate::get(&scene)->dragDropItem);
}
void tst_QGraphicsItem::QTBUG_12112_focusItem()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, 20, 20);
item1->setFlag(QGraphicsItem::ItemIsFocusable);
QGraphicsRectItem *item2 = new QGraphicsRectItem(20, 20, 20, 20);
item2->setFlag(QGraphicsItem::ItemIsFocusable);
item1->setFocus();
scene.addItem(item2);
scene.addItem(item1);
view.show();
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) {
view.window()->activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&view));
QCOMPARE(QApplication::activeWindow(), &view);
}
QVERIFY(QTest::qWaitForWindowExposed(&view));
QVERIFY(item1->focusItem());
QVERIFY(!item2->focusItem());
item2->setFocus();
QVERIFY(!item1->focusItem());
QVERIFY(item2->focusItem());
}
void tst_QGraphicsItem::QTBUG_13473_sceneposchange()
{
ScenePosChangeTester* parent = new ScenePosChangeTester;
ScenePosChangeTester* child = new ScenePosChangeTester(parent);
// parent's disabled ItemSendsGeometryChanges flag must not affect
// child's scene pos change notifications
parent->setFlag(QGraphicsItem::ItemSendsGeometryChanges, false);
child->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true);
QGraphicsScene scene;
scene.addItem(parent);
// ignore uninteresting changes
parent->clear();
child->clear();
// move
parent->moveBy(1.0, 1.0);
QCOMPARE(child->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1);
// transform
parent->setTransform(QTransform::fromScale(0.5, 0.5));
QCOMPARE(child->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2);
}
class MyGraphicsWidget : public QGraphicsWidget
{
Q_OBJECT
public:
MyGraphicsWidget() : QGraphicsWidget(nullptr)
{
QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical);
QLatin1String wiseWords("AZ BUKI VEDI");
QString sentence(wiseWords);
QStringList words = sentence.split(QLatin1Char(' '), Qt::SkipEmptyParts);
for (int i = 0; i < words.size(); ++i) {
QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(this);
QLabel *label = new QLabel(words.at(i));
proxy->setWidget(label);
proxy->setFocusPolicy(Qt::StrongFocus);
proxy->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true);
if (i % 2 == 0)
proxy->setVisible(false);
proxy->setFocus();
lay->addItem(proxy);
}
setLayout(lay);
}
};
class MyWidgetWindow : public QGraphicsWidget
{
public:
MyWidgetWindow() : QGraphicsWidget(nullptr, Qt::Window)
{
QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical);
MyGraphicsWidget *widget = new MyGraphicsWidget();
lay->addItem(widget);
setLayout(lay);
}
};
void tst_QGraphicsItem::QTBUG_16374_crashInDestructor()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
MyWidgetWindow win;
scene.addItem(&win);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
}
void tst_QGraphicsItem::QTBUG_20699_focusScopeCrash()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
QGraphicsPixmapItem fs;
fs.setFlags(QGraphicsItem::ItemIsFocusScope | QGraphicsItem::ItemIsFocusable);
scene.addItem(&fs);
QGraphicsPixmapItem* fs2 = new QGraphicsPixmapItem(&fs);
fs2->setFlags(QGraphicsItem::ItemIsFocusScope | QGraphicsItem::ItemIsFocusable);
QGraphicsPixmapItem* fi2 = new QGraphicsPixmapItem(&fs);
fi2->setFlags(QGraphicsItem::ItemIsFocusable);
QGraphicsPixmapItem* fi = new QGraphicsPixmapItem(fs2);
fi->setFlags(QGraphicsItem::ItemIsFocusable);
fs.setFocus();
fi->setFocus();
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
fi->setParentItem(fi2);
fi->setFocus();
fs.setFocus();
fi->setParentItem(fs2);
fi->setFocus();
fs2->setFocus();
fs.setFocus();
fi->setParentItem(fi2);
fi->setFocus();
fs.setFocus();
}
void tst_QGraphicsItem::QTBUG_30990_rightClickSelection()
{
QGraphicsScene scene;
QGraphicsItem *item1 = scene.addRect(10, 10, 10, 10);
item1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
QGraphicsItem *item2 = scene.addRect(100, 100, 10, 10);
item2->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
// right mouse press & release over an item should not make it selected
sendMousePress(&scene, item1->boundingRect().center(), Qt::RightButton);
QVERIFY(!item1->isSelected());
sendMouseRelease(&scene, item1->boundingRect().center(), Qt::RightButton);
QVERIFY(!item1->isSelected());
// right mouse press over one item, moving over another item,
// and then releasing should make neither of the items selected
sendMousePress(&scene, item1->boundingRect().center(), Qt::RightButton);
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
sendMouseMove(&scene, item2->boundingRect().center(), Qt::RightButton);
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
sendMouseRelease(&scene, item2->boundingRect().center(), Qt::RightButton);
QVERIFY(!item1->isSelected());
QVERIFY(!item2->isSelected());
}
void tst_QGraphicsItem::QTBUG_21618_untransformable_sceneTransform()
{
QGraphicsScene scene(0, 0, 150, 150);
scene.addRect(-2, -2, 4, 4);
QGraphicsItem *item1 = scene.addRect(0, 0, 100, 100, QPen(), Qt::red);
item1->setPos(50, 50);
item1->setTransform(QTransform::fromTranslate(50, 50), true);
item1->setTransform(QTransform().rotate(90), true);
QGraphicsItem *item2 = scene.addRect(0, 0, 100, 100, QPen(), Qt::green);
item2->setPos(50, 50);
item2->setTransform(QTransform::fromTranslate(50, 50), true);
item2->setTransform(QTransform().rotate(90), true);
item2->setFlags(QGraphicsItem::ItemIgnoresTransformations);
QGraphicsRectItem *item1_topleft = new QGraphicsRectItem(QRectF(-2, -2, 4, 4));
item1_topleft->setParentItem(item1);
item1_topleft->setBrush(Qt::black);
QGraphicsRectItem *item1_bottomright = new QGraphicsRectItem(QRectF(-2, -2, 4, 4));
item1_bottomright->setParentItem(item1);
item1_bottomright->setPos(100, 100);
item1_bottomright->setBrush(Qt::yellow);
QGraphicsRectItem *item2_topleft = new QGraphicsRectItem(QRectF(-2, -2, 4, 4));
item2_topleft->setParentItem(item2);
item2_topleft->setBrush(Qt::black);
QGraphicsRectItem *item2_bottomright = new QGraphicsRectItem(QRectF(-2, -2, 4, 4));
item2_bottomright->setParentItem(item2);
item2_bottomright->setPos(100, 100);
item2_bottomright->setBrush(Qt::yellow);
QCOMPARE(item1->sceneTransform(), item2->sceneTransform());
QCOMPARE(item1_topleft->sceneTransform(), item2_topleft->sceneTransform());
QCOMPARE(item1_bottomright->sceneTransform(), item2_bottomright->sceneTransform());
QCOMPARE(item1->deviceTransform(QTransform()), item2->deviceTransform(QTransform()));
QCOMPARE(item1->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 100));
QCOMPARE(item2->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 100));
QCOMPARE(item1->deviceTransform(QTransform()).map(QPointF(100, 100)), QPointF(0, 200));
QCOMPARE(item2->deviceTransform(QTransform()).map(QPointF(100, 100)), QPointF(0, 200));
QCOMPARE(item1_topleft->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 100));
QCOMPARE(item2_topleft->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 100));
QCOMPARE(item1_bottomright->deviceTransform(QTransform()).map(QPointF()), QPointF(0, 200));
QCOMPARE(item2_bottomright->deviceTransform(QTransform()).map(QPointF()), QPointF(0, 200));
item2->setParentItem(item1);
QCOMPARE(item2->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 200));
QCOMPARE(item2->deviceTransform(QTransform()).map(QPointF(100, 100)), QPointF(0, 300));
QCOMPARE(item2_topleft->deviceTransform(QTransform()).map(QPointF()), QPointF(100, 200));
QCOMPARE(item2_bottomright->deviceTransform(QTransform()).map(QPointF()), QPointF(0, 300));
QTransform tx = QTransform::fromTranslate(100, 0);
QCOMPARE(item1->deviceTransform(tx).map(QPointF()), QPointF(200, 100));
QCOMPARE(item1->deviceTransform(tx).map(QPointF(100, 100)), QPointF(100, 200));
QCOMPARE(item2->deviceTransform(tx).map(QPointF()), QPointF(200, 200));
QCOMPARE(item2->deviceTransform(tx).map(QPointF(100, 100)), QPointF(100, 300));
QCOMPARE(item2_topleft->deviceTransform(tx).map(QPointF()), QPointF(200, 200));
QCOMPARE(item2_bottomright->deviceTransform(tx).map(QPointF()), QPointF(100, 300));
}
void tst_QGraphicsItem::resolvePaletteForItemChildren()
{
QGraphicsScene scene;
QGraphicsRectItem item(0, 0, 50, -150);
scene.addItem(&item);
QGraphicsWidget widget;
widget.setParentItem(&item);
QColor green(Qt::green);
QPalette paletteForScene = scene.palette();
paletteForScene.setColor(QPalette::Active, QPalette::Window, green);
scene.setPalette(paletteForScene);
QCOMPARE(widget.palette().color(QPalette::Active, QPalette::Window), green);
}
QTEST_MAIN(tst_QGraphicsItem)
#include "tst_qgraphicsitem.moc"