diff options
Diffstat (limited to 'tests/auto/widgets/graphicsview')
81 files changed, 42984 insertions, 0 deletions
diff --git a/tests/auto/widgets/graphicsview/graphicsview.pro b/tests/auto/widgets/graphicsview/graphicsview.pro new file mode 100644 index 0000000000..9955e45b64 --- /dev/null +++ b/tests/auto/widgets/graphicsview/graphicsview.pro @@ -0,0 +1,32 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qgraphicsanchorlayout \ + qgraphicsanchorlayout1 \ + qgraphicseffectsource \ + qgraphicsgridlayout \ + qgraphicsitem \ + qgraphicsitemanimation \ + qgraphicslayout \ + qgraphicslayoutitem \ + qgraphicslinearlayout \ + qgraphicsobject \ + qgraphicspixmapitem \ + qgraphicspolygonitem \ + qgraphicsproxywidget \ + qgraphicsscene \ + qgraphicssceneindex \ + qgraphicstransform \ + qgraphicsview \ + qgraphicswidget \ + +!contains(QT_CONFIG, private_tests): SUBDIRS -= \ + qgraphicsanchorlayout \ + qgraphicsanchorlayout1 \ + qgraphicsitem \ + qgraphicsscene \ + qgraphicssceneindex \ + +# These tests require the cleanlooks style +!contains(styles, cleanlooks):SUBDIRS -= \ + qgraphicsproxywidget \ + qgraphicswidget \ diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/qgraphicsanchorlayout.pro b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/qgraphicsanchorlayout.pro new file mode 100644 index 0000000000..5aa2936e3e --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/qgraphicsanchorlayout.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qgraphicsanchorlayout.cpp +CONFIG += parallel_test diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp new file mode 100644 index 0000000000..5dbe501ea8 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -0,0 +1,2091 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtWidgets/qgraphicsanchorlayout.h> +#include <private/qgraphicsanchorlayout_p.h> +#include <QtWidgets/qgraphicswidget.h> +#include <QtWidgets/qgraphicsproxywidget.h> +#include <QtWidgets/qgraphicsview.h> +#include <QtWidgets/qwindowsstyle.h> + + +class tst_QGraphicsAnchorLayout : public QObject { + Q_OBJECT + +public: + tst_QGraphicsAnchorLayout() : QObject() { + hasSimplification = qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); + } + +private: + bool hasSimplification; + +private slots: + void simple(); + void simple_center(); + void simple_semifloat(); + void layoutDirection(); + void diagonal(); + void parallel(); + void parallel2(); + void snake(); + void snakeOppositeDirections(); + void fairDistribution(); + void fairDistributionOppositeDirections(); + void proportionalPreferred(); + void example(); + void setSpacing(); + void styleDefaults(); + void hardComplexS60(); + void stability(); + void delete_anchor(); + void conflicts(); + void sizePolicy(); + void floatConflict(); + void infiniteMaxSizes(); + void simplifiableUnfeasible(); + void simplificationVsOrder(); + void parallelSimplificationOfCenter(); + void simplificationVsRedundance(); + void spacingPersistency(); + void snakeParallelWithLayout(); + void parallelToHalfLayout(); + void globalSpacing(); + void graphicsAnchorHandling(); + void invalidHierarchyCheck(); +}; + +class RectWidget : public QGraphicsWidget +{ +public: + RectWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent){} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + Q_UNUSED(option); + Q_UNUSED(widget); + painter->drawRoundRect(rect()); + painter->drawLine(rect().topLeft(), rect().bottomRight()); + painter->drawLine(rect().bottomLeft(), rect().topRight()); + } +}; + +static QGraphicsWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), + const QSizeF &preferred = QSize(150.0, 100.0), + const QSizeF &maximum = QSizeF(200.0, 100.0), + const QString &name = QString()) +{ + QGraphicsWidget *w = new RectWidget; + w->setMinimumSize(minimum); + w->setPreferredSize(preferred); + w->setMaximumSize(maximum); + w->setData(0, name); + return w; +} + +static void setAnchor(QGraphicsAnchorLayout *l, + QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal spacing = 0) +{ + QGraphicsAnchor *anchor = l->addAnchor(firstItem, firstEdge, secondItem, secondEdge); + anchor->setSpacing(spacing); +} + +static bool checkReverseDirection(QGraphicsWidget *widget) +{ + QGraphicsLayout *layout = widget->layout(); + qreal left, top, right, bottom; + layout->getContentsMargins(&left, &top, &right, &bottom); + widget->setLayoutDirection(Qt::LeftToRight); + QApplication::processEvents(); + const QRectF layoutGeometry = layout->geometry(); + QMap<QGraphicsLayoutItem *, QRectF> geometries; + for (int i = 0; i < layout->count(); ++i) { + QGraphicsLayoutItem *item = layout->itemAt(i); + geometries.insert(item, item->geometry()); + } + widget->setLayoutDirection(Qt::RightToLeft); + QApplication::processEvents(); + layoutGeometry.adjusted(+right, +top, -left, -bottom); + for (int i = 0; i < layout->count(); ++i) { + QGraphicsLayoutItem *item = layout->itemAt(i); + const QRectF rightToLeftGeometry = item->geometry(); + const QRectF leftToRightGeometry = geometries.value(item); + QRectF expectedGeometry = leftToRightGeometry; + expectedGeometry.moveRight(layoutGeometry.right() - leftToRightGeometry.left()); + if (expectedGeometry != rightToLeftGeometry) { + qDebug() << "layout->geometry():" << layoutGeometry + << "expected:" << expectedGeometry + << "actual:" << rightToLeftGeometry; + return false; + } + } + return true; +} + +static bool layoutHasConflict(QGraphicsAnchorLayout *l) +{ + return QGraphicsAnchorLayoutPrivate::get(l)->hasConflicts(); +} + +static bool usedSimplex(QGraphicsAnchorLayout *l, Qt::Orientation o) +{ + QGraphicsAnchorLayoutPrivate::Orientation oo = (o == Qt::Horizontal) ? + QGraphicsAnchorLayoutPrivate::Horizontal : + QGraphicsAnchorLayoutPrivate::Vertical; + + return QGraphicsAnchorLayoutPrivate::get(l)->lastCalculationUsedSimplex[oo]; +} + +void tst_QGraphicsAnchorLayout::simple() +{ + QGraphicsWidget *w1 = createItem(); + QGraphicsWidget *w2 = createItem(); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // Horizontal + l->addAnchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); + l->addAnchor(w1, Qt::AnchorRight, w2, Qt::AnchorLeft); + l->addAnchor(w2, Qt::AnchorRight, l, Qt::AnchorRight); + + // Vertical + l->addAnchors(l, w1, Qt::Vertical); + l->addAnchors(l, w2, Qt::Vertical); + + QCOMPARE(l->count(), 2); + + QGraphicsWidget p; + p.setLayout(l); + p.adjustSize(); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::simple_center() +{ + QSizeF minSize(10, 10); + QSizeF pref(50, 10); + QSizeF maxSize(100, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "c"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + // horizontal + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + setAnchor(l, a, Qt::AnchorHorizontalCenter, c, Qt::AnchorLeft, 0); + setAnchor(l, c, Qt::AnchorRight, b, Qt::AnchorHorizontalCenter, 0); + + // vertical + setAnchor(l, l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + setAnchor(l, l, Qt::AnchorTop, b, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, c, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + p->setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize, QSizeF(20, 20)); + + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize, QSizeF(200, 20)); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + + delete p; +} + +void tst_QGraphicsAnchorLayout::simple_semifloat() +{ + // Useful for testing simplification between A_left and B_left. + // Unfortunately the only way to really test that now is to manually inspect the + // simplified graph. + QSizeF minSize(10, 10); + QSizeF pref(50, 10); + QSizeF maxSize(100, 10); + + QGraphicsWidget *A = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *B = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // horizontal + setAnchor(l, l, Qt::AnchorLeft, A, Qt::AnchorLeft, 0); + setAnchor(l, A, Qt::AnchorRight, B, Qt::AnchorLeft, 0); + setAnchor(l, B, Qt::AnchorRight, l, Qt::AnchorRight, 0); + + setAnchor(l, A, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, B, Qt::AnchorLeft, b, Qt::AnchorLeft, 0); + + // vertical + setAnchor(l, l, Qt::AnchorTop, A, Qt::AnchorTop, 0); + setAnchor(l, l, Qt::AnchorTop, B, Qt::AnchorTop, 0); + setAnchor(l, A, Qt::AnchorBottom, a, Qt::AnchorTop, 0); + setAnchor(l, B, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + setAnchor(l, b, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + p->setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize, QSizeF(20, 20)); + + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(100, 20)); + + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize, QSizeF(200, 20)); + + delete p; +} + +void tst_QGraphicsAnchorLayout::layoutDirection() +{ + QSizeF minSize(10, 10); + QSizeF pref(50, 10); + QSizeF maxSize(100, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + QGraphicsWidget *c = createItem(minSize, pref, QSizeF(100, 20), "c"); + + a->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + b->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + c->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 5, 10, 15); + // horizontal + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + setAnchor(l, a, Qt::AnchorHorizontalCenter, c, Qt::AnchorLeft, 0); + setAnchor(l, c, Qt::AnchorRight, b, Qt::AnchorHorizontalCenter, 0); + + // vertical + setAnchor(l, l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + setAnchor(l, l, Qt::AnchorTop, b, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, c, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + p->setLayoutDirection(Qt::LeftToRight); + p->setLayout(l); + + QGraphicsScene scene; + QGraphicsView *view = new QGraphicsView(&scene); + scene.addItem(p); + p->show(); + view->show(); + + QVERIFY(p->layout()); + QCOMPARE(checkReverseDirection(p), true); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + + delete p; + delete view; +} + +void tst_QGraphicsAnchorLayout::diagonal() +{ + QSizeF minSize(10, 100); + QSizeF pref(70, 100); + QSizeF maxSize(100, 100); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "C"); + QGraphicsWidget *d = createItem(minSize, pref, maxSize, "D"); + QGraphicsWidget *e = createItem(minSize, pref, maxSize, "E"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + // vertical + l->addAnchor(a, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorTop, a, Qt::AnchorBottom); + l->addAnchor(c, Qt::AnchorTop, b, Qt::AnchorBottom); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, e, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + + // horizontal + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, e, Qt::AnchorLeft); + + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(e, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorRight, e, Qt::AnchorLeft); + + QCOMPARE(l->count(), 5); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(30.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(170.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 10.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 0.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 100.0, 10.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 20.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(20.0, 200.0, 10.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(70.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(70.0, 100.0, 30.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 90.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 90.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + QSizeF testA(175.0, 300.0); + p.resize(testA); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 75.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(75.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(75.0, 100.0, 25.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 75.0, 100.0)); + QCOMPARE(p.size(), testA); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + + QVERIFY(p.layout()); + QCOMPARE(checkReverseDirection(&p), true); + + c->setMinimumWidth(300); + QVERIFY(layoutHasConflict(l)); +} + +void tst_QGraphicsAnchorLayout::parallel() +{ + QGraphicsWidget *a = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(200, 100), "A"); + + QGraphicsWidget *b = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(300, 100), "B"); + + QGraphicsWidget *c = createItem(QSizeF(100, 100), + QSizeF(200, 100), + QSizeF(350, 100), "C"); + + QGraphicsWidget *d = createItem(QSizeF(100, 100), + QSizeF(170, 100), + QSizeF(200, 100), "D"); + + QGraphicsWidget *e = createItem(QSizeF(150, 100), + QSizeF(150, 100), + QSizeF(200, 100), "E"); + + QGraphicsWidget *f = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(200, 100), "F"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, e, Qt::AnchorTop); + l->addAnchor(e, Qt::AnchorBottom, f, Qt::AnchorTop); + l->addAnchor(f, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, d, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, e, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, f, Qt::AnchorLeft); + l->addAnchor(d, Qt::AnchorRight, f, Qt::AnchorLeft); + l->addAnchor(e, Qt::AnchorRight, f, Qt::AnchorLeft); + l->addAnchor(f, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 6); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(450, 600)); + QCOMPARE(layoutPreferredSize, QSizeF(620, 600)); + QCOMPARE(layoutMaximumSize, QSizeF(750, 600)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0, 0, 100, 100)); + QCOMPARE(b->geometry(), QRectF(100, 100, 100, 100)); + QCOMPARE(c->geometry(), QRectF(100, 200, 250, 100)); + QCOMPARE(d->geometry(), QRectF(200, 300, 150, 100)); + QCOMPARE(e->geometry(), QRectF(200, 400, 150, 100)); + QCOMPARE(f->geometry(), QRectF(350, 500, 100, 100)); + QCOMPARE(p.size(), layoutMinimumSize); + + if (!hasSimplification) + return; + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0, 0, 150, 100)); + QCOMPARE(b->geometry(), QRectF(150, 100, 150, 100)); + QCOMPARE(c->geometry(), QRectF(150, 200, 320, 100)); + QCOMPARE(d->geometry(), QRectF(300, 300, 170, 100)); + QCOMPARE(e->geometry(), QRectF(300, 400, 170, 100)); + QCOMPARE(f->geometry(), QRectF(470, 500, 150, 100)); + QCOMPARE(p.size(), layoutPreferredSize); + + // Maximum size depends on simplification / fair distribution + // Without that, test may or may not pass, depending on the + // solution found by the solver at runtime. + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0, 0, 200, 100)); + QCOMPARE(b->geometry(), QRectF(200, 100, 175, 100)); + QCOMPARE(c->geometry(), QRectF(200, 200, 350, 100)); + QCOMPARE(d->geometry(), QRectF(375, 300, 175, 100)); + QCOMPARE(e->geometry(), QRectF(375, 400, 175, 100)); + QCOMPARE(f->geometry(), QRectF(550, 500, 200, 100)); + QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); +} + +void tst_QGraphicsAnchorLayout::parallel2() +{ + QGraphicsWidget *a = createItem(QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), + QSizeF(200.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(100.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(190.0, 100.0), "B"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchors(l, a, Qt::Horizontal); + l->addAnchor(l, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, a, Qt::AnchorRight); + + QCOMPARE(l->count(), 2); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(100.0, 200.0)); + QCOMPARE(layoutPreferredSize, QSizeF(150.0, 200.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 200.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::snake() +{ + QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(20.0, 100.0), + QSizeF(40.0, 100.0), "B"); + + QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), "C"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorRight); + l->addAnchor(b, Qt::AnchorLeft, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(120.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 50.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 100.0, 40.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 200.0, 50.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(50.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(50.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 200.0, 100.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(layoutHasConflict(l) == false); + + // Test QSizePolicy::ExpandFlag, it shouldn't change the extreme + // points of the layout... + b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QSizeF newLayoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF newLayoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF newLayoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, newLayoutMinimumSize); + QCOMPARE(layoutMaximumSize, newLayoutMaximumSize); + QCOMPARE(layoutPreferredSize, newLayoutPreferredSize); +} + +void tst_QGraphicsAnchorLayout::snakeOppositeDirections() +{ + QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(20.0, 100.0), + QSizeF(40.0, 100.0), "B"); + + QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), "C"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + + // Both a and c are 'pointing' to b + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorRight); + l->addAnchor(c, Qt::AnchorLeft, b, Qt::AnchorLeft); + + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(120.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 50.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 100.0, 40.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 200.0, 50.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(50.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(50.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 200.0, 100.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(p.layout()); + QCOMPARE(checkReverseDirection(&p), true); +} + +void tst_QGraphicsAnchorLayout::fairDistribution() +{ + QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "B"); + + QGraphicsWidget *c = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "C"); + + QGraphicsWidget *d = createItem(QSizeF(60.0, 100.0), + QSizeF(165.0, 100.0), + QSizeF(600.0, 100.0), "D"); + + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 400.0)); + QCOMPARE(layoutPreferredSize, QSizeF(165.0, 400.0)); + QCOMPARE(layoutMaximumSize, QSizeF(300.0, 400.0)); + + p.resize(layoutMinimumSize); + if (!hasSimplification) + QEXPECT_FAIL("", "Without simplification there is no fair distribution.", Abort); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 20.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(20.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(40.0, 200.0, 20.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 60.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 55.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(55.0, 100.0, 55.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(110.0, 200.0, 55.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 165.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(100.0, 100.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(200.0, 200.0, 100.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 300.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() +{ + QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "B"); + + QGraphicsWidget *c = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "C"); + + QGraphicsWidget *d = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "D"); + + QGraphicsWidget *e = createItem(QSizeF(60.0, 100.0), + QSizeF(220.0, 100.0), + QSizeF(600.0, 100.0), "E"); + + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, e, Qt::AnchorTop); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(a, Qt::AnchorLeft, l, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorLeft, a, Qt::AnchorRight); + l->addAnchor(c, Qt::AnchorLeft, b, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorLeft, c, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchors(l, e, Qt::Horizontal); + + QCOMPARE(l->count(), 5); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 500.0)); + QCOMPARE(layoutPreferredSize, QSizeF(220.0, 500.0)); + QCOMPARE(layoutMaximumSize, QSizeF(400.0, 500.0)); + + if (!hasSimplification) + return; + + p.resize(layoutMinimumSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); +} + +void tst_QGraphicsAnchorLayout::proportionalPreferred() +{ + QSizeF minSize(0, 100); + + QGraphicsWidget *a = createItem(minSize, QSizeF(10, 100), QSizeF(20, 100), "A"); + QGraphicsWidget *b = createItem(minSize, QSizeF(20, 100), QSizeF(30, 100), "B"); + QGraphicsWidget *c = createItem(minSize, QSizeF(14, 100), QSizeF(20, 100), "C"); + QGraphicsWidget *d = createItem(minSize, QSizeF(10, 100), QSizeF(20, 100), "D"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, d, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(0, 400)); + QCOMPARE(layoutPreferredSize, QSizeF(24, 400)); + QCOMPARE(layoutMaximumSize, QSizeF(30, 400)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(c->size().width(), d->size().width()); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); + + p.resize(QSizeF(12, 400)); + + // Proportionality between size given and preferred size, this + // should be respected in this graph for (a) and (b)|(c). + qreal factor = 12.0 / 24.0; + + QCOMPARE(c->size().width(), d->size().width()); + QCOMPARE(a->size().width(), 10 * factor); + QCOMPARE(c->size().width(), 14 * factor); + QCOMPARE(p.size(), QSizeF(12, 400)); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::example() +{ + QSizeF minSize(30, 100); + QSizeF pref(210, 100); + QSizeF maxSize(300, 100); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "C"); + QGraphicsWidget *d = createItem(minSize, pref, maxSize, "D"); + QGraphicsWidget *e = createItem(minSize, pref, maxSize, "E"); + QGraphicsWidget *f = createItem(QSizeF(30, 50), QSizeF(150, 50), maxSize, "F"); + QGraphicsWidget *g = createItem(QSizeF(30, 50), QSizeF(30, 100), maxSize, "G"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + // vertical + l->addAnchor(a, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorTop, l, Qt::AnchorTop); + + l->addAnchor(c, Qt::AnchorTop, a, Qt::AnchorBottom); + l->addAnchor(c, Qt::AnchorTop, b, Qt::AnchorBottom); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, e, Qt::AnchorTop); + + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(c, Qt::AnchorTop, f, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorVerticalCenter, f, Qt::AnchorBottom); + l->addAnchor(f, Qt::AnchorBottom, g, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, g, Qt::AnchorBottom); + + // horizontal + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, e, Qt::AnchorLeft); + + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(e, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorRight, e, Qt::AnchorLeft); + + l->addAnchor(l, Qt::AnchorLeft, f, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, g, Qt::AnchorLeft); + l->addAnchor(f, Qt::AnchorRight, g, Qt::AnchorRight); + + QCOMPARE(l->count(), 7); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(90.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(510.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(570.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); + + p.resize(layoutPreferredSize); + QCOMPARE(p.size(), layoutPreferredSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::setSpacing() +{ + QSizeF minSize(10, 10); + QSizeF pref(20, 20); + QSizeF maxSize(50, 50); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize); + QGraphicsWidget *b = createItem(minSize, pref, maxSize); + QGraphicsWidget *c = createItem(minSize, pref, maxSize); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->addCornerAnchors(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); + l->addCornerAnchors(b, Qt::TopRightCorner, l, Qt::TopRightCorner); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + + l->addAnchors(l, c, Qt::Horizontal); + + l->addAnchor(a, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + + p->setLayout(l); + l->setSpacing(1); + + // don't let the style influence the test. + l->setContentsMargins(0, 0, 0, 0); + + QGraphicsScene scene; + scene.addItem(p); + QGraphicsView *view = new QGraphicsView(&scene); + view->show(); + p->show(); + + QApplication::processEvents(); +#ifdef Q_WS_MAC + QTest::qWait(200); +#endif + + // 21x21 + QCOMPARE(p->size(), QSizeF(41, 41)); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(21, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 21, 41, 20)); + + l->setHorizontalSpacing(4); + QApplication::processEvents(); + p->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(24, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 21, 44, 20)); + + l->setVerticalSpacing(0); + QApplication::processEvents(); + p->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(24, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 20, 44, 20)); + + delete p; + delete view; +} + +class CustomLayoutStyle : public QWindowsStyle +{ + Q_OBJECT +public: + CustomLayoutStyle() : QWindowsStyle() + { + hspacing = 5; + vspacing = 10; + } + + virtual int pixelMetric(PixelMetric metric, const QStyleOption * option = 0, + const QWidget * widget = 0 ) const; + + int hspacing; + int vspacing; + +protected slots: + int layoutSpacingImplementation(QSizePolicy::ControlType control1, + QSizePolicy::ControlType control2, + Qt::Orientation orientation, + const QStyleOption *option = 0, + const QWidget *widget = 0) const; + +}; + +#define CT1(c) CT2(c, c) +#define CT2(c1, c2) ((uint)c1 << 16) | (uint)c2 + +int CustomLayoutStyle::layoutSpacingImplementation(QSizePolicy::ControlType control1, + QSizePolicy::ControlType control2, + Qt::Orientation orientation, + const QStyleOption * /*option = 0*/, + const QWidget * /*widget = 0*/) const +{ + if (orientation == Qt::Horizontal) { + switch (CT2(control1, control2)) { + case CT1(QSizePolicy::PushButton): + return 2; + break; + } + return 5; + } else { + switch (CT2(control1, control2)) { + case CT1(QSizePolicy::RadioButton): + return 2; + break; + + } + return 10; + } +} + +int CustomLayoutStyle::pixelMetric(PixelMetric metric, const QStyleOption * option /*= 0*/, + const QWidget * widget /*= 0*/ ) const +{ + switch (metric) { + case PM_LayoutLeftMargin: + return 0; + break; + case PM_LayoutTopMargin: + return 3; + break; + case PM_LayoutRightMargin: + return 6; + break; + case PM_LayoutBottomMargin: + return 9; + break; + case PM_LayoutHorizontalSpacing: + return hspacing; + case PM_LayoutVerticalSpacing: + return vspacing; + break; + default: + break; + } + return QWindowsStyle::pixelMetric(metric, option, widget); +} + +void tst_QGraphicsAnchorLayout::styleDefaults() +{ + QSizeF minSize (10, 10); + QSizeF pref(20, 20); + QSizeF maxSize (50, 50); + + /* + create this layout, where a,b have controlType QSizePolicy::RadioButton + c,d have controlType QSizePolicy::PushButton: + +-------+ + |a | + | b | + | c | + | d| + +-------+ + */ + QGraphicsScene scene; + QGraphicsWidget *a = createItem(minSize, pref, maxSize); + QSizePolicy spRadioButton = a->sizePolicy(); + spRadioButton.setControlType(QSizePolicy::RadioButton); + a->setSizePolicy(spRadioButton); + + QGraphicsWidget *b = createItem(minSize, pref, maxSize); + b->setSizePolicy(spRadioButton); + + QGraphicsWidget *c = createItem(minSize, pref, maxSize); + QSizePolicy spPushButton = c->sizePolicy(); + spPushButton.setControlType(QSizePolicy::PushButton); + c->setSizePolicy(spPushButton); + + QGraphicsWidget *d = createItem(minSize, pref, maxSize); + d->setSizePolicy(spPushButton); + + QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window); + + // Test layoutSpacingImplementation + CustomLayoutStyle *style = new CustomLayoutStyle; + style->hspacing = -1; + style->vspacing = -1; + window->setStyle(style); + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + + l->addCornerAnchors(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); + l->addCornerAnchors(a, Qt::BottomRightCorner, b, Qt::TopLeftCorner); + l->addCornerAnchors(b, Qt::BottomRightCorner, c, Qt::TopLeftCorner); + l->addCornerAnchors(c, Qt::BottomRightCorner, d, Qt::TopLeftCorner); + l->addCornerAnchors(d, Qt::BottomRightCorner, l, Qt::BottomRightCorner); + + window->setLayout(l); + + scene.addItem(window); + + window->show(); + QGraphicsView view(&scene); + view.resize(200, 200); + view.show(); + + window->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 3, 20, 20)); //radio + QCOMPARE(b->geometry(), QRectF(25, 25, 20, 20)); //radio + QCOMPARE(c->geometry(), QRectF(50, 55, 20, 20)); //push + QCOMPARE(d->geometry(), QRectF(72, 85, 20, 20)); //push + QCOMPARE(l->geometry(), QRectF(0, 0, 98, 114)); + + + // Test pixelMetric(PM_Layout{Horizontal|Vertical}Spacing + window->setStyle(0); + + style->hspacing = 1; + style->vspacing = 2; + + window->setStyle(style); + window->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 3, 20, 20)); + QCOMPARE(b->geometry(), QRectF(21, 25, 20, 20)); + QCOMPARE(c->geometry(), QRectF(42, 47, 20, 20)); + QCOMPARE(d->geometry(), QRectF(63, 69, 20, 20)); + QCOMPARE(l->geometry(), QRectF(0, 0, 89, 98)); + + window->setStyle(0); + delete style; +} + + +/*! + Taken from "hard" complex case, found at + https://cwiki.nokia.com/S60QTUI/AnchorLayoutComplexCases + + This layout has a special property, since it has two possible solutions for its minimum size. + + For instance, when it is in its minimum size - the layout have two possible solutions: + 1. c.width == 10, e.width == 10 and g.width == 10 + (all others have width 0) + 2. d.width == 10 and g.width == 10 + (all others have width 0) + + It also has several solutions for preferred size. +*/ +static QGraphicsAnchorLayout *createAmbiguousS60Layout() +{ + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + QSizeF minSize(0, 10); + QSizeF pref(50, 10); + QSizeF maxSize(100, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "c"); + QGraphicsWidget *d = createItem(minSize, pref, maxSize, "d"); + QGraphicsWidget *e = createItem(minSize, pref, maxSize, "e"); + QGraphicsWidget *f = createItem(minSize, pref, maxSize, "f"); + QGraphicsWidget *g = createItem(minSize, pref, maxSize, "g"); + + //<!-- Trunk --> + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 10); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 10); + setAnchor(l, b, Qt::AnchorRight, c, Qt::AnchorLeft, 10); + setAnchor(l, c, Qt::AnchorRight, d, Qt::AnchorLeft, 10); + setAnchor(l, d, Qt::AnchorRight, l, Qt::AnchorRight, 10); + + //<!-- Above trunk --> + setAnchor(l, b, Qt::AnchorLeft, e, Qt::AnchorLeft, 10); + setAnchor(l, e, Qt::AnchorRight, d, Qt::AnchorLeft, 10); + + //<!-- Below trunk --> + setAnchor(l, a, Qt::AnchorHorizontalCenter, g, Qt::AnchorLeft, 10); + setAnchor(l, g, Qt::AnchorRight, f, Qt::AnchorHorizontalCenter, 10); + setAnchor(l, c, Qt::AnchorLeft, f, Qt::AnchorLeft, 10); + setAnchor(l, f, Qt::AnchorRight, d, Qt::AnchorRight, 10); + + //<!-- vertical is simpler --> + setAnchor(l, l, Qt::AnchorTop, e, Qt::AnchorTop, 0); + setAnchor(l, e, Qt::AnchorBottom, a, Qt::AnchorTop, 0); + setAnchor(l, e, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + setAnchor(l, e, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, e, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, f, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, b, Qt::AnchorBottom, 0); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorBottom, 0); + setAnchor(l, a, Qt::AnchorBottom, d, Qt::AnchorBottom, 0); + setAnchor(l, f, Qt::AnchorBottom, g, Qt::AnchorTop, 0); + setAnchor(l, g, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + return l; +} + +void tst_QGraphicsAnchorLayout::hardComplexS60() +{ + QGraphicsAnchorLayout *l = createAmbiguousS60Layout(); + QCOMPARE(l->count(), 7); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + p->setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize, QSizeF(60, 40)); + // expected preferred might be wrong, (haven't manually verified it) + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QCOMPARE(layoutPreferredSize, QSizeF(220, 40)); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize, QSizeF(240, 40)); + + delete p; +} + +void tst_QGraphicsAnchorLayout::stability() +{ + QVector<QRectF> geometries; + geometries.resize(7); + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + bool sameAsPreviousArrangement = true; + // it usually fails after 3-4 iterations + for (int pass = 0; pass < 20 && sameAsPreviousArrangement; ++pass) { + // In case we need to "scramble" the heap allocator to provoke this bug. + //static const int primes[] = {2, 3, 5, 13, 89, 233, 1597, 28657, 514229}; // fibo primes + //const int primeCount = sizeof(primes)/sizeof(int); + //int alloc = primes[pass % primeCount] + pass; + //void *mem = qMalloc(alloc); + //qFree(mem); + QGraphicsAnchorLayout *l = createAmbiguousS60Layout(); + p->setLayout(l); + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + l->setGeometry(QRectF(QPointF(0,0), layoutMinimumSize)); + QApplication::processEvents(); + for (int i = l->count() - 1; i >=0 && sameAsPreviousArrangement; --i) { + QRectF geom = l->itemAt(i)->geometry(); + if (pass != 0) { + sameAsPreviousArrangement = (geometries[i] == geom); + } + geometries[i] = geom; + } + p->setLayout(0); // uninstalls and deletes the layout + QApplication::processEvents(); + } + delete p; + QEXPECT_FAIL("", "The layout have several solutions, but which solution it picks is not stable", Continue); + QCOMPARE(sameAsPreviousArrangement, true); +} + +void tst_QGraphicsAnchorLayout::delete_anchor() +{ + QGraphicsScene scene; + QSizeF minSize(0, 0); + QSizeF prefSize(50, 50); + QSizeF maxSize(100, 100); + QGraphicsWidget *w1 = createItem(minSize, prefSize, maxSize, "w1"); + QGraphicsWidget *w2 = createItem(minSize, prefSize, maxSize, "w2"); + QGraphicsWidget *w3 = createItem(minSize, prefSize, maxSize, "w3"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setSpacing(0); + l->setContentsMargins(0, 0, 0, 0); + + // Horizontal + l->addAnchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); + l->addAnchor(w1, Qt::AnchorRight, w2, Qt::AnchorLeft); + l->addAnchor(w2, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(w1, Qt::AnchorRight, w3, Qt::AnchorLeft); + l->addAnchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + + // Vertical + l->addAnchors(l, w1, Qt::Vertical); + l->addAnchors(l, w2, Qt::Vertical); + l->addAnchors(l, w3, Qt::Vertical); + + QGraphicsAnchor *anchor = l->anchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + anchor->setSpacing(10); + + QGraphicsWidget *p = new QGraphicsWidget; + p->setLayout(l); + + QCOMPARE(l->count(), 3); + + scene.addItem(p); + QGraphicsView *view = new QGraphicsView(&scene); + QApplication::processEvents(); + // Should now be simplified + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize).width(), qreal(110)); + QGraphicsAnchor *anchor1 = l->anchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + QVERIFY(anchor1); + QGraphicsAnchor *anchor2 = l->anchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + QVERIFY(anchor2); + QGraphicsAnchor *anchor3 = l->anchor(l, Qt::AnchorRight, w3, Qt::AnchorRight); + QVERIFY(anchor3); + QGraphicsAnchor *anchor4 = l->anchor(l, Qt::AnchorRight, w3, Qt::AnchorRight); + QVERIFY(anchor4); + + // should all be the same object + QCOMPARE(anchor1, anchor2); + QCOMPARE(anchor2, anchor3); + QCOMPARE(anchor3, anchor4); + + // check if removal works + delete anchor1; + + QApplication::processEvents(); + + // it should also change the preferred size of the layout + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize).width(), qreal(100)); + + delete p; + delete view; +} + +void tst_QGraphicsAnchorLayout::sizePolicy() +{ + QGraphicsScene scene; + QSizeF minSize(0, 0); + QSizeF prefSize(50, 50); + QSizeF maxSize(100, 100); + QGraphicsWidget *w1 = createItem(minSize, prefSize, maxSize, "w1"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setSpacing(0); + l->setContentsMargins(0, 0, 0, 0); + + // horizontal and vertical + l->addAnchors(l, w1); + + QGraphicsWidget *p = new QGraphicsWidget; + p->setLayout(l); + + scene.addItem(p); + QGraphicsView *view = new QGraphicsView(&scene); + + // QSizePolicy::Minimum + w1->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + + // QSizePolicy::Maximum + w1->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(50, 50)); + + // QSizePolicy::Fixed + w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(50, 50)); + + // QSizePolicy::Preferred + w1->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + + // QSizePolicy::Ignored + w1->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + + // Anchor size policies + w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + QGraphicsAnchor *anchor = l->anchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); + anchor->setSpacing(10); + + // QSizePolicy::Minimum + anchor->setSizePolicy(QSizePolicy::Minimum); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(60, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + // The layout has a maximum size of QWIDGETSIZE_MAX, so the result won't exceed that value. + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + + // QSizePolicy::Preferred + anchor->setSizePolicy(QSizePolicy::Preferred); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + // The layout has a maximum size of QWIDGETSIZE_MAX, so the result won't exceed that value. + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + + // QSizePolicy::Maximum + anchor->setSizePolicy(QSizePolicy::Maximum); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(60, 50)); + + // QSizePolicy::Ignored + anchor->setSizePolicy(QSizePolicy::Ignored); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + + delete p; + delete view; +} + +/*! + \internal + + Uses private API. (We have decided to pull hasConflicts() out of the API). However, it also + tests some tight conditions (almost-in-conflict) that we really want to test. +*/ +void tst_QGraphicsAnchorLayout::conflicts() +{ + QGraphicsWidget *a = createItem(QSizeF(80,10), QSizeF(90,10), QSizeF(100,10), "a"); + QGraphicsWidget *b = createItem(QSizeF(10,10), QSizeF(20,10), QSizeF(30,10), "b"); + QGraphicsWidget *c = createItem(QSizeF(10,10), QSizeF(20,10), QSizeF(30,10), "c"); + + QGraphicsAnchorLayout *l; + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + + l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // with the following setup, 'a' cannot be larger than 30 we will first have a Simplex conflict + + // horizontal + setAnchor(l, l, Qt::AnchorLeft, b, Qt::AnchorLeft); + setAnchor(l, b, Qt::AnchorRight, c, Qt::AnchorLeft); + setAnchor(l, c, Qt::AnchorRight, l, Qt::AnchorRight); + setAnchor(l, b, Qt::AnchorHorizontalCenter, a, Qt::AnchorLeft); + setAnchor(l, a, Qt::AnchorRight, c, Qt::AnchorHorizontalCenter); + + // vertical + setAnchor(l, l, Qt::AnchorTop, a, Qt::AnchorTop); + setAnchor(l, a, Qt::AnchorBottom, b, Qt::AnchorTop); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorTop); + setAnchor(l, b, Qt::AnchorBottom, l, Qt::AnchorBottom); + setAnchor(l, c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + p->setLayout(l); + + QCOMPARE(layoutHasConflict(l), true); + + a->setMinimumSize(QSizeF(29,10)); + QCOMPARE(layoutHasConflict(l), false); + + a->setMinimumSize(QSizeF(30,10)); + QCOMPARE(layoutHasConflict(l), false); + + delete p; +} + +void tst_QGraphicsAnchorLayout::floatConflict() +{ + QGraphicsWidget *a = createItem(QSizeF(80,10), QSizeF(90,10), QSizeF(100,10), "a"); + QGraphicsWidget *b = createItem(QSizeF(80,10), QSizeF(90,10), QSizeF(100,10), "b"); + + QGraphicsAnchorLayout *l; + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + + l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + p->setLayout(l); + + // horizontal + // with this anchor we have two floating items + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft); + + // Just checking if the layout is handling well the removal of floating items + delete l->anchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + QCOMPARE(l->count(), 0); + QCOMPARE(layoutHasConflict(l), false); + + // setting back the same anchor + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft); + + // We don't support floating items but they should be counted as if they are in the layout + QCOMPARE(l->count(), 2); + // Although, we have an invalid situation + QCOMPARE(layoutHasConflict(l), true); + + // Semi-floats are supported + setAnchor(l, a, Qt::AnchorLeft, l, Qt::AnchorLeft); + QCOMPARE(l->count(), 2); + + // Vertically the layout has floating items. Therefore, we have a conflict + QCOMPARE(layoutHasConflict(l), true); + + // No more floating items + setAnchor(l, b, Qt::AnchorRight, l, Qt::AnchorRight); + setAnchor(l, a, Qt::AnchorTop, l, Qt::AnchorTop); + setAnchor(l, a, Qt::AnchorBottom, l, Qt::AnchorBottom); + setAnchor(l, b, Qt::AnchorTop, l, Qt::AnchorTop); + setAnchor(l, b, Qt::AnchorBottom, l, Qt::AnchorBottom); + QCOMPARE(layoutHasConflict(l), false); + + delete p; +} + +void tst_QGraphicsAnchorLayout::infiniteMaxSizes() +{ + if (sizeof(qreal) <= 4) { + QSKIP("qreal has too little precision, result will be wrong", SkipAll); + } + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + QSizeF minSize(10, 10); + QSizeF pref(50, 10); + QSizeF maxSize(QWIDGETSIZE_MAX, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "c"); + QGraphicsWidget *d = createItem(minSize, pref, maxSize, "d"); + QGraphicsWidget *e = createItem(minSize, pref, maxSize, "e"); + + //<!-- Trunk --> + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + setAnchor(l, c, Qt::AnchorRight, d, Qt::AnchorLeft, 0); + setAnchor(l, d, Qt::AnchorRight, l, Qt::AnchorRight, 0); + setAnchor(l, b, Qt::AnchorHorizontalCenter, e, Qt::AnchorLeft, 0); + setAnchor(l, e, Qt::AnchorRight, c, Qt::AnchorHorizontalCenter, 0); + + QGraphicsWidget p; + p.setLayout(l); + + QCOMPARE(int(p.effectiveSizeHint(Qt::MaximumSize).width()), + QWIDGETSIZE_MAX); + + p.resize(200, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 50, 10)); + QCOMPARE(b->geometry(), QRectF(50, 0, 50, 10)); + QCOMPARE(c->geometry(), QRectF(100, 0, 50, 10)); + QCOMPARE(d->geometry(), QRectF(150, 0, 50, 10)); + + p.resize(1000, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 250, 10)); + QCOMPARE(b->geometry(), QRectF(250, 0, 250, 10)); + QCOMPARE(c->geometry(), QRectF(500, 0, 250, 10)); + QCOMPARE(d->geometry(), QRectF(750, 0, 250, 10)); + + p.resize(40000, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 10000, 10)); + QCOMPARE(b->geometry(), QRectF(10000, 0, 10000, 10)); + QCOMPARE(c->geometry(), QRectF(20000, 0, 10000, 10)); + QCOMPARE(d->geometry(), QRectF(30000, 0, 10000, 10)); +} + +void tst_QGraphicsAnchorLayout::simplifiableUnfeasible() +{ + QGraphicsWidget *a = createItem(QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(110.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(190.0, 100.0), "B"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchors(l, a, Qt::Horizontal); + l->addAnchor(l, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, a, Qt::AnchorRight); + + QCOMPARE(l->count(), 2); + + QGraphicsWidget p; + p.setLayout(l); + + l->invalidate(); + QVERIFY(layoutHasConflict(l)); + if (hasSimplification) + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + + // Now we make it valid + b->setMinimumWidth(100); + + l->invalidate(); + QVERIFY(!layoutHasConflict(l)); + if (hasSimplification) + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + + // And make it invalid again + a->setPreferredWidth(70); + a->setMaximumWidth(70); + + l->invalidate(); + QVERIFY(layoutHasConflict(l)); + if (hasSimplification) + QVERIFY(!usedSimplex(l, Qt::Horizontal)); +} + +/* + Test whether the anchor direction can prevent it from + being simplificated +*/ +void tst_QGraphicsAnchorLayout::simplificationVsOrder() +{ + QSizeF minSize(10, 10); + QSizeF pref(20, 10); + QSizeF maxSize(50, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "C"); + + QGraphicsWidget frame; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&frame); + + // Bulk anchors + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorLeft, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + + // Problematic anchor, direction b->c + QGraphicsAnchor *anchor = l->addAnchor(b, Qt::AnchorRight, c, Qt::AnchorRight); + anchor->setSpacing(5); + + l->effectiveSizeHint(Qt::MinimumSize); + if (hasSimplification) { + QCOMPARE(usedSimplex(l, Qt::Horizontal), false); + QCOMPARE(usedSimplex(l, Qt::Vertical), false); + } + + // Problematic anchor, direction c->b + delete anchor; + anchor = l->addAnchor(c, Qt::AnchorRight, b, Qt::AnchorRight); + anchor->setSpacing(5); + + l->effectiveSizeHint(Qt::MinimumSize); + if (hasSimplification) { + QCOMPARE(usedSimplex(l, Qt::Horizontal), false); + QCOMPARE(usedSimplex(l, Qt::Vertical), false); + } +} + +void tst_QGraphicsAnchorLayout::parallelSimplificationOfCenter() +{ + QSizeF minSize(10, 10); + QSizeF pref(20, 10); + QSizeF maxSize(50, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + + QGraphicsWidget parent; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&parent); + l->setContentsMargins(0, 0, 0, 0); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorRight, a, Qt::AnchorRight); + + l->addAnchor(a, Qt::AnchorHorizontalCenter, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, a, Qt::AnchorRight); + + parent.resize(l->effectiveSizeHint(Qt::PreferredSize)); + + QCOMPARE(a->geometry(), QRectF(0, 0, 40, 10)); + QCOMPARE(b->geometry(), QRectF(20, 0, 20, 10)); +} + +/* + Test whether redundance of anchors (in this case by using addCornerAnchors), will + prevent simplification to take place when it should. +*/ +void tst_QGraphicsAnchorLayout::simplificationVsRedundance() +{ + QSizeF minSize(10, 10); + QSizeF pref(20, 10); + QSizeF maxSize(50, 30); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "C"); + + QGraphicsWidget frame; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&frame); + + l->addCornerAnchors(a, Qt::TopLeftCorner, l, Qt::TopLeftCorner); + l->addCornerAnchors(a, Qt::BottomLeftCorner, l, Qt::BottomLeftCorner); + + l->addCornerAnchors(b, Qt::TopLeftCorner, a, Qt::TopRightCorner); + l->addCornerAnchors(b, Qt::TopRightCorner, l, Qt::TopRightCorner); + + l->addCornerAnchors(c, Qt::TopLeftCorner, b, Qt::BottomLeftCorner); + l->addCornerAnchors(c, Qt::BottomLeftCorner, a, Qt::BottomRightCorner); + l->addCornerAnchors(c, Qt::TopRightCorner, b, Qt::BottomRightCorner); + l->addCornerAnchors(c, Qt::BottomRightCorner, l, Qt::BottomRightCorner); + + l->effectiveSizeHint(Qt::MinimumSize); + + QCOMPARE(layoutHasConflict(l), false); + + if (!hasSimplification) + QEXPECT_FAIL("", "Test depends on simplification.", Abort); + + QCOMPARE(usedSimplex(l, Qt::Horizontal), false); + QCOMPARE(usedSimplex(l, Qt::Vertical), false); +} + +/* + Avoid regression where the saved prefSize would be lost. This was + solved by saving the original spacing in the QGraphicsAnchorPrivate class +*/ +void tst_QGraphicsAnchorLayout::spacingPersistency() +{ + QGraphicsWidget w; + QGraphicsWidget *a = createItem(); + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&w); + + l->addAnchors(l, a, Qt::Horizontal); + QGraphicsAnchor *anchor = l->anchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + + anchor->setSpacing(-30); + QCOMPARE(anchor->spacing(), -30.0); + + anchor->setSpacing(30); + QCOMPARE(anchor->spacing(), 30.0); + + anchor->setSizePolicy(QSizePolicy::Ignored); + w.effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(anchor->spacing(), 30.0); +} + +/* + Test whether a correct preferred size is set when a "snake" sequence is in parallel with the + layout or half of the layout. The tricky thing here is that all items on the snake should + keep their preferred sizes. +*/ +void tst_QGraphicsAnchorLayout::snakeParallelWithLayout() +{ + QSizeF minSize(10, 20); + QSizeF pref(50, 20); + QSizeF maxSize(100, 20); + + QGraphicsWidget *a = createItem(maxSize, maxSize, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(maxSize, maxSize, maxSize, "C"); + + QGraphicsWidget parent; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&parent); + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + // First we'll do the case in parallel with the entire layout... + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorRight); + l->addAnchor(b, Qt::AnchorLeft, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + parent.resize(l->effectiveSizeHint(Qt::PreferredSize)); + + // Note that A and C are fixed in the maximum size + QCOMPARE(l->geometry(), QRectF(QPointF(0, 0), QSizeF(150, 60))); + QCOMPARE(a->geometry(), QRectF(QPointF(0, 0), maxSize)); + QCOMPARE(b->geometry(), QRectF(QPointF(50, 20), pref)); + QCOMPARE(c->geometry(), QRectF(QPointF(50, 40), maxSize)); + + // Then, we change the "snake" to be in parallel with half of the layout + delete l->anchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorHorizontalCenter); + + parent.resize(l->effectiveSizeHint(Qt::PreferredSize)); + + QCOMPARE(l->geometry(), QRectF(QPointF(0, 0), QSizeF(300, 60))); + QCOMPARE(a->geometry(), QRectF(QPointF(0, 0), maxSize)); + QCOMPARE(b->geometry(), QRectF(QPointF(50, 20), pref)); + QCOMPARE(c->geometry(), QRectF(QPointF(50, 40), maxSize)); +} + +/* + Avoid regression where the sizeHint constraints would not be + created for a parallel anchor that included the first layout half +*/ +void tst_QGraphicsAnchorLayout::parallelToHalfLayout() +{ + QGraphicsWidget *a = createItem(); + + QGraphicsWidget w; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&w); + l->setContentsMargins(10, 10, 10, 10); + + l->addAnchors(l, a, Qt::Vertical); + + QGraphicsAnchor *anchor; + anchor = l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + anchor->setSpacing(5); + anchor = l->addAnchor(l, Qt::AnchorHorizontalCenter, a, Qt::AnchorRight); + anchor->setSpacing(-5); + + const QSizeF minimumSizeHint = w.effectiveSizeHint(Qt::MinimumSize); + const QSizeF preferredSizeHint = w.effectiveSizeHint(Qt::PreferredSize); + const QSizeF maximumSizeHint = w.effectiveSizeHint(Qt::MaximumSize); + + const QSizeF overhead = QSizeF(10 + 5 + 5, 10) * 2; + + QCOMPARE(minimumSizeHint, QSizeF(200, 100) + overhead); + QCOMPARE(preferredSizeHint, QSizeF(300, 100) + overhead); + QCOMPARE(maximumSizeHint, QSizeF(400, 100) + overhead); +} + +void tst_QGraphicsAnchorLayout::globalSpacing() +{ + QGraphicsWidget *a = createItem(); + QGraphicsWidget *b = createItem(); + + QGraphicsWidget w; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&w); + + l->addCornerAnchors(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); + l->addCornerAnchors(a, Qt::BottomRightCorner, b, Qt::TopLeftCorner); + l->addCornerAnchors(b, Qt::BottomRightCorner, l, Qt::BottomRightCorner); + + w.resize(w.effectiveSizeHint(Qt::PreferredSize)); + qreal vSpacing = b->geometry().top() - a->geometry().bottom(); + qreal hSpacing = b->geometry().left() - a->geometry().right(); + + // Set spacings manually + l->setVerticalSpacing(vSpacing + 10); + l->setHorizontalSpacing(hSpacing + 5); + + w.resize(w.effectiveSizeHint(Qt::PreferredSize)); + qreal newVSpacing = b->geometry().top() - a->geometry().bottom(); + qreal newHSpacing = b->geometry().left() - a->geometry().right(); + + QCOMPARE(newVSpacing, vSpacing + 10); + QCOMPARE(newHSpacing, hSpacing + 5); + + // Set a negative spacing. This will unset the previous spacing and + // bring back the widget-defined spacing. + l->setSpacing(-1); + + w.resize(w.effectiveSizeHint(Qt::PreferredSize)); + newVSpacing = b->geometry().top() - a->geometry().bottom(); + newHSpacing = b->geometry().left() - a->geometry().right(); + + QCOMPARE(newVSpacing, vSpacing); + QCOMPARE(newHSpacing, hSpacing); +} + +void tst_QGraphicsAnchorLayout::graphicsAnchorHandling() +{ + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(); + QGraphicsWidget *a = createItem(); + + l->addAnchors(l, a); + + QGraphicsAnchor *layoutAnchor = l->anchor(l, Qt::AnchorTop, l, Qt::AnchorBottom); + QGraphicsAnchor *itemAnchor = l->anchor(a, Qt::AnchorTop, a, Qt::AnchorBottom); + QGraphicsAnchor *invalidAnchor = l->anchor(a, Qt::AnchorTop, l, Qt::AnchorBottom); + + // Ensure none of these anchors are accessible. + QVERIFY(layoutAnchor == 0); + QVERIFY(itemAnchor == 0); + QVERIFY(invalidAnchor == 0); + + // Hook the anchors to a QObject + QObject object; + QGraphicsAnchor *userAnchor = l->anchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorBottom, a, Qt::AnchorBottom); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorRight, a, Qt::AnchorRight); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + userAnchor->setParent(&object); + + QCOMPARE(object.children().size(), 4); + + // Delete layout, this will cause all anchors to be deleted internally. + // We expect the public QGraphicsAnchor instances to be deleted too. + delete l; + QCOMPARE(object.children().size(), 0); + + delete a; +} + +void tst_QGraphicsAnchorLayout::invalidHierarchyCheck() +{ + QGraphicsWidget window(0, Qt::Window); + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + window.setLayout(l); + + QCOMPARE(l->count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): " + "You cannot add the parent of the layout to the layout."); + QVERIFY(!l->addAnchor(l, Qt::AnchorLeft, &window, Qt::AnchorLeft)); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): " + "You cannot add the parent of the layout to the layout."); + l->addAnchors(l, &window); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): " + "You cannot add the parent of the layout to the layout."); + l->addCornerAnchors(l, Qt::TopLeftCorner, &window, Qt::TopLeftCorner); + QCOMPARE(l->count(), 0); +} + +QTEST_MAIN(tst_QGraphicsAnchorLayout) +#include "tst_qgraphicsanchorlayout.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/qgraphicsanchorlayout1.pro b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/qgraphicsanchorlayout1.pro new file mode 100644 index 0000000000..bcad43fc12 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/qgraphicsanchorlayout1.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qgraphicsanchorlayout1.cpp +CONFIG += parallel_test diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp new file mode 100644 index 0000000000..05f08e8719 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -0,0 +1,3111 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtTest/QtTest> +#include <QTest> +#include <QMetaType> +#include <QtWidgets/qgraphicsanchorlayout.h> +#include <private/qgraphicsanchorlayout_p.h> + +#define TEST_COMPLEX_CASES + + +//---------------------- AnchorLayout helper class ---------------------------- +class TheAnchorLayout : public QGraphicsAnchorLayout +{ +public: + TheAnchorLayout() : QGraphicsAnchorLayout() + { + setContentsMargins( 0,0,0,0 ); + setSpacing( 0 ); + } + + bool isValid() + { + return !QGraphicsAnchorLayoutPrivate::get(this)->hasConflicts(); + } + + void setAnchor( + QGraphicsLayoutItem *startItem, + Qt::AnchorPoint startEdge, + QGraphicsLayoutItem *endItem, + Qt::AnchorPoint endEdge, + qreal value) + { + QGraphicsAnchor *anchor = addAnchor( startItem, startEdge, endItem, endEdge); + if (anchor) + anchor->setSpacing(value); + } + + int indexOf(const QGraphicsLayoutItem* item) const + { + for ( int i=0; i< count(); i++) { + if ( itemAt(i) == item ) { + return i; + } + } + return -1; + } + + void removeItem(QGraphicsLayoutItem* item) + { + removeAt(indexOf(item)); + } + + void removeAnchor( + QGraphicsLayoutItem *startItem, + Qt::AnchorPoint startEdge, + QGraphicsLayoutItem *endItem, + Qt::AnchorPoint endEdge) + { + delete QGraphicsAnchorLayout::anchor(startItem, startEdge, endItem, endEdge); + } +}; +//----------------------------------------------------------------------------- + + +struct BasicLayoutTestData +{ + inline BasicLayoutTestData( + int index1, Qt::AnchorPoint edge1, + int index2, Qt::AnchorPoint edge2, + qreal distance) + : firstIndex(index1), firstEdge(edge1), + secondIndex(index2), secondEdge(edge2), + spacing(distance) + { + } + + int firstIndex; + Qt::AnchorPoint firstEdge; + int secondIndex; + Qt::AnchorPoint secondEdge; + qreal spacing; +}; + +struct AnchorItemSizeHint +{ + inline AnchorItemSizeHint( + qreal hmin, qreal hpref, qreal hmax, + qreal vmin, qreal vpref, qreal vmax ) + : hmin(hmin), hpref(hpref), hmax(hmax), vmin(vmin), vpref(vpref), vmax(vmax) + { + } + qreal hmin, hpref, hmax; + qreal vmin, vpref, vmax; +}; + +// some test results + +struct BasicLayoutTestResult +{ + inline BasicLayoutTestResult( + int resultIndex, const QRectF& resultRect ) + : index(resultIndex), rect(resultRect) + { + } + + int index; + QRectF rect; +}; + +typedef QList<BasicLayoutTestData> BasicLayoutTestDataList; +Q_DECLARE_METATYPE(BasicLayoutTestDataList) + +typedef QList<BasicLayoutTestResult> BasicLayoutTestResultList; +Q_DECLARE_METATYPE(BasicLayoutTestResultList) + +typedef QList<AnchorItemSizeHint> AnchorItemSizeHintList; +Q_DECLARE_METATYPE(AnchorItemSizeHintList) + + +//---------------------- Test Widget used on all tests ------------------------ +class TestWidget : public QGraphicsWidget +{ +public: + inline TestWidget(QGraphicsItem *parent = 0, const QString &name = QString()) + : QGraphicsWidget(parent) + { + setContentsMargins( 0,0,0,0 ); + if (name.isEmpty()) + setData(0, QString::fromAscii("w%1").arg(quintptr(this))); + else + setData(0, name); + } + ~TestWidget() + { + } + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; +}; + +QSizeF TestWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED( constraint ); + if (which == Qt::MinimumSize) { + return QSizeF(5,5); + } + + if (which == Qt::PreferredSize) { + return QSizeF(50,50); + } + + return QSizeF(500,500); +} +//----------------------------------------------------------------------------- + + + +//----------------------------- Test class ------------------------------------ +class tst_QGraphicsAnchorLayout1 : public QObject +{ + Q_OBJECT + +private slots: + void testCount(); + + void testRemoveAt(); + void testRemoveItem(); + + void testItemAt(); + void testIndexOf(); + + void testAddAndRemoveAnchor(); + void testIsValid(); + void testSpecialCases(); + + void testBasicLayout_data(); + void testBasicLayout(); + + void testNegativeSpacing_data(); + void testNegativeSpacing(); + + void testMixedSpacing_data(); + void testMixedSpacing(); + + void testMulti_data(); + void testMulti(); + + void testCenterAnchors_data(); + void testCenterAnchors(); + + void testRemoveCenterAnchor_data(); + void testRemoveCenterAnchor(); + + void testSingleSizePolicy_data(); + void testSingleSizePolicy(); + + void testDoubleSizePolicy_data(); + void testDoubleSizePolicy(); + + void testSizeDistribution_data(); + void testSizeDistribution(); + + void testSizeHint(); + +#ifdef TEST_COMPLEX_CASES + void testComplexCases_data(); + void testComplexCases(); +#endif +}; + + +void tst_QGraphicsAnchorLayout1::testCount() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + + TheAnchorLayout *layout = new TheAnchorLayout(); + QVERIFY( layout->count() == 0 ); + + TestWidget *widget1 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + QCOMPARE( layout->count(), 1 ); + + // adding one more anchor for already added widget should not increase the count + layout->setAnchor(layout, Qt::AnchorRight, widget1, Qt::AnchorRight, 1); + QCOMPARE( layout->count(), 1 ); + + // create one more widget and attach with anchor layout + TestWidget *widget2 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 1); + QCOMPARE( layout->count(), 2 ); + + widget->setLayout(layout); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testRemoveAt() +{ + TheAnchorLayout *layout = new TheAnchorLayout(); + QVERIFY( layout->count() == 0 ); + + TestWidget *widget1 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 2); + QVERIFY( layout->count() == 1 ); + + TestWidget *widget2 = new TestWidget(); + layout->setAnchor(widget2, Qt::AnchorLeft, layout, Qt::AnchorLeft, 0.1); + QVERIFY( layout->count() == 2 ); + + layout->removeAt(0); + QVERIFY( layout->count() == 1 ); + + layout->removeAt(-55); + layout->removeAt(55); + QVERIFY( layout->count() == 1 ); + + layout->removeAt(0); + QVERIFY( layout->count() == 0 ); + + delete layout; + delete widget1; + delete widget2; +} + +void tst_QGraphicsAnchorLayout1::testRemoveItem() +{ + TheAnchorLayout *layout = new TheAnchorLayout(); + QCOMPARE( layout->count(), 0 ); + + TestWidget *widget1 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 2); + QCOMPARE( layout->count(), 1 ); + + TestWidget *widget2 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 0.1); + QCOMPARE( layout->count(), 2 ); + + layout->removeItem(0); + QCOMPARE( layout->count(), 2 ); + + layout->removeItem(widget1); + QCOMPARE( layout->count(), 1 ); + QCOMPARE( layout->indexOf(widget1), -1 ); + QCOMPARE( layout->indexOf(widget2), 0 ); + + layout->removeItem(widget1); + QCOMPARE( layout->count(), 1 ); + + layout->removeItem(widget2); + QVERIFY( layout->count() == 0 ); + + delete layout; + delete widget1; + delete widget2; +} + +void tst_QGraphicsAnchorLayout1::testItemAt() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + TestWidget *widget2 = new TestWidget(); + TestWidget *widget3 = new TestWidget(); + TestWidget *widget4 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget3, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget4, Qt::AnchorLeft, 0.1); + + QVERIFY( layout->itemAt(0) == widget1 ); + + layout->removeAt(0); + + QVERIFY( layout->itemAt(0) == widget2 ); + + delete widget1; + + widget->setLayout(layout); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testIndexOf() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + TestWidget *widget2 = new TestWidget(); + TestWidget *widget3 = new TestWidget(); + TestWidget *widget4 = new TestWidget(); + + QCOMPARE( layout->indexOf(widget1), -1 ); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget3, Qt::AnchorLeft, 0.1); + + QCOMPARE( layout->indexOf(widget4), -1 ); + layout->setAnchor(layout, Qt::AnchorLeft, widget4, Qt::AnchorLeft, 0.1); + + QCOMPARE( layout->count(), 4 ); + for (int i = 0; i < layout->count(); ++i) { + QCOMPARE(layout->indexOf(layout->itemAt(i)), i); + } + + QCOMPARE( layout->indexOf(0), -1 ); + widget->setLayout(layout); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testAddAndRemoveAnchor() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + TestWidget *widget2 = new TestWidget(); + TestWidget *widget3 = new TestWidget(); + TestWidget *widget4 = new TestWidget(); + TestWidget *widget5 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 0.5); + layout->setAnchor(layout, Qt::AnchorLeft, widget3, Qt::AnchorLeft, 10); + layout->setAnchor(layout, Qt::AnchorLeft, widget4, Qt::AnchorLeft, 0.1); + QCOMPARE( layout->count(), 4 ); + + // test setting invalid anchors + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor NULL items"); + layout->setAnchor(0, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor NULL items"); + layout->setAnchor(layout, Qt::AnchorLeft, 0, Qt::AnchorLeft, 1); + QCOMPARE( layout->count(), 4 ); + + // test removing invalid anchors + layout->removeAnchor(widget4, Qt::AnchorRight, widget1, Qt::AnchorRight); + + // anchor one horizontal edge with vertical edge. it should not add this widget as a child + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor edges of different orientations"); + layout->setAnchor(layout, Qt::AnchorLeft, widget5, Qt::AnchorTop, 10); + QCOMPARE( layout->count(), 4 ); + + // anchor two edges of a widget (to define width / height) + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + layout->setAnchor(widget5, Qt::AnchorLeft, widget5, Qt::AnchorRight, 10); + // QCOMPARE( layout->count(), 5 ); + QCOMPARE( layout->count(), 4 ); + + // anchor yet new widget properly + layout->setAnchor(layout, Qt::AnchorRight, widget5, Qt::AnchorRight, 20 ); + QCOMPARE( layout->count(), 5 ); + + // remove anchor for widget1. widget1 should be removed from layout since the + // last anchor was removed. + layout->removeAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft); + + QCOMPARE( layout->count(), 4 ); + QVERIFY( !widget1->parentLayoutItem() ); + + // test that item is not removed from layout if other anchors remain set + layout->setAnchor(widget2, Qt::AnchorLeft, widget3, Qt::AnchorRight, 10); + layout->removeAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft); + QCOMPARE( layout->count(), 4 ); + + // remove all the anchors + layout->removeAnchor(widget2, Qt::AnchorLeft, widget3, Qt::AnchorRight); + layout->removeAnchor(layout, Qt::AnchorLeft, widget3, Qt::AnchorLeft); + layout->removeAnchor(layout, Qt::AnchorLeft, widget4, Qt::AnchorLeft); + layout->removeAnchor(widget5, Qt::AnchorLeft, widget5, Qt::AnchorRight); + layout->removeAnchor(layout, Qt::AnchorRight, widget5, Qt::AnchorRight); + + QCOMPARE( layout->count(), 0 ); + + // set one anchor "another way round" to get full coverage for "removeAnchor" + layout->setAnchor(widget1, Qt::AnchorLeft, layout, Qt::AnchorLeft, 0.1); + layout->removeAnchor(widget1, Qt::AnchorLeft, layout, Qt::AnchorLeft); + + QCOMPARE( layout->count(), 0 ); + + delete widget1; + delete widget2; + delete widget3; + delete widget4; + delete widget5; + + widget->setLayout(layout); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testIsValid() +{ + // Empty, valid + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + widget->setLayout(layout); + widget->setGeometry(QRectF(0,0,100,100)); + + QCOMPARE(layout->isValid(), true); + delete widget; + } + + // One widget, valid + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorTop, widget1, Qt::AnchorTop, 0.1); + layout->setAnchor(widget1, Qt::AnchorRight, layout, Qt::AnchorRight, 0.1); + layout->setAnchor(widget1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 0.1); + + widget->setLayout(layout); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(layout->isValid(), true); + delete widget; + } + + // Overconstrained one widget, invalid + // ### Our understanding is that this case is valid. What happens though, + // is that the layout minimum and maximum vertical size hints become + // the same, 10.1. That means its height is fixed. + // What will "fail" then is the "setGeometry(0, 0, 100, 100)" call, + // after which the layout geometry will be (0, 0, 100, 10.1). + + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorTop, widget1, Qt::AnchorTop, 0.1); + layout->setAnchor(widget1, Qt::AnchorRight, layout, Qt::AnchorRight, 0.1); + layout->setAnchor(widget1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 0.1); + + layout->setAnchor(widget1, Qt::AnchorTop, layout, Qt::AnchorBottom, 10); + + widget->setLayout(layout); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(layout->isValid(), true); + delete widget; + } + + // Underconstrained two widgets, valid + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + TestWidget *widget2 = new TestWidget(); + + // Vertically the layout has floating items. Therefore, we have a conflict + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorRight, widget1, Qt::AnchorRight, -0.1); + + // Horizontally the layout has floating items. Therefore, we have a conflict + layout->setAnchor(layout, Qt::AnchorTop, widget2, Qt::AnchorTop, 0.1); + layout->setAnchor(layout, Qt::AnchorBottom, widget2, Qt::AnchorBottom, -0.1); + + widget->setLayout(layout); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(layout->isValid(), false); + delete widget; + } +} + +void tst_QGraphicsAnchorLayout1::testSpecialCases() +{ + // One widget, setLayout before defining layouts + { +#ifdef QT_DEBUG + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLayout::addChildLayoutItem: QGraphicsWidget \"\"" + " in wrong parent; moved to correct parent"); +#endif + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + widget->setLayout(layout); + + TestWidget *widget1 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout->setAnchor(widget1, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(widget1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(widget1->geometry(), QRectF(1,1,98,98)); + delete widget1; + delete widget; + } + + // One widget, layout inside layout, layout inside layout inside layout + { +#ifdef QT_DEBUG + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLayout::addChildLayoutItem: QGraphicsWidget \"\"" + " in wrong parent; moved to correct parent"); +#endif + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + widget->setLayout(layout); + + TheAnchorLayout *layout1 = new TheAnchorLayout(); + TestWidget *widget1 = new TestWidget(); + layout1->setAnchor(layout1, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout1->setAnchor(widget1, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(widget1, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + TheAnchorLayout *layout2 = new TheAnchorLayout(); + TestWidget *widget2 = new TestWidget(); + layout2->setAnchor(layout2, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 1); + layout2->setAnchor(layout2, Qt::AnchorTop, widget2, Qt::AnchorTop, 1); + layout2->setAnchor(widget2, Qt::AnchorRight, layout2, Qt::AnchorRight, 1); + layout2->setAnchor(widget2, Qt::AnchorBottom, layout2, Qt::AnchorBottom, 1); + + layout1->setAnchor(layout1, Qt::AnchorLeft, layout2, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, layout2, Qt::AnchorTop, 1); + layout1->setAnchor(layout2, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(layout2, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + layout->setAnchor(layout, Qt::AnchorLeft, layout1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout1, Qt::AnchorTop, 1); + layout->setAnchor(layout1, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(layout1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + // remove and add again to improve test coverage. + layout->removeItem(layout1); + + layout->setAnchor(layout, Qt::AnchorLeft, layout1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout1, Qt::AnchorTop, 1); + layout->setAnchor(layout1, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(layout1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(widget1->geometry(), QRectF(2,2,96,96)); + QCOMPARE(widget2->geometry(), QRectF(3,3,94,94)); + delete widget; + } + + // One widget, layout inside layout, setLayout after layout definition + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + + TheAnchorLayout *layout1 = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + layout1->setAnchor(layout1, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout1->setAnchor(widget1, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(widget1, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + layout->setAnchor(layout, Qt::AnchorLeft, layout1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout1, Qt::AnchorTop, 1); + layout->setAnchor(layout1, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(layout1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + widget->setLayout(layout); + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(widget1->geometry(), QRectF(2,2,96,96)); + delete widget; + } + + // One widget, layout inside layout, setLayout after layout definition, widget transferred from + // one layout to another + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + widget->setLayout(layout); + + TheAnchorLayout *layout1 = new TheAnchorLayout(); + TestWidget *widget1 = new TestWidget(); + + // Additional layout + widget to improve coverage. + TheAnchorLayout *layout0 = new TheAnchorLayout(); + TestWidget *widget0 = new TestWidget(); + + // widget0 to layout0 + layout0->setAnchor(layout0, Qt::AnchorLeft, widget0, Qt::AnchorLeft, 1); + layout0->setAnchor(layout0, Qt::AnchorTop, widget0, Qt::AnchorTop, 1); + layout0->setAnchor(widget0, Qt::AnchorRight, layout0, Qt::AnchorRight, 1); + layout0->setAnchor(widget0, Qt::AnchorBottom, layout0, Qt::AnchorBottom, 1); + + // layout0 to layout + layout->setAnchor(layout, Qt::AnchorLeft, layout0, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout0, Qt::AnchorTop, 1); + layout->setAnchor(layout0, Qt::AnchorRight, layout, Qt::AnchorRight, 50); + layout->setAnchor(layout0, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + // widget1 to layout1 + layout1->setAnchor(layout1, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout1->setAnchor(widget1, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(widget1, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + // layout1 to layout + layout->setAnchor(layout, Qt::AnchorLeft, layout1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout1, Qt::AnchorTop, 1); + layout->setAnchor(layout1, Qt::AnchorRight, layout, Qt::AnchorRight, 50); + layout->setAnchor(layout1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + TheAnchorLayout *layout2 = new TheAnchorLayout(); + + // layout2 to layout + layout->setAnchor(layout, Qt::AnchorLeft, layout2, Qt::AnchorLeft, 50); + layout->setAnchor(layout, Qt::AnchorTop, layout2, Qt::AnchorTop, 1); + layout->setAnchor(layout2, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(layout2, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + // transfer widget1 to layout2 + layout2->setAnchor(layout2, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout2->setAnchor(layout2, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout2->setAnchor(widget1, Qt::AnchorRight, layout2, Qt::AnchorRight, 1); + layout2->setAnchor(widget1, Qt::AnchorBottom, layout2, Qt::AnchorBottom, 1); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(widget1->geometry(), QRectF(51,2,47,96)); + delete widget; + } + + // One widget, set first to one layout then to another. Child reparented. + // In addition widget as a direct child of another widget. Child reparented. + { + QGraphicsWidget *widget1 = new QGraphicsWidget; + TheAnchorLayout *layout1 = new TheAnchorLayout(); + widget1->setLayout(layout1); + + TestWidget *childWidget = new TestWidget(); + + // childWidget to layout1 + layout1->setAnchor(layout1, Qt::AnchorLeft, childWidget, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, childWidget, Qt::AnchorTop, 1); + layout1->setAnchor(childWidget, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(childWidget, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + widget1->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(childWidget->geometry(), QRectF(1,1,98,98)); + QVERIFY(childWidget->parentLayoutItem() == layout1); + QGraphicsWidget *widget2 = new QGraphicsWidget; + TheAnchorLayout *layout2 = new TheAnchorLayout(); + widget2->setLayout(layout2); + + // childWidget to layout2 + layout2->setAnchor(layout2, Qt::AnchorLeft, childWidget, Qt::AnchorLeft, 1); + layout2->setAnchor(layout2, Qt::AnchorTop, childWidget, Qt::AnchorTop, 1); + layout2->setAnchor(childWidget, Qt::AnchorRight, layout2, Qt::AnchorRight, 1); + layout2->setAnchor(childWidget, Qt::AnchorBottom, layout2, Qt::AnchorBottom, 1); + + QGraphicsWidget *widget3 = new QGraphicsWidget; + QGraphicsWidget *widget4 = new QGraphicsWidget; + // widget4 is a direct child of widget3 (i.e. not in any layout) + widget4->setParentItem(widget3); + + // widget4 to layout2 + layout2->setAnchor(layout2, Qt::AnchorLeft, widget4, Qt::AnchorLeft, 1); + layout2->setAnchor(layout2, Qt::AnchorTop, widget4, Qt::AnchorTop, 1); + layout2->setAnchor(widget4, Qt::AnchorRight, layout2, Qt::AnchorRight, 1); + layout2->setAnchor(widget4, Qt::AnchorBottom, layout2, Qt::AnchorBottom, 1); + + widget2->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(childWidget->geometry(), QRectF(1,1,98,98)); + QVERIFY(childWidget->parentLayoutItem() == layout2); + QCOMPARE(widget4->geometry(), QRectF(1,1,98,98)); + QVERIFY(widget4->parentLayoutItem() == layout2); + QVERIFY(widget4->parentItem() == widget2); + + delete widget4; + delete widget3; + delete widget1; + delete childWidget; + delete widget2; + } +} + +void tst_QGraphicsAnchorLayout1::testBasicLayout_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<BasicLayoutTestDataList>("data"); + QTest::addColumn<BasicLayoutTestResultList>("result"); + + typedef BasicLayoutTestData BasicData; + typedef BasicLayoutTestResult BasicResult; + + // One widget, basic + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 20) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 30) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 40) + ; + + theResult + << BasicResult(0, QRectF(20, 10, 150, 50) ) + ; + + QTest::newRow("One, simple") << QSizeF(200, 100) << theData << theResult; + } + + // One widget, duplicates + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 20) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 30) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 40) + + << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 0) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorRight, 0) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 0) + ; + + theResult + << BasicResult(0, QRectF(0, 0, 200, 100) ) + ; + + QTest::newRow("One, duplicates") << QSizeF(200, 100) << theData << theResult; + } + + // One widget, mixed + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorBottom, 80) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorRight, 150) + << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorRight, 150) + << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorBottom, 80) + ; + + theResult + << BasicResult(0, QRectF(50, 20, 100, 60) ) + ; + + QTest::newRow("One, mixed") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets (same layout), different ordering + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 10) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 180, 80) ) + << BasicResult(1, QRectF(10, 10, 180, 80) ) + ; + + QTest::newRow("Two, orderings") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, duplicate anchors + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 30) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 20) + + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 10) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(1, Qt::AnchorTop, -1, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorRight, 1, Qt::AnchorRight, 0) + ; + + theResult + << BasicResult(0, QRectF(30, 10, 160, 70) ) + << BasicResult(1, QRectF(10, 0, 190, 90) ) + ; + + QTest::newRow("Two, duplicates") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, mixed + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorBottom, 90) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorRight, 190) + << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorRight, 190) + << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorBottom, 90) + + << BasicData(1, Qt::AnchorTop, -1, Qt::AnchorBottom, 20) + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 10) + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorRight, 20) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 180, 80) ) + << BasicResult(1, QRectF(10, 80, 10, 10) ) + ; + + QTest::newRow("Two, mixed") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 1 horizontal connection, first completely defined + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 180) + + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 10) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 20) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 10, 80) ) + << BasicResult(1, QRectF(30, 10, 160, 70) ) + ; + + QTest::newRow("Two, 1h connected") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 2 horizontal connections, first completely defined + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 180) + + // ### QGAL is not sensible to the argument order in this case + // To achieve the desired result we must explicitly set a negative + // spacing. + // << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorRight, 100) + << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorRight, -100) + + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 30) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 20) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 10, 80) ) + << BasicResult(1, QRectF(50, 10, 60, 70) ) + ; + + QTest::newRow("Two, 2h connected") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 1 vertical connection, first completely defined + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 180) + + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 30) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(0, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 20) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 10, 80) ) + << BasicResult(1, QRectF(30, 20, 160, 60) ) + ; + + QTest::newRow("Two, 1v connected") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 2 vertical connections, first completely defined + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 180) + + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 30) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(0, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorBottom, 0, Qt::AnchorBottom, 20) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 10, 80) ) + << BasicResult(1, QRectF(30, 20, 160, 50) ) + ; + + QTest::newRow("Two, 2v connected") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 1 horizontal and 1 vertical connection, first completely defined + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 180) + + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 80) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorRight, 100) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorBottom, 0, Qt::AnchorBottom, 10) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 10, 80) ) + << BasicResult(1, QRectF(80, 10, 40, 70) ) + ; + + QTest::newRow("Two, 1h+1v connected") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 2 horizontal and 2 vertical connections, first completely defined + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 180) + + << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorLeft, 80) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorRight, 100) + << BasicData(0, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorBottom, 0, Qt::AnchorBottom, 10) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 10, 80) ) + << BasicResult(1, QRectF(90, 20, 30, 60) ) + ; + + QTest::newRow("Two, 2h+2v connected") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 2 horizontal and 2 vertical connections, dependent on each other. + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorRight, 150) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorBottom, 1, Qt::AnchorBottom, 10) + + << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorLeft, 90) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(0, Qt::AnchorTop, 1, Qt::AnchorTop, 10) + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 20) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 30, 60) ) + << BasicResult(1, QRectF(100, 20, 90, 60) ) + ; + + QTest::newRow("Two, 2h+2v connected2") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, connected, overlapping + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + // << BasicData(1, Qt::AnchorLeft, 0, Qt::AnchorRight, 30) + // ### QGAL has different semantics and assumes right edges are always + // to the left of left edges. Thus we need the minus sign here. + << BasicData(1, Qt::AnchorLeft, 0, Qt::AnchorRight, -30) + << BasicData(0, Qt::AnchorBottom, 1, Qt::AnchorBottom, 40) + + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 40) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 20) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + ; + + theResult + << BasicResult(0, QRectF(10, 10, 60, 40) ) + << BasicResult(1, QRectF(40, 20, 150, 70) ) + ; + + QTest::newRow("Two, connected overlapping") << QSizeF(200, 100) << theData << theResult; + } +} + +void tst_QGraphicsAnchorLayout1::testNegativeSpacing_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<BasicLayoutTestDataList>("data"); + QTest::addColumn<BasicLayoutTestResultList>("result"); + + typedef BasicLayoutTestData BasicData; + typedef BasicLayoutTestResult BasicResult; + + // One widget, negative spacing + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + /// ### QGAL assumes items are always inside the layout. + // In this case, the negative spacing would make the item + // grow beyond the layout edges, which is OK, but gives a + // different result. + // Changing the direction of anchors (-1 to 0 or vice-versa) + // has no effect in this case. + + theData + // << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorTop, -10) + // << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorLeft, -20) + // << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorRight, -30) + // << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorBottom, -40) + + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, -10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, -20) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, -30) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, -40) + + ; + + theResult + // << BasicResult(0, QRectF(20, 10, 150, 50) ) + << BasicResult(0, QRectF(-20, -10, 250, 150) ) + ; + + QTest::newRow("One, simple (n)") << QSizeF(200, 100) << theData << theResult; + } + + // One widget, duplicates, negative spacing + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorTop, -20) + << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorLeft, -20) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorRight, -30) + << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorBottom, -40) + + << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorTop, -10) + << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorLeft, -10) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorRight, -10) + << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorBottom, -10) + ; + + theResult + // ### Same as above... + // << BasicResult(0, QRectF(10, 10, 180, 80) ) + << BasicResult(0, QRectF(-10, -10, 220, 120) ) + ; + + QTest::newRow("One, duplicates (n)") << QSizeF(200, 100) << theData << theResult; + } + + // One widget, mixed, negative spacing + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + // ### All anchors of negative spacing between the layout and an + // item are handled as to make sure the item is _outside_ the + // layout. + // To keep it inside, one _must_ use positive spacings. + // << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorTop, -80) + // << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorLeft, -150) + // << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorLeft, -150) + // << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorTop, -80) + + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorTop, 80) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorLeft, 150) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorLeft, 150) + << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorTop, 80) + ; + + theResult + << BasicResult(0, QRectF(50, 20, 100, 60) ) + ; + + QTest::newRow("One, mixed (n)") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 1 horizontal connection, first completely defined, negative spacing + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + // << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorTop, -10) + // << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorBottom, -10) + // << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorLeft, -10) + // << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorRight, -180) + + // << BasicData(1, Qt::AnchorLeft, 0, Qt::AnchorRight, -10) + // << BasicData(-1, Qt::AnchorRight, 1, Qt::AnchorRight, -10) + // << BasicData(1, Qt::AnchorTop, -1, Qt::AnchorTop, -10) + // << BasicData(-1, Qt::AnchorBottom, 1, Qt::AnchorBottom, -20) + + << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorTop, -10) + << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorBottom, -10) + << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorLeft, -10) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorRight, 180) + + << BasicData(1, Qt::AnchorLeft, 0, Qt::AnchorRight, -10) + << BasicData(-1, Qt::AnchorRight, 1, Qt::AnchorRight, -10) + << BasicData(1, Qt::AnchorTop, -1, Qt::AnchorTop, -10) + << BasicData(-1, Qt::AnchorBottom, 1, Qt::AnchorBottom, -20) + + ; + + theResult + // << BasicResult(0, QRectF(10, 10, 10, 80) ) + // << BasicResult(1, QRectF(30, 10, 160, 70) ) + + << BasicResult(0, QRectF(-10, -10, 30, 120) ) + << BasicResult(1, QRectF(10, -10, 200, 130) ) + ; + + QTest::newRow("Two, 1h connected (n)") << QSizeF(200, 100) << theData << theResult; + } + + // Basic case - two widgets, 2 horizontal and 2 vertical connections, dependent on each other, negative spacing + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorLeft, -10) + << BasicData(1, Qt::AnchorRight, 0, Qt::AnchorRight, -150) + << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorTop, -10) + << BasicData(1, Qt::AnchorBottom, 0, Qt::AnchorBottom, -10) + + << BasicData(1, Qt::AnchorLeft, 0, Qt::AnchorLeft, -90) + << BasicData(-1, Qt::AnchorRight, 1, Qt::AnchorRight, -10) + << BasicData(1, Qt::AnchorTop, 0, Qt::AnchorTop, -10) + << BasicData(-1, Qt::AnchorBottom, 1, Qt::AnchorBottom, -20) + ; + + theResult + // << BasicResult(0, QRectF(10, 10, 30, 60) ) + // << BasicResult(1, QRectF(100, 20, 90, 60) ) + << BasicResult(0, QRectF(-10, -10, 70, 120) ) + << BasicResult(1, QRectF(80, 0, 130, 120) ) + ; + + QTest::newRow("Two, 2h+2v connected2 (n)") << QSizeF(200, 100) << theData << theResult; + } +} + +void tst_QGraphicsAnchorLayout1::testMixedSpacing_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<BasicLayoutTestDataList>("data"); + QTest::addColumn<BasicLayoutTestResultList>("result"); + + typedef BasicLayoutTestData BasicData; + typedef BasicLayoutTestResult BasicResult; + + // Two widgets, partial overlapping + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorLeft, -50) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 50) + << BasicData(1, Qt::AnchorRight, 0, Qt::AnchorRight, 15) + + // << BasicData(1, Qt::AnchorTop, 0, Qt::AnchorBottom, 5) + << BasicData(1, Qt::AnchorTop, 0, Qt::AnchorBottom, -5) + << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorLeft, -10) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 20) + << BasicData(-1, Qt::AnchorBottom, 1, Qt::AnchorBottom, -5) + ; + + theResult + // << BasicResult(0, QRectF(50, 10, 45, 40) ) + // << BasicResult(1, QRectF(40, 45, 40, 50) ) + << BasicResult(0, QRectF(-50, 10, 145, 40) ) + << BasicResult(1, QRectF(-60, 45, 140, 60) ) + ; + + QTest::newRow("Two, partial overlap") << QSizeF(100, 100) << theData << theResult; + } + + // Two widgets, complete overlapping + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 5) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorRight, 0) + << BasicData(0, Qt::AnchorTop, 1, Qt::AnchorTop, 0) + << BasicData(1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 25) + + << BasicData(1, Qt::AnchorBottom, 0, Qt::AnchorBottom, 0) + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 5) + << BasicData(1, Qt::AnchorLeft, -1, Qt::AnchorRight, 50) + << BasicData(-1, Qt::AnchorRight, 1, Qt::AnchorRight, -10) + ; + + theResult + << BasicResult(0, QRectF(65, 5, 35, 35) ) + << BasicResult(1, QRectF(40, 5, 60, 35) ) + ; + + QTest::newRow("Two, complete overlap") << QSizeF(90, 45) << theData << theResult; + } + + // Five widgets, v shaped, edges shared + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + // edges shared + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 0) + << BasicData(1, Qt::AnchorRight, 2, Qt::AnchorLeft, 0) + << BasicData(2, Qt::AnchorRight, 3, Qt::AnchorLeft, 0) + << BasicData(3, Qt::AnchorRight, 4, Qt::AnchorLeft, 0) + << BasicData(1, Qt::AnchorBottom, 2, Qt::AnchorTop, 0) + << BasicData(0, Qt::AnchorBottom, 1, Qt::AnchorTop, 0) + << BasicData(3, Qt::AnchorBottom, 2, Qt::AnchorTop, 0) + << BasicData(4, Qt::AnchorBottom, 3, Qt::AnchorTop, 0) + << BasicData(0, Qt::AnchorBottom, 4, Qt::AnchorBottom, 0) + << BasicData(1, Qt::AnchorBottom, 3, Qt::AnchorBottom, 0) + << BasicData(0, Qt::AnchorTop, 4, Qt::AnchorTop, 0) + << BasicData(1, Qt::AnchorTop, 3, Qt::AnchorTop, 0) + + // margins + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 5) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 5) + << BasicData(2, Qt::AnchorBottom, -1, Qt::AnchorBottom, 5) + // << BasicData(-1, Qt::AnchorRight, 4, Qt::AnchorRight, -5) + << BasicData(-1, Qt::AnchorRight, 4, Qt::AnchorRight, 5) + + // additional details for exact size determination easily + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 25) + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorRight, 50) + // << BasicData(-1, Qt::AnchorRight, 3, Qt::AnchorRight, -25) + // << BasicData(-1, Qt::AnchorRight, 2, Qt::AnchorRight, -50) + << BasicData(-1, Qt::AnchorRight, 3, Qt::AnchorRight, 25) + << BasicData(-1, Qt::AnchorRight, 2, Qt::AnchorRight, 50) + << BasicData(-1, Qt::AnchorTop, 3, Qt::AnchorBottom, 50) + // << BasicData(-1, Qt::AnchorBottom, 3, Qt::AnchorTop, -50) + << BasicData(-1, Qt::AnchorBottom, 3, Qt::AnchorTop, 50) + + ; + + theResult + << BasicResult(0, QRectF(5,5,20,20)) + << BasicResult(1, QRectF(25,25,25,25)) + << BasicResult(2, QRectF(50,50,25,20)) + << BasicResult(3, QRectF(75,25,25,25)) + << BasicResult(4, QRectF(100,5,20,20)) + ; + + QTest::newRow("Five, V shape") << QSizeF(125, 75) << theData << theResult; + } + + // ### The behavior is different in QGraphicsAnchorLayout. What happens here is + // that when the above anchors are set, the layout size hints are changed. + // In the example, the minimum item width is 5, thus the minimum layout width + // becomes 105 (50 + 5 + 50). Once that size hint is set, trying to set + // the widget size to (10, 10) is not possible because + // QGraphicsWidget::setGeometry() will enforce the minimum is respected. + if (0) + // One widget, unsolvable + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 50) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 50) + ; + theResult + << BasicResult(0, QRectF(0,0,0,0)) + ; + + QTest::newRow("One widget, unsolvable") << QSizeF(10, 10) << theData << theResult; + } + + // Two widgets, one has fixed size + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 50) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 50) + // not supported, use sizePolicy instead + // << BasicData(0, Qt::AnchorLeft, 0, Qt::AnchorRight, 50) + + << BasicData(-1, Qt::AnchorLeft, 1, Qt::AnchorLeft, 50) + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 50) + ; + theResult + << BasicResult(0, QRectF(50,0,50,50)) + << BasicResult(1, QRectF(50,0,50,50)) + ; + + QTest::newRow("Two widgets, one has fixed size") << QSizeF(150, 150) << theData << theResult; + } +} + +void tst_QGraphicsAnchorLayout1::testMulti_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<BasicLayoutTestDataList>("data"); + QTest::addColumn<BasicLayoutTestResultList>("result"); + + typedef BasicLayoutTestData BasicData; + typedef BasicLayoutTestResult BasicResult; + + // Multiple widgets, all overllapping + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + const int n = 30; + for ( int i = 0 ; i < n; i++ ) { + theData + << BasicData(-1, Qt::AnchorTop, i, Qt::AnchorTop, 20) + << BasicData(-1, Qt::AnchorLeft, i, Qt::AnchorLeft, 10) + // << BasicData(-1, Qt::AnchorBottom, i, Qt::AnchorBottom, -40) + // << BasicData(-1, Qt::AnchorRight, i, Qt::AnchorRight, -30); + << BasicData(-1, Qt::AnchorBottom, i, Qt::AnchorBottom, 40) + << BasicData(-1, Qt::AnchorRight, i, Qt::AnchorRight, 30); + + theResult + << BasicResult(i, QRectF(10, 20, 160, 40) ); + } + + + QTest::newRow("Overlapping multi") << QSizeF(200, 100) << theData << theResult; + } + + // Multiple widgets, linear order + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + const qreal height = 1000.f; + const qreal width = 2000.f; + + const int n = 30; + + const qreal verticalStep = height/qreal(n+2); + const qreal horizontalStep = width/qreal(n+2); + + for ( int i = 0 ; i < n; i++ ) { + + if ( i == 0 ) { + // First item + theData + << BasicData(-1, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + << BasicData(-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(i+1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + << BasicData(i+1, Qt::AnchorRight, i, Qt::AnchorRight, -horizontalStep); + + } else if ( i == n-1 ) { + // Last item + theData + << BasicData(i-1, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + << BasicData(i-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(-1, Qt::AnchorBottom, i, Qt::AnchorBottom, verticalStep) + << BasicData(-1, Qt::AnchorRight, i, Qt::AnchorRight, horizontalStep); + // << BasicData(-1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + // << BasicData(-1, Qt::AnchorRight, i, Qt::AnchorRight, -horizontalStep); + + } else { + // items in the middle + theData + << BasicData(i-1, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + << BasicData(i-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(i+1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + << BasicData(i+1, Qt::AnchorRight, i, Qt::AnchorRight, -horizontalStep); + // << BasicData(i+1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + // << BasicData(i+1, Qt::AnchorRight, i, Qt::AnchorRight, -horizontalStep); + + } + + theResult + << BasicResult(i, QRectF((i+1)*horizontalStep, (i+1)*verticalStep, horizontalStep, verticalStep) ); + } + + + if (sizeof(qreal) == 4) { + qDebug("Linear multi: Skipping! (qreal has too little precision, result will be wrong)"); + } else { + QTest::newRow("Linear multi") << QSizeF(width, height) << theData << theResult; + } + } + + // Multiple widgets, V shape + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + const qreal height = 100.f; + const qreal width = 200.f; + + const int n = 31; // odd number please (3,5,7... ) + + const qreal verticalStep = height/(2.f+(n+1)/2.f); + const qreal horizontalStep = width/(n+2.f); + + for ( int i = 0 ; i < n; i++ ) { + + if ( i == 0 ) { + // First item + theData + << BasicData(-1, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + << BasicData(-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(i+1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + << BasicData(i, Qt::AnchorRight, i+1, Qt::AnchorRight, horizontalStep); + + } else if ( i == n-1 ) { + // Last item + theData + << BasicData(i-1, Qt::AnchorTop, i, Qt::AnchorTop, -verticalStep) + << BasicData(i-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(i-1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + << BasicData(i, Qt::AnchorRight, -1, Qt::AnchorRight, horizontalStep); + } else if ( i == ((n-1)/2) ) { + // midway + theData + << BasicData(i-1, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + << BasicData(i-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + // << BasicData(-1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + << BasicData(-1, Qt::AnchorBottom, i, Qt::AnchorBottom, verticalStep) + << BasicData(i, Qt::AnchorRight, i+1, Qt::AnchorRight, horizontalStep); + } else if ( i < ((n-1)/2) ) { + // before midway - going down + theData + << BasicData(i-1, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + << BasicData(i-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(i+1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + << BasicData(i, Qt::AnchorRight, i+1, Qt::AnchorRight, horizontalStep); + + } else { + // after midway - going up + theData + << BasicData(i-1, Qt::AnchorTop, i, Qt::AnchorTop, -verticalStep) + << BasicData(i-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(i-1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + << BasicData(i, Qt::AnchorRight, i+1, Qt::AnchorRight, horizontalStep); + + } + + if ( i <= ((n-1)/2) ) { + // until midway + theResult + << BasicResult(i, QRectF((i+1)*horizontalStep, (i+1)*verticalStep, horizontalStep, verticalStep) ); + } else { + // after midway + theResult + << BasicResult(i, QRectF((i+1)*horizontalStep, (n-i)*verticalStep, horizontalStep, verticalStep) ); + } + + } + if (sizeof(qreal) == 4) { + qDebug("V multi: Skipping! (qreal has too little precision, result will be wrong)"); + } else { + QTest::newRow("V multi") << QSizeF(width, height) << theData << theResult; + } + } + + // Multiple widgets, grid + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + const qreal height = 100.f; + const qreal width = 200.f; + + const int d = 10; // items per dimension + const int n = d*d; + + const qreal verticalStep = height/(d+2.f); + const qreal horizontalStep = width/(d+2.f); + + for ( int i = 0 ; i < n; i++ ) { + if ( i%d == 0 ) { + // left side item + theData + << BasicData(-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(i+1, Qt::AnchorRight, i, Qt::AnchorRight, -horizontalStep); + } else if ( (i+1)%d == 0 ) { + // rigth side item + theData + << BasicData(i-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + // << BasicData(-1, Qt::AnchorRight, i, Qt::AnchorRight, -horizontalStep); + << BasicData(-1, Qt::AnchorRight, i, Qt::AnchorRight, horizontalStep); + } else { + // horizontal middle + theData + << BasicData(i-1, Qt::AnchorLeft, i, Qt::AnchorLeft, horizontalStep) + << BasicData(i+1, Qt::AnchorRight, i, Qt::AnchorRight, -horizontalStep); + } + + if ( i < d ) { + // top line + theData + << BasicData(-1, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + << BasicData(i+d, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep); + } else if ( i >= (d-1)*d ){ + // bottom line + theData + << BasicData(i-d, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + // << BasicData(-1, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep) + << BasicData(-1, Qt::AnchorBottom, i, Qt::AnchorBottom, verticalStep); + } else { + // vertical middle + theData + << BasicData(i-d, Qt::AnchorTop, i, Qt::AnchorTop, verticalStep) + << BasicData(i+d, Qt::AnchorBottom, i, Qt::AnchorBottom, -verticalStep); + } + + theResult + << BasicResult(i, QRectF(((i%d)+1)*horizontalStep, ((i/d)+1)*verticalStep, horizontalStep, verticalStep) ); + } + + if (sizeof(qreal) == 4) { + qDebug("Grid multi: Skipping! (qreal has too little precision, result will be wrong)"); + } else { + QTest::newRow("Grid multi") << QSizeF(200, 100) << theData << theResult; + } + } +} + +inline QGraphicsLayoutItem *getItem( + int index, + const QList<QGraphicsWidget *>& widgets, + QGraphicsLayoutItem *defaultItem) +{ + if (index < 0) { + return defaultItem; + } + + return widgets[index]; +} + +static bool fuzzierCompare(qreal a, qreal b) +{ + return qAbs(a - b) <= qreal(0.0001); +} + +static bool fuzzierCompare(const QRectF &r1, const QRectF &r2) +{ + + return fuzzierCompare(r1.x(), r2.x()) && fuzzierCompare(r1.y(), r2.y()) + && fuzzierCompare(r1.width(), r2.width()) && fuzzierCompare(r1.height(), r2.height()); +} + +void tst_QGraphicsAnchorLayout1::testBasicLayout() +{ + QFETCH(QSizeF, size); + QFETCH(BasicLayoutTestDataList, data); + QFETCH(BasicLayoutTestResultList, result); + + QGraphicsWidget *widget = new QGraphicsWidget; + + // Determine amount of widgets to add. + int widgetCount = -1; + for (int i = 0; i < data.count(); ++i) { + const BasicLayoutTestData item = data[i]; + widgetCount = qMax(widgetCount, item.firstIndex); + widgetCount = qMax(widgetCount, item.secondIndex); + } + ++widgetCount; // widgetCount is max of indices. + + // Create dummy widgets + QList<QGraphicsWidget *> widgets; + for (int i = 0; i < widgetCount; ++i) { + TestWidget *w = new TestWidget(0, QString::fromAscii("W%1").arg(i)); + widgets << w; + } + + // Setup anchor layout + TheAnchorLayout *layout = new TheAnchorLayout; + + for (int i = 0; i < data.count(); ++i) { + const BasicLayoutTestData item = data[i]; + layout->setAnchor( + getItem(item.firstIndex, widgets, layout), + item.firstEdge, + getItem(item.secondIndex, widgets, layout), + item.secondEdge, + item.spacing ); + } + + widget->setLayout(layout); + widget->setContentsMargins(0,0,0,0); + + widget->resize(size); + QCOMPARE(widget->size(), size); + + // Validate + for (int i = 0; i < result.count(); ++i) { + const BasicLayoutTestResult item = result[i]; + QRectF expected = item.rect; + QRectF actual = widgets[item.index]->geometry(); + + QVERIFY(fuzzierCompare(actual, expected)); + } + + // Test mirrored mode + widget->setLayoutDirection(Qt::RightToLeft); + layout->activate(); + // Validate + for (int j = 0; j < result.count(); ++j) { + const BasicLayoutTestResult item = result[j]; + QRectF mirroredRect(item.rect); + // only valid cases are mirrored + if (mirroredRect.isValid()){ + mirroredRect.moveLeft(size.width()-item.rect.width()-item.rect.left()); + } + QRectF expected = mirroredRect; + QRectF actual = widgets[item.index]->geometry(); + + QVERIFY(fuzzierCompare(actual, expected)); + } + + qDeleteAll(widgets); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testNegativeSpacing() +{ + // use the same frame + testBasicLayout(); +} + +void tst_QGraphicsAnchorLayout1::testMixedSpacing() +{ + // use the same frame + testBasicLayout(); +} + +void tst_QGraphicsAnchorLayout1::testMulti() +{ + // use the same frame + testBasicLayout(); +} + +void tst_QGraphicsAnchorLayout1::testCenterAnchors_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<BasicLayoutTestDataList>("data"); + QTest::addColumn<BasicLayoutTestResultList>("result"); + + typedef BasicLayoutTestData BasicData; + typedef BasicLayoutTestResult BasicResult; + + // Basic center case + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + // << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorHorizontalCenter, -10) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorHorizontalCenter, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorRight, 15) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorVerticalCenter, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 5); + + theResult + << BasicResult(0, QRectF(5, 5, 10, 10) ); + + QTest::newRow("center, basic") << QSizeF(20, 20) << theData << theResult; + } + + // Basic center case, with invalid (shouldn't affect on result) + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + // << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorHorizontalCenter, -10) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorHorizontalCenter, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorRight, 15) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorVerticalCenter, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 5) + + // bogus definitions + << BasicData(0, Qt::AnchorHorizontalCenter, -1, Qt::AnchorBottom, 5) + << BasicData(0, Qt::AnchorHorizontalCenter, 1, Qt::AnchorVerticalCenter, 5) + << BasicData(0, Qt::AnchorVerticalCenter, -1, Qt::AnchorRight, 5) + << BasicData(0, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 666) + << BasicData(0, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 999) + << BasicData(0, Qt::AnchorLeft, 0, Qt::AnchorLeft, 333) + << BasicData(-1, Qt::AnchorRight, -1, Qt::AnchorRight, 222) + << BasicData(0, Qt::AnchorTop, 0, Qt::AnchorTop, 111) + << BasicData(0, Qt::AnchorBottom, 0, Qt::AnchorBottom, 444); + + theResult + << BasicResult(0, QRectF(5, 5, 10, 10) ); + + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor edges of different orientations"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor edges of different orientations"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor edges of different orientations"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::newRow("center, basic with invalid") << QSizeF(20, 20) << theData << theResult; + } + + // Basic center case 2 + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 0) + // Not supported << BasicData(0, Qt::AnchorHorizontalCenter, 0, Qt::AnchorRight, 5) + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorRight, 5) + << BasicData(-1, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 0) + << BasicData(-1, Qt::AnchorVerticalCenter, 0, Qt::AnchorTop, -5); + + theResult + << BasicResult(0, QRectF(5, 5, 10, 10) ); + + QTest::newRow("center, basic 2") << QSizeF(20, 20) << theData << theResult; + } + + // Basic center case, overrides + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 10) + << BasicData(-1, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 20) + << BasicData(0, Qt::AnchorHorizontalCenter, -1, Qt::AnchorHorizontalCenter, 30) + << BasicData(0, Qt::AnchorVerticalCenter, -1, Qt::AnchorVerticalCenter, 40) + // actual data: + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 0) + << BasicData(-1, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 0) + // << BasicData(0, Qt::AnchorHorizontalCenter, 0, Qt::AnchorRight, 5) + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorRight, 5) + << BasicData(-1, Qt::AnchorVerticalCenter, 0, Qt::AnchorTop, -5); + + theResult + << BasicResult(0, QRectF(5, 5, 10, 10) ); + + QTest::newRow("center, overrides") << QSizeF(20, 20) << theData << theResult; + } + + // Two nested + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorLeft, 0) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorBottom, 0) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorRight, 0) + << BasicData(0, Qt::AnchorVerticalCenter, 1, Qt::AnchorTop, 0) + << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorLeft, 0) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorRight, 0) + << BasicData(0, Qt::AnchorBottom, 1, Qt::AnchorBottom, 0); + + theResult + << BasicResult(0, QRectF(20, 0, 20, 40)) + << BasicResult(1, QRectF(20, 20, 20, 20)); + + QTest::newRow("center, two nested") << QSizeF(40, 40) << theData << theResult; + } + + // Two overlap + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + // theData + // // horizontal + // << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 20) + // << BasicData(0, Qt::AnchorHorizontalCenter, 1, Qt::AnchorLeft, 0) + // << BasicData(1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorRight, -5) + // << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + // << BasicData(0, Qt::AnchorHorizontalCenter, 0, Qt::AnchorRight, 10) + // // vertical is pretty much same as horizontal, just roles swapped + // << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 20) + // << BasicData(1, Qt::AnchorVerticalCenter, 0, Qt::AnchorTop, 0) + // << BasicData(0, Qt::AnchorVerticalCenter, 1, Qt::AnchorBottom, -5) + // << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 10) + // << BasicData(1, Qt::AnchorVerticalCenter, 1, Qt::AnchorBottom, 10); + + theData + // horizontal + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 20) + << BasicData(0, Qt::AnchorHorizontalCenter, 1, Qt::AnchorLeft, 0) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorHorizontalCenter, 5) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorRight, 20) + // vertical + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 20) + << BasicData(1, Qt::AnchorVerticalCenter, 0, Qt::AnchorTop, 0) + << BasicData(1, Qt::AnchorBottom, 0, Qt::AnchorVerticalCenter, 5) + << BasicData(1, Qt::AnchorBottom, 0, Qt::AnchorBottom, 20); + + theResult + << BasicResult(0, QRectF(20, 30, 20, 30)) + << BasicResult(1, QRectF(30, 20, 30, 20)); + + QTest::newRow("center, two overlap") << QSizeF(70, 70) << theData << theResult; + } + + // Three + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 0) + << BasicData(0, Qt::AnchorHorizontalCenter, 2, Qt::AnchorHorizontalCenter, 75) + << BasicData(1, Qt::AnchorRight, 2, Qt::AnchorLeft, 10) + << BasicData(1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, -30) + << BasicData(2, Qt::AnchorRight, -1, Qt::AnchorRight, 0) + << BasicData(1, Qt::AnchorLeft, 1, Qt::AnchorRight, 30) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 10) + + << BasicData(1, Qt::AnchorTop, -1, Qt::AnchorTop, 0) + << BasicData(1, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 35) + << BasicData(1, Qt::AnchorVerticalCenter, 2, Qt::AnchorVerticalCenter, 15) + << BasicData(1, Qt::AnchorBottom, 2, Qt::AnchorTop, 5) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 0) + << BasicData(2, Qt::AnchorBottom, 0, Qt::AnchorTop, 5) + << BasicData(0, Qt::AnchorTop, 0, Qt::AnchorBottom, 20); + + theResult + << BasicResult(0, QRectF(0, 30, 10, 20)) + << BasicResult(1, QRectF(20, 0, 30, 10)) + << BasicResult(2, QRectF(60, 15, 40, 10)); + + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + + QTest::newRow("center, three") << QSizeF(100, 50) << theData << theResult; + } + + // Two, parent center + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + // vertical is pretty much same as horizontal, just roles swapped + << BasicData(-1, Qt::AnchorVerticalCenter, 1, Qt::AnchorVerticalCenter, -15) + << BasicData(-1, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 10) + << BasicData(-1, Qt::AnchorBottom, 0, Qt::AnchorBottom, 0) + << BasicData(1, Qt::AnchorTop, 0, Qt::AnchorTop, 0) + // horizontal + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, -15) + << BasicData(-1, Qt::AnchorHorizontalCenter, 1, Qt::AnchorHorizontalCenter, 10) + << BasicData(-1, Qt::AnchorRight, 1, Qt::AnchorRight, 0) + << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorLeft, 0); + + theResult + << BasicResult(0, QRectF(20, 20, 30, 80)) + << BasicResult(1, QRectF(20, 20, 80, 30)); + + QTest::newRow("center, parent") << QSizeF(100, 100) << theData << theResult; + } + + // Two, parent center 2 + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + // << BasicData(1, Qt::AnchorLeft, -1, Qt::AnchorHorizontalCenter, 15) + << BasicData(1, Qt::AnchorLeft, -1, Qt::AnchorHorizontalCenter, -15) + << BasicData(1, Qt::AnchorRight, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, -1, Qt::AnchorRight, 5) + << BasicData(-1, Qt::AnchorHorizontalCenter, 1, Qt::AnchorRight, -5) + // vertical + << BasicData(0, Qt::AnchorVerticalCenter, 1, Qt::AnchorVerticalCenter, 20) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 10) + << BasicData(0, Qt::AnchorBottom, 1, Qt::AnchorBottom, 20) + << BasicData(0, Qt::AnchorTop, 1, Qt::AnchorTop, 20) + << BasicData(0, Qt::AnchorBottom, 1, Qt::AnchorTop, 10); + + theResult + << BasicResult(0, QRectF(30, 10, 15, 10)) + << BasicResult(1, QRectF(10, 30, 10, 10)); + + QTest::newRow("center, parent 2") << QSizeF(50, 50) << theData << theResult; + } + + // Two, parent center 3 + { + BasicLayoutTestDataList theData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorRight, -5) + << BasicData(-1, Qt::AnchorHorizontalCenter, 1, Qt::AnchorLeft, 5) + // << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorRight, 100) + << BasicData(0, Qt::AnchorLeft, 1, Qt::AnchorRight, -100) + << BasicData(0, Qt::AnchorLeft, -1, Qt::AnchorLeft, 0) + + // vertical + << BasicData(0, Qt::AnchorVerticalCenter, 1, Qt::AnchorVerticalCenter, 55) + << BasicData(0, Qt::AnchorTop, -1, Qt::AnchorTop, 0) + << BasicData(1, Qt::AnchorBottom, -1, Qt::AnchorBottom, 0) + << BasicData(0, Qt::AnchorBottom, 1, Qt::AnchorTop, 10) + // << BasicData(0, Qt::AnchorTop, 0, Qt::AnchorBottom, 45) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorBottom, 45) + ; + + theResult + << BasicResult(0, QRectF(0, 0, 45, 45)) + << BasicResult(1, QRectF(55, 55, 45, 45)); + + QTest::newRow("center, parent 3") << QSizeF(100, 100) << theData << theResult; + } + +} + +void tst_QGraphicsAnchorLayout1::testCenterAnchors() +{ + // use the same frame + testBasicLayout(); +} + +void tst_QGraphicsAnchorLayout1::testRemoveCenterAnchor_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<BasicLayoutTestDataList>("data"); + QTest::addColumn<BasicLayoutTestDataList>("removeData"); + QTest::addColumn<BasicLayoutTestResultList>("result"); + + typedef BasicLayoutTestData BasicData; + typedef BasicLayoutTestResult BasicResult; + + { + BasicLayoutTestDataList theData; + BasicLayoutTestDataList theRemoveData; + BasicLayoutTestResultList theResult; + + theData + // << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorHorizontalCenter, -10) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorHorizontalCenter, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorRight, 15) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorVerticalCenter, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 5) + + << BasicData(-1, Qt::AnchorHorizontalCenter, 1, Qt::AnchorHorizontalCenter, 66) + << BasicData(1, Qt::AnchorVerticalCenter, -1, Qt::AnchorVerticalCenter, 99) + << BasicData(0, Qt::AnchorHorizontalCenter, 1, Qt::AnchorHorizontalCenter, 33) + ; + + theRemoveData + << BasicData(-1, Qt::AnchorHorizontalCenter, 1, Qt::AnchorHorizontalCenter, 0) + << BasicData(1, Qt::AnchorVerticalCenter, -1, Qt::AnchorVerticalCenter, 0) + << BasicData(0, Qt::AnchorHorizontalCenter, 1, Qt::AnchorHorizontalCenter, 0); + + theResult + << BasicResult(0, QRectF(5, 5, 10, 10) ); + + QTest::newRow("remove, center, basic") << QSizeF(20, 20) << theData + << theRemoveData << theResult; + } + + { + BasicLayoutTestDataList theData; + BasicLayoutTestDataList theRemoveData; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 0) + << BasicData(0, Qt::AnchorHorizontalCenter, 2, Qt::AnchorHorizontalCenter, 75) + << BasicData(1, Qt::AnchorRight, 2, Qt::AnchorLeft, 10) + << BasicData(1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, -30) + << BasicData(2, Qt::AnchorRight, -1, Qt::AnchorRight, 0) + << BasicData(1, Qt::AnchorLeft, 1, Qt::AnchorRight, 30) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 10) + + // extra: + << BasicData(-1, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 66) + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 33) + << BasicData(0, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 55) + << BasicData(1, Qt::AnchorVerticalCenter, 1, Qt::AnchorVerticalCenter, 55) + + << BasicData(1, Qt::AnchorTop, -1, Qt::AnchorTop, 0) + << BasicData(1, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 35) + << BasicData(1, Qt::AnchorVerticalCenter, 2, Qt::AnchorVerticalCenter, 15) + << BasicData(1, Qt::AnchorBottom, 2, Qt::AnchorTop, 5) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 0) + << BasicData(2, Qt::AnchorBottom, 0, Qt::AnchorTop, 5) + << BasicData(0, Qt::AnchorTop, 0, Qt::AnchorBottom, 20); + + theRemoveData + << BasicData(-1, Qt::AnchorVerticalCenter, 0, Qt::AnchorVerticalCenter, 66) + << BasicData(-1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 33) + << BasicData(0, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 55) + << BasicData(1, Qt::AnchorVerticalCenter, 1, Qt::AnchorVerticalCenter, 55); + + theResult + << BasicResult(0, QRectF(0, 30, 10, 20)) + << BasicResult(1, QRectF(20, 0, 30, 10)) + << BasicResult(2, QRectF(60, 15, 40, 10)); + + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + + QTest::newRow("remove, center, three") << QSizeF(100, 50) << theData << theRemoveData << theResult; + } + + // add edge (item0,edge0,item1,edge1), remove (item1,edge1,item0,edge0) + { + BasicLayoutTestDataList theData; + BasicLayoutTestDataList theRemoveData; + BasicLayoutTestResultList theResult; + + theData + // << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorHorizontalCenter, -10) + << BasicData(-1, Qt::AnchorRight, 0, Qt::AnchorHorizontalCenter, 10) + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorRight, 15) + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorVerticalCenter, 10) + << BasicData(0, Qt::AnchorBottom, -1, Qt::AnchorBottom, 5) + + << BasicData(-1, Qt::AnchorHorizontalCenter, 1, Qt::AnchorHorizontalCenter, 66) + << BasicData(1, Qt::AnchorVerticalCenter, -1, Qt::AnchorVerticalCenter, 99) + << BasicData(0, Qt::AnchorHorizontalCenter, 1, Qt::AnchorHorizontalCenter, 33) + << BasicData(0, Qt::AnchorLeft, 0, Qt::AnchorRight, 22) + << BasicData(0, Qt::AnchorTop, 0, Qt::AnchorBottom, 11) + ; + + theRemoveData + << BasicData(1, Qt::AnchorHorizontalCenter, -1, Qt::AnchorHorizontalCenter, 0) + << BasicData(-1, Qt::AnchorVerticalCenter, 1, Qt::AnchorVerticalCenter, 0) + << BasicData(1, Qt::AnchorHorizontalCenter, 0, Qt::AnchorHorizontalCenter, 0) + << BasicData(0, Qt::AnchorRight, 0, Qt::AnchorLeft, 0) + << BasicData(0, Qt::AnchorBottom, 0, Qt::AnchorTop, 0) + ; + + theResult + << BasicResult(0, QRectF(5, 5, 10, 10) ); + + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + QTest::newRow("remove, center, basic 2") << QSizeF(20, 20) << theData + << theRemoveData << theResult; + } + +} + +void tst_QGraphicsAnchorLayout1::testRemoveCenterAnchor() +{ + QFETCH(QSizeF, size); + QFETCH(BasicLayoutTestDataList, data); + QFETCH(BasicLayoutTestDataList, removeData); + QFETCH(BasicLayoutTestResultList, result); + + QGraphicsWidget *widget = new QGraphicsWidget; + + // Determine amount of widgets to add. + int widgetCount = -1; + for (int i = 0; i < data.count(); ++i) { + const BasicLayoutTestData item = data[i]; + widgetCount = qMax(widgetCount, item.firstIndex); + widgetCount = qMax(widgetCount, item.secondIndex); + } + ++widgetCount; // widgetCount is max of indices. + + // Create dummy widgets + QList<QGraphicsWidget *> widgets; + for (int i = 0; i < widgetCount; ++i) { + TestWidget *w = new TestWidget; + widgets << w; + } + + // Setup anchor layout + TheAnchorLayout *layout = new TheAnchorLayout; + + for (int i = 0; i < data.count(); ++i) { + const BasicLayoutTestData item = data[i]; + layout->setAnchor( + getItem(item.firstIndex, widgets, layout), + item.firstEdge, + getItem(item.secondIndex, widgets, layout), + item.secondEdge, + item.spacing ); + } + + for (int i = 0; i < removeData.count(); ++i) { + const BasicLayoutTestData item = removeData[i]; + layout->removeAnchor( + getItem(item.firstIndex, widgets, layout), + item.firstEdge, + getItem(item.secondIndex, widgets, layout), + item.secondEdge); + } + + widget->setLayout(layout); + widget->setContentsMargins(0,0,0,0); + + widget->resize(size); + QCOMPARE(widget->size(), size); + + // Validate + for (int i = 0; i < result.count(); ++i) { + const BasicLayoutTestResult item = result[i]; + + QCOMPARE(widgets[item.index]->geometry(), item.rect); + } + + qDeleteAll(widgets); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testSingleSizePolicy_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<QSizePolicy>("policy"); + QTest::addColumn<bool>("valid"); + +// FIXED + { + QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + QTest::newRow("single size policy: fixed ok") << QSizeF(70, 70) << sizePolicy << true; + } +/* + { + QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + QTest::newRow("single size policy: fixed too big") << QSizeF(100, 100) << sizePolicy << false; + } + + { + QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + QTest::newRow("single size policy: fixed too small") << QSizeF(50, 50) << sizePolicy << false; + } +*/ +// MINIMUM + { + QSizePolicy sizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); + QTest::newRow("single size policy: minimum bigger ok") << QSizeF(100, 100) << sizePolicy << true; + } + + { + QSizePolicy sizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); + QTest::newRow("single size policy: minimum limit ok") << QSizeF(70, 70) << sizePolicy << true; + } +/* + { + QSizePolicy sizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); + QTest::newRow("single size policy: minimum too small") << QSizeF(50, 50) << sizePolicy << false; + } +*/ +// MAXIMUM + { + QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ); + QTest::newRow("single size policy: maximum small ok") << QSizeF(50, 50) << sizePolicy << true; + } + + { + QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ); + QTest::newRow("single size policy: maximum limit ok") << QSizeF(70, 70) << sizePolicy << true; + } +/* + { + QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ); + QTest::newRow("single size policy: maximum bigger fail") << QSizeF(100, 100) << sizePolicy << false; + } +*/ +// PREFERRED + { + QSizePolicy sizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); + QTest::newRow("single size policy: preferred bigger ok") << QSizeF(100, 100) << sizePolicy << true; + } + + { + QSizePolicy sizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); + QTest::newRow("single size policy: preferred smaller ok") << QSizeF(50, 50) << sizePolicy << true; + } +/* + { + QSizePolicy sizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); + QTest::newRow("single size policy: preferred too big") << QSizeF(700, 700) << sizePolicy << false; + } + + { + QSizePolicy sizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); + QTest::newRow("single size policy: preferred too small") << QSizeF(21, 21) << sizePolicy << false; + } +*/ +// MINIMUMEXPANDING + + { + QSizePolicy sizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); + QTest::newRow("single size policy: min.expanding bigger ok") << QSizeF(100, 100) << sizePolicy << true; + } + + { + QSizePolicy sizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); + QTest::newRow("single size policy: min.expanding limit ok") << QSizeF(70, 70) << sizePolicy << true; + } + + /*{ + QSizePolicy sizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); + QTest::newRow("single size policy: min.expanding too small") << QSizeF(50, 50) << sizePolicy << false; + }*/ + +// EXPANDING + + { + QSizePolicy sizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + QTest::newRow("single size policy: expanding bigger ok") << QSizeF(100, 100) << sizePolicy << true; + } + + { + QSizePolicy sizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + QTest::newRow("single size policy: expanding smaller ok") << QSizeF(50, 50) << sizePolicy << true; + } + + // IGNORED + + { + QSizePolicy sizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ); + QTest::newRow("single size policy: ignored bigger ok") << QSizeF(100, 100) << sizePolicy << true; + } + + { + QSizePolicy sizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ); + QTest::newRow("single size policy: ignored smaller ok") << QSizeF(50, 50) << sizePolicy << true; + } +} + +void tst_QGraphicsAnchorLayout1::testSingleSizePolicy() +{ + QFETCH(QSizeF, size); + QFETCH(QSizePolicy, policy); + QFETCH(bool, valid); + + // create objects + QGraphicsWidget widget; + TheAnchorLayout *layout = new TheAnchorLayout; + TestWidget *childWidget = new TestWidget; + + // set anchors + layout->setAnchor( layout, Qt::AnchorLeft, childWidget, Qt::AnchorLeft, 10 ); + layout->setAnchor( childWidget, Qt::AnchorRight, layout, Qt::AnchorRight, 10 ); + layout->setAnchor( layout, Qt::AnchorTop, childWidget, Qt::AnchorTop, 10 ); + layout->setAnchor( childWidget, Qt::AnchorBottom, layout, Qt::AnchorBottom, 10 ); + + widget.setLayout( layout ); + + // set test case specific: policy and size + childWidget->setSizePolicy( policy ); + widget.setGeometry( QRectF( QPoint(0,0), size ) ); + + QCOMPARE( layout->isValid() , valid ); + + const QRectF childRect = childWidget->geometry(); + Q_UNUSED( childRect ); +} + +void tst_QGraphicsAnchorLayout1::testDoubleSizePolicy_data() +{ + // tests only horizontal direction + QTest::addColumn<QSizePolicy>("policy1"); + QTest::addColumn<QSizePolicy>("policy2"); + QTest::addColumn<qreal>("width1"); + QTest::addColumn<qreal>("width2"); + + // layout size always 100x100 and size hints for items are 5<50<500 + // gabs: 10-item1-10-item2-10 + + { + QSizePolicy sizePolicy1( QSizePolicy::Fixed, QSizePolicy::Fixed ); + QSizePolicy sizePolicy2( QSizePolicy::Preferred, QSizePolicy::Preferred ); + const qreal width1 = 50; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: fixed-preferred") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + { + QSizePolicy sizePolicy1( QSizePolicy::Minimum, QSizePolicy::Minimum ); + QSizePolicy sizePolicy2( QSizePolicy::Preferred, QSizePolicy::Preferred ); + const qreal width1 = 50; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: minimum-preferred") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + { + QSizePolicy sizePolicy1( QSizePolicy::Maximum, QSizePolicy::Maximum ); + QSizePolicy sizePolicy2( QSizePolicy::Preferred, QSizePolicy::Preferred ); + const qreal width1 = 35; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: maximum-preferred") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + { + QSizePolicy sizePolicy1( QSizePolicy::Preferred, QSizePolicy::Preferred ); + QSizePolicy sizePolicy2( QSizePolicy::Preferred, QSizePolicy::Preferred ); + const qreal width1 = 35; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: preferred-preferred") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + { + QSizePolicy sizePolicy1( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); + QSizePolicy sizePolicy2( QSizePolicy::Preferred, QSizePolicy::Preferred ); + const qreal width1 = 50; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: min.expanding-preferred") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + { + QSizePolicy sizePolicy1( QSizePolicy::Expanding, QSizePolicy::Expanding ); + QSizePolicy sizePolicy2( QSizePolicy::Preferred, QSizePolicy::Preferred ); + const qreal width1 = 35; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: expanding-preferred") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + // QGAL handling of ignored flag is different + if (0) + { + QSizePolicy sizePolicy1( QSizePolicy::Ignored, QSizePolicy::Ignored ); + QSizePolicy sizePolicy2( QSizePolicy::Preferred, QSizePolicy::Preferred ); + const qreal width1 = 35; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: ignored-preferred") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + /*{ + QSizePolicy sizePolicy1( QSizePolicy::Fixed, QSizePolicy::Fixed ); + QSizePolicy sizePolicy2( QSizePolicy::Fixed, QSizePolicy::Fixed ); + const qreal width1 = -1; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: fixed-fixed invalid") << sizePolicy1 << sizePolicy2 << width1 << width2; + }*/ + + /*{ + QSizePolicy sizePolicy1( QSizePolicy::Minimum, QSizePolicy::Minimum ); + QSizePolicy sizePolicy2( QSizePolicy::Fixed, QSizePolicy::Fixed ); + const qreal width1 = -1; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: minimum-fixed invalid") << sizePolicy1 << sizePolicy2 << width1 << width2; + }*/ + + { + QSizePolicy sizePolicy1( QSizePolicy::Maximum, QSizePolicy::Maximum ); + QSizePolicy sizePolicy2( QSizePolicy::Fixed, QSizePolicy::Fixed ); + const qreal width1 = 20; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: maximum-fixed") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + { + QSizePolicy sizePolicy1( QSizePolicy::Preferred, QSizePolicy::Preferred ); + QSizePolicy sizePolicy2( QSizePolicy::Fixed, QSizePolicy::Fixed ); + const qreal width1 = 20; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: preferred-fixed") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + /*{ + QSizePolicy sizePolicy1( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); + QSizePolicy sizePolicy2( QSizePolicy::Fixed, QSizePolicy::Fixed ); + const qreal width1 = -1; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: min.expanding-fixed invalid") << sizePolicy1 << sizePolicy2 << width1 << width2; + }*/ + + { + QSizePolicy sizePolicy1( QSizePolicy::Expanding, QSizePolicy::Expanding ); + QSizePolicy sizePolicy2( QSizePolicy::Fixed, QSizePolicy::Fixed ); + const qreal width1 = 20; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: expanding-fixed") << sizePolicy1 << sizePolicy2 << width1 << width2; + } + + { + QSizePolicy sizePolicy1( QSizePolicy::Ignored, QSizePolicy::Ignored ); + QSizePolicy sizePolicy2( QSizePolicy::Fixed, QSizePolicy::Fixed ); + const qreal width1 = 20; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("double size policy: ignored-fixed") << sizePolicy1 << sizePolicy2 << width1 << width2; + } +} + +void tst_QGraphicsAnchorLayout1::testDoubleSizePolicy() +{ + QFETCH(QSizePolicy, policy1); + QFETCH(QSizePolicy, policy2); + QFETCH(qreal, width1); + QFETCH(qreal, width2); + + // create objects + QGraphicsWidget widget; + TheAnchorLayout *layout = new TheAnchorLayout; + TestWidget *childWidget1 = new TestWidget; + TestWidget *childWidget2 = new TestWidget; + + // set anchors + layout->setAnchor( layout, Qt::AnchorLeft, childWidget1, Qt::AnchorLeft, 10 ); + layout->setAnchor( childWidget1, Qt::AnchorRight, childWidget2, Qt::AnchorLeft, 10 ); + layout->setAnchor( childWidget2, Qt::AnchorRight, layout, Qt::AnchorRight, 10 ); + + widget.setLayout( layout ); + + // set test case specific: policy + childWidget1->setSizePolicy( policy1 ); + childWidget2->setSizePolicy( policy2 ); + + widget.setGeometry( QRectF( QPoint(0,0), QSize( 100,100 ) ) ); + + // check results: + if ( width1 == -1.0f ) { + // invalid + QCOMPARE( layout->isValid() , false ); + } else { + // valid + QCOMPARE( childWidget1->geometry().width(), width1 ); + QCOMPARE( childWidget2->geometry().width(), width2 ); + } +} + +typedef QMap<int,qreal> SizeHintArray; +Q_DECLARE_METATYPE(SizeHintArray) + +void tst_QGraphicsAnchorLayout1::testSizeDistribution_data() +{ + // tests only horizontal direction + QTest::addColumn<SizeHintArray>("sizeHints1"); + QTest::addColumn<SizeHintArray>("sizeHints2"); + QTest::addColumn<qreal>("width1"); + QTest::addColumn<qreal>("width2"); + + // layout size always 100x100 and size policy for items is preferred-preferred + // gabs: 10-item1-10-item2-10 + + { + SizeHintArray sizeHints1; + sizeHints1.insert( Qt::MinimumSize, 30 ); + sizeHints1.insert( Qt::PreferredSize, 35 ); + sizeHints1.insert( Qt::MaximumSize, 40 ); + + SizeHintArray sizeHints2; + sizeHints2.insert( Qt::MinimumSize, 5 ); + sizeHints2.insert( Qt::PreferredSize, 35 ); + sizeHints2.insert( Qt::MaximumSize, 300 ); + + const qreal width1 = 35; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("size distribution: preferred equal") << sizeHints1 << sizeHints2 << width1 << width2; + } + + { + SizeHintArray sizeHints1; + sizeHints1.insert( Qt::MinimumSize, 0 ); + sizeHints1.insert( Qt::PreferredSize, 20 ); + sizeHints1.insert( Qt::MaximumSize, 100 ); + + SizeHintArray sizeHints2; + sizeHints2.insert( Qt::MinimumSize, 0 ); + sizeHints2.insert( Qt::PreferredSize, 50 ); + sizeHints2.insert( Qt::MaximumSize, 100 ); + + const qreal width1 = 20; + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("size distribution: preferred non-equal") << sizeHints1 << sizeHints2 << width1 << width2; + } + + { + SizeHintArray sizeHints1; + sizeHints1.insert( Qt::MinimumSize, 0 ); + sizeHints1.insert( Qt::PreferredSize, 40 ); + sizeHints1.insert( Qt::MaximumSize, 100 ); + + SizeHintArray sizeHints2; + sizeHints2.insert( Qt::MinimumSize, 0 ); + sizeHints2.insert( Qt::PreferredSize, 60 ); + sizeHints2.insert( Qt::MaximumSize, 100 ); + + const qreal width1 = 28; // got from manual calculation + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("size distribution: below preferred") << sizeHints1 << sizeHints2 << width1 << width2; + } + + { + SizeHintArray sizeHints1; + sizeHints1.insert( Qt::MinimumSize, 0 ); + sizeHints1.insert( Qt::PreferredSize, 10 ); + sizeHints1.insert( Qt::MaximumSize, 100 ); + + SizeHintArray sizeHints2; + sizeHints2.insert( Qt::MinimumSize, 0 ); + sizeHints2.insert( Qt::PreferredSize, 40 ); + sizeHints2.insert( Qt::MaximumSize, 100 ); + + const qreal width1 = 22; // got from manual calculation + const qreal width2 = 100-10-10-10-width1; + QTest::newRow("size distribution: above preferred") << sizeHints1 << sizeHints2 << width1 << width2; + } + +} + +void tst_QGraphicsAnchorLayout1::testSizeDistribution() +{ + QFETCH(SizeHintArray, sizeHints1); + QFETCH(SizeHintArray, sizeHints2); + QFETCH(qreal, width1); + QFETCH(qreal, width2); + + // sanity-check the test data - MinimumSize <= PreferredSize <= MaximumSize + QVERIFY( sizeHints1.value( Qt::MinimumSize ) <= sizeHints1.value( Qt::PreferredSize ) ); + QVERIFY( sizeHints1.value( Qt::PreferredSize ) <= sizeHints1.value( Qt::MaximumSize ) ); + QVERIFY( sizeHints2.value( Qt::MinimumSize ) <= sizeHints2.value( Qt::PreferredSize ) ); + QVERIFY( sizeHints2.value( Qt::PreferredSize ) <= sizeHints2.value( Qt::MaximumSize ) ); + + // create objects + QGraphicsWidget widget; + TheAnchorLayout *layout = new TheAnchorLayout; + TestWidget *childWidget1 = new TestWidget; + TestWidget *childWidget2 = new TestWidget; + + // set anchors + layout->setAnchor( layout, Qt::AnchorLeft, childWidget1, Qt::AnchorLeft, 10 ); + layout->setAnchor( childWidget1, Qt::AnchorRight, childWidget2, Qt::AnchorLeft, 10 ); + layout->setAnchor( childWidget2, Qt::AnchorRight, layout, Qt::AnchorRight, 10 ); + + widget.setLayout( layout ); + + // set test case specific: size hints + childWidget1->setMinimumWidth( sizeHints1.value( Qt::MinimumSize ) ); + childWidget1->setPreferredWidth( sizeHints1.value( Qt::PreferredSize ) ); + childWidget1->setMaximumWidth( sizeHints1.value( Qt::MaximumSize ) ); + + childWidget2->setMinimumWidth( sizeHints2.value( Qt::MinimumSize ) ); + childWidget2->setPreferredWidth( sizeHints2.value( Qt::PreferredSize ) ); + childWidget2->setMaximumWidth( sizeHints2.value( Qt::MaximumSize ) ); + + widget.setGeometry( QRectF( QPoint(0,0), QSize( 100,100 ) ) ); + + // check results: + if ( width1 == -1.0f ) { + // invalid + QCOMPARE( layout->isValid() , false ); + } else { + // valid + QCOMPARE( float(childWidget1->geometry().width()), float(width1) ); + QCOMPARE( float(childWidget2->geometry().width()), float(width2) ); + } +} + +void tst_QGraphicsAnchorLayout1::testSizeHint() +{ + QGraphicsWidget *widget[5]; + + for( int i = 0; i < 5; i++ ) { + widget[i] = new QGraphicsWidget; + widget[i]->setMinimumSize( 10, 10 ); + widget[i]->setPreferredSize( 20, 20 ); + widget[i]->setMaximumSize( 40, 40 ); + } + + // one, basic + { + TheAnchorLayout *layout = new TheAnchorLayout(); + + + layout->setAnchor(layout, Qt::AnchorLeft, widget[0], Qt::AnchorLeft, 0 ); + layout->setAnchor(layout, Qt::AnchorRight, widget[0], Qt::AnchorRight, 0 ); + + layout->setAnchor(layout, Qt::AnchorTop, widget[0], Qt::AnchorTop, 0 ); + layout->setAnchor(layout, Qt::AnchorBottom, widget[0], Qt::AnchorBottom, 0 ); + + QCOMPARE( layout->minimumSize(), widget[0]->minimumSize() ); + QCOMPARE( layout->preferredSize(), widget[0]->preferredSize() ); + QCOMPARE( layout->maximumSize(), widget[0]->maximumSize() ); + + + delete layout; + } + + // one, basic again + { + TheAnchorLayout *layout = new TheAnchorLayout(); + + + layout->setAnchor(layout, Qt::AnchorLeft, widget[0], Qt::AnchorLeft, 10 ); + // layout->setAnchor(layout, Qt::AnchorRight, widget[0], Qt::AnchorRight, -10 ); + layout->setAnchor(layout, Qt::AnchorRight, widget[0], Qt::AnchorRight, 10 ); + + layout->setAnchor(layout, Qt::AnchorTop, widget[0], Qt::AnchorTop, 10 ); + // layout->setAnchor(layout, Qt::AnchorBottom, widget[0], Qt::AnchorBottom, -10 ); + layout->setAnchor(layout, Qt::AnchorBottom, widget[0], Qt::AnchorBottom, 10 ); + + QCOMPARE( layout->minimumSize(), widget[0]->minimumSize() + QSizeF( 20, 20 ) ); + QCOMPARE( layout->preferredSize(), widget[0]->preferredSize() + QSizeF( 20, 20 ) ); + QCOMPARE( layout->maximumSize(), widget[0]->maximumSize() + QSizeF( 20, 20 ) ); + + delete layout; + } + + // two, serial + { + TheAnchorLayout *layout = new TheAnchorLayout(); + + + layout->setAnchor(layout, Qt::AnchorLeft, widget[0], Qt::AnchorLeft, 0 ); + layout->setAnchor(layout, Qt::AnchorTop, widget[0], Qt::AnchorTop, 0 ); + layout->setAnchor(layout, Qt::AnchorBottom, widget[0], Qt::AnchorBottom, 0 ); + + layout->setAnchor(widget[0], Qt::AnchorRight, widget[1], Qt::AnchorLeft, 0 ); + layout->setAnchor(widget[1], Qt::AnchorRight, layout, Qt::AnchorRight, 0 ); + + + QCOMPARE( layout->minimumSize(), widget[0]->minimumSize() + QSizeF( widget[1]->minimumWidth(), 0 ) ); + QCOMPARE( layout->preferredSize(), widget[0]->preferredSize() + QSizeF( widget[1]->preferredWidth(), 0 ) ); + QCOMPARE( layout->maximumSize(), widget[0]->maximumSize() + QSizeF( widget[1]->maximumWidth(), 0 ) ); + + delete layout; + } + + // two, parallel + { + TheAnchorLayout *layout = new TheAnchorLayout(); + + + layout->setAnchor(layout, Qt::AnchorLeft, widget[0], Qt::AnchorLeft, 0 ); + layout->setAnchor(layout, Qt::AnchorTop, widget[0], Qt::AnchorTop, 0 ); + layout->setAnchor(layout, Qt::AnchorBottom, widget[0], Qt::AnchorBottom, 0 ); + layout->setAnchor(layout, Qt::AnchorRight, widget[0], Qt::AnchorRight, 0 ); + + layout->setAnchor(layout, Qt::AnchorLeft, widget[1], Qt::AnchorLeft, 0 ); + layout->setAnchor(layout, Qt::AnchorTop, widget[1], Qt::AnchorTop, 0 ); + layout->setAnchor(layout, Qt::AnchorBottom, widget[1], Qt::AnchorBottom, 0 ); + layout->setAnchor(layout, Qt::AnchorRight, widget[1], Qt::AnchorRight, 0 ); + + QCOMPARE( layout->minimumSize(), widget[0]->minimumSize() ); + QCOMPARE( layout->preferredSize(), widget[0]->preferredSize() ); + QCOMPARE( layout->maximumSize(), widget[0]->maximumSize() ); + + delete layout; + } + + // five, serial + { + TheAnchorLayout *layout = new TheAnchorLayout(); + + + layout->setAnchor(layout, Qt::AnchorLeft, widget[0], Qt::AnchorLeft, 0 ); + layout->setAnchor(layout, Qt::AnchorTop, widget[0], Qt::AnchorTop, 0 ); + layout->setAnchor(layout, Qt::AnchorBottom, widget[0], Qt::AnchorBottom, 0 ); + + layout->setAnchor(widget[0], Qt::AnchorRight, widget[1], Qt::AnchorLeft, 0 ); + layout->setAnchor(widget[1], Qt::AnchorRight, widget[2], Qt::AnchorLeft, 0 ); + layout->setAnchor(widget[2], Qt::AnchorRight, widget[3], Qt::AnchorLeft, 0 ); + layout->setAnchor(widget[3], Qt::AnchorRight, widget[4], Qt::AnchorLeft, 0 ); + layout->setAnchor(widget[4], Qt::AnchorRight, layout, Qt::AnchorRight, 0 ); + + + QCOMPARE( layout->minimumSize(), widget[0]->minimumSize() + + QSizeF( widget[1]->minimumWidth() + + widget[2]->minimumWidth() + + widget[3]->minimumWidth() + + widget[4]->minimumWidth(), 0 ) ); + + QCOMPARE( layout->preferredSize(), widget[0]->preferredSize() + + QSizeF( widget[1]->preferredWidth() + + widget[2]->preferredWidth() + + widget[3]->preferredWidth() + + widget[4]->preferredWidth(), 0 ) ); + + QCOMPARE( layout->maximumSize(), widget[0]->maximumSize() + + QSizeF( widget[1]->maximumWidth() + + widget[2]->maximumWidth() + + widget[3]->maximumWidth() + + widget[4]->maximumWidth(), 0 ) ); + + delete layout; + } + + + for( int i = 0; i < 5; i++ ) { + delete widget[i]; + } +} + +#ifdef TEST_COMPLEX_CASES + +void tst_QGraphicsAnchorLayout1::testComplexCases_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<BasicLayoutTestDataList>("data"); + QTest::addColumn<AnchorItemSizeHintList>("sizehint"); + QTest::addColumn<BasicLayoutTestResultList>("result"); + + typedef BasicLayoutTestData BasicData; + typedef BasicLayoutTestResult BasicResult; + + // Three widgets, the same sizehint + { + BasicLayoutTestDataList theData; + AnchorItemSizeHintList theSizeHint; + BasicLayoutTestResultList theResult1; + BasicLayoutTestResultList theResult2; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, 2, Qt::AnchorLeft, 10) + + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(2, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorTop, 2, Qt::AnchorTop, 0) + ; + + theSizeHint + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + ; + theResult1 + << BasicResult(0, QRectF(10, 0, 30, 50) ) + << BasicResult(1, QRectF(50, 0, 30, 50) ) + << BasicResult(2, QRectF(50, 0, 30, 50) ) + ; + + theResult2 + << BasicResult(0, QRectF(10, 0, 60, 50) ) + << BasicResult(1, QRectF(80, 0, 60, 50) ) + << BasicResult(2, QRectF(80, 0, 60, 50) ) + ; + + QTest::newRow("Three, the same sizehint(1)") << QSizeF(90, 50) << theData << theSizeHint << theResult1; + QTest::newRow("Three, the same sizehint(2)") << QSizeF(150, 50) << theData << theSizeHint << theResult2; + } + + // Three widgets, serial is bigger + { + BasicLayoutTestDataList theData; + AnchorItemSizeHintList theSizeHint; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, 2, Qt::AnchorLeft, 10) + + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(2, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorTop, 2, Qt::AnchorTop, 0) + ; + + theSizeHint + << AnchorItemSizeHint( 0, 100, 200, 0, 50, 100 ) + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + ; + + theResult + << BasicResult(0, QRectF(10, 0, 70, 50) ) + << BasicResult(1, QRectF(90, 0, 35, 50) ) + << BasicResult(2, QRectF(90, 0, 35, 50) ); + + QTest::newRow("Three, serial is bigger") << QSizeF(135, 50) << theData << theSizeHint << theResult; + + // theResult + // << BasicResult(0, QRectF(10, 0, 80, 50) ) + // << BasicResult(1, QRectF(100, 0, 60, 50) ) + // << BasicResult(2, QRectF(100, 0, 60, 50) ) + // ; + + // QTest::newRow("Three, serial is bigger") << QSizeF(170, 50) << theData << theSizeHint << theResult; + } + + + // Three widgets, parallel is bigger + { + BasicLayoutTestDataList theData; + AnchorItemSizeHintList theSizeHint; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, 2, Qt::AnchorLeft, 10) + + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(2, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorTop, 2, Qt::AnchorTop, 0) + ; + + theSizeHint + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + << AnchorItemSizeHint( 0, 100, 200, 0, 50, 100 ) + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + ; + + // ### QGAL uses a different preferred size calculation algorithm. + // This algorithm was discussed with Jan-Arve and tries to grow + // items instead of shrinking them. + // In this case, the preferred size of each item becomes: + // Item 0: 50 + // Item 1: 100 (grows to avoid shrinking item 2) + // Item 2: 100 + // Therefore, the preferred size of the parent widget becomes + // 180 == (10 + 50 + 10 + 100 + 10) + // As we set its size to 150, each widget is shrinked in the same + // ratio, in order to achieve the width of 150 == (10 + 40 + 10 + 80 + 10) + + theResult + << BasicResult(0, QRectF(10, 0, 40, 50) ) + << BasicResult(1, QRectF(60, 0, 80, 50) ) + << BasicResult(2, QRectF(60, 0, 80, 50) ) + ; + + QTest::newRow("Three, parallel is bigger") << QSizeF(150, 50) << theData << theSizeHint << theResult; + + // #ifdef PREFERRED_IS_AVERAGE + // theResult + // << BasicResult(0, QRectF(10, 0, 50, 50) ) + // << BasicResult(1, QRectF(70, 0, 75, 50) ) + // << BasicResult(2, QRectF(70, 0, 75, 50) ) + // ; + + // QTest::newRow("Three, parallel is bigger") << QSizeF(155, 50) << theData << theSizeHint << theResult; + // #else + // theResult + // << BasicResult(0, QRectF(10, 0, 50, 50) ) + // << BasicResult(1, QRectF(70, 0, 66.66666666666666, 50) ) + // << BasicResult(2, QRectF(70, 0, 60.66666666666666, 50) ) + // ; + + // QTest::newRow("Three, parallel is bigger") << QSizeF(146.66666666666666, 50) << theData << theSizeHint << theResult; + // #endif + } + + // Three widgets, the same sizehint, one center anchor + { + BasicLayoutTestDataList theData; + AnchorItemSizeHintList theSizeHint; + BasicLayoutTestResultList theResult; + + theData + << BasicData(-1, Qt::AnchorLeft, 0, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorRight, 1, Qt::AnchorLeft, 10) + << BasicData(0, Qt::AnchorHorizontalCenter, 2, Qt::AnchorLeft, 10) + + << BasicData(1, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + << BasicData(2, Qt::AnchorRight, -1, Qt::AnchorRight, 10) + + << BasicData(-1, Qt::AnchorTop, 0, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorTop, 1, Qt::AnchorTop, 0) + << BasicData(-1, Qt::AnchorTop, 2, Qt::AnchorTop, 0) + ; + + theSizeHint + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + << AnchorItemSizeHint( 0, 50, 100, 0, 50, 100 ) + ; + theResult + << BasicResult(0, QRectF(10, 0, 40, 50) ) + << BasicResult(1, QRectF(60, 0, 40, 50) ) + << BasicResult(2, QRectF(40, 0, 60, 50) ) + ; + + ; + + QTest::newRow("Three, the same sizehint, one center anchor") << QSizeF(110, 50) << theData << theSizeHint << theResult; + } +} + +void tst_QGraphicsAnchorLayout1::testComplexCases() +{ + QFETCH(QSizeF, size); + QFETCH(BasicLayoutTestDataList, data); + QFETCH(AnchorItemSizeHintList, sizehint); + QFETCH(BasicLayoutTestResultList, result); + + QGraphicsWidget *widget = new QGraphicsWidget; + + // Determine amount of widgets to add. + int widgetCount = -1; + for (int i = 0; i < data.count(); ++i) { + const BasicLayoutTestData item = data[i]; + widgetCount = qMax(widgetCount, item.firstIndex); + widgetCount = qMax(widgetCount, item.secondIndex); + } + ++widgetCount; // widgetCount is max of indices. + + // Create dummy widgets + QList<QGraphicsWidget *> widgets; + for (int i = 0; i < widgetCount; ++i) { + TestWidget *w = new TestWidget; + + w->setMinimumWidth( sizehint[i].hmin ); + w->setPreferredWidth( sizehint[i].hpref ); + w->setMaximumWidth( sizehint[i].hmax ); + + w->setMinimumHeight( sizehint[i].vmin ); + w->setPreferredHeight( sizehint[i].vpref ); + w->setMaximumHeight( sizehint[i].vmax ); + + widgets << w; + } + + // Setup anchor layout + TheAnchorLayout *layout = new TheAnchorLayout; + + for (int i = 0; i < data.count(); ++i) { + const BasicLayoutTestData item = data[i]; + layout->setAnchor( + getItem(item.firstIndex, widgets, layout), + item.firstEdge, + getItem(item.secondIndex, widgets, layout), + item.secondEdge, + item.spacing ); + } + + widget->setLayout(layout); + widget->setContentsMargins(0,0,0,0); + + widget->resize(size); + QCOMPARE(widget->size(), size); + +// QTest::qWait(500); // layouting is asynchronous.. + + // Validate + for (int i = 0; i < result.count(); ++i) { + const BasicLayoutTestResult item = result[i]; + QCOMPARE(widgets[item.index]->geometry(), item.rect); + } + + // Test mirrored mode + widget->setLayoutDirection(Qt::RightToLeft); + layout->activate(); + // Validate + for (int j = 0; j < result.count(); ++j) { + const BasicLayoutTestResult item = result[j]; + QRectF mirroredRect(item.rect); + // only valid cases are mirrored + if (mirroredRect.isValid()){ + mirroredRect.moveLeft(size.width()-item.rect.width()-item.rect.left()); + } + QCOMPARE(widgets[item.index]->geometry(), mirroredRect); + delete widgets[item.index]; + } + + delete widget; +} +#endif //TEST_COMPLEX_CASES + + +QTEST_MAIN(tst_QGraphicsAnchorLayout1) +#include "tst_qgraphicsanchorlayout1.moc" +//----------------------------------------------------------------------------- + diff --git a/tests/auto/widgets/graphicsview/qgraphicseffectsource/qgraphicseffectsource.pro b/tests/auto/widgets/graphicsview/qgraphicseffectsource/qgraphicseffectsource.pro new file mode 100644 index 0000000000..44ec70eef3 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicseffectsource/qgraphicseffectsource.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +QT += widgets widgets-private +QT += core-private gui-private + +SOURCES += tst_qgraphicseffectsource.cpp +CONFIG += parallel_test diff --git a/tests/auto/widgets/graphicsview/qgraphicseffectsource/tst_qgraphicseffectsource.cpp b/tests/auto/widgets/graphicsview/qgraphicseffectsource/tst_qgraphicseffectsource.cpp new file mode 100644 index 0000000000..4f39f991c6 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicseffectsource/tst_qgraphicseffectsource.cpp @@ -0,0 +1,422 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtWidgets/qgraphicseffect.h> +#include <QtWidgets/qgraphicsview.h> +#include <QtWidgets/qgraphicsscene.h> +#include <QtWidgets/qgraphicsitem.h> +#include <QtWidgets/qstyleoption.h> + +#include <private/qgraphicseffect_p.h> + +//TESTED_CLASS= +//TESTED_FILES= + +class CustomItem : public QGraphicsRectItem +{ +public: + CustomItem(qreal x, qreal y, qreal width, qreal height) + : QGraphicsRectItem(x, y, width, height), numRepaints(0), + m_painter(0) + {} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + m_painter = painter; + ++numRepaints; + QGraphicsRectItem::paint(painter, option, widget); + } + + void reset() + { + numRepaints = 0; + m_painter = 0; + } + + int numRepaints; + QPainter *m_painter; +}; + +class CustomEffect : public QGraphicsEffect +{ +public: + CustomEffect() + : QGraphicsEffect(), numRepaints(0), m_margin(10), m_sourceChanged(false), + m_sourceBoundingRectChanged(false), doNothingInDraw(false), + storeDeviceDependentStuff(false), + m_painter(0), m_source(0) + {} + + QRectF boundingRectFor(const QRectF &rect) const + { return rect.adjusted(-m_margin, -m_margin, m_margin, m_margin); } + + void reset() + { + numRepaints = 0; + m_sourceChanged = false; + m_sourceBoundingRectChanged = false; + m_painter = 0; + m_source = 0; + deviceCoordinatesPixmap = QPixmap(); + deviceRect = QRect(); + sourceDeviceBoundingRect = QRectF(); + } + + void setMargin(int margin) + { + m_margin = margin; + updateBoundingRect(); + } + + int margin() const + { return m_margin; } + + void draw(QPainter *painter) + { + ++numRepaints; + if (storeDeviceDependentStuff) { + deviceCoordinatesPixmap = source()->pixmap(Qt::DeviceCoordinates); + deviceRect = QRect(0, 0, painter->device()->width(), painter->device()->height()); + sourceDeviceBoundingRect = source()->boundingRect(Qt::DeviceCoordinates); + } + if (doNothingInDraw) + return; + m_source = source(); + m_painter = painter; + source()->draw(painter); + } + + void sourceChanged() + { m_sourceChanged = true; } + + void sourceBoundingRectChanged() + { m_sourceBoundingRectChanged = true; } + + int numRepaints; + int m_margin; + bool m_sourceChanged; + bool m_sourceBoundingRectChanged; + bool doNothingInDraw; + bool storeDeviceDependentStuff; + QPainter *m_painter; + QGraphicsEffectSource *m_source; + QPixmap deviceCoordinatesPixmap; + QRect deviceRect; + QRectF sourceDeviceBoundingRect; +}; + +class tst_QGraphicsEffectSource : public QObject +{ + Q_OBJECT +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + +private slots: + void graphicsItem(); + void styleOption(); + void isPixmap(); + void draw(); + void update(); + void boundingRect(); + void clippedBoundingRect(); + void deviceRect(); + void pixmap(); + + void pixmapPadding_data(); + void pixmapPadding(); + +private: + QGraphicsView *view; + QGraphicsScene *scene; + CustomItem *item; + CustomEffect *effect; +}; + +void tst_QGraphicsEffectSource::initTestCase() +{ + scene = new QGraphicsScene; + item = new CustomItem(0, 0, 100, 100); + effect = new CustomEffect; + item->setGraphicsEffect(effect); + scene->addItem(item); + view = new QGraphicsView(scene); + view->show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(view); +#endif + QTest::qWait(200); +} + +void tst_QGraphicsEffectSource::cleanupTestCase() +{ + delete view; +} + +void tst_QGraphicsEffectSource::init() +{ + QVERIFY(effect); + QVERIFY(item); + QVERIFY(effect->source()); + effect->reset(); + effect->storeDeviceDependentStuff = false; + effect->doNothingInDraw = false; + item->reset(); +} + +void tst_QGraphicsEffectSource::graphicsItem() +{ + QVERIFY(effect->source()); + QCOMPARE(effect->source()->graphicsItem(), (const QGraphicsItem*)item); +} + +void tst_QGraphicsEffectSource::styleOption() +{ + // We don't have style options unless the source is drawing. + QVERIFY(effect->source()); + QVERIFY(!effect->source()->styleOption()); + + // Trigger an update and check that the style option in QGraphicsEffect::draw + // was the same as the one in QGraphicsItem::paint. + QCOMPARE(item->numRepaints, 0); + QCOMPARE(effect->numRepaints, 0); + item->update(); + QTRY_COMPARE(item->numRepaints, 1); + QTRY_COMPARE(effect->numRepaints, 1); +} + +void tst_QGraphicsEffectSource::isPixmap() +{ + // Current source is a CustomItem (which is not a pixmap item). + QVERIFY(!effect->source()->isPixmap()); + + // Make sure isPixmap() returns true for QGraphicsPixmapItem. + QGraphicsPixmapItem *pixmapItem = new QGraphicsPixmapItem; + CustomEffect *anotherEffect = new CustomEffect; + pixmapItem->setGraphicsEffect(anotherEffect); + QVERIFY(anotherEffect->source()); + QCOMPARE(anotherEffect->source()->graphicsItem(), static_cast<const QGraphicsItem *>(pixmapItem)); + QVERIFY(anotherEffect->source()->isPixmap()); + delete pixmapItem; +} + +void tst_QGraphicsEffectSource::draw() +{ + // The source can only draw as a result of QGraphicsEffect::draw. + QTest::ignoreMessage(QtWarningMsg, "QGraphicsEffectSource::draw: Can only begin as a result of QGraphicsEffect::draw"); + QPixmap dummyPixmap(10, 10); + QPainter dummyPainter(&dummyPixmap); + effect->source()->draw(&dummyPainter); +} + +void tst_QGraphicsEffectSource::update() +{ + QCOMPARE(item->numRepaints, 0); + QCOMPARE(effect->numRepaints, 0); + + effect->source()->update(); + + QTRY_COMPARE(item->numRepaints, 1); + QTRY_COMPARE(effect->numRepaints, 1); +} + +void tst_QGraphicsEffectSource::boundingRect() +{ + QTest::ignoreMessage(QtWarningMsg, "QGraphicsEffectSource::boundingRect: Not yet implemented, lacking device context"); + QCOMPARE(effect->source()->boundingRect(Qt::DeviceCoordinates), QRectF()); + + QRectF itemBoundingRect = item->boundingRect(); + if (!item->children().isEmpty()) + itemBoundingRect |= item->childrenBoundingRect(); + + // We can at least check that the device bounding rect was correct in QGraphicsEffect::draw. + effect->storeDeviceDependentStuff = true; + effect->source()->update(); + const QTransform deviceTransform = item->deviceTransform(view->viewportTransform()); + QTRY_COMPARE(effect->sourceDeviceBoundingRect, deviceTransform.mapRect(itemBoundingRect)); + + // Bounding rect in logical coordinates is of course fine. + QTRY_COMPARE(effect->source()->boundingRect(Qt::LogicalCoordinates), itemBoundingRect); + // Make sure default value is Qt::LogicalCoordinates. + QTRY_COMPARE(effect->source()->boundingRect(), itemBoundingRect); +} + +void tst_QGraphicsEffectSource::clippedBoundingRect() +{ + QRectF itemBoundingRect = item->boundingRect(); + item->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + QGraphicsRectItem *child = new QGraphicsRectItem(-1000, -1000, 2000, 2000); + child->setBrush(Qt::red); + child->setParentItem(item); + + effect->storeDeviceDependentStuff = true; + effect->source()->update(); + QTRY_COMPARE(effect->source()->boundingRect(Qt::LogicalCoordinates), itemBoundingRect); +} + +void tst_QGraphicsEffectSource::deviceRect() +{ + effect->storeDeviceDependentStuff = true; + effect->source()->update(); + QTRY_COMPARE(effect->deviceRect, view->viewport()->rect()); +} + +void tst_QGraphicsEffectSource::pixmap() +{ + QTest::ignoreMessage(QtWarningMsg, "QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context"); + QCOMPARE(effect->source()->pixmap(Qt::DeviceCoordinates), QPixmap()); + + // We can at least verify a valid pixmap from QGraphicsEffect::draw. + effect->storeDeviceDependentStuff = true; + effect->source()->update(); + QTRY_VERIFY(!effect->deviceCoordinatesPixmap.isNull()); + + // Pixmaps in logical coordinates we can do fine. + QPixmap pixmap1 = effect->source()->pixmap(Qt::LogicalCoordinates); + QVERIFY(!pixmap1.isNull()); + + // Make sure default value is Qt::LogicalCoordinates. + QPixmap pixmap2 = effect->source()->pixmap(); + QCOMPARE(pixmap1, pixmap2); +} + +class PaddingEffect : public QGraphicsEffect +{ +public: + PaddingEffect(QObject *parent) : QGraphicsEffect(parent) + { + } + + QRectF boundingRectFor(const QRectF &src) const { + return src.adjusted(-10, -10, 10, 10); + } + + void draw(QPainter *) { + pix = source()->pixmap(coordinateMode, &offset, padMode); + } + + QPixmap pix; + QPoint offset; + QGraphicsEffect::PixmapPadMode padMode; + Qt::CoordinateSystem coordinateMode; +}; + +void tst_QGraphicsEffectSource::pixmapPadding_data() +{ + QTest::addColumn<int>("coordinateMode"); + QTest::addColumn<int>("padMode"); + QTest::addColumn<QSize>("size"); + QTest::addColumn<QPoint>("offset"); + QTest::addColumn<uint>("ulPixel"); + + QTest::newRow("log,nopad") << int(Qt::LogicalCoordinates) + << int(QGraphicsEffect::NoPad) + << QSize(10, 10) << QPoint(0, 0) + << 0xffff0000u; + + QTest::newRow("log,transparent") << int(Qt::LogicalCoordinates) + << int(QGraphicsEffect::PadToTransparentBorder) + << QSize(14, 14) << QPoint(-2, -2) + << 0x00000000u; + + QTest::newRow("log,effectrect") << int(Qt::LogicalCoordinates) + << int(QGraphicsEffect::PadToEffectiveBoundingRect) + << QSize(20, 20) << QPoint(-5, -5) + << 0x00000000u; + + QTest::newRow("dev,nopad") << int(Qt::DeviceCoordinates) + << int(QGraphicsEffect::NoPad) + << QSize(20, 20) << QPoint(40, 40) + << 0xffff0000u; + + QTest::newRow("dev,transparent") << int(Qt::DeviceCoordinates) + << int(QGraphicsEffect::PadToTransparentBorder) + << QSize(24, 24) << QPoint(38, 38) + << 0x00000000u; + + QTest::newRow("dev,effectrect") << int(Qt::DeviceCoordinates) + << int(QGraphicsEffect::PadToEffectiveBoundingRect) + << QSize(40, 40) << QPoint(30, 30) + << 0x00000000u; + +} + +void tst_QGraphicsEffectSource::pixmapPadding() +{ + QPixmap dummyTarget(100, 100); + QPainter dummyPainter(&dummyTarget); + dummyPainter.translate(40, 40); + dummyPainter.scale(2, 2); + + QPixmap pm(10, 10); + pm.fill(Qt::red); + + QGraphicsScene *scene = new QGraphicsScene(); + PaddingEffect *effect = new PaddingEffect(scene); + QGraphicsPixmapItem *pmItem = new QGraphicsPixmapItem(pm); + scene->addItem(pmItem); + pmItem->setGraphicsEffect(effect); + + QFETCH(int, coordinateMode); + QFETCH(int, padMode); + QFETCH(QPoint, offset); + QFETCH(QSize, size); + QFETCH(uint, ulPixel); + + effect->padMode = (QGraphicsEffect::PixmapPadMode) padMode; + effect->coordinateMode = (Qt::CoordinateSystem) coordinateMode; + + scene->render(&dummyPainter, scene->itemsBoundingRect(), scene->itemsBoundingRect()); + + QCOMPARE(effect->pix.size(), size); + QCOMPARE(effect->offset, offset); + QCOMPARE(effect->pix.toImage().pixel(0, 0), ulPixel); + + // ### Fix corruption in scene destruction, then enable... + // delete scene; +} + +QTEST_MAIN(tst_QGraphicsEffectSource) +#include "tst_qgraphicseffectsource.moc" + diff --git a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/.gitignore b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/.gitignore new file mode 100644 index 0000000000..e3a8cc317c --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/.gitignore @@ -0,0 +1 @@ +tst_qgraphicsgridlayout diff --git a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/qgraphicsgridlayout.pro b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/qgraphicsgridlayout.pro new file mode 100644 index 0000000000..7db7c1ae6f --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/qgraphicsgridlayout.pro @@ -0,0 +1,6 @@ +load(qttest_p4) + +QT += widgets +SOURCES += tst_qgraphicsgridlayout.cpp +CONFIG += parallel_test +contains(QT_CONFIG,xcb):qpa:CONFIG+=insignificant_test # QTBUG-20756 crashes on qpa, xcb diff --git a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp new file mode 100644 index 0000000000..e9d9cb67bb --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp @@ -0,0 +1,3465 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qgraphicsgridlayout.h> +#include <qgraphicswidget.h> +#include <qgraphicsscene.h> +#include <qgraphicsview.h> + +class tst_QGraphicsGridLayout : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qgraphicsgridlayout_data(); + void qgraphicsgridlayout(); + void addItem_data(); + void addItem(); +#ifndef Q_WS_MAC + void alignment_data(); + void alignment(); +#endif + void alignment2(); + void alignment2_data(); +#ifndef Q_WS_MAC + void columnAlignment_data(); + void columnAlignment(); +#endif + void columnCount_data(); + void columnCount(); + void columnMaximumWidth_data(); + void columnMaximumWidth(); + void columnMinimumWidth_data(); + void columnMinimumWidth(); + void columnPreferredWidth_data(); + void columnPreferredWidth(); + void setColumnFixedWidth(); + void columnSpacing(); + void columnStretchFactor(); + void count(); + void contentsMargins(); + void horizontalSpacing_data(); + void horizontalSpacing(); + void itemAt(); + void removeAt(); + void removeItem(); + void rowAlignment_data(); + void rowAlignment(); + void rowCount_data(); + void rowCount(); + void rowMaximumHeight_data(); + void rowMaximumHeight(); + void rowMinimumHeight_data(); + void rowMinimumHeight(); + void rowPreferredHeight_data(); + void rowPreferredHeight(); + void rowSpacing(); + void rowStretchFactor_data(); + void rowStretchFactor(); + void setColumnSpacing_data(); + void setColumnSpacing(); + void setGeometry_data(); + void setGeometry(); + void setRowFixedHeight(); + void setRowSpacing_data(); + void setRowSpacing(); + void setSpacing_data(); + void setSpacing(); + void sizeHint_data(); + void sizeHint(); + void verticalSpacing_data(); + void verticalSpacing(); + void layoutDirection_data(); + void layoutDirection(); + void removeLayout(); + void defaultStretchFactors_data(); + void defaultStretchFactors(); + void geometries_data(); + void geometries(); + void avoidRecursionInInsertItem(); + void styleInfoLeak(); + void task236367_maxSizeHint(); + void spanningItem2x2_data(); + void spanningItem2x2(); + void spanningItem2x3_data(); + void spanningItem2x3(); + void spanningItem(); + void heightForWidth(); + void widthForHeight(); + void heightForWidthWithSpanning(); + void stretchAndHeightForWidth(); + void testDefaultAlignment(); +}; + +class RectWidget : public QGraphicsWidget +{ +public: + RectWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent), m_fnConstraint(0) {} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + Q_UNUSED(option); + Q_UNUSED(widget); + painter->drawRoundRect(rect()); + painter->drawLine(rect().topLeft(), rect().bottomRight()); + painter->drawLine(rect().bottomLeft(), rect().topRight()); + } + + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const + { + if (constraint.width() < 0 && constraint.height() < 0 && m_sizeHints[which].isValid()) { + return m_sizeHints[which]; + } + if (m_fnConstraint) { + return m_fnConstraint(which, constraint); + } + return QGraphicsWidget::sizeHint(which, constraint); + } + + void setSizeHint(Qt::SizeHint which, const QSizeF &size) { + m_sizeHints[which] = size; + updateGeometry(); + } + + void setConstraintFunction(QSizeF (*fnConstraint)(Qt::SizeHint, const QSizeF &)) { + m_fnConstraint = fnConstraint; + } + + QSizeF m_sizeHints[Qt::NSizeHints]; + QSizeF (*m_fnConstraint)(Qt::SizeHint, const QSizeF &); + +}; + +struct ItemDesc +{ + ItemDesc(int row, int col) + : m_pos(qMakePair(row, col)), + m_rowSpan(1), + m_colSpan(1), + m_sizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)), + m_align(0), + m_fnConstraint(0) + { + } + + ItemDesc &rowSpan(int span) { + m_rowSpan = span; + return (*this); + } + + ItemDesc &colSpan(int span) { + m_colSpan = span; + return (*this); + } + + ItemDesc &sizePolicy(const QSizePolicy &sp) { + m_sizePolicy = sp; + return (*this); + } + + ItemDesc &sizePolicy(QSizePolicy::Policy horAndVer) { + m_sizePolicy = QSizePolicy(horAndVer, horAndVer); + return (*this); + } + + ItemDesc &sizePolicyH(QSizePolicy::Policy hor) { + m_sizePolicy.setHorizontalPolicy(hor); + return (*this); + } + + ItemDesc &sizePolicyV(QSizePolicy::Policy ver) { + m_sizePolicy.setVerticalPolicy(ver); + return (*this); + } + + ItemDesc &sizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver) { + m_sizePolicy = QSizePolicy(hor, ver); + return (*this); + } + + ItemDesc &sizeHint(Qt::SizeHint which, const QSizeF &sh) { + m_sizeHints[which] = sh; + return (*this); + } + + ItemDesc &preferredSizeHint(const QSizeF &sh) { + m_sizeHints[Qt::PreferredSize] = sh; + return (*this); + } + + ItemDesc &minSize(const QSizeF &sz) { + m_sizes[Qt::MinimumSize] = sz; + return (*this); + } + ItemDesc &preferredSize(const QSizeF &sz) { + m_sizes[Qt::PreferredSize] = sz; + return (*this); + } + ItemDesc &maxSize(const QSizeF &sz) { + m_sizes[Qt::MaximumSize] = sz; + return (*this); + } + + ItemDesc &alignment(Qt::Alignment alignment) { + m_align = alignment; + return (*this); + } + + ItemDesc &dynamicConstraint(QSizeF (*fnConstraint)(Qt::SizeHint, const QSizeF &), + Qt::Orientation orientation) { + m_fnConstraint = fnConstraint; + m_constraintOrientation = orientation; + return (*this); + } + + void apply(QGraphicsGridLayout *layout, QGraphicsWidget *item) { + QSizePolicy sp = m_sizePolicy; + if (m_fnConstraint) { + sp.setHeightForWidth(m_constraintOrientation == Qt::Vertical); + sp.setWidthForHeight(m_constraintOrientation == Qt::Horizontal); + } + + item->setSizePolicy(sp); + for (int i = 0; i < Qt::NSizeHints; ++i) { + if (!m_sizes[i].isValid()) + continue; + switch ((Qt::SizeHint)i) { + case Qt::MinimumSize: + item->setMinimumSize(m_sizes[i]); + break; + case Qt::PreferredSize: + item->setPreferredSize(m_sizes[i]); + break; + case Qt::MaximumSize: + item->setMaximumSize(m_sizes[i]); + break; + default: + qWarning("not implemented"); + break; + } + } + + layout->addItem(item, m_pos.first, m_pos.second, m_rowSpan, m_colSpan); + layout->setAlignment(item, m_align); + } + + void apply(QGraphicsGridLayout *layout, RectWidget *item) { + for (int i = 0; i < Qt::NSizeHints; ++i) + item->setSizeHint((Qt::SizeHint)i, m_sizeHints[i]); + item->setConstraintFunction(m_fnConstraint); + apply(layout, static_cast<QGraphicsWidget*>(item)); + } + +//private: + QPair<int,int> m_pos; // row,col + int m_rowSpan; + int m_colSpan; + QSizePolicy m_sizePolicy; + QSizeF m_sizeHints[Qt::NSizeHints]; + QSizeF m_sizes[Qt::NSizeHints]; + Qt::Alignment m_align; + + Qt::Orientation m_constraintOrientation; + QSizeF (*m_fnConstraint)(Qt::SizeHint, const QSizeF &); +}; + +typedef QList<ItemDesc> ItemList; +Q_DECLARE_METATYPE(ItemList); + +typedef QList<QSizeF> SizeList; +Q_DECLARE_METATYPE(SizeList); + + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsGridLayout::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsGridLayout::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsGridLayout::init() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +// This will be called after every test function. +void tst_QGraphicsGridLayout::cleanup() +{ +} + +void tst_QGraphicsGridLayout::qgraphicsgridlayout_data() +{ +} + +void tst_QGraphicsGridLayout::qgraphicsgridlayout() +{ + QGraphicsGridLayout layout; + QTest::ignoreMessage(QtWarningMsg, "QGraphicsGridLayout::addItem: invalid row span/column span: 0"); + layout.addItem(0, 0, 0, 0, 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsGridLayout::addItem: cannot add null item"); + layout.addItem(0, 0, 0); + layout.alignment(0); + layout.columnAlignment(0); + layout.columnCount(); + layout.columnMaximumWidth(0); + layout.columnMinimumWidth(0); + layout.columnPreferredWidth(0); + layout.columnSpacing(0); + layout.columnStretchFactor(0); + layout.count(); + layout.horizontalSpacing(); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsGridLayout::itemAt: invalid row, column 0, 0"); + layout.itemAt(0, 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsGridLayout::itemAt: invalid index 0"); + layout.itemAt(0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsGridLayout::removeAt: invalid index 0"); + layout.removeAt(0); + layout.rowAlignment(0); + layout.rowCount(); + layout.rowMaximumHeight(0); + layout.rowMinimumHeight(0); + layout.rowPreferredHeight(0); + layout.rowSpacing(0); + layout.rowStretchFactor(0); + layout.setAlignment(0, Qt::AlignRight); + layout.setColumnAlignment(0, Qt::AlignRight); + layout.setColumnFixedWidth(0, 0); + layout.setColumnMaximumWidth(0, 0); + layout.setColumnMinimumWidth(0, 0); + layout.setColumnPreferredWidth(0, 0); + layout.setColumnSpacing(0, 0); + layout.setColumnStretchFactor(0, 0); + layout.setGeometry(QRectF()); + layout.setHorizontalSpacing(0); + layout.setRowAlignment(0, 0); + layout.setRowFixedHeight(0, 0); + layout.setRowMaximumHeight(0, 0); + layout.setRowMinimumHeight(0, 0); + layout.setRowPreferredHeight(0, 0); + layout.setRowSpacing(0, 0); + layout.setRowStretchFactor(0, 0); + layout.setSpacing(0); + layout.setVerticalSpacing(0); + layout.sizeHint(Qt::MinimumSize); + layout.verticalSpacing(); +} + +static void populateLayout(QGraphicsGridLayout *gridLayout, int width, int height, bool hasHeightForWidth = false) +{ + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + QGraphicsWidget *item = new RectWidget(); + item->setMinimumSize(10, 10); + item->setPreferredSize(25, 25); + item->setMaximumSize(50, 50); + gridLayout->addItem(item, y, x); + QSizePolicy policy = item->sizePolicy(); + policy.setHeightForWidth(hasHeightForWidth); + item->setSizePolicy(policy); + } + } +} + + +/** populates \a gridLayout with a 3x2 layout: + * +----+----+----+ + * |+---|---+|xxxx| + * ||span=2 ||hole| + * |+---|---+|xxxx| + * +----+----+----+ + * |xxxx|+---|---+| + * |hole||span=2 || + * |xxxx|+---|---+| + * +----+----+----+ + */ +static void populateLayoutWithSpansAndHoles(QGraphicsGridLayout *gridLayout, bool hasHeightForWidth = false) +{ + QGraphicsWidget *item = new RectWidget(); + item->setMinimumSize(10, 10); + item->setPreferredSize(25, 25); + item->setMaximumSize(50, 50); + QSizePolicy sizepolicy = item->sizePolicy(); + sizepolicy.setHeightForWidth(hasHeightForWidth); + item->setSizePolicy(sizepolicy); + gridLayout->addItem(item, 0, 0, 1, 2); + + item = new RectWidget(); + item->setMinimumSize(10, 10); + item->setPreferredSize(25, 25); + item->setMaximumSize(50, 50); + item->setSizePolicy(sizepolicy); + gridLayout->addItem(item, 1, 1, 1, 2); +} + +Q_DECLARE_METATYPE(Qt::Alignment) +void tst_QGraphicsGridLayout::addItem_data() +{ + QTest::addColumn<int>("row"); + QTest::addColumn<int>("column"); + QTest::addColumn<int>("rowSpan"); + QTest::addColumn<int>("columnSpan"); + QTest::addColumn<Qt::Alignment>("alignment"); + + for (int a = -1; a < 3; ++a) { + for (int b = -1; b < 2; ++b) { + for (int c = -1; c < 2; ++c) { + for (int d = -1; d < 2; ++d) { + for (int e = 0; e < 9; ++e) { + int row = a; + int column = b; + int rowSpan = c; + int columnSpan = d; + QString name = QString::fromAscii("(%1,%2,%3,%4").arg(a).arg(b).arg(c).arg(d); + Qt::Alignment alignment = Qt::AlignLeft; + QTest::newRow(name.toLatin1()) << row << column << rowSpan << columnSpan << alignment; + }}}}} +} + +// public void addItem(QGraphicsLayoutItem* item, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment = 0) +void tst_QGraphicsGridLayout::addItem() +{ + QFETCH(int, row); + QFETCH(int, column); + QFETCH(int, rowSpan); + QFETCH(int, columnSpan); + QFETCH(Qt::Alignment, alignment); + + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + + QGraphicsWidget *wid = new QGraphicsWidget; + if (row < 0 || column < 0) { + QTest::ignoreMessage(QtWarningMsg, "QGraphicsGridLayout::addItem: invalid row/column: -1"); + } else if (rowSpan < 1 || columnSpan < 1) { + char buf[1024]; + ::qsnprintf(buf, sizeof(buf), "QGraphicsGridLayout::addItem: invalid row span/column span: %d", + rowSpan < 1 ? rowSpan : columnSpan); + QTest::ignoreMessage(QtWarningMsg, buf); + } + layout->addItem(wid, row, column, rowSpan, columnSpan, alignment); + + delete layout; +} + +// Resizing a QGraphicsWidget to effectiveSizeHint(Qt::MaximumSize) is currently not supported on mac. +#ifndef Q_WS_MAC +void tst_QGraphicsGridLayout::alignment_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} + +// public Qt::Alignment alignment(QGraphicsLayoutItem* item) const +void tst_QGraphicsGridLayout::alignment() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + // no alignment (the default) + QCOMPARE(layout->itemAt(0, 0)->geometry().left(), 0.0); + QCOMPARE(layout->itemAt(0, 0)->geometry().right(), layout->itemAt(0, 1)->geometry().left()); + QCOMPARE(layout->itemAt(0, 1)->geometry().left(), 25.0); + QCOMPARE(layout->itemAt(0, 1)->geometry().right(), layout->itemAt(0, 2)->geometry().left()); + QCOMPARE(layout->itemAt(0, 2)->geometry().left(), 50.0); + QCOMPARE(layout->itemAt(0, 2)->geometry().right(), 75.0); + + QCOMPARE(layout->itemAt(1, 0)->geometry().left(), 0.0); + QCOMPARE(layout->itemAt(1, 0)->geometry().right(), layout->itemAt(1, 1)->geometry().left()); + QCOMPARE(layout->itemAt(1, 1)->geometry().left(), 25.0); + QCOMPARE(layout->itemAt(1, 1)->geometry().right(), layout->itemAt(1, 2)->geometry().left()); + QCOMPARE(layout->itemAt(1, 2)->geometry().left(), 50.0); + QCOMPARE(layout->itemAt(1, 2)->geometry().right(), 75.0); + + QCOMPARE(layout->itemAt(0, 0)->geometry().top(), 0.0); + QCOMPARE(layout->itemAt(0, 0)->geometry().bottom(), layout->itemAt(1, 0)->geometry().top()); + QCOMPARE(layout->itemAt(1, 0)->geometry().top(), 25.0); + QCOMPARE(layout->itemAt(1, 0)->geometry().bottom(), 50.0); + + // align first column left, second hcenter, third right + layout->setColumnMinimumWidth(0, 100); + layout->setAlignment(layout->itemAt(0,0), Qt::AlignLeft); + layout->setAlignment(layout->itemAt(1,0), Qt::AlignLeft); + layout->setColumnMinimumWidth(1, 100); + layout->setAlignment(layout->itemAt(0,1), Qt::AlignHCenter); + layout->setAlignment(layout->itemAt(1,1), Qt::AlignHCenter); + layout->setColumnMinimumWidth(2, 100); + layout->setAlignment(layout->itemAt(0,2), Qt::AlignRight); + layout->setAlignment(layout->itemAt(1,2), Qt::AlignRight); + + widget->resize(widget->effectiveSizeHint(Qt::MaximumSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 50, 50, 50)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(125, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(125, 50, 50, 50)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(250, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(250, 50, 50, 50)); + + delete widget; +} +#endif + +// Resizing a QGraphicsWidget to effectiveSizeHint(Qt::MaximumSize) is currently not supported on mac. +#ifndef Q_WS_MAC +void tst_QGraphicsGridLayout::columnAlignment_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} + +// public void setColumnAlignment(int column, Qt::Alignment alignment) +// public Qt::Alignment columnAlignment(int column) const +void tst_QGraphicsGridLayout::columnAlignment() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(1); + widget->setContentsMargins(0, 0, 0, 0); + + layout->setColumnMinimumWidth(0, 100); + layout->setColumnMinimumWidth(1, 100); + layout->setColumnMinimumWidth(2, 100); + + view.resize(450,150); + widget->resize(widget->effectiveSizeHint(Qt::MaximumSize)); + view.show(); + widget->show(); + QApplication::sendPostedEvents(0, 0); + // Check default + QCOMPARE(layout->columnAlignment(0), 0); + QCOMPARE(layout->columnAlignment(1), 0); + QCOMPARE(layout->columnAlignment(2), 0); + + layout->setColumnAlignment(0, Qt::AlignLeft); + layout->setColumnAlignment(1, Qt::AlignHCenter); + layout->setColumnAlignment(2, Qt::AlignRight); + + // see if item alignment takes preference over columnAlignment + layout->setAlignment(layout->itemAt(1,0), Qt::AlignHCenter); + layout->setAlignment(layout->itemAt(1,1), Qt::AlignRight); + layout->setAlignment(layout->itemAt(1,2), Qt::AlignLeft); + + QApplication::sendPostedEvents(0, 0); // process LayoutRequest + /* + +----------+------------+---------+ + | Left | HCenter | Right | + +----------+------------+---------+ + | HCenter | Right | Left | + +---------------------------------+ + */ + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(25, 51, 50, 50)); // item is king + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(126, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(151, 51, 50, 50)); // item is king + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(252, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(202, 51, 50, 50)); // item is king + + delete widget; +} +#endif + +void tst_QGraphicsGridLayout::columnCount_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} +// public int columnCount() const +void tst_QGraphicsGridLayout::columnCount() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + widget->setContentsMargins(0, 0, 0, 0); + + view.show(); + widget->show(); + QApplication::processEvents(); + + QCOMPARE(layout->columnCount(), 0); + layout->addItem(new RectWidget(widget), 0, 0); + QCOMPARE(layout->columnCount(), 1); + layout->addItem(new RectWidget(widget), 1, 1); + QCOMPARE(layout->columnCount(), 2); + layout->addItem(new RectWidget(widget), 0, 2); + QCOMPARE(layout->columnCount(), 3); + layout->addItem(new RectWidget(widget), 1, 0); + QCOMPARE(layout->columnCount(), 3); + layout->addItem(new RectWidget(widget), 0, 1); + QCOMPARE(layout->columnCount(), 3); + layout->addItem(new RectWidget(widget), 1, 2); + QCOMPARE(layout->columnCount(), 3); + + // ### Talk with Jasmin. Not sure if removeAt() should adjust columnCount(). + widget->setLayout(0); + layout = new QGraphicsGridLayout(); + populateLayout(layout, 3, 2, hasHeightForWidth); + QCOMPARE(layout->columnCount(), 3); + layout->removeAt(5); + layout->removeAt(3); + QCOMPARE(layout->columnCount(), 3); + layout->removeAt(1); + QCOMPARE(layout->columnCount(), 3); + layout->removeAt(0); + QCOMPARE(layout->columnCount(), 3); + layout->removeAt(0); + QCOMPARE(layout->columnCount(), 2); + + delete widget; +} + +void tst_QGraphicsGridLayout::columnMaximumWidth_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} +// public qreal columnMaximumWidth(int column) const +void tst_QGraphicsGridLayout::columnMaximumWidth() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QCOMPARE(layout->minimumSize(), QSizeF(10+10+10, 10+10)); + QCOMPARE(layout->preferredSize(), QSizeF(25+25+25, 25+25)); + QCOMPARE(layout->maximumSize(), QSizeF(50+50+50, 50+50)); + + // should at least be a very large number + QVERIFY(layout->columnMaximumWidth(0) >= 10000); + QCOMPARE(layout->columnMaximumWidth(0), layout->columnMaximumWidth(1)); + QCOMPARE(layout->columnMaximumWidth(1), layout->columnMaximumWidth(2)); + layout->setColumnMaximumWidth(0, 20); + layout->setColumnMaximumWidth(2, 60); + + QCOMPARE(layout->minimumSize(), QSizeF(10+10+10, 10+10)); + QCOMPARE(layout->preferredSize(), QSizeF(20+25+25, 25+25)); + QCOMPARE(layout->maximumSize(), QSizeF(20+50+60, 50+50)); + QCOMPARE(layout->maximumSize(), widget->maximumSize()); + + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + layout->activate(); + + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 20, 25)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 25, 20, 25)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20, 0, 25, 25)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20, 25, 25, 25)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(45, 0, 25, 25)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(45, 25, 25, 25)); + + layout->setColumnAlignment(2, Qt::AlignCenter); + widget->resize(widget->effectiveSizeHint(Qt::MaximumSize)); + layout->activate(); + QCOMPARE(layout->geometry(), QRectF(0,0,20+50+60, 50+50)); + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 20, 50)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 50, 20, 50)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20, 50, 50, 50)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(75, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(75, 50, 50, 50)); + + for (int i = 0; i < layout->count(); i++) + layout->setAlignment(layout->itemAt(i), Qt::AlignRight | Qt::AlignBottom); + layout->activate(); + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 20, 50)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 50, 20, 50)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20, 50, 50, 50)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(80, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(80, 50, 50, 50)); + for (int i = 0; i < layout->count(); i++) + layout->setAlignment(layout->itemAt(i), Qt::AlignCenter); + + layout->setMaximumSize(layout->maximumSize() + QSizeF(60,60)); + widget->resize(widget->effectiveSizeHint(Qt::MaximumSize)); + layout->activate(); + + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 15, 20, 50)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 95, 20, 50)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20+30, 15, 50, 50)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20+30, 95, 50, 50)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(20+60+50+5, 15, 50, 50)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(20+60+50+5, 95, 50, 50)); + + layout->setMaximumSize(layout->preferredSize() + QSizeF(20,20)); + widget->resize(widget->effectiveSizeHint(Qt::MaximumSize)); + layout->activate(); + + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 20, 35)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 35, 20, 35)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20, 0, 35, 35)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20, 35, 35, 35)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(55, 0, 35, 35)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(55, 35, 35, 35)); + + delete widget; +} + +void tst_QGraphicsGridLayout::columnMinimumWidth_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} +// public qreal columnMinimumWidth(int column) const +void tst_QGraphicsGridLayout::columnMinimumWidth() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + // should at least be a very large number + QCOMPARE(layout->columnMinimumWidth(0), 0.0); + QCOMPARE(layout->columnMinimumWidth(0), layout->columnMinimumWidth(1)); + QCOMPARE(layout->columnMinimumWidth(1), layout->columnMinimumWidth(2)); + layout->setColumnMinimumWidth(0, 20); + layout->setColumnMinimumWidth(2, 40); + + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(1,0)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(0,1)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(1,1)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(0,2)->geometry().width(), 40.0); + QCOMPARE(layout->itemAt(1,2)->geometry().width(), 40.0); + + delete widget; +} + +void tst_QGraphicsGridLayout::columnPreferredWidth_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} +// public qreal columnPreferredWidth(int column) const +void tst_QGraphicsGridLayout::columnPreferredWidth() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + // default preferred width ?? + QCOMPARE(layout->columnPreferredWidth(0), 0.0); + QCOMPARE(layout->columnPreferredWidth(0), layout->columnPreferredWidth(1)); + QCOMPARE(layout->columnPreferredWidth(1), layout->columnPreferredWidth(2)); + layout->setColumnPreferredWidth(0, 20); + layout->setColumnPreferredWidth(2, 40); + + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(1,0)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(0,1)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(1,1)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(0,2)->geometry().width(), 40.0); + QCOMPARE(layout->itemAt(1,2)->geometry().width(), 40.0); + + delete widget; +} + +// public void setColumnFixedWidth(int row, qreal height) +void tst_QGraphicsGridLayout::setColumnFixedWidth() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + layout->setColumnFixedWidth(0, 20); + layout->setColumnFixedWidth(2, 40); + + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry().width(), 20.0); + QCOMPARE(layout->itemAt(1,0)->geometry().width(), 20.0); + QCOMPARE(layout->itemAt(0,1)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(1,1)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(0,2)->geometry().width(), 40.0); + QCOMPARE(layout->itemAt(1,2)->geometry().width(), 40.0); + + delete widget; +} + +// public qreal columnSpacing(int column) const +void tst_QGraphicsGridLayout::columnSpacing() +{ + { + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + QCOMPARE(layout->columnSpacing(0), 0.0); + + layout->setColumnSpacing(0, 20); + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry().left(), 0.0); + QCOMPARE(layout->itemAt(0,0)->geometry().right(), 25.0); + QCOMPARE(layout->itemAt(0,1)->geometry().left(), 45.0); + QCOMPARE(layout->itemAt(0,1)->geometry().right(), 70.0); + QCOMPARE(layout->itemAt(0,2)->geometry().left(), 70.0); + QCOMPARE(layout->itemAt(0,2)->geometry().right(), 95.0); + + delete widget; + } + + { + // don't include items and spacings that was previously part of the layout + // (horizontal) + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + populateLayout(layout, 3, 1); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->setColumnSpacing(0, 10); + layout->setColumnSpacing(1, 10); + layout->setColumnSpacing(2, 10); + layout->setColumnSpacing(3, 10); + QCOMPARE(layout->preferredSize(), QSizeF(95, 25)); + layout->removeAt(2); + QCOMPARE(layout->preferredSize(), QSizeF(60, 25)); + layout->removeAt(1); + QCOMPARE(layout->preferredSize(), QSizeF(25, 25)); + delete layout; + } + { + // don't include items and spacings that was previously part of the layout + // (vertical) + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + populateLayout(layout, 2, 2); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->setColumnSpacing(0, 10); + layout->setColumnSpacing(1, 10); + layout->setRowSpacing(0, 10); + layout->setRowSpacing(1, 10); + QCOMPARE(layout->preferredSize(), QSizeF(60, 60)); + layout->removeAt(3); + QCOMPARE(layout->preferredSize(), QSizeF(60, 60)); + layout->removeAt(2); + QCOMPARE(layout->preferredSize(), QSizeF(60, 25)); + layout->removeAt(1); + QCOMPARE(layout->preferredSize(), QSizeF(25, 25)); + delete layout; + } + +} + +// public int columnStretchFactor(int column) const +void tst_QGraphicsGridLayout::columnStretchFactor() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + layout->setColumnStretchFactor(0, 1); + layout->setColumnStretchFactor(1, 2); + layout->setColumnStretchFactor(2, 3); + view.show(); + widget->show(); + widget->resize(130, 50); + QApplication::processEvents(); + + QVERIFY(layout->itemAt(0,0)->geometry().width() < layout->itemAt(0,1)->geometry().width()); + QVERIFY(layout->itemAt(0,1)->geometry().width() < layout->itemAt(0,2)->geometry().width()); + QVERIFY(layout->itemAt(1,0)->geometry().width() < layout->itemAt(1,1)->geometry().width()); + QVERIFY(layout->itemAt(1,1)->geometry().width() < layout->itemAt(1,2)->geometry().width()); + + delete widget; +} + + +// public int count() const +void tst_QGraphicsGridLayout::count() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2); + QCOMPARE(layout->count(), 6); + layout->removeAt(5); + layout->removeAt(3); + QCOMPARE(layout->count(), 4); + layout->removeAt(1); + QCOMPARE(layout->count(), 3); + layout->removeAt(0); + QCOMPARE(layout->count(), 2); + layout->removeAt(0); + QCOMPARE(layout->count(), 1); + layout->removeAt(0); + QCOMPARE(layout->count(), 0); + + delete widget; +} + +void tst_QGraphicsGridLayout::horizontalSpacing_data() +{ + QTest::addColumn<qreal>("horizontalSpacing"); + QTest::newRow("zero") << qreal(0.0); + QTest::newRow("10") << qreal(10.0); +} + +// public qreal horizontalSpacing() const +void tst_QGraphicsGridLayout::horizontalSpacing() +{ + QFETCH(qreal, horizontalSpacing); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2); + layout->setContentsMargins(0, 0, 0, 0); + qreal w = layout->sizeHint(Qt::PreferredSize, QSizeF()).width(); + qreal oldSpacing = layout->horizontalSpacing(); + if (oldSpacing != -1) { + layout->setHorizontalSpacing(horizontalSpacing); + QApplication::processEvents(); + qreal new_w = layout->sizeHint(Qt::PreferredSize, QSizeF()).width(); + QCOMPARE(new_w, w - (3-1)*(oldSpacing - horizontalSpacing)); + } else { + QSKIP("The current style uses non-uniform layout spacing", SkipAll); + } + delete widget; +} + +void tst_QGraphicsGridLayout::contentsMargins() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + QGraphicsGridLayout *sublayout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + layout->addItem(sublayout,0, 1); + + qreal left, top, right, bottom; + // sublayouts have 0 margin + sublayout->getContentsMargins(&left, &top, &right, &bottom); + QCOMPARE(left, 0.0); + QCOMPARE(top, 0.0); + QCOMPARE(right, 0.0); + QCOMPARE(bottom, 0.0); + + // top level layouts have style dependent margins. + // we'll just check if its different from 0. (applies to all our styles) + layout->getContentsMargins(&left, &top, &right, &bottom); + QVERIFY(left >= 0.0); + QVERIFY(top >= 0.0); + QVERIFY(right >= 0.0); + QVERIFY(bottom >= 0.0); + + delete widget; +} + +// public QGraphicsLayoutItem* itemAt(int index) const +void tst_QGraphicsGridLayout::itemAt() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayoutWithSpansAndHoles(layout); + + //itemAt(int row, int column) + QVERIFY( layout->itemAt(0,0)); + QVERIFY( layout->itemAt(0,1)); + QCOMPARE(layout->itemAt(0,2), static_cast<QGraphicsLayoutItem*>(0)); + QCOMPARE(layout->itemAt(1,0), static_cast<QGraphicsLayoutItem*>(0)); + QVERIFY( layout->itemAt(1,1)); + QVERIFY( layout->itemAt(1,2)); + + + //itemAt(int index) + for (int i = -2; i < layout->count() + 2; ++i) { + if (i >= 0 && i < layout->count()) { + QVERIFY(layout->itemAt(i)); + } else { + QTest::ignoreMessage(QtWarningMsg, QString::fromAscii("QGraphicsGridLayout::itemAt: invalid index %1").arg(i).toLatin1().constData()); + QCOMPARE(layout->itemAt(i), static_cast<QGraphicsLayoutItem*>(0)); + } + } + delete widget; +} + +// public void removeAt(int index) +void tst_QGraphicsGridLayout::removeAt() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2); + QCOMPARE(layout->count(), 6); + layout->removeAt(5); + layout->removeAt(3); + QCOMPARE(layout->count(), 4); + layout->removeAt(1); + QCOMPARE(layout->count(), 3); + layout->removeAt(0); + QCOMPARE(layout->count(), 2); + layout->removeAt(0); + QCOMPARE(layout->count(), 1); + QGraphicsLayoutItem *item0 = layout->itemAt(0); + QCOMPARE(item0->parentLayoutItem(), static_cast<QGraphicsLayoutItem *>(layout)); + layout->removeAt(0); + QCOMPARE(item0->parentLayoutItem(), static_cast<QGraphicsLayoutItem *>(0)); + QCOMPARE(layout->count(), 0); + QTest::ignoreMessage(QtWarningMsg, QString::fromAscii("QGraphicsGridLayout::removeAt: invalid index 0").toLatin1().constData()); + layout->removeAt(0); + QCOMPARE(layout->count(), 0); + delete widget; +} + +void tst_QGraphicsGridLayout::removeItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + scene.addItem(widget); + QGraphicsGridLayout *l = new QGraphicsGridLayout(); + widget->setLayout(l); + + populateLayout(l, 3, 2); + QCOMPARE(l->count(), 6); + l->removeItem(l->itemAt(5)); + l->removeItem(l->itemAt(4)); + QCOMPARE(l->count(), 4); + + // Avoid crashing. Note that the warning message might change in the future. + QTest::ignoreMessage(QtWarningMsg, QString::fromAscii("QGraphicsGridLayout::removeAt: invalid index -1").toLatin1().constData()); + l->removeItem(0); + QCOMPARE(l->count(), 4); + + QTest::ignoreMessage(QtWarningMsg, QString::fromAscii("QGraphicsGridLayout::removeAt: invalid index -1").toLatin1().constData()); + l->removeItem(new QGraphicsWidget); + QCOMPARE(l->count(), 4); +} + +void tst_QGraphicsGridLayout::rowAlignment_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} + +// public Qt::Alignment rowAlignment(int row) const +void tst_QGraphicsGridLayout::rowAlignment() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 2, 3, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(1); + widget->setContentsMargins(0, 0, 0, 0); + + view.resize(330,450); + widget->resize(300, 400); + view.show(); + widget->show(); + QApplication::sendPostedEvents(0, 0); + // Check default + QCOMPARE(layout->rowAlignment(0), 0); + QCOMPARE(layout->rowAlignment(1), 0); + QCOMPARE(layout->rowAlignment(2), 0); + + // make the grids larger than the items, so that alignment kicks in + layout->setRowMinimumHeight(0, 100.0); + layout->setRowMinimumHeight(1, 100.0); + layout->setRowMinimumHeight(2, 100.0); + // expand columns also, so we can test combination of horiz and vertical alignment + layout->setColumnMinimumWidth(0, 100.0); + layout->setColumnMinimumWidth(1, 100.0); + + layout->setRowAlignment(0, Qt::AlignBottom); + layout->setRowAlignment(1, Qt::AlignVCenter); + layout->setRowAlignment(2, Qt::AlignTop); + + // see if item alignment takes preference over rowAlignment + layout->setAlignment(layout->itemAt(0,0), Qt::AlignRight); + layout->setAlignment(layout->itemAt(1,0), Qt::AlignTop); + layout->setAlignment(layout->itemAt(2,0), Qt::AlignHCenter); + + QApplication::sendPostedEvents(0, 0); // process LayoutRequest + + QCOMPARE(layout->alignment(layout->itemAt(0,0)), Qt::AlignRight); //Qt::AlignRight | Qt::AlignBottom + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(50, 50, 50, 50)); + QCOMPARE(layout->rowAlignment(0), Qt::AlignBottom); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(101, 50, 50, 50)); + QCOMPARE(layout->alignment(layout->itemAt(1,0)), Qt::AlignTop); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 101, 50, 50)); + QCOMPARE(layout->rowAlignment(1), Qt::AlignVCenter); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(101, 126, 50, 50)); + QCOMPARE(layout->alignment(layout->itemAt(2,0)), Qt::AlignHCenter); + QCOMPARE(layout->itemAt(2,0)->geometry(), QRectF(25, 202, 50, 50)); + QCOMPARE(layout->rowAlignment(2), Qt::AlignTop); + QCOMPARE(layout->itemAt(2,1)->geometry(), QRectF(101,202, 50, 50)); + + delete widget; +} + +void tst_QGraphicsGridLayout::rowCount_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} + +// public int rowCount() const +// public int columnCount() const +void tst_QGraphicsGridLayout::rowCount() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 2, 3, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + widget->setContentsMargins(0, 0, 0, 0); + QCOMPARE(layout->rowCount(), 3); + QCOMPARE(layout->columnCount(), 2); + + // with spans and holes... + widget->setLayout(0); + layout = new QGraphicsGridLayout(); + populateLayoutWithSpansAndHoles(layout, hasHeightForWidth); + QCOMPARE(layout->rowCount(), 2); + QCOMPARE(layout->columnCount(), 3); + + delete widget; +} + +void tst_QGraphicsGridLayout::rowMaximumHeight_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} + +// public qreal rowMaximumHeight(int row) const +void tst_QGraphicsGridLayout::rowMaximumHeight() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 2, 3, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + // should at least be a very large number + QVERIFY(layout->rowMaximumHeight(0) >= 10000); + QCOMPARE(layout->rowMaximumHeight(0), layout->rowMaximumHeight(1)); + QCOMPARE(layout->rowMaximumHeight(1), layout->rowMaximumHeight(2)); + layout->setRowMaximumHeight(0, 20); + layout->setRowMaximumHeight(2, 60); + + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry().height(), 20.0); + QCOMPARE(layout->itemAt(0,1)->geometry().height(), 20.0); + QCOMPARE(layout->itemAt(1,0)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(1,1)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(2,0)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(2,1)->geometry().height(), 25.0); + + delete widget; +} + +void tst_QGraphicsGridLayout::rowMinimumHeight_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} +// public qreal rowMinimumHeight(int row) const +void tst_QGraphicsGridLayout::rowMinimumHeight() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 2, 3, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + // should at least be a very large number + QCOMPARE(layout->rowMinimumHeight(0), 0.0); + QCOMPARE(layout->rowMinimumHeight(0), layout->rowMinimumHeight(1)); + QCOMPARE(layout->rowMinimumHeight(1), layout->rowMinimumHeight(2)); + layout->setRowMinimumHeight(0, 20); + layout->setRowMinimumHeight(2, 40); + + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(0,1)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(1,0)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(1,1)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(2,0)->geometry().height(), 40.0); + QCOMPARE(layout->itemAt(2,1)->geometry().height(), 40.0); + + delete widget; +} + +void tst_QGraphicsGridLayout::rowPreferredHeight_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} +// public qreal rowPreferredHeight(int row) const +void tst_QGraphicsGridLayout::rowPreferredHeight() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 2, 3, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + // default preferred height ?? + QCOMPARE(layout->rowPreferredHeight(0), 0.0); + QCOMPARE(layout->rowPreferredHeight(0), layout->rowPreferredHeight(1)); + QCOMPARE(layout->rowPreferredHeight(1), layout->rowPreferredHeight(2)); + layout->setRowPreferredHeight(0, 20); + layout->setRowPreferredHeight(2, 40); + + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + // ### Jasmin: Should rowPreferredHeight have precedence over sizeHint(Qt::PreferredSize) ? + QCOMPARE(layout->itemAt(0,0)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(0,1)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(1,0)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(1,1)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(2,0)->geometry().height(), 40.0); + QCOMPARE(layout->itemAt(2,1)->geometry().height(), 40.0); + + delete widget; +} + +// public void setRowFixedHeight(int row, qreal height) +void tst_QGraphicsGridLayout::setRowFixedHeight() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 2, 3); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + layout->setRowFixedHeight(0, 20.); + layout->setRowFixedHeight(2, 40.); + + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry().height(), 20.0); + QCOMPARE(layout->itemAt(0,1)->geometry().height(), 20.0); + QCOMPARE(layout->itemAt(1,0)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(1,1)->geometry().height(), 25.0); + QCOMPARE(layout->itemAt(2,0)->geometry().height(), 40.0); + QCOMPARE(layout->itemAt(2,1)->geometry().height(), 40.0); + + delete widget; +} + +// public qreal rowSpacing(int row) const +void tst_QGraphicsGridLayout::rowSpacing() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + QCOMPARE(layout->columnSpacing(0), 0.0); + + layout->setColumnSpacing(0, 20); + view.show(); + widget->show(); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + QApplication::processEvents(); + + QCOMPARE(layout->itemAt(0,0)->geometry().left(), 0.0); + QCOMPARE(layout->itemAt(0,0)->geometry().right(), 25.0); + QCOMPARE(layout->itemAt(0,1)->geometry().left(), 45.0); + QCOMPARE(layout->itemAt(0,1)->geometry().right(), 70.0); + QCOMPARE(layout->itemAt(0,2)->geometry().left(), 70.0); + QCOMPARE(layout->itemAt(0,2)->geometry().right(), 95.0); + + delete widget; + +} + +void tst_QGraphicsGridLayout::rowStretchFactor_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} + +// public int rowStretchFactor(int row) const +void tst_QGraphicsGridLayout::rowStretchFactor() +{ + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 2, 3, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + layout->setRowStretchFactor(0, 1); + layout->setRowStretchFactor(1, 2); + layout->setRowStretchFactor(2, 3); + view.show(); + widget->show(); + widget->resize(50, 130); + QApplication::processEvents(); + + QVERIFY(layout->itemAt(0,0)->geometry().height() < layout->itemAt(1,0)->geometry().height()); + QVERIFY(layout->itemAt(1,0)->geometry().height() < layout->itemAt(2,0)->geometry().height()); + QVERIFY(layout->itemAt(0,1)->geometry().height() < layout->itemAt(1,1)->geometry().height()); + QVERIFY(layout->itemAt(1,1)->geometry().height() < layout->itemAt(2,1)->geometry().height()); + + delete widget; +} + +void tst_QGraphicsGridLayout::setColumnSpacing_data() +{ + QTest::addColumn<int>("column"); + QTest::addColumn<qreal>("spacing"); + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("null") << 0 << qreal(0.0) << false; + QTest::newRow("10") << 0 << qreal(10.0) << false; + QTest::newRow("null, hasHeightForWidth") << 0 << qreal(0.0) << true; + QTest::newRow("10, hasHeightForWidth") << 0 << qreal(10.0) << true; +} + +// public void setColumnSpacing(int column, qreal spacing) +void tst_QGraphicsGridLayout::setColumnSpacing() +{ + QFETCH(int, column); + QFETCH(qreal, spacing); + QFETCH(bool, hasHeightForWidth); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2, hasHeightForWidth); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + qreal oldSpacing = layout->columnSpacing(column); + QCOMPARE(oldSpacing, 0.0); + qreal w = layout->sizeHint(Qt::PreferredSize, QSizeF()).width(); + layout->setColumnSpacing(column, spacing); + QApplication::processEvents(); + QCOMPARE(layout->sizeHint(Qt::PreferredSize, QSizeF()).width(), w + spacing); +} + +void tst_QGraphicsGridLayout::setGeometry_data() +{ + QTest::addColumn<QRectF>("rect"); + QTest::newRow("null") << QRectF(); + QTest::newRow("normal") << QRectF(0,0, 50, 50); +} + +// public void setGeometry(QRectF const& rect) +void tst_QGraphicsGridLayout::setGeometry() +{ + QFETCH(QRectF, rect); + + QGraphicsWidget *window = new QGraphicsWidget; + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + window->setLayout(layout); + QGraphicsGridLayout *layout2 = new QGraphicsGridLayout(); + layout2->setMaximumSize(100, 100); + layout->addItem(layout2, 0, 0); + layout2->setGeometry(rect); + QCOMPARE(layout2->geometry(), rect); +} + +void tst_QGraphicsGridLayout::setRowSpacing_data() +{ + QTest::addColumn<int>("row"); + QTest::addColumn<qreal>("spacing"); + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("null") << 0 << qreal(0.0) << false; + QTest::newRow("10") << 0 << qreal(10.0) << false; + QTest::newRow("null, hasHeightForWidth") << 0 << qreal(0.0) << true; + QTest::newRow("10, hasHeightForWidth") << 0 << qreal(10.0) << true; +} + +// public void setRowSpacing(int row, qreal spacing) +void tst_QGraphicsGridLayout::setRowSpacing() +{ + QFETCH(int, row); + QFETCH(qreal, spacing); + QFETCH(bool, hasHeightForWidth); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2, hasHeightForWidth); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + qreal oldSpacing = layout->rowSpacing(row); + QCOMPARE(oldSpacing, 0.0); + qreal h = layout->sizeHint(Qt::PreferredSize, QSizeF()).height(); + layout->setRowSpacing(row, spacing); + QApplication::processEvents(); + QCOMPARE(layout->sizeHint(Qt::PreferredSize, QSizeF()).height(), h + spacing); +} + +void tst_QGraphicsGridLayout::setSpacing_data() +{ + QTest::addColumn<qreal>("spacing"); + QTest::addColumn<bool>("hasHeightForWidth"); + QTest::newRow("zero") << qreal(0.0) << false; + QTest::newRow("17") << qreal(17.0) << false; + QTest::newRow("zero, hasHeightForWidth") << qreal(0.0) << true; + QTest::newRow("17, hasHeightForWidth") << qreal(17.0) << true; +} + +// public void setSpacing(qreal spacing) +void tst_QGraphicsGridLayout::setSpacing() +{ + QFETCH(qreal, spacing); + QFETCH(bool, hasHeightForWidth); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2, hasHeightForWidth); + layout->setContentsMargins(0, 0, 0, 0); + QSizeF sh = layout->sizeHint(Qt::PreferredSize, QSizeF()); + qreal oldVSpacing = layout->verticalSpacing(); + qreal oldHSpacing = layout->horizontalSpacing(); + if (oldVSpacing != -1) { + layout->setSpacing(spacing); + QApplication::processEvents(); + QSizeF newSH = layout->sizeHint(Qt::PreferredSize, QSizeF()); + QCOMPARE(newSH.height(), sh.height() - (2-1)*(oldVSpacing - spacing)); + QCOMPARE(newSH.width(), sh.width() - (3-1)*(oldHSpacing - spacing)); + } else { + QSKIP("The current style uses non-uniform layout spacing", SkipAll); + } + delete widget; +} + +void tst_QGraphicsGridLayout::sizeHint_data() +{ + QTest::addColumn<ItemList>("itemDescriptions"); + QTest::addColumn<QSizeF>("expectedMinimumSizeHint"); + QTest::addColumn<QSizeF>("expectedPreferredSizeHint"); + QTest::addColumn<QSizeF>("expectedMaximumSizeHint"); + + QTest::newRow("rowSpan_larger_than_rows") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(50,300)) + .maxSize(QSizeF(50,300)) + .rowSpan(2) + << ItemDesc(0,1) + .minSize(QSizeF(50,0)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSize(50, 1000)) + << ItemDesc(1,1) + .minSize(QSizeF(50,0)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSize(50, 1000)) + ) + << QSizeF(100, 300) + << QSizeF(100, 300) + << QSizeF(100, 2000); + + QTest::newRow("rowSpan_smaller_than_rows") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(50, 0)) + .preferredSize(QSizeF(50, 50)) + .maxSize(QSizeF(50, 300)) + .rowSpan(2) + << ItemDesc(0,1) + .minSize(QSizeF(50, 50)) + .preferredSize(QSizeF(50, 50)) + .maxSize(QSize(50, 50)) + << ItemDesc(1,1) + .minSize(QSizeF(50, 50)) + .preferredSize(QSizeF(50, 50)) + .maxSize(QSize(50, 50)) + ) + << QSizeF(100, 100) + << QSizeF(100, 100) + << QSizeF(100, 100); + +} + +// public QSizeF sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const +void tst_QGraphicsGridLayout::sizeHint() +{ + QFETCH(ItemList, itemDescriptions); + QFETCH(QSizeF, expectedMinimumSizeHint); + QFETCH(QSizeF, expectedPreferredSizeHint); + QFETCH(QSizeF, expectedMaximumSizeHint); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + scene.addItem(widget); + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0.0); + widget->setContentsMargins(0, 0, 0, 0); + + int i; + for (i = 0; i < itemDescriptions.count(); ++i) { + ItemDesc desc = itemDescriptions.at(i); + RectWidget *item = new RectWidget(widget); + desc.apply(layout, item); + } + + QApplication::sendPostedEvents(0, 0); + + widget->show(); + view.show(); + view.resize(400,300); + QCOMPARE(layout->sizeHint(Qt::MinimumSize), expectedMinimumSizeHint); + QCOMPARE(layout->sizeHint(Qt::PreferredSize), expectedPreferredSizeHint); + QCOMPARE(layout->sizeHint(Qt::MaximumSize), expectedMaximumSizeHint); + +} + +void tst_QGraphicsGridLayout::verticalSpacing_data() +{ + QTest::addColumn<qreal>("verticalSpacing"); + QTest::newRow("zero") << qreal(0.0); + QTest::newRow("17") << qreal(10.0); +} + +// public qreal verticalSpacing() const +void tst_QGraphicsGridLayout::verticalSpacing() +{ + QFETCH(qreal, verticalSpacing); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(); + scene.addItem(widget); + widget->setLayout(layout); + populateLayout(layout, 3, 2); + layout->setContentsMargins(0, 0, 0, 0); + qreal h = layout->sizeHint(Qt::PreferredSize, QSizeF()).height(); + qreal oldSpacing = layout->verticalSpacing(); + if (oldSpacing != -1) { + layout->setVerticalSpacing(verticalSpacing); + QApplication::processEvents(); + qreal new_h = layout->sizeHint(Qt::PreferredSize, QSizeF()).height(); + QCOMPARE(new_h, h - (2-1)*(oldSpacing - verticalSpacing)); + } else { + QSKIP("The current style uses non-uniform layout spacing", SkipAll); + } + delete widget; +} + +void tst_QGraphicsGridLayout::layoutDirection_data() +{ + QTest::addColumn<bool>("hasHeightForWidth"); + + QTest::newRow("") << false; + QTest::newRow("hasHeightForWidth") << true; +} + +void tst_QGraphicsGridLayout::layoutDirection() +{ + QFETCH(bool, hasHeightForWidth); + + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + layout->setContentsMargins(1, 2, 3, 4); + layout->setSpacing(6); + RectWidget *w1 = new RectWidget; + w1->setMinimumSize(30, 20); + layout->addItem(w1, 0, 0); + RectWidget *w2 = new RectWidget; + w2->setMinimumSize(20, 20); + w2->setMaximumSize(20, 20); + layout->addItem(w2, 0, 1); + RectWidget *w3 = new RectWidget; + w3->setMinimumSize(20, 20); + w3->setMaximumSize(20, 20); + layout->addItem(w3, 1, 0); + RectWidget *w4 = new RectWidget; + w4->setMinimumSize(30, 20); + layout->addItem(w4, 1, 1); + + QSizePolicy policy = w1->sizePolicy(); + policy.setHeightForWidth(hasHeightForWidth); + w1->setSizePolicy(policy); + w2->setSizePolicy(policy); + w4->setSizePolicy(policy); + + layout->setAlignment(w2, Qt::AlignRight); + layout->setAlignment(w3, Qt::AlignLeft); + + scene.addItem(window); + window->setLayout(layout); + view.show(); + window->resize(70, 52); + QApplication::processEvents(); + QCOMPARE(w1->geometry().left(), 1.0); + QCOMPARE(w1->geometry().right(), 31.0); + QCOMPARE(w2->geometry().left(), 47.0); + QCOMPARE(w2->geometry().right(), 67.0); + QCOMPARE(w3->geometry().left(), 1.0); + QCOMPARE(w3->geometry().right(), 21.0); + QCOMPARE(w4->geometry().left(), 37.0); + QCOMPARE(w4->geometry().right(), 67.0); + + window->setLayoutDirection(Qt::RightToLeft); + QApplication::processEvents(); + QCOMPARE(w1->geometry().left(), 39.0); + QCOMPARE(w1->geometry().right(), 69.0); + QCOMPARE(w2->geometry().left(), 3.0); + QCOMPARE(w2->geometry().right(), 23.0); + QCOMPARE(w3->geometry().left(), 49.0); + QCOMPARE(w3->geometry().right(), 69.0); + QCOMPARE(w4->geometry().left(), 3.0); + QCOMPARE(w4->geometry().right(), 33.0); + + delete window; +} + +void tst_QGraphicsGridLayout::removeLayout() +{ + QGraphicsScene scene; + RectWidget *textEdit = new RectWidget; + RectWidget *pushButton = new RectWidget; + scene.addItem(textEdit); + scene.addItem(pushButton); + + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + layout->addItem(textEdit, 0, 0); + layout->addItem(pushButton, 1, 0); + + QGraphicsWidget *form = new QGraphicsWidget; + form->setLayout(layout); + scene.addItem(form); + + QGraphicsView view(&scene); + view.show(); + QTest::qWait(20); + + QRectF r1 = textEdit->geometry(); + QRectF r2 = pushButton->geometry(); + form->setLayout(0); + //documentation of QGraphicsWidget::setLayout: + //If layout is 0, the widget is left without a layout. Existing subwidgets' geometries will remain unaffected. + QCOMPARE(textEdit->geometry(), r1); + QCOMPARE(pushButton->geometry(), r2); +} + +void tst_QGraphicsGridLayout::defaultStretchFactors_data() +{ + QTest::addColumn<ItemList>("itemDescriptions"); + QTest::addColumn<QSizeF>("newSize"); + QTest::addColumn<SizeList>("expectedSizes"); + + QTest::newRow("usepreferredsize") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(10,10) << QSizeF(10,10) << QSizeF(10,10) + << QSizeF(10,10) << QSizeF(10,10) << QSizeF(10,10) + ); + + QTest::newRow("ignoreitem01") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(10,10) << QSizeF(10,10) << QSizeF(10,10) + << QSizeF(10,10) << QSizeF(10,10) << QSizeF(10,10) + ); + + QTest::newRow("ignoreitem01_resize120x40") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(20,10)) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(30,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(20,10)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(30,10)) + ) + << QSizeF(120, 40) + << (SizeList() + << QSizeF(20,20) << QSizeF(40,20) << QSizeF(60,20) + << QSizeF(20,20) << QSizeF(40,20) << QSizeF(60,20) + ); + + QTest::newRow("ignoreitem11_resize120x40") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(20,10)) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(30,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,20)) + << ItemDesc(1,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(20,20)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(30,20)) + ) + << QSizeF(120, 60) + << (SizeList() + << QSizeF(20,20) << QSizeF(40,20) << QSizeF(60,20) + << QSizeF(20,40) << QSizeF(40,40) << QSizeF(60,40) + ); + + QTest::newRow("ignoreitem01_span01_resize70x60") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(20,10)) + .sizePolicy(QSizePolicy::Ignored) + .rowSpan(2) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(30,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,20)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(30,20)) + ) + << QSizeF(70, 60) + << (SizeList() + << QSizeF(20,20) << QSizeF(10,60) << QSizeF(40,20) + << QSizeF(20,40) << QSizeF(40,40) + ); + + QTest::newRow("ignoreitem10_resize40x120") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,0) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,20)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(10,20)) + << ItemDesc(2,0) + .preferredSizeHint(QSizeF(10,30)) + << ItemDesc(2,1) + .preferredSizeHint(QSizeF(10,30)) + ) + << QSizeF(40, 120) + << (SizeList() + << QSizeF(20,20) << QSizeF(20,20) + << QSizeF(20,40) << QSizeF(20,40) + << QSizeF(20,60) << QSizeF(20,60) + ); + + QTest::newRow("ignoreitem01_span02") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,20)) + .rowSpan(2) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(10,10) << QSizeF(0,20) << QSizeF(10,10) + << QSizeF(10,10) << QSizeF(10,10) + ); + + QTest::newRow("ignoreitem02_span02") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,2) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,20)) + .rowSpan(2) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(10,10) << QSizeF(10,10) << QSizeF(0,20) + << QSizeF(10,10) << QSizeF(10,10) + ); + + QTest::newRow("ignoreitem02_span00_span02") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + .rowSpan(2) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,2) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,20)) + .rowSpan(2) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(10,20) << QSizeF(10,10) << QSizeF(0,20) + << QSizeF(10,10) + ); + + QTest::newRow("ignoreitem00_colspan00") << (ItemList() + << ItemDesc(0,0) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,20)) + .colSpan(2) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(20,10) << QSizeF(10,10) << QSizeF(10,10) + << QSizeF(10,10) << QSizeF(10,10) + ); + + QTest::newRow("ignoreitem01_colspan01") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,20)) + .colSpan(2) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(10,10) << QSizeF(20,10) << QSizeF(10,10) + << QSizeF(10,10) << QSizeF(10,10) + ); + + QTest::newRow("ignorecolumn1_resize70x60") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(20,10)) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(30,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,20)) + << ItemDesc(1,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(20,20)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(30,20)) + ) + << QSizeF(70, 60) + << (SizeList() + << QSizeF(20,20) << QSizeF(10,20) << QSizeF(40,20) + << QSizeF(20,40) << QSizeF(10,40) << QSizeF(40,40) + ); + + QTest::newRow("ignorerow0") << (ItemList() + << ItemDesc(0,0) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,2) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(10,0) << QSizeF(10,0) << QSizeF(10,0) + << QSizeF(10,10) << QSizeF(10,10) << QSizeF(10,10) + ); + + QTest::newRow("ignorerow1") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,2) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,0) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(1,2) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,10)) + ) + << QSizeF() + << (SizeList() + << QSizeF(10,10) << QSizeF(10,10) << QSizeF(10,10) + << QSizeF(10,0) << QSizeF(10,0) << QSizeF(10,0) + ); + + QTest::newRow("ignorerow0_resize60x50") << (ItemList() + << ItemDesc(0,0) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(10,10)) + << ItemDesc(0,1) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(20,10)) + << ItemDesc(0,2) + .sizePolicy(QSizePolicy::Ignored) + .preferredSizeHint(QSizeF(30,10)) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,30)) + << ItemDesc(1,1) + .preferredSizeHint(QSizeF(20,30)) + << ItemDesc(1,2) + .preferredSizeHint(QSizeF(30,30)) + ) + << QSizeF(60, 50) + << (SizeList() + << QSizeF(10,10) << QSizeF(20,10) << QSizeF(30,10) + << QSizeF(10,40) << QSizeF(20,40) << QSizeF(30,40) + ); + +} + +void tst_QGraphicsGridLayout::defaultStretchFactors() +{ + QFETCH(ItemList, itemDescriptions); + QFETCH(QSizeF, newSize); + QFETCH(SizeList, expectedSizes); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + scene.addItem(widget); + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0.0); + widget->setContentsMargins(0, 0, 0, 0); + + int i; + for (i = 0; i < itemDescriptions.count(); ++i) { + ItemDesc desc = itemDescriptions.at(i); + RectWidget *item = new RectWidget(widget); + desc.apply(layout, item); + } + + QApplication::sendPostedEvents(0, 0); + + widget->show(); + view.show(); + view.resize(400,300); + if (newSize.isValid()) + widget->resize(newSize); + + QApplication::sendPostedEvents(0, 0); + for (i = 0; i < expectedSizes.count(); ++i) { + QSizeF itemSize = layout->itemAt(i)->geometry().size(); + QCOMPARE(itemSize, expectedSizes.at(i)); + } + + delete widget; +} + +typedef QList<QRectF> RectList; +Q_DECLARE_METATYPE(RectList); + +void tst_QGraphicsGridLayout::alignment2_data() +{ + QTest::addColumn<ItemList>("itemDescriptions"); + QTest::addColumn<QSizeF>("newSize"); + QTest::addColumn<RectList>("expectedGeometries"); + + QTest::newRow("hor_sizepolicy_fixed") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,20)) + .sizePolicyV(QSizePolicy::Fixed) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + .sizePolicyV(QSizePolicy::Fixed) + ) + << QSizeF() + << (RectList() + << QRectF(0, 0, 10,20) << QRectF(10, 0, 10,10) + ); + + QTest::newRow("hor_sizepolicy_fixed_alignvcenter") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,20)) + .sizePolicyV(QSizePolicy::Fixed) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + .sizePolicyV(QSizePolicy::Fixed) + .alignment(Qt::AlignVCenter) + ) + << QSizeF() + << (RectList() + << QRectF(0, 0, 10,20) << QRectF(10, 5, 10,10) + ); + + QTest::newRow("hor_sizepolicy_fixed_aligntop") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,20)) + .sizePolicyV(QSizePolicy::Fixed) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + .sizePolicyV(QSizePolicy::Fixed) + .alignment(Qt::AlignTop) + ) + << QSizeF() + << (RectList() + << QRectF(0, 0, 10,20) << QRectF(10, 0, 10,10) + ); + + QTest::newRow("hor_sizepolicy_fixed_alignbottom") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(10,20)) + .sizePolicyV(QSizePolicy::Fixed) + << ItemDesc(0,1) + .preferredSizeHint(QSizeF(10,10)) + .sizePolicyV(QSizePolicy::Fixed) + .alignment(Qt::AlignBottom) + ) + << QSizeF() + << (RectList() + << QRectF(0, 0, 10,20) << QRectF(10, 10, 10,10) + ); + + QTest::newRow("ver_sizepolicy_fixed") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(20,10)) + .sizePolicyH(QSizePolicy::Fixed) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + .sizePolicyH(QSizePolicy::Fixed) + ) + << QSizeF() + << (RectList() + << QRectF(0, 0, 20,10) << QRectF(0, 10, 10,10) + ); + + QTest::newRow("ver_sizepolicy_fixed_alignhcenter") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(20,10)) + .sizePolicyH(QSizePolicy::Fixed) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + .sizePolicyH(QSizePolicy::Fixed) + .alignment(Qt::AlignHCenter) + ) + << QSizeF() + << (RectList() + << QRectF(0, 0, 20,10) << QRectF(5, 10, 10,10) + ); + + QTest::newRow("ver_sizepolicy_fixed_alignleft") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(20,10)) + .sizePolicyH(QSizePolicy::Fixed) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + .sizePolicyH(QSizePolicy::Fixed) + .alignment(Qt::AlignLeft) + ) + << QSizeF() + << (RectList() + << QRectF(0, 0, 20,10) << QRectF(0, 10, 10,10) + ); + + QTest::newRow("ver_sizepolicy_fixed_alignright") << (ItemList() + << ItemDesc(0,0) + .preferredSizeHint(QSizeF(20,10)) + .sizePolicyH(QSizePolicy::Fixed) + << ItemDesc(1,0) + .preferredSizeHint(QSizeF(10,10)) + .sizePolicyH(QSizePolicy::Fixed) + .alignment(Qt::AlignRight) + ) + << QSizeF() + << (RectList() + << QRectF(0, 0, 20,10) << QRectF(10, 10, 10,10) + ); +} + +void tst_QGraphicsGridLayout::alignment2() +{ + QFETCH(ItemList, itemDescriptions); + QFETCH(QSizeF, newSize); + QFETCH(RectList, expectedGeometries); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + scene.addItem(widget); + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0.0); + widget->setContentsMargins(0, 0, 0, 0); + + int i; + for (i = 0; i < itemDescriptions.count(); ++i) { + ItemDesc desc = itemDescriptions.at(i); + RectWidget *item = new RectWidget(widget); + desc.apply(layout, item); + } + + QApplication::sendPostedEvents(0, 0); + + widget->show(); + view.resize(400,300); + view.show(); + if (newSize.isValid()) + widget->resize(newSize); + + QApplication::sendPostedEvents(0, 0); + for (i = 0; i < expectedGeometries.count(); ++i) { + QRectF itemRect = layout->itemAt(i)->geometry(); + QCOMPARE(itemRect, expectedGeometries.at(i)); + } + + delete widget; +} + +static QSizeF hfw1(Qt::SizeHint, const QSizeF &constraint) +{ + QSizeF result(constraint); + const qreal ch = constraint.height(); + const qreal cw = constraint.width(); + if (cw < 0 && ch < 0) { + return QSizeF(50, 400); + } else if (cw > 0) { + result.setHeight(20000./cw); + } + return result; +} + +static QSizeF wfh1(Qt::SizeHint, const QSizeF &constraint) +{ + QSizeF result(constraint); + const qreal ch = constraint.height(); + const qreal cw = constraint.width(); + if (cw < 0 && ch < 0) { + return QSizeF(400, 50); + } else if (ch > 0) { + result.setWidth(20000./ch); + } + return result; +} + +static QSizeF wfh2(Qt::SizeHint, const QSizeF &constraint) +{ + QSizeF result(constraint); + const qreal ch = constraint.height(); + const qreal cw = constraint.width(); + if (ch < 0 && cw < 0) + return QSizeF(50, 50); + if (ch >= 0) + result.setWidth(ch); + return result; +} + +static QSizeF hfw3(Qt::SizeHint, const QSizeF &constraint) +{ + QSizeF result(constraint); + const qreal ch = constraint.height(); + const qreal cw = constraint.width(); + if (cw < 0 && ch < 0) { + return QSizeF(10, 10); + } else if (cw > 0) { + result.setHeight(100./cw); + } + return result; +} + +static QSizeF hfw2(Qt::SizeHint /*which*/, const QSizeF &constraint) +{ + return QSizeF(constraint.width(), constraint.width()); +} + +void tst_QGraphicsGridLayout::geometries_data() +{ + + QTest::addColumn<ItemList>("itemDescriptions"); + QTest::addColumn<QSizeF>("newSize"); + QTest::addColumn<RectList>("expectedGeometries"); + + QTest::newRow("combine_max_sizes") << (ItemList() + << ItemDesc(0,0) + .maxSize(QSizeF(50,10)) + << ItemDesc(1,0) + .maxSize(QSizeF(10,10)) + ) + << QSizeF(50, 20) + << (RectList() + << QRectF(0, 0, 50,10) << QRectF(0, 10, 10,10) + ); + + QTest::newRow("combine_min_sizes") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(50,10)) + << ItemDesc(1,0) + .minSize(QSizeF(10,10)) + ) + << QSizeF(60, 20) + << (RectList() + << QRectF(0, 0, 60,10) << QRectF(0, 10, 60,10) + ); + + // change layout height and verify + QTest::newRow("hfw-100x401") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .minSize(QSizeF(40,-1)) + .preferredSize(QSizeF(50,-1)) + .maxSize(QSizeF(500, -1)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(100, 401) + << (RectList() + << QRectF(0, 0, 50, 1) << QRectF(50, 0, 50, 1) + << QRectF(0, 1, 50,100) << QRectF(50, 1, 50,400) + ); + QTest::newRow("hfw-h408") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(40,40)) + .sizeHint(Qt::PreferredSize, QSizeF(50,400)) + .sizeHint(Qt::MaximumSize, QSizeF(500, 500)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(100, 408) + << (RectList() + << QRectF(0, 0, 50, 8) << QRectF(50, 0, 50, 8) + << QRectF(0, 8, 50,100) << QRectF(50, 8, 50,400) + ); + QTest::newRow("hfw-h410") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .minSize(QSizeF(40,40)) + .preferredSize(QSizeF(50,400)) + .maxSize(QSizeF(500, 500)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(100, 410) + << (RectList() + << QRectF(0, 0, 50,10) << QRectF(50, 0, 50,10) + << QRectF(0, 10, 50,100) << QRectF(50, 10, 50,400) + ); + + QTest::newRow("hfw-100x470") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(40,40)) + .sizeHint(Qt::PreferredSize, QSizeF(50,400)) + .sizeHint(Qt::MaximumSize, QSizeF(500,500)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(100, 470) + << (RectList() + << QRectF(0, 0, 50,70) << QRectF(50, 0, 50,70) + << QRectF(0, 70, 50,100) << QRectF(50, 70, 50,400) + ); + + // change layout width and verify + QTest::newRow("hfw-100x401") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .minSize(QSizeF(-1,-1)) + .preferredSize(QSizeF(-1,-1)) + .maxSize(QSizeF(-1, -1)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(100, 401) + << (RectList() + << QRectF( 0, 0, 50, 1) << QRectF( 50, 0, 50, 1) + << QRectF( 0, 1, 50, 100) << QRectF( 50, 1, 50, 400) + ); + + QTest::newRow("hfw-160x350") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(40,40)) + .sizeHint(Qt::PreferredSize, QSizeF(50,400)) + .sizeHint(Qt::MaximumSize, QSizeF(5000,5000)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(160, 350) + << (RectList() + << QRectF( 0, 0, 80, 100) << QRectF( 80, 0, 80, 100) + << QRectF( 0, 100, 80, 100) << QRectF( 80, 100, 80, 250) + ); + + QTest::newRow("hfw-160x300") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(40,40)) + .sizeHint(Qt::PreferredSize, QSizeF(50,400)) + .sizeHint(Qt::MaximumSize, QSizeF(5000, 5000)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(160, 300) + << (RectList() + << QRectF( 0, 0, 80, 50) << QRectF( 80, 0, 80, 50) + << QRectF( 0, 50, 80, 100) << QRectF( 80, 50, 80, 250) + ); + + QTest::newRow("hfw-20x40") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,10)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(1, 1)) + .sizeHint(Qt::PreferredSize, QSizeF(50, 50)) + .sizeHint(Qt::MaximumSize, QSizeF(100, 100)) + .dynamicConstraint(hfw3, Qt::Vertical) + ) + << QSizeF(20, 40) + << (RectList() + << QRectF(0, 0, 10, 20) << QRectF(10, 0, 10, 20) + << QRectF(0, 20, 10, 20) << QRectF(10, 20, 10, 10) + ); + + QTest::newRow("wfh-300x160") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(10,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(10,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(10,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(10,10)) + .sizeHint(Qt::PreferredSize, QSizeF(400,50)) + .sizeHint(Qt::MaximumSize, QSizeF(5000, 5000)) + .dynamicConstraint(wfh1, Qt::Horizontal) + ) + << QSizeF(300, 160) + << (RectList() + << QRectF( 0, 0, 50, 80) << QRectF( 50, 0, 100, 80) + << QRectF( 0, 80, 50, 80) << QRectF( 50, 80, 250, 80) + ); + + QTest::newRow("wfh-40x20") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + // Note, must be 10 in order to match stretching of wfh item + // below (the same stretch makes it easier to test) + .minSize(QSizeF(10,1)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(1,1)) + .sizeHint(Qt::PreferredSize, QSizeF(50,50)) + .sizeHint(Qt::MaximumSize, QSizeF(100, 100)) + .dynamicConstraint(wfh2, Qt::Horizontal) + ) + << QSizeF(40, 20) + << (RectList() + << QRectF(0, 0, 20, 10) << QRectF(20, 0, 20, 10) + << QRectF(0, 10, 20, 10) << QRectF(20, 10, 10, 10) + ); + + QTest::newRow("wfh-400x160") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(1,1)) + .sizeHint(Qt::PreferredSize, QSizeF(50,50)) + .sizeHint(Qt::MaximumSize, QSizeF(100, 100)) + .dynamicConstraint(wfh2, Qt::Horizontal) + ) + + << QSizeF(400, 160) + << (RectList() + << QRectF(0, 0, 100, 80) << QRectF(100, 0, 100, 80) + << QRectF(0, 80, 100, 80) << QRectF(100, 80, 80, 80) + ); + + QTest::newRow("wfh-160x100") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + // Note, preferred width must be 50 in order to match + // preferred width of wfh item below. + // (The same preferred size makes the stretch the same, and + // makes it easier to test) (The stretch algorithm is a + // blackbox) + .preferredSize(QSizeF(50,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(10,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(10,50)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(1,1)) + .sizeHint(Qt::PreferredSize, QSizeF(10,50)) + .sizeHint(Qt::MaximumSize, QSizeF(500, 500)) + .dynamicConstraint(wfh2, Qt::Horizontal) + ) + << QSizeF(160, 100) + << (RectList() + << QRectF(0, 0, 80, 50) << QRectF( 80, 0, 80, 50) + << QRectF(0, 50, 80, 50) << QRectF( 80, 50, 50, 50) + ); + + QTest::newRow("hfw-h470") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(40,40)) + .sizeHint(Qt::PreferredSize, QSizeF(50,400)) + .sizeHint(Qt::MaximumSize, QSizeF(500,500)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(100, 470) + << (RectList() + << QRectF(0, 0, 50,70) << QRectF(50, 0, 50,70) + << QRectF(0, 70, 50,100) << QRectF(50, 70, 50,400) + ); + + // change layout width and verify + QTest::newRow("hfw-w100") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(40,40)) + .sizeHint(Qt::PreferredSize, QSizeF(50,400)) + .sizeHint(Qt::MaximumSize, QSizeF(5000,5000)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(100, 401) + << (RectList() + << QRectF( 0, 0, 50, 1) << QRectF( 50, 0, 50, 1) + << QRectF( 0, 1, 50, 100) << QRectF( 50, 1, 50, 400) + ); + + QTest::newRow("hfw-w160") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(40,40)) + .sizeHint(Qt::PreferredSize, QSizeF(50,400)) + .sizeHint(Qt::MaximumSize, QSizeF(5000,5000)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(160, 350) + << (RectList() + << QRectF( 0, 0, 80, 100) << QRectF( 80, 0, 80, 100) + << QRectF( 0, 100, 80, 100) << QRectF( 80, 100, 80, 250) + ); + + QTest::newRow("hfw-w500") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(0,1) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,0) + .minSize(QSizeF(1,1)) + .preferredSize(QSizeF(50,10)) + .maxSize(QSizeF(100, 100)) + << ItemDesc(1,1) + .sizeHint(Qt::MinimumSize, QSizeF(40,40)) + .sizeHint(Qt::PreferredSize, QSizeF(50,400)) + .sizeHint(Qt::MaximumSize, QSizeF(5000,5000)) + .dynamicConstraint(hfw1, Qt::Vertical) + ) + << QSizeF(500, 200) + << (RectList() + << QRectF( 0, 0, 100, 100) << QRectF(100, 0, 100, 100) + << QRectF( 0, 100, 100, 100) << QRectF(100, 100, 400, 50) + ); + + QTest::newRow("hfw-alignment-defaults") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(100, 100)) + .maxSize(QSizeF(100, 100)) + .dynamicConstraint(hfw2, Qt::Vertical) + << ItemDesc(1,0) + .minSize(QSizeF(200, 200)) + .maxSize(QSizeF(200, 200)) + .dynamicConstraint(hfw2, Qt::Vertical) + << ItemDesc(2,0) + .minSize(QSizeF(300, 300)) + .maxSize(QSizeF(300, 300)) + ) + << QSizeF(300, 600) + << (RectList() + << QRectF(0, 0, 100, 100) + << QRectF(0, 100, 200, 200) + << QRectF(0, 300, 300, 300) + ); + + QTest::newRow("hfw-alignment2") << (ItemList() + << ItemDesc(0,0) + .minSize(QSizeF(100, 100)) + .maxSize(QSizeF(100, 100)) + .dynamicConstraint(hfw2, Qt::Vertical) + .alignment(Qt::AlignRight) + << ItemDesc(1,0) + .minSize(QSizeF(200, 200)) + .maxSize(QSizeF(200, 200)) + .dynamicConstraint(hfw2, Qt::Vertical) + .alignment(Qt::AlignHCenter) + << ItemDesc(2,0) + .minSize(QSizeF(300, 300)) + .maxSize(QSizeF(300, 300)) + ) + << QSizeF(300, 600) + << (RectList() + << QRectF(200, 0, 100, 100) + << QRectF( 50, 100, 200, 200) + << QRectF( 0, 300, 300, 300) + ); + +} + +void tst_QGraphicsGridLayout::geometries() +{ + QFETCH(ItemList, itemDescriptions); + QFETCH(QSizeF, newSize); + QFETCH(RectList, expectedGeometries); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + scene.addItem(widget); + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0.0); + widget->setContentsMargins(0, 0, 0, 0); + + int i; + for (i = 0; i < itemDescriptions.count(); ++i) { + ItemDesc desc = itemDescriptions.at(i); + RectWidget *item = new RectWidget(widget); + desc.apply(layout, item); + } + + QApplication::processEvents(); + + widget->show(); + view.resize(400,300); + view.show(); + if (newSize.isValid()) + widget->resize(newSize); + + QApplication::processEvents(); + for (i = 0; i < expectedGeometries.count(); ++i) { + QRectF itemRect = layout->itemAt(i)->geometry(); + QCOMPARE(itemRect, expectedGeometries.at(i)); + } + + delete widget; +} + +void tst_QGraphicsGridLayout::avoidRecursionInInsertItem() +{ + QGraphicsWidget window(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(&window); + QCOMPARE(layout->count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsGridLayout::addItem: cannot insert itself"); + layout->addItem(layout, 0, 0); + QCOMPARE(layout->count(), 0); +} + +void tst_QGraphicsGridLayout::styleInfoLeak() +{ + QGraphicsGridLayout grid; + grid.horizontalSpacing(); +} + +void tst_QGraphicsGridLayout::task236367_maxSizeHint() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + int w = 203; + int h = 204; + widget->resize(w, h); + QCOMPARE(widget->size(), QSizeF(w, h)); +} + +static QSizeF hfw(Qt::SizeHint /*which*/, const QSizeF &constraint) +{ + QSizeF result(constraint); + const qreal cw = constraint.width(); + const qreal ch = constraint.height(); + if (cw < 0 && ch < 0) { + return QSizeF(200, 100); + } else if (cw >= 0) { + result.setHeight(20000./cw); + } else if (cw == 0) { + result.setHeight(20000); + } else if (ch >= 0) { + result.setWidth(20000./ch); + } else if (ch == 0) { + result.setWidth(20000); + } + return result; +} + +static QSizeF wfh(Qt::SizeHint /*which*/, const QSizeF &constraint) +{ + QSizeF result(constraint); + const qreal ch = constraint.height(); + if (ch >= 0) { + result.setWidth(ch); + } + return result; +} + +bool qFuzzyCompare(const QSizeF &a, const QSizeF &b) +{ + return qFuzzyCompare(a.width(), b.width()) && qFuzzyCompare(a.height(), b.height()); +} + +void tst_QGraphicsGridLayout::heightForWidth() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + RectWidget *w00 = new RectWidget; + w00->setSizeHint(Qt::MinimumSize, QSizeF(1,1)); + w00->setSizeHint(Qt::PreferredSize, QSizeF(10,10)); + w00->setSizeHint(Qt::MaximumSize, QSizeF(100,100)); + layout->addItem(w00, 0, 0); + + RectWidget *w01 = new RectWidget; + w01->setSizeHint(Qt::MinimumSize, QSizeF(1,1)); + w01->setSizeHint(Qt::PreferredSize, QSizeF(10,10)); + w01->setSizeHint(Qt::MaximumSize, QSizeF(100,100)); + layout->addItem(w01, 0, 1); + + RectWidget *w10 = new RectWidget; + w10->setSizeHint(Qt::MinimumSize, QSizeF(1,1)); + w10->setSizeHint(Qt::PreferredSize, QSizeF(10,10)); + w10->setSizeHint(Qt::MaximumSize, QSizeF(100,100)); + layout->addItem(w10, 1, 0); + + RectWidget *w11 = new RectWidget; + w11->setSizeHint(Qt::MinimumSize, QSizeF(1,1)); + w11->setSizeHint(Qt::MaximumSize, QSizeF(30000,30000)); + w11->setConstraintFunction(hfw); + QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::Preferred); + sp.setHeightForWidth(true); + w11->setSizePolicy(sp); + layout->addItem(w11, 1, 1); + + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, -1)), QSizeF(2, 2)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, -1)), QSizeF(210, 110)); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(-1, -1)), QSizeF(30100, 30100)); + + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(2, -1)), QSizeF(2, 20001)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(2, -1)), QSizeF(2, 20010)); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(2, -1)), QSizeF(2, 20100)); + + // Since 20 is somewhere between "minimum width hint" (2) and + // "preferred width hint" (210), it will try to do distribution by + // stretching them with different factors. + // Since column 1 has a "preferred width" of 200 it means that + // column 1 will be a bit wider than column 0. Thus it will also be a bit + // shorter than 2001, (the expected height if all columns had width=10) + QSizeF sh = layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(20, -1)); + // column 1 cannot be wider than 19, which means that it must be taller than 20000/19~=1052 + QVERIFY(sh.height() < 2000 + 1 && sh.height() > 1052 + 1); + + sh = layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(20, -1)); + QVERIFY(sh.height() < 2000 + 10 && sh.height() > 1052 + 10); + + sh = layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(20, -1)); + QVERIFY(sh.height() < 2000 + 100 && sh.height() > 1052 + 100); + + // the height of the hfw widget is shorter than the one to the left, which is 100, so + // the total height of the last row is 100 (which leaves the layout height to be 200) + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(500, -1)), QSizeF(500, 100 + 100)); + +} + +void tst_QGraphicsGridLayout::widthForHeight() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + RectWidget *w00 = new RectWidget; + w00->setMinimumSize(1, 1); + w00->setPreferredSize(50, 50); + w00->setMaximumSize(100, 100); + + layout->addItem(w00, 0, 0); + + RectWidget *w01 = new RectWidget; + w01->setMinimumSize(1,1); + w01->setPreferredSize(50,50); + w01->setMaximumSize(100,100); + layout->addItem(w01, 0, 1); + + RectWidget *w10 = new RectWidget; + w10->setMinimumSize(1,1); + w10->setPreferredSize(50,50); + w10->setMaximumSize(100,100); + layout->addItem(w10, 1, 0); + + RectWidget *w11 = new RectWidget; + w11->setSizeHint(Qt::MinimumSize, QSizeF(1,1)); + w11->setSizeHint(Qt::PreferredSize, QSizeF(50,50)); + w11->setSizeHint(Qt::MaximumSize, QSizeF(30000,30000)); + + // This will make sure its always square. + w11->setConstraintFunction(wfh); + QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::Preferred); + sp.setWidthForHeight(true); + w11->setSizePolicy(sp); + layout->addItem(w11, 1, 1); + + /* + | 1, 50, 100 | 1, 50, 100 | + -----+--------------+--------------+ + 1| | | + 50| | | + 100| | | + -----|--------------+--------------+ + 1| | | + 50| | WFH | + 100| | | + -----------------------------------+ + */ + + + QSizeF prefSize = layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, -1)); + QCOMPARE(prefSize, QSizeF(50+50, 50+50)); + + // wfh(1): = 1 + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, 2)), QSizeF(1 + 1, 2)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, 2)), QSizeF(50 + 50, 2)); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(-1, 2)), QSizeF(100 + 100, 2)); + + // wfh(40) = 40 + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, 80)), QSizeF(1 + 40, 80)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, 80)), QSizeF(50 + 50, 80)); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(-1, 80)), QSizeF(100 + 100, 80)); + + // wfh(80) = 80 + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, 160)), QSizeF(1 + 80, 160)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, 160)), QSizeF(50 + 80, 160)); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(-1, 160)), QSizeF(100 + 100, 160)); + + // wfh(200) = 200 + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, 300)), QSizeF(1 + 200, 300)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, 300)), QSizeF(50 + 200, 300)); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(-1, 300)), QSizeF(100 + 200, 300)); +} + +void tst_QGraphicsGridLayout::heightForWidthWithSpanning() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + RectWidget *w = new RectWidget; + w->setSizeHint(Qt::MinimumSize, QSizeF(1,1)); + w->setSizeHint(Qt::MaximumSize, QSizeF(30000,30000)); + w->setConstraintFunction(hfw); + QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::Preferred); + sp.setHeightForWidth(true); + w->setSizePolicy(sp); + layout->addItem(w, 0,0,2,2); + + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, -1)), QSizeF(1, 1)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, -1)), QSizeF(200, 100)); + QEXPECT_FAIL("", "Due to an old bug this wrongly returns QWIDGETSIZE_MAX", Continue); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(-1, -1)), QSizeF(30000, 30000)); + + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(200, -1)), QSizeF(200, 100)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(200, -1)), QSizeF(200, 100)); + QEXPECT_FAIL("", "Due to an old bug this wrongly returns QWIDGETSIZE_MAX", Continue); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(200, -1)), QSizeF(200, 100)); + + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(2, -1)), QSizeF(2, 10000)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(2, -1)), QSizeF(2, 10000)); + QEXPECT_FAIL("", "Due to an old bug this wrongly returns QWIDGETSIZE_MAX", Continue); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(2, -1)), QSizeF(2, 10000)); + + QCOMPARE(layout->effectiveSizeHint(Qt::MinimumSize, QSizeF(200, -1)), QSizeF(200, 100)); + QCOMPARE(layout->effectiveSizeHint(Qt::PreferredSize, QSizeF(200, -1)), QSizeF(200, 100)); + QEXPECT_FAIL("", "Due to an old bug this wrongly returns QWIDGETSIZE_MAX", Continue); + QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(200, -1)), QSizeF(200, 10000)); +} + +Q_DECLARE_METATYPE(QSizePolicy::Policy) +void tst_QGraphicsGridLayout::spanningItem2x2_data() +{ + QTest::addColumn<QSizePolicy::Policy>("sizePolicy"); + QTest::addColumn<int>("itemHeight"); + QTest::addColumn<int>("expectedHeight"); + + QTest::newRow("A larger spanning item with 2 widgets with fixed policy") << QSizePolicy::Fixed << 39 << 80; + QTest::newRow("A larger spanning item with 2 widgets with preferred policy") << QSizePolicy::Preferred << 39 << 80; + QTest::newRow("An equally-sized spanning item with 2 widgets with fixed policy") << QSizePolicy::Fixed << 40 << 80; + QTest::newRow("An equally-sized spanning item with 2 widgets with preferred policy") << QSizePolicy::Preferred << 40 << 80; + QTest::newRow("A smaller spanning item with 2 widgets with fixed policy") << QSizePolicy::Fixed << 41 << 82; + QTest::newRow("A smaller spanning item with 2 widgets with preferred policy") << QSizePolicy::Preferred << 41 << 82; +} + +void tst_QGraphicsGridLayout::spanningItem2x2() +{ + QFETCH(QSizePolicy::Policy, sizePolicy); + QFETCH(int, itemHeight); + QFETCH(int, expectedHeight); + QGraphicsWidget *form = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(form); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QGraphicsWidget *w1 = new QGraphicsWidget; + w1->setMinimumSize(80,80); + w1->setMaximumSize(80,80); + + QGraphicsWidget *w2 = new QGraphicsWidget; + w2->setMinimumSize(80,itemHeight); + w2->setPreferredSize(80,itemHeight); + w2->setSizePolicy(QSizePolicy::Fixed, sizePolicy); + + QGraphicsWidget *w3 = new QGraphicsWidget; + w3->setMinimumSize(80,itemHeight); + w3->setPreferredSize(80,itemHeight); + w3->setSizePolicy(QSizePolicy::Fixed, sizePolicy); + + layout->addItem(w1, 0, 0, 2, 1); + layout->addItem(w2, 0, 1); + layout->addItem(w3, 1, 1); + + QCOMPARE(layout->minimumSize(), QSizeF(160,expectedHeight)); + if(sizePolicy == QSizePolicy::Fixed) + QCOMPARE(layout->maximumSize(), QSizeF(160,expectedHeight)); + else + QCOMPARE(layout->maximumSize(), QSizeF(160,QWIDGETSIZE_MAX)); +} + +void tst_QGraphicsGridLayout::spanningItem2x3_data() +{ + QTest::addColumn<bool>("w1_fixed"); + QTest::addColumn<bool>("w2_fixed"); + QTest::addColumn<bool>("w3_fixed"); + QTest::addColumn<bool>("w4_fixed"); + QTest::addColumn<bool>("w5_fixed"); + + for(int w1 = 0; w1 < 2; w1++) + for(int w2 = 0; w2 < 2; w2++) + for(int w3 = 0; w3 < 2; w3++) + for(int w4 = 0; w4 < 2; w4++) + for(int w5 = 0; w5 < 2; w5++) { + QString description = QString("Fixed sizes:") + (w1?" w1":"") + (w2?" w2":"") + (w3?" w3":"") + (w4?" w4":"") + (w5?" w5":""); + QTest::newRow(description.toLatin1()) << (bool)w1 << (bool)w2 << (bool)w3 << (bool)w4 << (bool)w5; + } +} + +void tst_QGraphicsGridLayout::spanningItem2x3() +{ + QFETCH(bool, w1_fixed); + QFETCH(bool, w2_fixed); + QFETCH(bool, w3_fixed); + QFETCH(bool, w4_fixed); + QFETCH(bool, w5_fixed); + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QGraphicsWidget *w1 = new QGraphicsWidget; + w1->setMinimumSize(80,80); + w1->setMaximumSize(80,80); + if (w1_fixed) + w1->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + + QGraphicsWidget *w2 = new QGraphicsWidget; + w2->setMinimumSize(80,48); + w2->setPreferredSize(80,48); + if (w2_fixed) + w2->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + + QGraphicsWidget *w3 = new QGraphicsWidget; + w3->setMinimumSize(80,30); + w3->setPreferredSize(80,30); + if (w3_fixed) + w3->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + + QGraphicsWidget *w4 = new QGraphicsWidget; + w4->setMinimumSize(80,30); + w4->setMaximumSize(80,30); + if (w4_fixed) + w4->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + + QGraphicsWidget *w5 = new QGraphicsWidget; + w5->setMinimumSize(40,24); + w5->setMaximumSize(40,24); + if (w5_fixed) + w5->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + + layout->addItem(w1, 0, 0, 2, 1); + layout->addItem(w2, 0, 1); + layout->addItem(w3, 1, 1); + layout->addItem(w4, 0, 2); + layout->addItem(w5, 1, 2); + + QCOMPARE(layout->minimumSize(), QSizeF(240,80)); + // Only w2 and w3 grow vertically, so when they have a fixed vertical size policy, + // the whole layout cannot grow vertically. + if (w2_fixed && w3_fixed) + QCOMPARE(layout->maximumSize(), QSizeF(QWIDGETSIZE_MAX,80)); + else + QCOMPARE(layout->maximumSize(), QSizeF(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX)); +} + +void tst_QGraphicsGridLayout::spanningItem() +{ + QGraphicsWidget *form = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(form); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QGraphicsWidget *w1 = new QGraphicsWidget; + w1->setMinimumSize(80,80); + w1->setMaximumSize(80,80); + + QGraphicsWidget *w2 = new QGraphicsWidget; + w2->setMinimumSize(80,38); + w2->setPreferredSize(80,38); + w2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + QGraphicsWidget *w3 = new QGraphicsWidget; + w3->setMinimumSize(80,38); + w3->setPreferredSize(80,38); + w3->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + layout->addItem(w1, 0, 0, 2, 1); + layout->addItem(w2, 0, 1); + layout->addItem(w3, 1, 1); + + QCOMPARE(layout->minimumSize(), QSizeF(160,80)); + QCOMPARE(layout->maximumSize(), QSizeF(160,80)); +} + +void tst_QGraphicsGridLayout::stretchAndHeightForWidth() +{ + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout; + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + RectWidget *w1 = new RectWidget; + w1->setSizeHint(Qt::MinimumSize, QSizeF(10, 10)); + w1->setSizeHint(Qt::PreferredSize, QSizeF(100, 100)); + w1->setSizeHint(Qt::MaximumSize, QSizeF(500, 500)); + layout->addItem(w1, 0,0,1,1); + + RectWidget *w2 = new RectWidget; + w2->setSizeHint(Qt::MinimumSize, QSizeF(10, 10)); + w2->setSizeHint(Qt::PreferredSize, QSizeF(100, 100)); + w2->setSizeHint(Qt::MaximumSize, QSizeF(500, 500)); + layout->addItem(w2, 0,1,1,1); + layout->setColumnStretchFactor(1, 2); + + QApplication::sendPostedEvents(); + QGraphicsScene scene; + QGraphicsView *view = new QGraphicsView(&scene); + + scene.addItem(widget); + + view->show(); + + widget->resize(500, 100); + // w1 should stay at its preferred size + QCOMPARE(w1->geometry(), QRectF(0, 0, 100, 100)); + QCOMPARE(w2->geometry(), QRectF(100, 0, 400, 100)); + + + // only w1 has hfw + w1->setConstraintFunction(hfw); + QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::Preferred); + sp.setHeightForWidth(true); + w1->setSizePolicy(sp); + QApplication::sendPostedEvents(); + + QCOMPARE(w1->geometry(), QRectF(0, 0, 100, 200)); + QCOMPARE(w2->geometry(), QRectF(100, 0, 400, 200)); + + // only w2 has hfw + w2->setConstraintFunction(hfw); + w2->setSizePolicy(sp); + + w1->setConstraintFunction(0); + sp.setHeightForWidth(false); + w1->setSizePolicy(sp); + QApplication::sendPostedEvents(); + + QCOMPARE(w1->geometry(), QRectF(0, 0, 100, 100)); + QCOMPARE(w2->geometry(), QRectF(100, 0, 400, 50)); + +} + +void tst_QGraphicsGridLayout::testDefaultAlignment() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsGridLayout *layout = new QGraphicsGridLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QGraphicsWidget *w = new QGraphicsWidget; + w->setMinimumSize(50,50); + w->setMaximumSize(50,50); + layout->addItem(w,0,0); + + //Default alignment should be to the top-left + + //First, check by forcing the layout to be bigger + layout->setMinimumSize(100,100); + layout->activate(); + QCOMPARE(layout->geometry(), QRectF(0,0,100,100)); + QCOMPARE(w->geometry(), QRectF(0,0,50,50)); + layout->setMinimumSize(-1,-1); + + //Second, check by forcing the column and row to be bigger instead + layout->setColumnMinimumWidth(0, 100); + layout->setRowMinimumHeight(0, 100); + layout->activate(); + QCOMPARE(layout->geometry(), QRectF(0,0,100,100)); + QCOMPARE(w->geometry(), QRectF(0,0,50,50)); + layout->setMinimumSize(-1,-1); + layout->setColumnMinimumWidth(0, 0); + layout->setRowMinimumHeight(0, 0); + + + //Third, check by adding a larger item in the column + QGraphicsWidget *w2 = new QGraphicsWidget; + w2->setMinimumSize(100,100); + w2->setMaximumSize(100,100); + layout->addItem(w2,1,0); + layout->activate(); + QCOMPARE(layout->geometry(), QRectF(0,0,100,150)); + QCOMPARE(w->geometry(), QRectF(0,0,50,50)); + QCOMPARE(w2->geometry(), QRectF(0,50,100,100)); +} +QTEST_MAIN(tst_QGraphicsGridLayout) +#include "tst_qgraphicsgridlayout.moc" + diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/.gitignore b/tests/auto/widgets/graphicsview/qgraphicsitem/.gitignore new file mode 100644 index 0000000000..b766388e3e --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/.gitignore @@ -0,0 +1 @@ +tst_qgraphicsitem diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/qgraphicsitem.pro b/tests/auto/widgets/graphicsview/qgraphicsitem/qgraphicsitem.pro new file mode 100644 index 0000000000..51a4426680 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/qgraphicsitem.pro @@ -0,0 +1,9 @@ +load(qttest_p4) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qgraphicsitem.cpp +DEFINES += QT_NO_CAST_TO_ASCII + +win32:!wince*: LIBS += -lUser32 + +contains(QT_CONFIG,xcb):qpa:CONFIG+=insignificant_test # QTBUG-20756 crashes on qpa, xcb diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp new file mode 100644 index 0000000000..8ac1f6b5c5 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp @@ -0,0 +1,11402 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <private/qgraphicsitem_p.h> +#include <private/qgraphicsview_p.h> +#include <private/qgraphicsscene_p.h> +#include <QStyleOptionGraphicsItem> +#include <QAbstractTextDocumentLayout> +#include <QBitmap> +#include <QCursor> +#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 <QInputContext> +#include <QPushButton> +#include <QLineEdit> +#include <QGraphicsLinearLayout> +#include <float.h> + +//TESTED_CLASS= +//TESTED_FILES= + +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<QRectF>) +Q_DECLARE_METATYPE(QPainterPath) +Q_DECLARE_METATYPE(QPointF) +Q_DECLARE_METATYPE(QRectF) + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +#include <windows.h> +#define Q_CHECK_PAINTEVENTS \ + if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \ + QSKIP("The Graphics View doesn't get the paint events", SkipSingle); +#else +#define Q_CHECK_PAINTEVENTS +#endif + +#if defined(Q_WS_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 + +#include "../../../platformquirks.h" + +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); + QApplication::sendEvent(scene, &event); +} + +static void sendMouseMove(QGraphicsScene *scene, const QPointF &point, + Qt::MouseButton button = Qt::NoButton, Qt::MouseButtons /* buttons */ = 0) +{ + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setScenePos(point); + event.setButton(button); + event.setButtons(button); + QApplication::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); + QApplication::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); + QApplication::sendEvent(scene, &keyEvent); +} + +static void sendKeyRelease(QGraphicsScene *scene, Qt::Key key) +{ + QKeyEvent keyEvent(QEvent::KeyRelease, key, Qt::NoModifier); + QApplication::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) + : _count(0), spied(type) + { + watched->installEventFilter(this); + } + + EventSpy(QGraphicsScene *scene, QGraphicsItem *watched, QEvent::Type type) + : _count(0), spied(type) + { + scene->addItem(this); + watched->installSceneEventFilter(this); + } + + int count() const { return _count; } + +protected: + bool eventFilter(QObject *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + int _count; + QEvent::Type spied; +}; + +class 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) + { + Q_UNUSED(watched); + ++counts[event->type()]; + return false; + } + + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + Q_UNUSED(watched); + ++counts[event->type()]; + return false; + } +}; + +class EventTester : public QGraphicsItem +{ +public: + EventTester(QGraphicsItem *parent = 0) : QGraphicsItem(parent), repaints(0) + { br = QRectF(-10, -10, 20, 20); } + + void setGeometry(const QRectF &rect) + { + prepareGeometryChange(); + br = rect; + update(); + } + + QRectF boundingRect() const + { return br; } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *o, QWidget *) + { + hints = painter->renderHints(); + painter->setBrush(brush); + painter->drawRect(boundingRect()); + lastExposedRect = o->exposedRect; + ++repaints; + } + + bool sceneEvent(QEvent *event) + { + events << event->type(); + return QGraphicsItem::sceneEvent(event); + } + + void reset() + { + events.clear(); + hints = QPainter::RenderHints(0); + repaints = 0; + lastExposedRect = QRectF(); + } + + QList<QEvent::Type> events; + QPainter::RenderHints hints; + int repaints; + QRectF br; + QRectF lastExposedRect; + QBrush brush; +}; + +class MyGraphicsView : public QGraphicsView +{ +public: + int repaints; + QRegion paintedRegion; + MyGraphicsView(QGraphicsScene *scene, QWidget *parent=0) : QGraphicsView(scene,parent), repaints(0) {} + void paintEvent(QPaintEvent *e) + { + paintedRegion += e->region(); + ++repaints; + QGraphicsView::paintEvent(e); + } + void reset() { repaints = 0; paintedRegion = QRegion(); } +}; + +class tst_QGraphicsItem : public QObject +{ + Q_OBJECT + +public slots: + void init(); + +private slots: + void construction(); + void constructionWithParent(); + void destruction(); + void deleteChildItem(); + void scene(); + void parentItem(); + void setParentItem(); + void children(); + void flags(); + void inputMethodHints(); + void toolTip(); + void visible(); + void isVisibleTo(); + void explicitlyVisible(); + void enabled(); + void explicitlyEnabled(); + void selected(); + void selected2(); + void selected_group(); + void selected_textItem(); + void selected_multi(); + void acceptedMouseButtons(); + void acceptsHoverEvents(); + void childAcceptsHoverEvents(); + void hasFocus(); + void pos(); + void scenePos(); + void matrix(); + void sceneMatrix(); + void setMatrix(); + 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(); + void cursor(); + //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 ancestorFlags(); + void untransformable(); + void contextMenuEventPropagation(); + 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 panelWithFocusItem(); + void activate(); + void setActivePanelOnInactiveScene(); + void activationOnShowHide(); + void moveWhileDeleting(); + void ensureDirtySceneTransform(); + void focusScope(); + void focusScope2(); + void stackBefore(); + void sceneModality(); + void panelModality(); + void mixedModality(); + void modality_hover(); + void modality_mouseGrabber(); + void modality_clickFocus(); + void modality_keyEvents(); + void itemIsInFront(); + void scenePosChange(); + void updateMicroFocus(); + void textItem_shortcuts(); + void scroll(); + void focusHandling_data(); + void focusHandling(); + void touchEventPropagation_data(); + void touchEventPropagation(); + void deviceCoordinateCache_simpleRotations(); + + // 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(); + +private: + QList<QGraphicsItem *> paintedItems; +}; + +void tst_QGraphicsItem::init() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +void tst_QGraphicsItem::construction() +{ + for (int i = 0; i < 7; ++i) { + QGraphicsItem *item; + switch (i) { + case 0: + item = new QGraphicsEllipseItem; + QCOMPARE(int(item->type()), int(QGraphicsEllipseItem::Type)); + QCOMPARE(qgraphicsitem_cast<QGraphicsEllipseItem *>(item), (QGraphicsEllipseItem *)item); + QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 1: + item = new QGraphicsLineItem; + QCOMPARE(int(item->type()), int(QGraphicsLineItem::Type)); + QCOMPARE(qgraphicsitem_cast<QGraphicsLineItem *>(item), (QGraphicsLineItem *)item); + QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 2: + item = new QGraphicsPathItem; + QCOMPARE(int(item->type()), int(QGraphicsPathItem::Type)); + QCOMPARE(qgraphicsitem_cast<QGraphicsPathItem *>(item), (QGraphicsPathItem *)item); + QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 3: + item = new QGraphicsPixmapItem; + QCOMPARE(int(item->type()), int(QGraphicsPixmapItem::Type)); + QCOMPARE(qgraphicsitem_cast<QGraphicsPixmapItem *>(item), (QGraphicsPixmapItem *)item); + QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 4: + item = new QGraphicsPolygonItem; + QCOMPARE(int(item->type()), int(QGraphicsPolygonItem::Type)); + QCOMPARE(qgraphicsitem_cast<QGraphicsPolygonItem *>(item), (QGraphicsPolygonItem *)item); + QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 5: + item = new QGraphicsRectItem; + QCOMPARE(int(item->type()), int(QGraphicsRectItem::Type)); + QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)item); + QCOMPARE(qgraphicsitem_cast<QGraphicsLineItem *>(item), (QGraphicsLineItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 6: + item = new QGraphicsTextItem; + QCOMPARE(int(item->type()), int(QGraphicsTextItem::Type)); + QCOMPARE(qgraphicsitem_cast<QGraphicsTextItem *>(item), (QGraphicsTextItem *)item); + QCOMPARE(qgraphicsitem_cast<QGraphicsRectItem *>(item), (QGraphicsRectItem *)0); + // This is the only item that uses an extended style option. + QCOMPARE(item->flags(), QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemUsesExtendedStyleOption)); + break; + default: + qFatal("You broke the logic, please fix!"); + break; + } + + QCOMPARE(item->scene(), (QGraphicsScene *)0); + QCOMPARE(item->parentItem(), (QGraphicsItem *)0); + QVERIFY(item->children().isEmpty()); + QVERIFY(item->isVisible()); + QVERIFY(item->isEnabled()); + QVERIFY(!item->isSelected()); + QCOMPARE(item->acceptedMouseButtons(), Qt::MouseButtons(0x1f)); + if (item->type() == QGraphicsTextItem::Type) + QVERIFY(item->acceptsHoverEvents()); + else + QVERIFY(!item->acceptsHoverEvents()); + QVERIFY(!item->hasFocus()); + QCOMPARE(item->pos(), QPointF()); + QCOMPARE(item->matrix(), QMatrix()); + QCOMPARE(item->sceneMatrix(), QMatrix()); + QCOMPARE(item->zValue(), qreal(0)); + QCOMPARE(item->sceneBoundingRect(), QRectF()); + QCOMPARE(item->shape(), QPainterPath()); + QVERIFY(!item->contains(QPointF(0, 0))); + QVERIFY(!item->collidesWithItem(0)); + QVERIFY(item->collidesWithItem(item)); + QVERIFY(!item->collidesWithPath(QPainterPath())); + QVERIFY(!item->isAncestorOf(0)); + QVERIFY(!item->isAncestorOf(item)); + QCOMPARE(item->data(0), QVariant()); + delete item; + } +} + +class BoundingRectItem : public QGraphicsRectItem +{ +public: + BoundingRectItem(QGraphicsItem *parent = 0) + : QGraphicsRectItem(0, 0, parent ? 200 : 100, parent ? 200 : 100, + parent) + {} + + QRectF boundingRect() const + { + QRectF tmp = QGraphicsRectItem::boundingRect(); + foreach (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->children(), QList<QGraphicsItem *>() << item2); + QCOMPARE(item1->boundingRect(), QRectF(0, 0, 200, 200)); + + item2->setParentItem(item0); + QCOMPARE(item0->children(), QList<QGraphicsItem *>() << 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->children().size(), 1); + delete child; + QCOMPARE(parent->children().size(), 0); + delete parent; + QCOMPARE(itemDeleted, 2); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + QCOMPARE(child->parentItem(), (QGraphicsItem *)0); + 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->children().size(), 1); + delete child; + QCOMPARE(parent->children().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(), (QGraphicsScene *)0); + delete parent; + QCOMPARE(itemDeleted, 5); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + QCOMPARE(child->scene(), (QGraphicsScene *)0); + QCOMPARE(parent->scene(), (QGraphicsScene *)0); + scene.addItem(parent); + QCOMPARE(child->scene(), &scene); + scene.removeItem(child); + QCOMPARE(child->scene(), (QGraphicsScene *)0); + QCOMPARE(parent->scene(), &scene); + QCOMPARE(child->parentItem(), (QGraphicsItem *)0); + QVERIFY(parent->children().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 = 0; + 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(0); + delete child2; +} + +void tst_QGraphicsItem::scene() +{ + QGraphicsRectItem *item = new QGraphicsRectItem; + QCOMPARE(item->scene(), (QGraphicsScene *)0); + + QGraphicsScene scene; + scene.addItem(item); + QCOMPARE(item->scene(), (QGraphicsScene *)&scene); + + QGraphicsScene scene2; + scene2.addItem(item); + QCOMPARE(item->scene(), (QGraphicsScene *)&scene2); + + scene2.removeItem(item); + QCOMPARE(item->scene(), (QGraphicsScene *)0); + + delete item; +} + +void tst_QGraphicsItem::parentItem() +{ + QGraphicsRectItem item; + QCOMPARE(item.parentItem(), (QGraphicsItem *)0); + + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(), &item); + QCOMPARE(item2->parentItem(), (QGraphicsItem *)&item); + item2->setParentItem(&item); + QCOMPARE(item2->parentItem(), (QGraphicsItem *)&item); + item2->setParentItem(0); + QCOMPARE(item2->parentItem(), (QGraphicsItem *)0); + + delete item2; +} + +void tst_QGraphicsItem::setParentItem() +{ + QGraphicsScene scene; + QGraphicsItem *item = scene.addRect(QRectF(0, 0, 10, 10)); + QCOMPARE(item->scene(), &scene); + + QGraphicsRectItem *child = new QGraphicsRectItem; + QCOMPARE(child->scene(), (QGraphicsScene *)0); + + // This implicitly adds the item to the parent's scene + child->setParentItem(item); + QCOMPARE(child->scene(), &scene); + + // This just makes it a toplevel + child->setParentItem(0); + QCOMPARE(child->scene(), &scene); + + // Add the child back to the parent, then remove the parent from the scene + child->setParentItem(item); + scene.removeItem(item); + QCOMPARE(child->scene(), (QGraphicsScene *)0); +} + +void tst_QGraphicsItem::children() +{ + QGraphicsRectItem item; + QVERIFY(item.children().isEmpty()); + + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(), &item); + QCOMPARE(item.children().size(), 1); + QCOMPARE(item.children().first(), (QGraphicsItem *)item2); + QVERIFY(item2->children().isEmpty()); + + delete item2; + QVERIFY(item.children().isEmpty()); +} + +void tst_QGraphicsItem::flags() +{ + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20)); + QCOMPARE(item->flags(), 0); + + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::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); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); // mouse grabber is reset + + QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove); + event2.setScenePos(QPointF(10, 10)); + event2.setButton(Qt::LeftButton); + event2.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event2); + QCOMPARE(item->pos(), QPointF()); + + QGraphicsSceneMouseEvent event3(QEvent::GraphicsSceneMouseRelease); + event3.setScenePos(QPointF(10, 10)); + event3.setButtons(0); + QApplication::sendEvent(&scene, &event3); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + item->setFlag(QGraphicsItem::ItemIsMovable, true); + QGraphicsSceneMouseEvent event4(QEvent::GraphicsSceneMousePress); + event4.setScenePos(QPointF(0, 0)); + event4.setButton(Qt::LeftButton); + event4.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + QGraphicsSceneMouseEvent event5(QEvent::GraphicsSceneMouseMove); + event5.setScenePos(QPointF(10, 10)); + event5.setButton(Qt::LeftButton); + event5.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event5); + QCOMPARE(item->pos(), QPointF(10, 10)); + } + { + QGraphicsItem* clippingParent = new QGraphicsRectItem; + clippingParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); + + QGraphicsItem* nonClippingParent = new QGraphicsRectItem; + nonClippingParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); + + QGraphicsItem* child = new QGraphicsRectItem(nonClippingParent); + QVERIFY(!child->isClipped()); + + child->setParentItem(clippingParent); + QVERIFY(child->isClipped()); + + child->setParentItem(nonClippingParent); + QVERIFY(!child->isClipped()); + } +} + +class ImhTester : public QGraphicsItem +{ + QRectF boundingRect() const { return QRectF(); } + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) {} +}; + +void tst_QGraphicsItem::inputMethodHints() +{ + 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); + QApplication::setActiveWindow(&view); + view.show(); + QTest::qWaitForWindowShown(&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() +{ + 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.setFixedSize(200, 200); + view.show(); + QTest::qWait(250); + { + QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(), + view.viewport()->mapToGlobal(view.viewport()->rect().topLeft())); + QApplication::sendEvent(view.viewport(), &helpEvent); + QTest::qWait(250); + + bool foundView = false; + bool foundTipLabel = false; + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + 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())); + QApplication::sendEvent(view.viewport(), &helpEvent); + QTest::qWait(250); + + bool foundView = false; + bool foundTipLabel = false; + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + 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())); + QApplication::sendEvent(view.viewport(), &helpEvent); + QTest::qWait(1000); + + bool foundView = false; + bool foundTipLabel = false; + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + 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); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(item); + QVERIFY(item->isVisible()); + QCOMPARE(scene.itemAt(0, 0), item); + item->setVisible(false); + QCOMPARE(scene.itemAt(0, 0), (QGraphicsItem *)0); + item->setVisible(true); + QCOMPARE(scene.itemAt(0, 0), item); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), item); + item->setVisible(false); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + item->setVisible(true); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + 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(0)); + QVERIFY(child->isVisible()); + QVERIFY(child->isVisibleTo(child)); + QVERIFY(child->isVisibleTo(parent)); + QVERIFY(child->isVisibleTo(0)); + QVERIFY(parent->isVisible()); + QVERIFY(parent->isVisibleTo(parent)); + QVERIFY(parent->isVisibleTo(0)); + 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(0)); + QVERIFY(!child->isVisible()); + QVERIFY(child->isVisibleTo(child)); + QVERIFY(child->isVisibleTo(parent)); + QVERIFY(!child->isVisibleTo(0)); + QVERIFY(!parent->isVisible()); + QVERIFY(!parent->isVisibleTo(parent)); + QVERIFY(!parent->isVisibleTo(0)); + 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(0)); + QVERIFY(!child->isVisible()); + QVERIFY(!child->isVisibleTo(child)); + QVERIFY(!child->isVisibleTo(parent)); + QVERIFY(!child->isVisibleTo(0)); + QVERIFY(parent->isVisible()); + QVERIFY(parent->isVisibleTo(parent)); + QVERIFY(parent->isVisibleTo(0)); + 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(0)); + QVERIFY(child->isVisible()); + QVERIFY(child->isVisibleTo(child)); + QVERIFY(child->isVisibleTo(parent)); + QVERIFY(child->isVisibleTo(0)); + QVERIFY(parent->isVisible()); + QVERIFY(parent->isVisibleTo(parent)); + QVERIFY(parent->isVisibleTo(0)); + 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(0); + QVERIFY(child->isVisible()); + + // Reparent an explicitly hidden child to root. + child->hide(); + child->setParentItem(parent2); + parent2->show(); + QVERIFY(!child->isVisible()); + child->setParentItem(0); + 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); + QApplication::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)); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + item->setEnabled(true); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + item->setEnabled(false); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); +} + +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(0); + QVERIFY(child->isEnabled()); + + // Reparent an explicitly hidden child to root. + child->setEnabled(false); + child->setParentItem(parent2); + parent2->setEnabled(true); + QVERIFY(!child->isEnabled()); + child->setParentItem(0); + 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) + { + 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.last(), true); + QVERIFY(item->isSelected()); + item->setSelected(false); + QCOMPARE(item->values.size(), 2); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + item->setSelected(true); + QCOMPARE(item->values.size(), 3); + item->setEnabled(false); + QCOMPARE(item->values.size(), 4); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + item->setEnabled(true); + QCOMPARE(item->values.size(), 4); + item->setSelected(true); + QCOMPARE(item->values.size(), 5); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + item->setVisible(false); + QCOMPARE(item->values.size(), 6); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + item->setVisible(true); + QCOMPARE(item->values.size(), 6); + item->setSelected(true); + QCOMPARE(item->values.size(), 7); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + + QGraphicsScene scene(-100, -100, 200, 200); + scene.addItem(item); + QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>() << item); + item->setSelected(false); + QVERIFY(scene.selectedItems().isEmpty()); + item->setSelected(true); + QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>() << item); + item->setSelected(false); + QVERIFY(scene.selectedItems().isEmpty()); + + // Interactive selection + QGraphicsView view(&scene); + view.setFixedSize(250, 250); + view.show(); + + QTest::qWaitForWindowShown(&view); + qApp->processEvents(); + qApp->processEvents(); + + scene.clearSelection(); + QCOMPARE(item->values.size(), 10); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + + // Click inside and check that it's selected + QTest::mouseMove(view.viewport()); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos())); + QCOMPARE(item->values.size(), 11); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + + // Click outside and check that it's not selected + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos() + QPointF(item->boundingRect().width(), item->boundingRect().height()))); + QCOMPARE(item->values.size(), 12); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + + 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, 0, view.mapFromScene(item->scenePos())); + QCOMPARE(item->values.size(), 13); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + + // Click inside item2 and check that it's selected, and item is not + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item2->scenePos())); + QCOMPARE(item->values.size(), 14); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + QCOMPARE(item2->values.size(), 1); + QCOMPARE(item2->values.last(), 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); + QApplication::sendEvent(&scene, &mousePress); + QVERIFY(mousePress.isAccepted()); + } + { + QGraphicsSceneMouseEvent mouseMove(QEvent::GraphicsSceneMouseMove); + mouseMove.setScenePos(QPointF(60, 60)); + mouseMove.setButton(Qt::LeftButton); + mouseMove.setButtons(Qt::LeftButton); + QApplication::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(QList<QGraphicsItem *>() << item1 << item2); + QCOMPARE(group->scene(), &scene); + group->setFlag(QGraphicsItem::ItemIsSelectable); + foreach (QGraphicsItem *item, scene.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()); + foreach (QGraphicsItem *item, scene.items()) + QVERIFY(item->isSelected()); + group->setSelected(false); + QVERIFY(!group->isSelected()); + foreach (QGraphicsItem *item, scene.items()) + QVERIFY(!item->isSelected()); + leaf->setSelected(true); + foreach (QGraphicsItem *item, scene.items()) + QVERIFY(item->isSelected()); + leaf->setSelected(false); + foreach (QGraphicsItem *item, scene.items()) + QVERIFY(!item->isSelected()); + + 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.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(20); + + QTRY_VERIFY(!text->isSelected()); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(text->mapToScene(0, 0))); + QTRY_VERIFY(text->isSelected()); + + text->setSelected(false); + text->setTextInteractionFlags(Qt::TextEditorInteraction); + + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, + 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.show(); + view.fitInView(scene.sceneRect()); + qApp->processEvents(); + + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Start clicking + QTest::qWait(200); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Click on item2 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item2->scenePos())); + QTest::qWait(20); + QVERIFY(item2->isSelected()); + QVERIFY(!item1->isSelected()); + + // Ctrl-click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item2->isSelected()); + QVERIFY(item1->isSelected()); + + // Ctrl-click on item1 again + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item2->isSelected()); + QVERIFY(!item1->isSelected()); + + // Ctrl-click on item2 + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item2->scenePos())); + QTest::qWait(20); + QVERIFY(!item2->isSelected()); + QVERIFY(!item1->isSelected()); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Click on scene + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(0, 0)); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Ctrl-click on scene + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(0, 0)); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Press on item2 + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item2->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(item2->isSelected()); + + // Release on item2 + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item2->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(item2->isSelected()); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Ctrl-click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Ctrl-press on item1 + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + { + // Ctrl-move on item1 + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(item1->scenePos()) + QPoint(1, 0), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); + QApplication::sendEvent(view.viewport(), &event); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + } + + // Release on item1 + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + 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())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + { + // Ctrl-move on item1 + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(item1->scenePos()) + QPoint(1, 0), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); + QApplication::sendEvent(view.viewport(), &event); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + } + + // Release on item1 + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + 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)); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item2); + item2->setAcceptedMouseButtons(0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item1); +} + +class HoverItem : public QGraphicsRectItem +{ +public: + HoverItem(const QRectF &rect) + : QGraphicsRectItem(rect), hoverInCount(0), + hoverMoveCount(0), hoverOutCount(0) + { } + + int hoverInCount; + int hoverMoveCount; + int hoverOutCount; +protected: + void hoverEnterEvent(QGraphicsSceneHoverEvent *) + { ++hoverInCount; } + + void hoverMoveEvent(QGraphicsSceneHoverEvent *) + { ++hoverMoveCount; } + + void hoverLeaveEvent(QGraphicsSceneHoverEvent *) + { ++hoverOutCount; } +}; + +void tst_QGraphicsItem::acceptsHoverEvents() +{ + 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->acceptsHoverEvents()); + QVERIFY(!item2->acceptsHoverEvents()); + item1->setAcceptsHoverEvents(true); + item2->setAcceptsHoverEvents(true); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setScenePos(QPointF(-100, -100)); + QApplication::sendEvent(&scene, &event); + event.setScenePos(QPointF(-2.5, -2.5)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item2->hoverInCount, 1); + + item1->setAcceptsHoverEvents(false); + item2->setAcceptsHoverEvents(false); + + event.setScenePos(QPointF(-100, -100)); + QApplication::sendEvent(&scene, &event); + event.setScenePos(QPointF(-2.5, -2.5)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item2->hoverInCount, 1); + + item1->setAcceptsHoverEvents(true); + item2->setAcceptsHoverEvents(false); + + event.setScenePos(QPointF(-100, -100)); + QApplication::sendEvent(&scene, &event); + event.setScenePos(QPointF(-2.5, -2.5)); + QApplication::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)); + QApplication::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)); + QApplication::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)); + QApplication::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)); + QApplication::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)); + QApplication::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)); + QApplication::sendEvent(&scene, &event); + + event.setScenePos(QPointF(-15, -15)); + QApplication::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); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(line); + + line->setFocus(); + QVERIFY(!line->hasFocus()); + line->setFlag(QGraphicsItem::ItemIsFocusable); + line->setFocus(); + QVERIFY(line->hasFocus()); + + QGraphicsScene scene2; + QApplication::sendEvent(&scene2, &activate); + + scene2.addItem(line); + QVERIFY(!line->hasFocus()); + + QCOMPARE(scene.focusItem(), (QGraphicsItem *)0); + QCOMPARE(scene2.focusItem(), (QGraphicsItem *)0); + + 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.matrix(), QMatrix()); + line.setMatrix(QMatrix().rotate(90)); + QCOMPARE(line.matrix(), QMatrix().rotate(90)); + line.setMatrix(QMatrix().rotate(90)); + QCOMPARE(line.matrix(), QMatrix().rotate(90)); + line.setMatrix(QMatrix().rotate(90), true); + QCOMPARE(line.matrix(), QMatrix().rotate(180)); + line.setMatrix(QMatrix().rotate(-90), true); + QCOMPARE(line.matrix(), QMatrix().rotate(90)); + line.resetMatrix(); + QCOMPARE(line.matrix(), QMatrix()); + + line.rotate(90); + QCOMPARE(line.matrix(), QMatrix().rotate(90)); + line.rotate(90); + QCOMPARE(line.matrix(), QMatrix().rotate(90).rotate(90)); + line.resetMatrix(); + + line.scale(2, 4); + QCOMPARE(line.matrix(), QMatrix().scale(2, 4)); + line.scale(2, 4); + QCOMPARE(line.matrix(), QMatrix().scale(2, 4).scale(2, 4)); + line.resetMatrix(); + + line.shear(2, 4); + QCOMPARE(line.matrix(), QMatrix().shear(2, 4)); + line.shear(2, 4); + QCOMPARE(line.matrix(), QMatrix().shear(2, 4).shear(2, 4)); + line.resetMatrix(); + + line.translate(10, 10); + QCOMPARE(line.matrix(), QMatrix().translate(10, 10)); + line.translate(10, 10); + QCOMPARE(line.matrix(), QMatrix().translate(10, 10).translate(10, 10)); + line.resetMatrix(); +} + +void tst_QGraphicsItem::sceneMatrix() +{ + QGraphicsLineItem *parent = new QGraphicsLineItem; + QGraphicsLineItem *child = new QGraphicsLineItem(QLineF(), parent); + + QCOMPARE(parent->sceneMatrix(), QMatrix()); + QCOMPARE(child->sceneMatrix(), QMatrix()); + + parent->translate(10, 10); + QCOMPARE(parent->sceneMatrix(), QMatrix().translate(10, 10)); + QCOMPARE(child->sceneMatrix(), QMatrix().translate(10, 10)); + + child->translate(10, 10); + QCOMPARE(parent->sceneMatrix(), QMatrix().translate(10, 10)); + QCOMPARE(child->sceneMatrix(), QMatrix().translate(20, 20)); + + parent->rotate(90); + QCOMPARE(parent->sceneMatrix(), QMatrix().translate(10, 10).rotate(90)); + QCOMPARE(child->sceneMatrix(), QMatrix().translate(10, 10).rotate(90).translate(10, 10)); + + delete child; + delete parent; +} + +void tst_QGraphicsItem::setMatrix() +{ + QGraphicsScene scene; + qRegisterMetaType<QList<QRectF> >("QList<QRectF>"); + QSignalSpy spy(&scene, SIGNAL(changed(QList<QRectF>))); + QRectF unrotatedRect(-12, -34, 56, 78); + QGraphicsRectItem item(unrotatedRect, 0, &scene); + scene.update(scene.sceneRect()); + QApplication::instance()->processEvents(); + + QCOMPARE(spy.count(), 1); + + item.setMatrix(QMatrix().rotate(qreal(12.34))); + QRectF rotatedRect = scene.sceneRect(); + QVERIFY(unrotatedRect != rotatedRect); + scene.update(scene.sceneRect()); + QApplication::instance()->processEvents(); + + QCOMPARE(spy.count(), 2); + + item.setMatrix(QMatrix()); + + scene.update(scene.sceneRect()); + QApplication::instance()->processEvents(); + + QCOMPARE(spy.count(), 3); + QList<QRectF> rlist = qVariantValue<QList<QRectF> >(spy.last().at(0)); + + QCOMPARE(rlist.size(), 3); + QCOMPARE(rlist.at(0), rotatedRect); // From item.setMatrix() (clearing rotated rect) + QCOMPARE(rlist.at(1), rotatedRect); // From scene.update() (updating scene rect) + QCOMPARE(rlist.at(2), unrotatedRect); // From post-update (update current state) +} + +static QList<QGraphicsItem *> _paintedItems; +class PainterItem : public QGraphicsItem +{ +protected: + QRectF boundingRect() const + { return QRectF(-10, -10, 20, 20); } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { _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.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QApplication::processEvents(); +#ifdef Q_WS_QWS + QApplication::sendPostedEvents(); //glib workaround +#endif + + 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)); + + // 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)); + 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)); + 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); + 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); + 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", SkipAll); + } + + // 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, 0, &scene); + QGraphicsRectItem rect2(40, 40, 50, 50, 0, &scene); + rect2.setZValue(1); + QGraphicsLineItem line(0, 0, 200, 200, 0, &scene); + line.setZValue(2); + + QCOMPARE(scene.items().size(), 3); + + QList<QGraphicsItem *> col1 = rect.collidingItems(); + QCOMPARE(col1.size(), 2); + QCOMPARE(col1.first(), static_cast<QGraphicsItem *>(&line)); + QCOMPARE(col1.last(), static_cast<QGraphicsItem *>(&rect2)); + + QList<QGraphicsItem *> col2 = rect2.collidingItems(); + QCOMPARE(col2.size(), 2); + QCOMPARE(col2.first(), static_cast<QGraphicsItem *>(&line)); + QCOMPARE(col2.last(), static_cast<QGraphicsItem *>(&rect)); + + QList<QGraphicsItem *> col3 = line.collidingItems(); + QCOMPARE(col3.size(), 2); + QCOMPARE(col3.first(), static_cast<QGraphicsItem *>(&rect2)); + QCOMPARE(col3.last(), static_cast<QGraphicsItem *>(&rect)); + } +} + +void tst_QGraphicsItem::collidesWith_path_data() +{ + QTest::addColumn<QPointF>("pos"); + QTest::addColumn<QMatrix>("matrix"); + QTest::addColumn<QPainterPath>("shape"); + QTest::addColumn<bool>("rectCollides"); + QTest::addColumn<bool>("ellipseCollides"); + + QTest::newRow("nothing") << QPointF(0, 0) << QMatrix() << QPainterPath() << false << false; + + QPainterPath rect; + rect.addRect(0, 0, 20, 20); + + QTest::newRow("rect1") << QPointF(0, 0) << QMatrix() << rect << true << true; + QTest::newRow("rect2") << QPointF(0, 0) << QMatrix().translate(21, 21) << rect << false << false; + QTest::newRow("rect3") << QPointF(21, 21) << QMatrix() << rect << false << false; +} + +void tst_QGraphicsItem::collidesWith_path() +{ + QFETCH(QPointF, pos); + QFETCH(QMatrix, matrix); + 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.setMatrix(matrix); + + ellipse.setPos(pos); + ellipse.setMatrix(matrix); + + QPainterPath mappedShape = rect.sceneMatrix().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 + { + const MyItem *myItem = qgraphicsitem_cast<const MyItem *>(item); + if (myItem) { + if (item->zValue() > zValue()) { + 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()); + + if (mappedShape.contains(topMid) && + mappedShape.contains(botMid) && + mappedShape.contains(leftMid) && + mappedShape.contains(rightMid)) + return true; + else + return false; + } + else return false; + } + else + return QGraphicsItem::isObscuredBy(item); + } + + QPainterPath opaqueArea() const + { + return shape(); + } + + enum { + Type = UserType+1 + }; + int type() const { return Type; } +}; + +void tst_QGraphicsItem::isObscuredBy() +{ + QGraphicsScene scene; + + MyItem myitem1, myitem2; + + myitem1.setRect(QRectF(50, 50, 40, 200)); + myitem1.rotate(67); + + 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 + { + return shape(); + } +}; + +void tst_QGraphicsItem::isObscured() +{ + if (sizeof(qreal) != sizeof(double)) { + QSKIP("Skipped due to rounding errors", SkipAll); + } + + 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) { + QMatrix matrix; + matrix.rotate(i * 90); + matrix.translate(i * 100, -i * 100); + matrix.scale(2, 4); + item1->setMatrix(matrix); + + QCOMPARE(item1->mapToParent(QPointF(0, 0)), item1->pos() + matrix.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() + matrix.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() + matrix.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()), matrix.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)), + matrix.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)), + matrix.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 + QMatrix matrix; matrix.rotate(90); + item1->setMatrix(matrix); + 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->setMatrix(matrix); + 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 + QMatrix matrix2; + matrix2.translate(50, 0); + matrix2.rotate(-90); + item3->setMatrix(matrix2); + 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)); + + QMatrix matrix; + matrix.translate(100, 100); + item1->setMatrix(matrix); + + 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)); + + matrix.rotate(90); + item1->setMatrix(matrix); + item2->setMatrix(matrix); + item3->setMatrix(matrix); + item4->setMatrix(matrix); + + 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(0, 10, -5), item1->mapFromScene(10, -5)); + QCOMPARE(item2->mapFromItem(0, 10, -5), item2->mapFromScene(10, -5)); + QCOMPARE(item3->mapFromItem(0, 10, -5), item3->mapFromScene(10, -5)); + QCOMPARE(item4->mapFromItem(0, 10, -5), item4->mapFromScene(10, -5)); + QCOMPARE(item1->mapToItem(0, 10, -5), item1->mapToScene(10, -5)); + QCOMPARE(item2->mapToItem(0, 10, -5), item2->mapToScene(10, -5)); + QCOMPARE(item3->mapToItem(0, 10, -5), item3->mapToScene(10, -5)); + QCOMPARE(item4->mapToItem(0, 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(0)); + QVERIFY(!child->isAncestorOf(0)); + 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(0); + + delete child; + delete parent; + delete grandPa; +} + +void tst_QGraphicsItem::commonAncestorItem() +{ + QGraphicsItem *ancestor = new QGraphicsRectItem; + 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(0), (QGraphicsItem *)0); + 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 = 0; + QVERIFY(!qgraphicsitem_cast<QGraphicsTextItem *>(ptr)); + QVERIFY(!qgraphicsitem_cast<QGraphicsItem *>(ptr)); +} + +void tst_QGraphicsItem::hoverEventsGenerateRepaints() +{ + Q_CHECK_PAINTEVENTS + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(150); + + EventTester *tester = new EventTester; + scene.addItem(tester); + tester->setAcceptsHoverEvents(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)); + QApplication::sendEvent(&scene, &hoverEnterEvent); + + // Check that we get a repaint + int npaints = tester->repaints; + qApp->processEvents(); + qApp->processEvents(); + QCOMPARE(tester->events.size(), 2); // enter + move + QCOMPARE(tester->repaints, npaints + 1); + QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverMove); + + // Send a hover move event + QGraphicsSceneHoverEvent hoverMoveEvent(QEvent::GraphicsSceneHoverMove); + hoverMoveEvent.setScenePos(QPointF(0, 0)); + hoverMoveEvent.setPos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &hoverMoveEvent); + + // Check that we don't get a repaint + qApp->processEvents(); + qApp->processEvents(); + + QCOMPARE(tester->events.size(), 3); + QCOMPARE(tester->repaints, npaints + 1); + QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverMove); + + // Send a hover leave event + QGraphicsSceneHoverEvent hoverLeaveEvent(QEvent::GraphicsSceneHoverLeave); + hoverLeaveEvent.setScenePos(QPointF(-100, -100)); + hoverLeaveEvent.setPos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &hoverLeaveEvent); + + // Check that we get a repaint + qApp->processEvents(); + qApp->processEvents(); + + QCOMPARE(tester->events.size(), 4); + QCOMPARE(tester->repaints, npaints + 2); + QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverLeave); +} + +void tst_QGraphicsItem::boundingRects_data() +{ + QTest::addColumn<QGraphicsItem *>("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") << (QGraphicsItem *)new QGraphicsPathItem(path) << adjustedRect; + QTest::newRow("rect") << (QGraphicsItem *)new QGraphicsRectItem(rect) << adjustedRect; + QTest::newRow("ellipse") << (QGraphicsItem *)new QGraphicsEllipseItem(rect) << adjustedRect; + QTest::newRow("polygon") << (QGraphicsItem *)new QGraphicsPolygonItem(rect) << adjustedRect; +} + +void tst_QGraphicsItem::boundingRects() +{ + QFETCH(QGraphicsItem *, item); + QFETCH(QRectF, boundingRect); + + ((QAbstractGraphicsShapeItem *)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->rotate(90); + + 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->rotate(90); + + scene.addPolygon(parent->mapToScene(parent->boundingRect() | parent->childrenBoundingRect()))->setPen(QPen(Qt::red));; + + QGraphicsView view(&scene); + view.show(); + + QTest::qWaitForWindowShown(&view); + QTest::qWait(30); + + QCOMPARE(parent->childrenBoundingRect(), QRectF(-500, -100, 600, 800)); +} + +void tst_QGraphicsItem::childrenBoundingRectTransformed() +{ + QGraphicsScene scene; + + QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect2 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect3 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect4 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect5 = scene.addRect(QRectF(0, 0, 100, 100)); + rect2->setParentItem(rect); + rect3->setParentItem(rect2); + rect4->setParentItem(rect3); + rect5->setParentItem(rect4); + + rect2->setTransform(QTransform().translate(50, 50).rotate(45)); + rect2->setPos(25, 25); + rect3->setTransform(QTransform().translate(50, 50).rotate(45)); + rect3->setPos(25, 25); + rect4->setTransform(QTransform().translate(50, 50).rotate(45)); + rect4->setPos(25, 25); + rect5->setTransform(QTransform().translate(50, 50).rotate(45)); + rect5->setPos(25, 25); + + QRectF subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(subTreeRect.left(), qreal(-206.0660171779821)); + QCOMPARE(subTreeRect.top(), qreal(75.0)); + QCOMPARE(subTreeRect.width(), qreal(351.7766952966369)); + QCOMPARE(subTreeRect.height(), qreal(251.7766952966369)); + + rect->rotate(45); + rect2->rotate(-45); + rect3->rotate(45); + rect4->rotate(-45); + rect5->rotate(45); + + subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(rect->childrenBoundingRect(), QRectF(-100, 75, 275, 250)); +} + +void tst_QGraphicsItem::childrenBoundingRect2() +{ + QGraphicsItemGroup box; + 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. + QCOMPARE(box.childrenBoundingRect(), QRectF(0, 0, 100, 100)); +} + +void tst_QGraphicsItem::childrenBoundingRect3() +{ + QGraphicsScene scene; + + QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect2 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect3 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect4 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect5 = scene.addRect(QRectF(0, 0, 100, 100)); + rect2->setParentItem(rect); + rect3->setParentItem(rect2); + rect4->setParentItem(rect3); + rect5->setParentItem(rect4); + + rect2->setTransform(QTransform().translate(50, 50).rotate(45)); + rect2->setPos(25, 25); + rect3->setTransform(QTransform().translate(50, 50).rotate(45)); + rect3->setPos(25, 25); + rect4->setTransform(QTransform().translate(50, 50).rotate(45)); + rect4->setPos(25, 25); + rect5->setTransform(QTransform().translate(50, 50).rotate(45)); + rect5->setPos(25, 25); + + // Try to mess up the cached bounding rect. + (void)rect2->childrenBoundingRect(); + + QRectF subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(subTreeRect.left(), qreal(-206.0660171779821)); + QCOMPARE(subTreeRect.top(), qreal(75.0)); + QCOMPARE(subTreeRect.width(), qreal(351.7766952966369)); + QCOMPARE(subTreeRect.height(), qreal(251.7766952966369)); +} + +void tst_QGraphicsItem::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.show(); + + QTest::qWaitForWindowShown(&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); + + QGraphicsView view(&scene); + view.show(); + + QTest::qWaitForWindowShown(&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->rotate(45); + child->setParentItem(parent); + parent->setPos(25, 25); + child->setPos(25, 25); + + QCOMPARE(parent->group(), (QGraphicsItemGroup *)0); + QCOMPARE(parent2->group(), (QGraphicsItemGroup *)0); + QCOMPARE(child->group(), (QGraphicsItemGroup *)0); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + 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(), (QGraphicsItem *)group); + QCOMPARE(group->children().size(), 1); + QCOMPARE(scene.items().size(), 4); + QCOMPARE(scene.items(group->sceneBoundingRect()).size(), 3); + + QTest::qWait(25); + + QRectF parent2SceneBoundingRect = parent2->sceneBoundingRect(); + group->addToGroup(parent2); + QCOMPARE(parent2->group(), group); + QCOMPARE(parent2->sceneBoundingRect(), parent2SceneBoundingRect); + + QCOMPARE(parent2->parentItem(), (QGraphicsItem *)group); + QCOMPARE(group->children().size(), 2); + QCOMPARE(scene.items().size(), 4); + QCOMPARE(scene.items(group->sceneBoundingRect()).size(), 4); + + QTest::qWait(25); + + QList<QGraphicsItem *> newItems; + for (int i = 0; i < 100; ++i) { + QGraphicsItem *item = scene.addRect(QRectF(-25, -25, 50, 50), QPen(Qt::black, 0), + QBrush(QColor(rand() % 255, rand() % 255, + rand() % 255, rand() % 255))); + newItems << item; + item->setPos(-1000 + rand() % 2000, + -1000 + rand() % 2000); + item->rotate(rand() % 90); + } + + view.fitInView(scene.itemsBoundingRect()); + + int n = 0; + foreach (QGraphicsItem *item, newItems) { + group->addToGroup(item); + QCOMPARE(item->group(), group); + if ((n++ % 100) == 0) + QTest::qWait(10); + } +} + +void tst_QGraphicsItem::setGroup() +{ + QGraphicsItemGroup group1; + QGraphicsItemGroup group2; + + QGraphicsRectItem *rect = new QGraphicsRectItem; + QCOMPARE(rect->group(), (QGraphicsItemGroup *)0); + QCOMPARE(rect->parentItem(), (QGraphicsItem *)0); + rect->setGroup(&group1); + QCOMPARE(rect->group(), &group1); + QCOMPARE(rect->parentItem(), (QGraphicsItem *)&group1); + rect->setGroup(&group2); + QCOMPARE(rect->group(), &group2); + QCOMPARE(rect->parentItem(), (QGraphicsItem *)&group2); + rect->setGroup(0); + QCOMPARE(rect->group(), (QGraphicsItemGroup *)0); + QCOMPARE(rect->parentItem(), (QGraphicsItem *)0); +} + +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->translate(20,20); + group.translate(-30,-40); + 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(0); + QCOMPARE(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(), (QGraphicsItemGroup *)0); + + QGraphicsScene scene; + scene.addItem(group1); + + QCOMPARE(rect->group(), group1); + QCOMPARE(rect2->group(), group1); + QCOMPARE(group1->group(), (QGraphicsItemGroup *)0); + QVERIFY(group2->children().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->rotate(90); + 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->scale(2, 2); + + 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.show(); + + qApp->processEvents(); // index items + qApp->processEvents(); // emit changed + + QGraphicsItemGroup *group = scene.createItemGroup(scene.selectedItems()); + QVERIFY(group); + QCOMPARE(group->children().size(), 2); + qApp->processEvents(); // index items + qApp->processEvents(); // emit changed + + scene.destroyItemGroup(group); // calls removeFromGroup. + + qApp->processEvents(); // index items + qApp->processEvents(); // emit changed + + QCOMPARE(scene.items().size(), 2); + QVERIFY(!rect1->group()); + QVERIFY(!rect2->group()); +} + +class ChildEventTester : public QGraphicsRectItem +{ +public: + ChildEventTester(const QRectF &rect, QGraphicsItem *parent = 0) + : QGraphicsRectItem(rect, parent), counter(0) + { } + + int counter; + +protected: + void focusInEvent(QFocusEvent *event) + { ++counter; QGraphicsRectItem::focusInEvent(event); } + void mousePressEvent(QGraphicsSceneMouseEvent *) + { ++counter; } + void mouseMoveEvent(QGraphicsSceneMouseEvent *) + { ++counter; } + void mouseReleaseEvent(QGraphicsSceneMouseEvent *) + { ++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.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(20); + + // Pull out the items, closest item first + QList<QGraphicsItem *> items = scene.items(scene.itemsBoundingRect()); + QCOMPARE(items.at(0), (QGraphicsItem *)yellow); + QCOMPARE(items.at(1), (QGraphicsItem *)gray); + QCOMPARE(items.at(2), (QGraphicsItem *)green); + QCOMPARE(items.at(3), (QGraphicsItem *)red); + QCOMPARE(items.at(4), (QGraphicsItem *)blue); + + QCOMPARE(blue->counter, 0); + + // Send events to the toplevel item + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(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())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::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())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::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())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::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())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::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())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::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())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::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())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::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.show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(5, 5), + view.viewport()->mapToGlobal(view.mapFromScene(5, 5)), Qt::LeftButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + + QTRY_COMPARE(root->counter, 1); +} + +void tst_QGraphicsItem::handlesChildEvents3() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + ChildEventTester *group2 = new ChildEventTester(QRectF(), 0); + ChildEventTester *group1 = new ChildEventTester(QRectF(), group2); + ChildEventTester *leaf = new ChildEventTester(QRectF(), group1); + 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 = 0) + : ChildEventTester(rect, parent), filter(QEvent::None) + { } + + QEvent::Type filter; + +protected: + bool sceneEventFilter(QGraphicsItem *item, QEvent *event) + { + Q_UNUSED(item); + if (event->type() == filter) { + ++counter; + return true; + } + return false; + } +}; + +void tst_QGraphicsItem::filtersChildEvents() +{ + QGraphicsScene scene; + ChildEventFilterTester *root = new ChildEventFilterTester(QRectF(0, 0, 10, 10)); + ChildEventFilterTester *filter = new ChildEventFilterTester(QRectF(10, 10, 10, 10), root); + ChildEventTester *child = new ChildEventTester(QRectF(20, 20, 10, 10), filter); + + // setup filter + filter->setFiltersChildEvents(true); + filter->filter = QEvent::GraphicsSceneMousePress; + + scene.addItem(root); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(20); + + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + + // send event to child + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(25, 25));//child->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setButton(Qt::LeftButton); + releaseEvent.setScenePos(QPointF(25, 25));//child->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + 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 + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(child->counter, 1); + QCOMPARE(filter->counter, 2); // mouse press is filtered + QCOMPARE(root->counter, 1); // mouse release is filtered + + // reparent to another sub-graph + ChildEventTester *parent = new ChildEventTester(QRectF(10, 10, 10, 10), root); + child->setParentItem(parent); + + // send event to child + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(child->counter, 2); // mouse press is _not_ filtered + QCOMPARE(parent->counter, 0); + QCOMPARE(filter->counter, 2); + QCOMPARE(root->counter, 2); // mouse release is filtered +} + +void tst_QGraphicsItem::filtersChildEvents2() +{ + ChildEventFilterTester *root = new ChildEventFilterTester(QRectF(0, 0, 10, 10)); + root->setFiltersChildEvents(true); + root->filter = QEvent::GraphicsSceneMousePress; + QVERIFY(root->filtersChildEvents()); + + ChildEventTester *child = new ChildEventTester(QRectF(0, 0, 10, 10), root); + QVERIFY(!child->filtersChildEvents()); + + ChildEventTester *child2 = new ChildEventTester(QRectF(0, 0, 10, 10)); + ChildEventTester *child3 = new ChildEventTester(QRectF(0, 0, 10, 10), child2); + ChildEventTester *child4 = new ChildEventTester(QRectF(0, 0, 10, 10), child3); + + child2->setParentItem(root); + QVERIFY(!child2->filtersChildEvents()); + QVERIFY(!child3->filtersChildEvents()); + QVERIFY(!child4->filtersChildEvents()); + + QGraphicsScene scene; + scene.addItem(root); + + QGraphicsView view(&scene); + view.show(); + + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(5, 5), + view.viewport()->mapToGlobal(view.mapFromScene(5, 5)), Qt::LeftButton, 0, 0); + QApplication::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 + { return QRectF(-110, -110, 220, 220); } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + 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) { + for (int y = -100; y < 100; y += 25) { + painter->drawText(QRectF(x, y, 25, 25), Qt::AlignCenter, QString("%1x%2").arg(x).arg(y)); + } + } + } +}; + +void tst_QGraphicsItem::ensureVisible() +{ + QGraphicsScene scene; + scene.setSceneRect(-200, -200, 400, 400); + QGraphicsItem *item = new CustomItem; + scene.addItem(item); + + QGraphicsView view(&scene); + view.setFixedSize(300, 300); + view.show(); + QTest::qWaitForWindowShown(&view); + + for (int i = 0; i < 25; ++i) { + view.scale(qreal(1.06), qreal(1.06)); + QApplication::processEvents(); + } + + item->ensureVisible(-100, -100, 25, 25); + QTest::qWait(25); + + for (int x = -100; x < 100; x += 25) { + for (int y = -100; y < 100; y += 25) { + int xmargin = rand() % 75; + int ymargin = rand() % 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); +} + +void tst_QGraphicsItem::cursor() +{ +#ifndef QT_NO_CURSOR + QGraphicsScene scene; + QGraphicsRectItem *item1 = scene.addRect(QRectF(0, 0, 50, 50)); + QGraphicsRectItem *item2 = scene.addRect(QRectF(0, 0, 50, 50)); + item1->setPos(-100, 0); + item2->setPos(50, 0); + + 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); + + QWidget topLevel; + QGraphicsView view(&scene,&topLevel); + view.setFixedSize(200, 100); + topLevel.show(); + QTest::mouseMove(&view, view.rect().center()); + + QTest::qWait(25); + + QCursor cursor = view.viewport()->cursor(); + + { + QMouseEvent event(QEvent::MouseMove, QPoint(100, 50), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + QTest::qWait(25); + + QCOMPARE(view.viewport()->cursor().shape(), cursor.shape()); + + { + QTest::mouseMove(view.viewport(), view.mapFromScene(item1->sceneBoundingRect().center())); + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(item1->sceneBoundingRect().center()), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + if (!PlatformQuirks::haveMouseCursor()) + return; +#if !defined(Q_OS_WINCE) + QTest::qWait(250); +#else + // Test environment does not have any cursor, therefore no shape + return; +#endif + + QCOMPARE(view.viewport()->cursor().shape(), item1->cursor().shape()); + + { + QTest::mouseMove(view.viewport(), view.mapFromScene(item2->sceneBoundingRect().center())); + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(item2->sceneBoundingRect().center()), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + QTest::qWait(25); + + QCOMPARE(view.viewport()->cursor().shape(), item2->cursor().shape()); + + { + QTest::mouseMove(view.viewport(), view.rect().center()); + QMouseEvent event(QEvent::MouseMove, QPoint(100, 25), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + QTest::qWait(25); + + QCOMPARE(view.viewport()->cursor().shape(), cursor.shape()); +#endif +} +/* +void tst_QGraphicsItem::textControlGetterSetter() +{ + QGraphicsTextItem *item = new QGraphicsTextItem; + QVERIFY(item->textControl()->parent() == item); + QPointer<QTextControl> control = item->textControl(); + delete item; + QVERIFY(!control); + + item = new QGraphicsTextItem; + + QPointer<QTextControl> oldControl = control; + control = new QTextControl; + + item->setTextControl(control); + QVERIFY(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 QTextControl; + 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() != 0); + 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); + QApplication::sendEvent(&scene, &event); + QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove); + event2.setScenePos(QPointF(11, 11)); + event2.setButton(Qt::LeftButton); + event2.setButtons(Qt::LeftButton); + QApplication::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); + QApplication::sendEvent(&scene, &event2); + } + + QCOMPARE(text->pos(), QPointF(20, 20)); // clicked on edge, item moved +} + +void tst_QGraphicsItem::defaultItemTest_QGraphicsEllipseItem() +{ + QGraphicsEllipseItem item; + 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); + qFuzzyCompare(item.boundingRect().left(), qreal(50.0)); + qFuzzyCompare(item.boundingRect().top(), qreal(0.0)); + qFuzzyCompare(item.boundingRect().width(), qreal(50.0)); + qFuzzyCompare(item.boundingRect().height(), qreal(50.0)); + + 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() + { setFlag(ItemSendsGeometryChanges); clear(); } + ItemChangeTester(QGraphicsItem *parent) : QGraphicsRectItem(parent) + { setFlag(ItemSendsGeometryChanges); clear(); } + + void clear() + { + itemChangeReturnValue = QVariant(); + itemSceneChangeTargetScene = 0; + changes.clear(); + values.clear(); + oldValues.clear(); + } + + QVariant itemChangeReturnValue; + QGraphicsScene *itemSceneChangeTargetScene; + + QList<GraphicsItemChange> changes; + QList<QVariant> values; + QList<QVariant> oldValues; +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value) + { + changes << change; + values << value; + switch (change) { + case QGraphicsItem::ItemPositionChange: + oldValues << pos(); + break; + case QGraphicsItem::ItemPositionHasChanged: + break; + case QGraphicsItem::ItemMatrixChange: { + QVariant variant; + qVariantSetValue<QMatrix>(variant, matrix()); + oldValues << variant; + } + break; + case QGraphicsItem::ItemTransformChange: { + QVariant variant; + qVariantSetValue<QTransform>(variant, 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 << qVariantFromValue<void *>(parentItem()); + break; + case QGraphicsItem::ItemParentHasChanged: + break; + case QGraphicsItem::ItemChildAddedChange: + oldValues << children().size(); + break; + case QGraphicsItem::ItemChildRemovedChange: + oldValues << children().size(); + break; + case QGraphicsItem::ItemSceneChange: + oldValues << qVariantFromValue<QGraphicsScene *>(scene()); + if (itemSceneChangeTargetScene + && qVariantValue<QGraphicsScene *>(value) + && itemSceneChangeTargetScene != qVariantValue<QGraphicsScene *>(value)) { + return qVariantFromValue<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 = 0; + + 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.last(), QVariant(true)); + QCOMPARE(tester.isEnabled(), true); + } + { + // ItemMatrixChange / ItemTransformHasChanged + qVariantSetValue<QMatrix>(tester.itemChangeReturnValue, QMatrix().rotate(90)); + tester.setMatrix(QMatrix().translate(50, 0), true); + ++changeCount; // notification sent too + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(int(tester.changes.at(tester.changes.size() - 2)), int(QGraphicsItem::ItemMatrixChange)); + QCOMPARE(int(tester.changes.last()), int(QGraphicsItem::ItemTransformHasChanged)); + QCOMPARE(qVariantValue<QMatrix>(tester.values.at(tester.values.size() - 2)), + QMatrix().translate(50, 0)); + QCOMPARE(tester.values.last(), QVariant(QTransform(QMatrix().rotate(90)))); + QVariant variant; + qVariantSetValue<QMatrix>(variant, QMatrix()); + QCOMPARE(tester.oldValues.last(), variant); + QCOMPARE(tester.matrix(), QMatrix().rotate(90)); + } + { + tester.resetTransform(); + ++changeCount; + ++changeCount; // notification sent too + + // ItemTransformChange / ItemTransformHasChanged + qVariantSetValue<QTransform>(tester.itemChangeReturnValue, QTransform().rotate(90)); + tester.translate(50, 0); + ++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(qVariantValue<QTransform>(tester.values.at(tester.values.size() - 2)), + QTransform().translate(50, 0)); + QCOMPARE(qVariantValue<QTransform>(tester.values.at(tester.values.size() - 1)), + QTransform().rotate(90)); + QVariant variant; + qVariantSetValue<QTransform>(variant, QTransform()); + QCOMPARE(tester.oldValues.last(), 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.last(), 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.last(), 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.last(), 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.last(), 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.last(), 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 = qVariantFromValue<quint32>(QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges)); + QCOMPARE(tester.values.at(tester.values.size() - 2), expectedFlags); + QCOMPARE(tester.values.at(tester.values.size() - 1), qVariantFromValue<quint32>((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.last(), 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 + qVariantSetValue<QGraphicsItem *>(tester.itemChangeReturnValue, 0); + tester.setParentItem(&testerHelper); + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(tester.changes.last(), QGraphicsItem::ItemParentChange); + QCOMPARE(qVariantValue<QGraphicsItem *>(tester.values.last()), (QGraphicsItem *)&testerHelper); + QCOMPARE(qVariantValue<QGraphicsItem *>(tester.oldValues.last()), (QGraphicsItem *)0); + QCOMPARE(tester.parentItem(), (QGraphicsItem *)0); + } + { + // ItemOpacityChange + tester.itemChangeReturnValue = 1.0; + tester.setOpacity(0.7); + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(tester.changes.last(), 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.last(), QGraphicsItem::ItemChildAddedChange); + QCOMPARE(qVariantValue<QGraphicsItem *>(tester.values.last()), (QGraphicsItem *)&testerHelper); + } + { + // ItemChildRemovedChange 1 + testerHelper.setParentItem(0); + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(tester.changes.last(), QGraphicsItem::ItemChildRemovedChange); + QCOMPARE(qVariantValue<QGraphicsItem *>(tester.values.last()), (QGraphicsItem *)&testerHelper); + + // ItemChildRemovedChange 1 + ItemChangeTester *test = new ItemChangeTester; + test->itemSceneChangeTargetScene = 0; + 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.last(), QGraphicsItem::ItemChildAddedChange); + delete child; + child = 0; + QCOMPARE(test->changes.size(), ++count); + QCOMPARE(test->changes.last(), 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.last(), 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.last(), QGraphicsItem::ItemChildRemovedChange); + delete scene; + } + { + // ItemChildRemovedChange 2 + ItemChangeTester parent; + ItemChangeTester *child = new ItemChangeTester; + child->setParentItem(&parent); + QCOMPARE(parent.changes.last(), QGraphicsItem::ItemChildAddedChange); + QCOMPARE(qVariantValue<QGraphicsItem *>(parent.values.last()), (QGraphicsItem *)child); + delete child; + QCOMPARE(parent.changes.last(), QGraphicsItem::ItemChildRemovedChange); + QCOMPARE(qVariantValue<QGraphicsItem *>(parent.values.last()), (QGraphicsItem *)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(qVariantValue<QGraphicsScene *>(tester.oldValues.last()), (QGraphicsScene *)0); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.last()), (QGraphicsScene *)&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(qVariantValue<QGraphicsScene *>(tester.oldValues.at(tester.oldValues.size() - 2)), (QGraphicsScene *)&scene); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.oldValues.at(tester.oldValues.size() - 1)), (QGraphicsScene *)0); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 4)), (QGraphicsScene *)0); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 3)), (QGraphicsScene *)0); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), (QGraphicsScene *)&scene2); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), (QGraphicsScene *)&scene2); + // Item's last old value was 0 + // Item's last current value is scene2 + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.oldValues.last()), (QGraphicsScene *)0); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.last()), (QGraphicsScene *)&scene2); + + scene2.removeItem(&tester); + ++changeCount; // ItemSceneChange (0) was: (scene2) + ++changeCount; // ItemSceneHasChanged (0) + QCOMPARE(tester.changes.size(), changeCount); + + QCOMPARE(tester.scene(), (QGraphicsScene *)0); + 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(qVariantValue<QGraphicsScene *>(tester.oldValues.last()), (QGraphicsScene *)&scene2); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), (QGraphicsScene *)0); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), (QGraphicsScene *)0); + + 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(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 3)), (QGraphicsScene *)&scene2); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 2)), (QGraphicsScene *)&scene); + QCOMPARE(qVariantValue<QGraphicsScene *>(tester.values.at(tester.values.size() - 1)), (QGraphicsScene *)&scene); + + QCOMPARE(tester.scene(), &scene); + tester.itemSceneChangeTargetScene = 0; + tester.itemChangeReturnValue = QVariant(); + scene.removeItem(&tester); + ++changeCount; // ItemSceneChange + ++changeCount; // ItemSceneHasChanged + QCOMPARE(tester.scene(), (QGraphicsScene *)0); + } + { + // 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; + QList<QGraphicsItem *> filteredEventReceivers; + bool handlesSceneEvents; + + QList<QEvent::Type> receivedEvents; + + EventFilterTesterItem() : handlesSceneEvents(false) {} + +protected: + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + filteredEvents << event->type(); + filteredEventReceivers << watched; + return handlesSceneEvents; + } + + bool sceneEvent(QEvent *event) + { + return QGraphicsLineItem::sceneEvent(event); + } +}; + +void tst_QGraphicsItem::sceneEventFilter() +{ + QGraphicsScene scene; + + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTest::qWait(25); + + 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(); + QTest::qWaitForWindowShown(&gv); + QTest::qWait(25); + 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 QGraphicsItem +{ +public: + void changeGeometry() + { prepareGeometryChange(); } +}; + +void tst_QGraphicsItem::prepareGeometryChange() +{ + { + QGraphicsScene scene; + QGraphicsItem *item = scene.addRect(QRectF(0, 0, 100, 100)); + QVERIFY(scene.items(QRectF(0, 0, 100, 100)).contains(item)); + ((GeometryChanger *)item)->changeGeometry(); + QVERIFY(scene.items(QRectF(0, 0, 100, 100)).contains(item)); + } +} + + +class PaintTester : public QGraphicsRectItem +{ +public: + PaintTester() : widget(NULL), painted(0) { setRect(QRectF(10, 10, 20, 20));} + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *w) + { + widget = w; + painted++; + } + + QWidget* widget; + int painted; +}; + +void tst_QGraphicsItem::paint() +{ + QGraphicsScene scene; + + PaintTester paintTester; + scene.addItem(&paintTester); + + QGraphicsView view(&scene); + + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&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", SkipSingle); + } +#endif + + QTRY_COMPARE(paintTester.widget, view.viewport()); + + view.hide(); + + QGraphicsScene scene2; + QGraphicsView view2(&scene2); + view2.show(); + QTest::qWaitForWindowShown(&view2); + QTest::qWait(25); + + PaintTester tester2; + scene2.addItem(&tester2); + qApp->processEvents(); + + //First show one paint + QTRY_COMPARE(tester2.painted, 1); + + //nominal case, update call paint + tester2.update(); + qApp->processEvents(); + QTRY_VERIFY(tester2.painted == 2); + + //we remove the item from the scene, number of updates is still the same + tester2.update(); + scene2.removeItem(&tester2); + qApp->processEvents(); + QTRY_VERIFY(tester2.painted == 2); + + //We re-add the item, the number of paint should increase + scene2.addItem(&tester2); + tester2.update(); + qApp->processEvents(); + QTRY_VERIFY(tester2.painted == 3); +} + +class HarakiriItem : public QGraphicsRectItem +{ +public: + HarakiriItem(int harakiriPoint) + : QGraphicsRectItem(QRectF(0, 0, 100, 100)), harakiri(harakiriPoint) + { dead = 0; } + + static int dead; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QGraphicsRectItem::paint(painter, option, widget); + if (harakiri == 0) { + // delete unsupported since 4.5 + /* + dead = 1; + delete this; + */ + } + } + + void advance(int n) + { + if (harakiri == 1 && n == 0) { + // delete unsupported + /* + dead = 1; + delete this; + */ + } + if (harakiri == 2 && n == 1) { + dead = 1; + delete this; + } + } + +protected: + void contextMenuEvent(QGraphicsSceneContextMenuEvent *) + { + if (harakiri == 3) { + dead = 1; + delete this; + } + } + + void dragEnterEvent(QGraphicsSceneDragDropEvent *event) + { + // ?? + QGraphicsRectItem::dragEnterEvent(event); + } + + void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) + { + // ?? + QGraphicsRectItem::dragLeaveEvent(event); + } + + void dragMoveEvent(QGraphicsSceneDragDropEvent *event) + { + // ?? + QGraphicsRectItem::dragMoveEvent(event); + } + + void dropEvent(QGraphicsSceneDragDropEvent *event) + { + // ?? + QGraphicsRectItem::dropEvent(event); + } + + void focusInEvent(QFocusEvent *) + { + if (harakiri == 4) { + dead = 1; + delete this; + } + } + + void focusOutEvent(QFocusEvent *) + { + if (harakiri == 5) { + dead = 1; + delete this; + } + } + + void hoverEnterEvent(QGraphicsSceneHoverEvent *) + { + if (harakiri == 6) { + dead = 1; + delete this; + } + } + + void hoverLeaveEvent(QGraphicsSceneHoverEvent *) + { + if (harakiri == 7) { + dead = 1; + delete this; + } + } + + void hoverMoveEvent(QGraphicsSceneHoverEvent *) + { + if (harakiri == 8) { + dead = 1; + delete this; + } + } + + void inputMethodEvent(QInputMethodEvent *event) + { + // ?? + QGraphicsRectItem::inputMethodEvent(event); + } + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const + { + // ?? + return QGraphicsRectItem::inputMethodQuery(query); + } + + QVariant itemChange(GraphicsItemChange change, const QVariant &value) + { + // deletion not supported + return QGraphicsRectItem::itemChange(change, value); + } + + void keyPressEvent(QKeyEvent *) + { + if (harakiri == 9) { + dead = 1; + delete this; + } + } + + void keyReleaseEvent(QKeyEvent *) + { + if (harakiri == 10) { + dead = 1; + delete this; + } + } + + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) + { + if (harakiri == 11) { + dead = 1; + delete this; + } + } + + void mouseMoveEvent(QGraphicsSceneMouseEvent *) + { + if (harakiri == 12) { + dead = 1; + delete this; + } + } + + void mousePressEvent(QGraphicsSceneMouseEvent *) + { + if (harakiri == 13) { + dead = 1; + delete this; + } + } + + void mouseReleaseEvent(QGraphicsSceneMouseEvent *) + { + if (harakiri == 14) { + dead = 1; + delete this; + } + } + + bool sceneEvent(QEvent *event) + { + // deletion not supported + return QGraphicsRectItem::sceneEvent(event); + } + + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + // deletion not supported + return QGraphicsRectItem::sceneEventFilter(watched, event); + } + + void wheelEvent(QGraphicsSceneWheelEvent *) + { + if (harakiri == 16) { + dead = 1; + delete this; + } + } + +private: + int harakiri; +}; + +int HarakiriItem::dead; + +void tst_QGraphicsItem::deleteItemInEventHandlers() +{ + for (int i = 0; i < 17; ++i) { + QGraphicsScene scene; + HarakiriItem *item = new HarakiriItem(i); + item->setAcceptsHoverEvents(true); + item->setFlag(QGraphicsItem::ItemIsFocusable); + + scene.addItem(item); + + item->installSceneEventFilter(item); // <- ehey! + + QGraphicsView view(&scene); + view.show(); + + qApp->processEvents(); + qApp->processEvents(); + + if (!item->dead) + scene.advance(); + + if (!item->dead) { + QContextMenuEvent event(QContextMenuEvent::Other, + view.mapFromScene(item->scenePos())); + QCoreApplication::sendEvent(view.viewport(), &event); + } + if (!item->dead) + QTest::mouseMove(view.viewport(), view.mapFromScene(item->scenePos())); + if (!item->dead) + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos())); + if (!item->dead) + QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos())); + if (!item->dead) + QTest::mouseClick(view.viewport(), Qt::RightButton, 0, view.mapFromScene(item->scenePos())); + if (!item->dead) + QTest::mouseMove(view.viewport(), view.mapFromScene(item->scenePos() + QPointF(20, -20))); + if (!item->dead) + item->setFocus(); + if (!item->dead) + item->clearFocus(); + if (!item->dead) + item->setFocus(); + if (!item->dead) + QTest::keyPress(view.viewport(), Qt::Key_A); + if (!item->dead) + QTest::keyRelease(view.viewport(), Qt::Key_A); + if (!item->dead) + QTest::keyPress(view.viewport(), Qt::Key_A); + if (!item->dead) + QTest::keyRelease(view.viewport(), Qt::Key_A); + } +} + +class ItemPaintsOutsideShape : public QGraphicsItem +{ +public: + QRectF boundingRect() const + { + return QRectF(0, 0, 100, 100); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + 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.itemAt(5, 5), (QGraphicsItem *)parent); + QCOMPARE(scene.itemAt(15, 5), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(5, 15), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(60, 60), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(140, 60), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(60, 140), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(140, 140), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(75, 75), (QGraphicsItem *)child2); + QCOMPARE(scene.itemAt(75, 100), (QGraphicsItem *)child1); + QCOMPARE(scene.itemAt(100, 75), (QGraphicsItem *)child1); + +#if 1 + 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()); +#else + QGraphicsView view(&scene); + view.show(); + QTest::qWait(5000); +#endif +} + +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.itemAt(25,25), (QGraphicsItem *)parent); + QCOMPARE(scene.itemAt(75,75), (QGraphicsItem *)child); + QCOMPARE(scene.itemAt(125,125), (QGraphicsItem *)grandchild); + QCOMPARE(scene.itemAt(175,175), (QGraphicsItem *)0); + + // 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.itemAt(25,25), (QGraphicsItem *)child); + QCOMPARE(scene.itemAt(75,75), (QGraphicsItem *)grandchild); + QCOMPARE(scene.itemAt(125,125), (QGraphicsItem *)grandchild); + QCOMPARE(scene.itemAt(175,175), (QGraphicsItem *)0); +} + +class MyProxyWidget : public QGraphicsProxyWidget +{ +public: + MyProxyWidget(QGraphicsItem *parent) : QGraphicsProxyWidget(parent) + { + painted = false; + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QGraphicsProxyWidget::paint(painter, option, widget); + painted = true; + } + bool painted; +}; + +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 ); + QApplication::setActiveWindow(&view); + view.show(); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&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: + ParentItem(qreal x, qreal y, qreal width, qreal height) + : QGraphicsRectItem(x, y, width, height) {} + + QPainterPath shape() const + { + 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); + + QPoint controlPoints[17] = { + QPoint(5, 5) , QPoint(95, 5) , QPoint(205, 5) , QPoint(295, 5) , + QPoint(5, 95) , QPoint(95, 95) , QPoint(205, 95) , QPoint(295, 95) , + QPoint(150, 150), + QPoint(5, 205), QPoint(95, 205), QPoint(205, 205), QPoint(295, 205), + QPoint(5, 295), QPoint(95, 295), QPoint(205, 295), QPoint(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 (int i = 0; i < 17; ++i) { \ + QPoint controlPoint = controlPoints[i]; \ + 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.count(); + + for (int i = 0; i < 5; ++i) { + QString clipString; + QString childString; + switch (i) { + case 0: + // All children stacked in front. + childString = QLatin1String("ChildrenInFront.png"); + foreach (QGraphicsItem *child, children) + child->setFlag(QGraphicsItem::ItemStacksBehindParent, false); + break; + case 1: + // All children stacked behind. + childString = QLatin1String("ChildrenBehind.png"); + foreach (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.itemAt(10, 10), (QGraphicsItem *)leftRectItem); + QCOMPARE(scene.itemAt(90, 90), (QGraphicsItem *)rightRectItem); + + // The ellipse clips the rects now. + clipItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + // The rect items are no longer visible at these points. + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)0); + if (sizeof(qreal) != sizeof(double)) + QSKIP("This fails due to internal rounding errors", SkipSingle); + QCOMPARE(scene.itemAt(90, 90), (QGraphicsItem *)0); +} + +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(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 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(0); + 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 < 2; ++i) { + QGraphicsItem::GraphicsItemFlag flag = !i ? QGraphicsItem::ItemClipsChildrenToShape + : QGraphicsItem::ItemIgnoresTransformations; + int ancestorFlag = !i ? QGraphicsItemPrivate::AncestorClipsChildren + : QGraphicsItemPrivate::AncestorIgnoresTransformations; + + 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(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 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(0); + 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() +{ + QGraphicsItem *item1 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100)); + item1->setZValue(1); + item1->setFlag(QGraphicsItem::ItemIgnoresTransformations); + item1->rotate(45); + ((QGraphicsEllipseItem *)item1)->setBrush(Qt::red); + + QGraphicsItem *item2 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100)); + item2->setParentItem(item1); + item2->rotate(45); + item2->setPos(100, 0); + ((QGraphicsEllipseItem *)item2)->setBrush(Qt::green); + + QGraphicsItem *item3 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100)); + item3->setParentItem(item2); + item3->setPos(100, 0); + ((QGraphicsEllipseItem *)item3)->setBrush(Qt::blue); + + QGraphicsScene scene(-500, -500, 1000, 1000); + scene.addItem(item1); + + QWidget topLevel; + QGraphicsView view(&scene,&topLevel); + 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_WS_MAC) || 1 + view.setBackgroundBrush(QBrush(Qt::black, Qt::DiagCrossPattern)); +#endif + + QTest::qWaitForWindowShown(&view); + + 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); + QTest::qWait(25); + } +} + +class ContextMenuItem : public QGraphicsRectItem +{ +public: + ContextMenuItem() + : ignoreEvent(true), gotEvent(false), eventWasAccepted(false) + { } + bool ignoreEvent; + bool gotEvent; + bool eventWasAccepted; +protected: + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) + { + 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.setAlignment(Qt::AlignLeft | Qt::AlignTop); + view.show(); + view.resize(200, 200); + QTest::qWaitForWindowShown(&view); + QTest::qWait(20); + + QContextMenuEvent event(QContextMenuEvent::Mouse, QPoint(10, 10), + view.viewport()->mapToGlobal(QPoint(10, 10))); + event.ignore(); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(!event.isAccepted()); + + scene.addItem(bottomItem); + topItem->ignoreEvent = true; + bottomItem->ignoreEvent = true; + + QApplication::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; + + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + QCOMPARE(topItem->gotEvent, true); + QCOMPARE(bottomItem->gotEvent, false); + QCOMPARE(topItem->eventWasAccepted, true); +} + +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); + qApp->sendEvent(&scene, &event); + } + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(rect->pos(), QPointF(0, 0)); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setButtons(Qt::LeftButton); + event.setScenePos(QPointF(10, 10)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(rect->pos(), QPointF(10, 10)); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setButtons(Qt::RightButton); + event.setScenePos(QPointF(20, 20)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(rect->pos(), QPointF(10, 10)); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setButtons(Qt::LeftButton); + event.setScenePos(QPointF(30, 30)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(rect->pos(), QPointF(30, 30)); +} + +class ItemAddScene : public QGraphicsScene +{ + Q_OBJECT +public: + ItemAddScene() + { + QTimer::singleShot(500, this, SLOT(newTextItem())); + } + +public slots: + void newTextItem() + { + // Add a text item + QGraphicsItem *item = new QGraphicsTextItem("This item will not ensure that it's visible", 0, this); + item->setPos(.0, .0); + item->show(); + } +}; + +void tst_QGraphicsItem::task141694_textItemEnsureVisible() +{ + ItemAddScene scene; + scene.setSceneRect(-1000, -1000, 2000, 2000); + + QGraphicsView view(&scene); + view.setFixedSize(200, 200); + view.show(); + QTest::qWaitForWindowShown(&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.setFixedSize(200, 200); + view.show(); + + QGraphicsSceneMouseEvent event1(QEvent::GraphicsSceneMousePress); + event1.setScenePos(QPointF(0, 0)); + event1.setButton(Qt::LeftButton); + event1.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + + QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove); + event2.setScenePos(QPointF(10, 10)); + event2.setButton(Qt::LeftButton); + event2.setButtons(Qt::LeftButton); + QApplication::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)); + + QVERIFY(!scene.items(9, 9, 2, 2, Qt::IntersectsItemShape).isEmpty()); + QVERIFY(!scene.items(9, 9, 2, 2, Qt::ContainsItemShape).isEmpty()); + QVERIFY(!scene.items(9, 9, 2, 2, Qt::IntersectsItemBoundingRect).isEmpty()); + QVERIFY(!scene.items(9, 9, 2, 2, Qt::ContainsItemBoundingRect).isEmpty()); +} + +void tst_QGraphicsItem::task240400_clickOnTextItem_data() +{ + QTest::addColumn<int>("flags"); + QTest::addColumn<int>("textFlags"); + QTest::newRow("editor, noflags") << 0 << int(Qt::TextEditorInteraction); + QTest::newRow("editor, movable") << int(QGraphicsItem::ItemIsMovable) << int(Qt::TextEditorInteraction); + QTest::newRow("editor, selectable") << int(QGraphicsItem::ItemIsSelectable) << int(Qt::TextEditorInteraction); + QTest::newRow("editor, movable | selectable") << int(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable) + << int(Qt::TextEditorInteraction); + QTest::newRow("noninteractive, noflags") << 0 << int(Qt::NoTextInteraction); + QTest::newRow("noninteractive, movable") << int(QGraphicsItem::ItemIsMovable) << int(Qt::NoTextInteraction); + QTest::newRow("noninteractive, selectable") << int(QGraphicsItem::ItemIsSelectable) << int(Qt::NoTextInteraction); + QTest::newRow("noninteractive, movable | selectable") << int(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable) + << int(Qt::NoTextInteraction); +} + +void tst_QGraphicsItem::task240400_clickOnTextItem() +{ + QFETCH(int, flags); + QFETCH(int, textFlags); + + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsTextItem *item = scene.addText("Hello"); + item->setFlags(QGraphicsItem::GraphicsItemFlags(flags)); + item->setTextInteractionFlags(Qt::TextInteractionFlags(textFlags)); + bool focusable = (item->flags() & 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); + QApplication::sendEvent(&scene, &event); + } + if (flags || textFlags) + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + else + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setScenePos(item->sceneBoundingRect().topLeft() + QPointF(0.1, 0.1)); + event.setButton(Qt::LeftButton); + event.setButtons(0); + QApplication::sendEvent(&scene, &event); + } + if (textFlags) + QVERIFY(item->hasFocus()); + else + QVERIFY(!item->hasFocus()); + QVERIFY(!scene.mouseGrabberItem()); + bool selectable = (flags & 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); + QApplication::sendEvent(&scene, &event); + } + if (flags || textFlags) + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + else + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setScenePos(item->sceneBoundingRect().center()); + event.setButton(Qt::LeftButton); + event.setButtons(0); + QApplication::sendEvent(&scene, &event); + } + if (textFlags) + QVERIFY(item->hasFocus()); + else + QVERIFY(!item->hasFocus()); + QVERIFY(!scene.mouseGrabberItem()); + + QVERIFY(selectable ? item->isSelected() : !item->isSelected()); + + // + if (textFlags & Qt::TextEditorInteraction) + QVERIFY(item->textCursor().columnNumber() > column); + else + QCOMPARE(item->textCursor().columnNumber(), 0); +} + +class TextItem : public QGraphicsSimpleTextItem +{ +public: + TextItem(const QString& text) : QGraphicsSimpleTextItem(text) + { + updates = 0; + } + + void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) + { + updates++; + QGraphicsSimpleTextItem::paint(painter, option, widget); + } + + int updates; +}; + +void tst_QGraphicsItem::ensureUpdateOnTextItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(25); + TextItem *text1 = new TextItem(QLatin1String("123")); + scene.addItem(text1); + qApp->processEvents(); + QTRY_COMPARE(text1->updates,1); + + //same bouding rect but we have to update + text1->setText(QLatin1String("321")); + qApp->processEvents(); + 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)); +#if 0 + { + QRegion r; + r += QRect(0, 0, 6, 2); + r += QRect(0, 2, 8, 2); + r += QRect(0, 4, 10, 2); + r += QRect(2, 6, 8, 2); + r += QRect(4, 8, 6, 2); + QTest::newRow("(0, 0, 10, 10) | 0.5 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 10) << qreal(0.5) << QTransform() << r; + } + { + QRegion r; + r += QRect(0, 0, 4, 1); r += QRect(0, 1, 5, 1); r += QRect(0, 2, 6, 1); + r += QRect(0, 3, 7, 1); r += QRect(1, 4, 7, 1); r += QRect(2, 5, 7, 1); + r += QRect(3, 6, 7, 1); r += QRect(4, 7, 6, 1); r += QRect(5, 8, 5, 1); + r += QRect(6, 9, 4, 1); + QTest::newRow("(0, 0, 10, 10) | 1.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 10) << qreal(1.0) << QTransform() << r; + } +#endif + 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); + 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->scale(2, 2); + child->rotate(90); + + 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->scale(10, 5); + parent->rotate(-180); + parent->shear(2, 3); + + brother->setParentItem(parent); + sister->setParentItem(parent); + + brother->setPos(10, 10); + brother->scale(2, 2); + brother->rotate(90); + sister->setPos(10, 10); + sister->scale(2, 2); + sister->rotate(90); + + 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->scale(2, 2); + stranger1->rotate(90); + stranger2->setPos(10, 10); + stranger2->scale(2, 2); + stranger2->rotate(90); + + 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); + + QGraphicsRectItem *p = new QGraphicsRectItem; + QGraphicsRectItem *c1 = new QGraphicsRectItem(p); + 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); + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + 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); + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 1); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 0); + QCOMPARE(grandChild->repaints, 0); + + RESET_REPAINT_COUNTERS + + child->setOpacity(1.0); + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 1); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + + RESET_REPAINT_COUNTERS + + parent->setOpacity(0.0); + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 1); + QCOMPARE(parent->repaints, 0); + QCOMPARE(child->repaints, 0); + QCOMPARE(grandChild->repaints, 0); + + RESET_REPAINT_COUNTERS + + parent->setOpacity(1.0); + QTest::qWait(10); + 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); + QTest::qWait(10); + 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); + QTRY_COMPARE(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.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.repaints > 0); + + view.reset(); + parent->setOpacity(0.0); + + QTest::qWait(20); + + // 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(QList<QGraphicsItem *> *paintedItems, const QRectF &rect, QGraphicsItem *parent = 0) + : QGraphicsRectItem(rect, parent), paintedItems(paintedItems) + { } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QGraphicsRectItem::paint(painter, option, widget); + paintedItems->append(this); + } + +private: + QList<QGraphicsItem *> *paintedItems; +}; + +void tst_QGraphicsItem::itemStacksBehindParent() +{ + 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.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(!paintedItems.isEmpty()); + QTest::qWait(100); + paintedItems.clear(); + view.viewport()->update(); + QApplication::processEvents(); + QTRY_COMPARE(scene.items(0, 0, 100, 100), (QList<QGraphicsItem *>() + << grandChild111 << child11 + << grandChild121 << child12 << parent1 + << grandChild211 << child21 + << grandChild221 << child22 << parent2)); + QTRY_COMPARE(paintedItems, QList<QGraphicsItem *>() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << parent1 << child12 << grandChild121 + << child11 << grandChild111); + + child11->setFlag(QGraphicsItem::ItemStacksBehindParent); + scene.update(); + paintedItems.clear(); + QApplication::processEvents(); + + QTRY_COMPARE(scene.items(0, 0, 100, 100), (QList<QGraphicsItem *>() + << grandChild121 << child12 << parent1 + << grandChild111 << child11 + << grandChild211 << child21 + << grandChild221 << child22 << parent2)); + QCOMPARE(paintedItems, QList<QGraphicsItem *>() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << child11 << grandChild111 + << parent1 << child12 << grandChild121); + + child12->setFlag(QGraphicsItem::ItemStacksBehindParent); + paintedItems.clear(); + scene.update(); + QApplication::processEvents(); + + QTRY_COMPARE(scene.items(0, 0, 100, 100), (QList<QGraphicsItem *>() + << parent1 << grandChild111 << child11 + << grandChild121 << child12 + << grandChild211 << child21 + << grandChild221 << child22 << parent2)); + QCOMPARE(paintedItems, QList<QGraphicsItem *>() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << child12 << grandChild121 + << child11 << grandChild111 << parent1); +} + +class ClippingAndTransformsScene : public QGraphicsScene +{ +public: + QList<QGraphicsItem *> drawnItems; +protected: + void drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], + const QStyleOptionGraphicsItem options[], QWidget *widget = 0) + { + 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.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(25); + + QList<QGraphicsItem *> expected; + 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)) + { + setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + } + + QTransform x; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget = 0) + { + 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; + QGraphicsRectItem *rect = scene.addRect(0, 0, 100, 100); + QGraphicsRectItem *rect2 = scene.addRect(0, 0, 100, 100); + rect2->setParentItem(rect); + rect2->rotate(90); + rect->translate(0, 50); + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + rect->translate(0, 100); + QTransform x; + x.translate(0, 150); + x.rotate(90); + QCOMPARE(rect2->sceneTransform(), x); + + scene.removeItem(rect); + + //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)); + + rect4->setParentItem(rect3); + rect4->setPos(QPointF(10,10)); + + 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)); + + rect6->setParentItem(rect5); + rect6->setPos(QPointF(10,10)); + //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() +{ + 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.setLayout(layout); + widget.show(); + QTest::qWaitForWindowShown(&widget); + QTest::qWait(2000); + + QTRY_VERIFY(scene.isActive()); + + dial1->setFocus(); + QTest::qWait(15); + QTRY_VERIFY(dial1->hasFocus()); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(15); + QTRY_VERIFY(view->hasFocus()); + QTRY_VERIFY(item->hasFocus()); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(15); + + 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() +{ + QGraphicsScene scene(0, 0, 100, 100); + QGraphicsView view(&scene); + view.resize(150, 150); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + + // Increase the probability of window activation + // not causing another repaint of test items. + QTest::qWait(50); + + 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); + QTest::qWait(10); + + 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); + QTest::qWait(25); + 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->translate(10, 10); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 2); + QCOMPARE(testerChild->repaints, 2); + QCOMPARE(testerChild2->repaints, 2); + + // Rotating results in a repaint. + tester->rotate(45); + QTest::qWait(25); + 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 + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 4); + QCOMPARE(testerChild->repaints, 4); + QCOMPARE(testerChild2->repaints, 3); + + // Rotating items with ItemCoordinateCache doesn't cause a repaint. + tester->rotate(22); + testerChild->rotate(22); + testerChild2->rotate(22); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 4); + QTRY_COMPARE(testerChild->repaints, 4); + QTRY_COMPARE(testerChild2->repaints, 3); + tester->resetTransform(); + testerChild->resetTransform(); + testerChild2->resetTransform(); + + // Explicit update causes a repaint. + tester->update(0, 0, 5, 5); + QTest::qWait(25); + 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); + QTRY_COMPARE(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)); + QTest::qWait(25); + 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)); + QTest::qWait(20); + 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)); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 7); + QCOMPARE(testerChild->repaints, 6); + QCOMPARE(testerChild2->repaints, 4); + + // Scaling the view does not cause a repaint. + view.scale(0.7, 0.7); + QTest::qWait(25); + QTRY_COMPARE(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); + QTest::qWait(25); + 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()); + QTest::qWait(25); + 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)); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 10); + QCOMPARE(testerChild->repaints, 9); + QCOMPARE(testerChild2->repaints, 5); + + // Moving the middle item should case a repaint even if it's a move, + // because the parent is rotated with a perspective. + testerChild->setPos(1, 1); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 11); + QTRY_COMPARE(testerChild->repaints, 10); + QTRY_COMPARE(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); + QTRY_COMPARE(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); + QTRY_COMPARE(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() +{ + QGraphicsScene scene(0, 0, 100, 100); + QGraphicsView view(&scene); + view.resize(150, 150); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + + // Increase the probability of window activation + // not causing another repaint of test items. + QTest::qWait(50); + + EventTester *tester = new EventTester; + scene.addItem(tester); + QTest::qWait(10); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from NoCache to NoCache (no repaint) + tester->setCacheMode(QGraphicsItem::NoCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from NoCache to DeviceCoordinateCache (no repaint) + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from DeviceCoordinateCache to DeviceCoordinateCache (no repaint) + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from DeviceCoordinateCache to NoCache (no repaint) + tester->setCacheMode(QGraphicsItem::NoCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from NoCache to ItemCoordinateCache (repaint) + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 2); + + // Switching from ItemCoordinateCache to ItemCoordinateCache (no repaint) + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 2); + + // Switching from ItemCoordinateCache to ItemCoordinateCache with different size (repaint) + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(100, 100)); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 3); + + // Switching from ItemCoordinateCache to NoCache (repaint) + tester->setCacheMode(QGraphicsItem::NoCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 4); + + // Switching from DeviceCoordinateCache to ItemCoordinateCache (repaint) + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 4); + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 5); + + // Switching from ItemCoordinateCache to DeviceCoordinateCache (repaint) + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(50); + 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.show(); + QTest::qWaitForWindowShown(&view); + + QTest::qWait(12); + 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(); + QTest::qWait(12); + QCOMPARE(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 = 0) + { + QGraphicsRectItem::paint(painter, option, widget); + painter->drawText(boundingRect(), Qt::AlignCenter, QString("%1x%2\n%3x%4").arg(p.x()).arg(p.y()).arg(sp.x()).arg(sp.y())); + } + +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) + { + 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->rotate(rotation1); + rect2->rotate(rotation2); + rect3->rotate(rotation3); + 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; + MyGraphicsView view(&scene,&topLevel); + + topLevel.resize(300, 300); + topLevel.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + EventTester *item = new EventTester; + scene.addItem(item); + QTest::qWait(100); // Make sure all pending updates are processed. + item->repaints = 0; + + item->update(); // Item marked as dirty + scene.update(); // Entire scene marked as dirty + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + + // Make sure the dirty state from the previous update is reset so that + // the item don't think it is already dirty and discards this update. + item->update(); + qApp->processEvents(); + QCOMPARE(item->repaints, 2); + + // Make sure a partial update doesn't cause a full update to be discarded. + view.reset(); + item->repaints = 0; + item->update(QRectF(0, 0, 5, 5)); + item->update(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + QRect itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform()) + .mapRect(item->boundingRect()).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) + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + + // Make sure the area occupied by an item is repainted when hiding it. + view.reset(); + item->repaints = 0; + item->update(); // Full update; all sub-sequent update requests are discarded. + item->hide(); // visible set to 0. ignoreVisible must be set to 1; the item won't be processed otherwise. + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + // Make sure item is repainted when shown (after being hidden). + view.reset(); + item->repaints = 0; + item->show(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + item->repaints = 0; + item->hide(); + qApp->processEvents(); + view.reset(); + const QPointF originalPos = item->pos(); + item->setPos(5000, 5000); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + qApp->processEvents(); + + item->setPos(originalPos); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + item->show(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + QGraphicsViewPrivate *viewPrivate = static_cast<QGraphicsViewPrivate *>(qt_widget_private(&view)); + item->setPos(originalPos + QPoint(50, 50)); + viewPrivate->updateAll(); + QVERIFY(viewPrivate->fullUpdatePending); + QTest::qWait(50); + item->repaints = 0; + view.reset(); + item->setPos(originalPos); + QTest::qWait(50); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + 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); + QTest::qWait(50); + itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform()) + .mapRect(item->boundingRect()).toAlignedRect(); + expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2); + view.reset(); + item->repaints = 0; + parent->translate(-400, 0); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 1); + QCOMPARE(view.paintedRegion, expectedRegion); + view.reset(); + item->repaints = 0; + parent->translate(400, 0); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + QCOMPARE(view.paintedRegion, expectedRegion); + QCOMPARE(view.paintedRegion, expectedRegion); +} + +void tst_QGraphicsItem::setTransformProperties_data() +{ + QTest::addColumn<QPointF>("origin"); + QTest::addColumn<qreal>("rotation"); + QTest::addColumn<qreal>("scale"); + + QTest::newRow("nothing") << QPointF() << qreal(0.0) << qreal(1.0); + + QTest::newRow("rotation") << QPointF() << qreal(42.2) << qreal(1.0); + + QTest::newRow("rotation dicentred") << QPointF(qreal(22.3), qreal(-56.2)) + << qreal(-2578.2) + << qreal(1.0); + + QTest::newRow("Scale") << QPointF() << qreal(0.0) + << qreal(6); + + QTest::newRow("Everything dicentred") << QPointF(qreal(22.3), qreal(-56.2)) << qreal(-175) << qreal(196); +} + +/** + * the normal QCOMPARE doesn't work because it doesn't use qFuzzyCompare + */ +#define QCOMPARE_TRANSFORM(X1, X2) QVERIFY(((X1)*(X2).inverted()).isIdentity()) + +void tst_QGraphicsItem::setTransformProperties() +{ + QFETCH(QPointF,origin); + QFETCH(qreal,rotation); + QFETCH(qreal,scale); + + QTransform result; + result.translate(origin.x(), origin.y()); + result.rotate(rotation, Qt::ZAxis); + result.scale(scale, scale); + result.translate(-origin.x(), -origin.y()); + + QGraphicsScene scene; + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + scene.addItem(item); + + item->setRotation(rotation); + item->setScale(scale); + item->setTransformOriginPoint(origin); + + QCOMPARE(item->rotation(), rotation); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + + QCOMPARE(QTransform(), item->transform()); + QCOMPARE(result, item->sceneTransform()); + + //----------------------------------------------------------------- + //Change the rotation Z + item->setRotation(45); + QTransform result2; + result2.translate(origin.x(), origin.y()); + result2.rotate(45); + result2.scale(scale, scale); + result2.translate(-origin.x(), -origin.y()); + + QCOMPARE(item->rotation(), 45.); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + + QCOMPARE(QTransform(), item->transform()); + QCOMPARE(result2, item->sceneTransform()); + + //----------------------------------------------------------------- + // calling setTransform() and setPos should change the sceneTransform + item->setTransform(result); + item->setPos(100, -150.5); + + QCOMPARE(item->rotation(), 45.); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + QCOMPARE(result, item->transform()); + + QTransform result3(result); + + result3.translate(origin.x(), origin.y()); + result3.rotate(45); + result3.scale(scale, scale); + result3.translate(-origin.x(), -origin.y()); + + result3 *= QTransform::fromTranslate(100, -150.5); //the pos; + + QCOMPARE(result3, item->sceneTransform()); + + //----------------------------------------------------- + // setting the propertiees should be the same as setting a transform + {//with center origin on the matrix + QGraphicsRectItem *item1 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item1); + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item2); + + item1->setPos(12.3, -5); + item2->setPos(12.3, -5); + item1->setRotation(rotation); + item1->setScale(scale); + item1->setTransformOriginPoint(origin); + + item2->setTransform(result); + + QCOMPARE_TRANSFORM(item1->sceneTransform(), item2->sceneTransform()); + + QCOMPARE_TRANSFORM(item1->itemTransform(item2), QTransform()); + QCOMPARE_TRANSFORM(item2->itemTransform(item1), QTransform()); + } +} + +class MyStyleOptionTester : public QGraphicsRectItem +{ +public: + MyStyleOptionTester(const QRectF &rect) + : QGraphicsRectItem(rect), startTrack(false) + {} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) + { + if (startTrack) { + //Doesn't use the extended style option so the exposed rect is the boundingRect + if (!(flags() & QGraphicsItem::ItemUsesExtendedStyleOption)) { + QCOMPARE(option->exposedRect, boundingRect()); + QCOMPARE(option->matrix, QMatrix()); + } else { + QVERIFY(option->exposedRect != QRect()); + QVERIFY(option->exposedRect != boundingRect()); + QCOMPARE(option->matrix, sceneTransform().toAffine()); + } + } + QGraphicsRectItem::paint(painter, option, widget); + } + bool startTrack; +}; + +void tst_QGraphicsItem::itemUsesExtendedStyleOption() +{ + QGraphicsScene scene(0, 0, 300, 300); + QGraphicsPixmapItem item; + item.setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); + QCOMPARE(item.flags(), QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemUsesExtendedStyleOption)); + item.setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false); + QCOMPARE(item.flags(), 0); + + //We now test the content of the style option + MyStyleOptionTester *rect = new MyStyleOptionTester(QRect(0, 0, 100, 100)); + scene.addItem(rect); + rect->setPos(200, 200); + QWidget topLevel; + QGraphicsView view(&scene, &topLevel); + topLevel.setWindowFlags(Qt::X11BypassWindowManagerHint); + rect->startTrack = false; + topLevel.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(60); + rect->startTrack = true; + rect->update(10, 10, 10, 10); + QTest::qWait(60); + rect->startTrack = false; + rect->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); + QVERIFY((rect->flags() & QGraphicsItem::ItemUsesExtendedStyleOption)); + QTest::qWait(60); + rect->startTrack = true; + rect->update(10, 10, 10, 10); + QTest::qWait(60); +} + +void tst_QGraphicsItem::itemSendsGeometryChanges() +{ + ItemChangeTester item; + item.setFlags(0); + 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)); + + QCOMPARE(item.changes, QList<QGraphicsItem::GraphicsItemChange>() + << 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); +} + +// Make sure we update moved items correctly. +void tst_QGraphicsItem::moveItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-50, -50, 200, 200); + + MyGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + EventTester *parent = new EventTester; + EventTester *child = new EventTester(parent); + EventTester *grandChild = new EventTester(child); + +#define RESET_COUNTERS \ + parent->repaints = 0; \ + child->repaints = 0; \ + grandChild->repaints = 0; \ + view.reset(); + + scene.addItem(parent); + QTest::qWait(100); + + RESET_COUNTERS + + // Item's boundingRect: (-10, -10, 20, 20). + QRect parentDeviceBoundingRect = parent->deviceTransform(view.viewportTransform()) + .mapRect(parent->boundingRect()).toAlignedRect() + .adjusted(-2, -2, 2, 2); // Adjusted for antialiasing. + + parent->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(view.repaints, 1); + QRegion expectedParentRegion = parentDeviceBoundingRect; // old position + parentDeviceBoundingRect.translate(20, 20); + expectedParentRegion += parentDeviceBoundingRect; // new position + COMPARE_REGIONS(view.paintedRegion, expectedParentRegion); + + RESET_COUNTERS + + child->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(view.repaints, 1); + const QRegion expectedChildRegion = expectedParentRegion.translated(20, 20); + COMPARE_REGIONS(view.paintedRegion, expectedChildRegion); + + RESET_COUNTERS + + grandChild->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + QCOMPARE(view.repaints, 1); + const QRegion expectedGrandChildRegion = expectedParentRegion.translated(40, 40); + COMPARE_REGIONS(view.paintedRegion, expectedGrandChildRegion); + + RESET_COUNTERS + + parent->translate(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + QCOMPARE(view.repaints, 1); + expectedParentRegion.translate(20, 20); + expectedParentRegion += expectedChildRegion.translated(20, 20); + expectedParentRegion += expectedGrandChildRegion.translated(20, 20); + 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.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(200); + view.reset(); + + QRectF brect = item->boundingRect(); + // Do same adjustments as in qgraphicsscene.cpp + if (!brect.width()) + brect.adjust(qreal(-0.00001), 0, qreal(0.00001), 0); + if (!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(); + QTest::qWait(10); + QTRY_COMPARE(view.paintedRegion, expectedRegion); + view.reset(); + + // Old position: (50, 50) + item->setPos(50, 100); + expectedRegion += expectedRegion.translated(0, 50); + QTest::qWait(10); + QCOMPARE(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 (PlatformQuirks::isAutoMaximizing()) + QSKIP("Skipped because Platform is auto maximizing", SkipAll); + + _paintedItems.clear(); + + QGraphicsScene scene; + QGraphicsItem *grid[100][100]; + for (int x = 0; x < 100; ++x) { + for (int y = 0; y < 100; ++y) { + PainterItem *item = new PainterItem; + item->setPos(x * 25, y * 25); + item->setData(0, QString("%1x%2").arg(x).arg(y)); + grid[x][y] = item; + scene.addItem(item); + } + } + + PainterItem *item1 = new PainterItem; + PainterItem *item2 = new PainterItem; + item1->setData(0, "item1"); + item2->setData(0, "item2"); + scene.addItem(item1); + scene.addItem(item2); + + QGraphicsView view(&scene); + view.setResizeAnchor(QGraphicsView::NoAnchor); + view.setTransformationAnchor(QGraphicsView::NoAnchor); + view.resize(120, 100); + view.setFrameStyle(0); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + _paintedItems.clear(); + + view.viewport()->repaint(); +#if defined(Q_WS_MAC) + // There's no difference between repaint and update on the Mac, + // so we have to process events here to make sure we get the event. + QTest::qWait(100); +#endif + + QCOMPARE(_paintedItems, QList<QGraphicsItem *>() + << grid[0][0] << grid[0][1] << grid[0][2] << grid[0][3] + << grid[1][0] << grid[1][1] << grid[1][2] << grid[1][3] + << grid[2][0] << grid[2][1] << grid[2][2] << grid[2][3] + << grid[3][0] << grid[3][1] << grid[3][2] << grid[3][3] + << grid[4][0] << grid[4][1] << grid[4][2] << grid[4][3] + << item1 << item2); +} + +void tst_QGraphicsItem::itemHasNoContents() +{ + PainterItem *item1 = new PainterItem; + PainterItem *item2 = new PainterItem; + item2->setParentItem(item1); + item2->setPos(50, 50); + item1->setFlag(QGraphicsItem::ItemHasNoContents); + item1->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + QGraphicsScene scene; + scene.addItem(item1); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(!_paintedItems.isEmpty()); + + _paintedItems.clear(); + + view.viewport()->repaint(); +#ifdef Q_WS_MAC + // There's no difference between update() and repaint() on the Mac, + // so we have to process events here to make sure we get the event. + QTest::qWait(10); +#endif + + QTRY_COMPARE(_paintedItems, QList<QGraphicsItem *>() << item2); +} + +void tst_QGraphicsItem::hitTestUntransformableItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + // Confuse the BSP with dummy items. + QGraphicsRectItem *dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100, -100); + scene.addItem(dummy); + for (int i = 0; i < 100; ++i) { + QGraphicsItem *parent = dummy; + dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100 + i, -100 + i); + dummy->setParentItem(parent); + } + + QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, 20, 20); + item1->setPos(-200, -200); + + QGraphicsRectItem *item2 = new QGraphicsRectItem(0, 0, 20, 20); + item2->setFlag(QGraphicsItem::ItemIgnoresTransformations); + item2->setParentItem(item1); + item2->setPos(200, 200); + + QGraphicsRectItem *item3 = new QGraphicsRectItem(0, 0, 20, 20); + item3->setParentItem(item2); + item3->setPos(80, 80); + + scene.addItem(item1); + QTest::qWait(100); + + QList<QGraphicsItem *> items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast<QGraphicsItem*>(item3)); + + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + QTest::qWait(100); + + items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast<QGraphicsItem*>(item3)); +} + +void tst_QGraphicsItem::hitTestGraphicsEffectItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + + QWidget toplevel; + + QGraphicsView view(&scene, &toplevel); + toplevel.resize(300, 300); + toplevel.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&toplevel); +#endif + QTest::qWait(100); + + // Confuse the BSP with dummy items. + QGraphicsRectItem *dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100, -100); + scene.addItem(dummy); + for (int i = 0; i < 100; ++i) { + QGraphicsItem *parent = dummy; + dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100 + i, -100 + i); + dummy->setParentItem(parent); + } + + const QRectF itemBoundingRect(0, 0, 20, 20); + EventTester *item1 = new EventTester; + item1->br = itemBoundingRect; + item1->setPos(-200, -200); + 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); + QTest::qWait(100); + + item1->repaints = 0; + item2->repaints = 0; + item3->repaints = 0; + + // Apply shadow effect to the entire sub-tree. + QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect; + shadow->setOffset(-20, -20); + item1->setGraphicsEffect(shadow); + QTest::qWait(50); + + // Make sure all visible items are repainted. + QCOMPARE(item1->repaints, 1); + QCOMPARE(item2->repaints, 1); + QCOMPARE(item3->repaints, 1); + + // Make sure an item doesn't respond to a click on its shadow. + QList<QGraphicsItem *> items = scene.items(QPointF(75, 75)); + QVERIFY(items.isEmpty()); + items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast<QGraphicsItem *>(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); + QApplication::sendEvent(&scene, &activate); + + QGraphicsItem *item = scene.addRect(0, 0, 10, 10); + item->setFlag(QGraphicsItem::ItemIsFocusable); + QVERIFY(!item->focusProxy()); + + QGraphicsItem *item2 = scene.addRect(0, 0, 10, 10); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFocusProxy(item2); + QCOMPARE(item->focusProxy(), item2); + + item->setFocus(); + QVERIFY(item->hasFocus()); + QVERIFY(item2->hasFocus()); + + // Try to make a focus chain loop + QString err; + QTextStream stream(&err); + stream << "QGraphicsItem::setFocusProxy: " + << (void*)item << " is already in the focus proxy chain" << flush; + QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData()); + item2->setFocusProxy(item); // fails + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + + // Try to assign self as focus proxy + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::setFocusProxy: cannot assign self as focus proxy"); + item->setFocusProxy(item); // fails + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + + // Reset the focus proxy + item->setFocusProxy(0); + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + QVERIFY(!item->hasFocus()); + QVERIFY(item2->hasFocus()); + + // Test deletion + item->setFocusProxy(item2); + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + delete item2; + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + + // Test event delivery + item2 = scene.addRect(0, 0, 10, 10); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFocusProxy(item2); + item->clearFocus(); + + EventSpy focusInSpy(&scene, item, QEvent::FocusIn); + EventSpy focusOutSpy(&scene, item, QEvent::FocusOut); + EventSpy focusInSpy2(&scene, item2, QEvent::FocusIn); + EventSpy focusOutSpy2(&scene, item2, QEvent::FocusOut); + QCOMPARE(focusInSpy.count(), 0); + QCOMPARE(focusOutSpy.count(), 0); + QCOMPARE(focusInSpy2.count(), 0); + QCOMPARE(focusOutSpy2.count(), 0); + + item->setFocus(); + QCOMPARE(focusInSpy.count(), 0); + QCOMPARE(focusInSpy2.count(), 1); + item->clearFocus(); + QCOMPARE(focusOutSpy.count(), 0); + QCOMPARE(focusOutSpy2.count(), 1); + + // Test two items proxying one item. + QGraphicsItem *item3 = scene.addRect(0, 0, 10, 10); + item3->setFlag(QGraphicsItem::ItemIsFocusable); + item3->setFocusProxy(item2); // item and item3 use item2 as proxy + + QCOMPARE(item->focusProxy(), item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + QCOMPARE(item3->focusProxy(), item2); + delete item2; + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + QCOMPARE(item3->focusProxy(), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::subFocus() +{ + // Construct a text item that's not part of a scene (yet) + // and has no parent. Setting focus on it will not make + // the item gain input focus; that requires a scene. But + // it does set subfocus, indicating that the item wishes + // to gain focus later. + QGraphicsTextItem *text = new QGraphicsTextItem("Hello"); + text->setTextInteractionFlags(Qt::TextEditorInteraction); + QVERIFY(!text->hasFocus()); + text->setFocus(); + QVERIFY(!text->hasFocus()); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text); + + // Add a sibling. + QGraphicsTextItem *text2 = new QGraphicsTextItem("Hi"); + text2->setTextInteractionFlags(Qt::TextEditorInteraction); + text2->setPos(30, 30); + + // Add both items to a scene and check that it's text that + // got input focus. + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(text); + scene.addItem(text2); + QVERIFY(text->hasFocus()); + + text->setData(0, "text"); + text2->setData(0, "text2"); + + // Remove text2 and set subfocus on it. Then readd. Reparent it onto the + // other item and see that it gains input focus. + scene.removeItem(text2); + text2->setFocus(); + scene.addItem(text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + text2->setParentItem(text); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + QVERIFY(!text->hasFocus()); + QVERIFY(text2->hasFocus()); + + // Remove both items from the scene, restore subfocus and + // readd them. Now the subfocus should kick in and give + // text2 focus. + scene.removeItem(text); + QCOMPARE(text->focusItem(), (QGraphicsItem *)0); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)0); + text2->setFocus(); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + scene.addItem(text); + + // Hiding and showing text should pass focus to text2. + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QVERIFY(text2->hasFocus()); + + // Subfocus should repropagate to root when reparenting. + QGraphicsRectItem *rect = new QGraphicsRectItem; + QGraphicsRectItem *rect2 = new QGraphicsRectItem(rect); + QGraphicsRectItem *rect3 = new QGraphicsRectItem(rect2); + rect3->setFlag(QGraphicsItem::ItemIsFocusable); + + text->setData(0, "text"); + text2->setData(0, "text2"); + rect->setData(0, "rect"); + rect2->setData(0, "rect2"); + rect3->setData(0, "rect3"); + + rect3->setFocus(); + QVERIFY(!rect3->hasFocus()); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect3->focusItem(), (QGraphicsItem *)rect3); + rect->setParentItem(text2); + QCOMPARE(text->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect3->focusItem(), (QGraphicsItem *)rect3); + QVERIFY(!rect->hasFocus()); + QVERIFY(!rect2->hasFocus()); + QVERIFY(rect3->hasFocus()); + + delete rect2; + QCOMPARE(text->focusItem(), (QGraphicsItem *)0); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)0); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::focusProxyDeletion() +{ + QGraphicsRectItem *rect = new QGraphicsRectItem; + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + + delete rect2; + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + + rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + delete rect; // don't crash + + rect = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QGraphicsScene *scene = new QGraphicsScene; + scene->addItem(rect); + scene->addItem(rect2); + delete rect2; + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + + rect2 = new QGraphicsRectItem; + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::setFocusProxy: focus proxy must be in same scene"); + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + scene->addItem(rect2); + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + delete rect; // don't crash + + rect = new QGraphicsRectItem; + rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + scene->addItem(rect); + scene->addItem(rect2); + rect->setFocusProxy(rect2); + delete scene; // don't crash +} + +void tst_QGraphicsItem::negativeZStacksBehindParent() +{ + QGraphicsRectItem rect; + QCOMPARE(rect.zValue(), qreal(0.0)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemNegativeZStacksBehindParent)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(-1); + QCOMPARE(rect.zValue(), qreal(-1.0)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(0); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent); + QVERIFY(rect.flags() & QGraphicsItem::ItemNegativeZStacksBehindParent); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(-1); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); + rect.setZValue(0); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, false); + rect.setZValue(-1); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, true); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, false); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); +} + +void tst_QGraphicsItem::setGraphicsEffect() +{ + // Check that we don't have any effect by default. + QGraphicsItem *item = new QGraphicsRectItem(0, 0, 10, 10); + QVERIFY(!item->graphicsEffect()); + + // SetGet check. + QPointer<QGraphicsEffect> blurEffect = new QGraphicsBlurEffect; + item->setGraphicsEffect(blurEffect); + QCOMPARE(item->graphicsEffect(), static_cast<QGraphicsEffect *>(blurEffect)); + + // Ensure the existing effect is deleted when setting a new one. + QPointer<QGraphicsEffect> shadowEffect = new QGraphicsDropShadowEffect; + item->setGraphicsEffect(shadowEffect); + QVERIFY(!blurEffect); + QCOMPARE(item->graphicsEffect(), static_cast<QGraphicsEffect *>(shadowEffect)); + blurEffect = new QGraphicsBlurEffect; + + // Ensure the effect is uninstalled when setting it on a new target. + QGraphicsItem *anotherItem = new QGraphicsRectItem(0, 0, 10, 10); + anotherItem->setGraphicsEffect(blurEffect); + item->setGraphicsEffect(blurEffect); + QVERIFY(!anotherItem->graphicsEffect()); + QVERIFY(!shadowEffect); + + // Ensure the existing effect is deleted when deleting the item. + delete item; + QVERIFY(!blurEffect); + delete anotherItem; + + // 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(0); + 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); + + QApplication::sendEvent(&scene, &activate); + + // No previous activation, so the scene is active. + QVERIFY(scene.isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)panel1); + QVERIFY(panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(!notPanel1->isActive()); + QVERIFY(!notPanel2->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 0); + QCOMPARE(spy_deactivate_notPanel2.count(), 0); + QCOMPARE(spy_activate_panel1.count(), 1); + QCOMPARE(spy_activate_panel2.count(), 0); + QCOMPARE(spy_activate_panel3.count(), 0); + QCOMPARE(spy_activate_panel4.count(), 0); + + // Switch back to scene. + scene.setActivePanel(0); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 1); + QCOMPARE(spy_activate_notPanel2.count(), 1); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(!notPanel1->isActive()); + QVERIFY(!notPanel2->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 1); + QCOMPARE(spy_deactivate_notPanel2.count(), 1); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 2); + QCOMPARE(spy_activate_notPanel2.count(), 2); + + // Switch to panel1 + scene.setActivePanel(panel1); + QVERIFY(panel1->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 2); + QCOMPARE(spy_deactivate_notPanel2.count(), 2); + QCOMPARE(spy_activate_panel1.count(), 2); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!panel1->isActive()); + QCOMPARE(spy_deactivate_panel1.count(), 2); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(panel1->isActive()); + QCOMPARE(spy_activate_panel1.count(), 3); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!panel1->isActive()); + QVERIFY(!scene.activePanel()); + scene.setActivePanel(0); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(!panel1->isActive()); +} + +void tst_QGraphicsItem::panelWithFocusItem() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsRectItem *parentPanel = new QGraphicsRectItem; + QGraphicsRectItem *parentPanelFocusItem = new QGraphicsRectItem(parentPanel); + parentPanel->setFlag(QGraphicsItem::ItemIsPanel); + parentPanelFocusItem->setFlag(QGraphicsItem::ItemIsFocusable); + parentPanelFocusItem->setFocus(); + scene.addItem(parentPanel); + + QVERIFY(parentPanel->isActive()); + QVERIFY(parentPanelFocusItem->hasFocus()); + QCOMPARE(parentPanel->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + QCOMPARE(parentPanelFocusItem->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + + QGraphicsRectItem *childPanel = new QGraphicsRectItem; + QGraphicsRectItem *childPanelFocusItem = new QGraphicsRectItem(childPanel); + childPanel->setFlag(QGraphicsItem::ItemIsPanel); + childPanelFocusItem->setFlag(QGraphicsItem::ItemIsFocusable); + childPanelFocusItem->setFocus(); + + QVERIFY(!childPanelFocusItem->hasFocus()); + QCOMPARE(childPanel->focusItem(), (QGraphicsItem *)childPanelFocusItem); + QCOMPARE(childPanelFocusItem->focusItem(), (QGraphicsItem *)childPanelFocusItem); + + childPanel->setParentItem(parentPanel); + + QVERIFY(!parentPanel->isActive()); + QVERIFY(!parentPanelFocusItem->hasFocus()); + QCOMPARE(parentPanel->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + QCOMPARE(parentPanelFocusItem->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + + QVERIFY(childPanel->isActive()); + QVERIFY(childPanelFocusItem->hasFocus()); + QCOMPARE(childPanel->focusItem(), (QGraphicsItem *)childPanelFocusItem); + QCOMPARE(childPanelFocusItem->focusItem(), (QGraphicsItem *)childPanelFocusItem); + + childPanel->hide(); + + QVERIFY(parentPanel->isActive()); + QVERIFY(parentPanelFocusItem->hasFocus()); + QCOMPARE(parentPanel->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + QCOMPARE(parentPanelFocusItem->focusItem(), (QGraphicsItem *)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); + + QApplication::sendEvent(&scene, &activate); + QVERIFY(scene.isActive()); + scene.addItem(rect); + QVERIFY(rect->isActive()); + scene.removeItem(rect); + + rect->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(rect); + QVERIFY(rect->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect); + + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + scene.addItem(rect2); + QVERIFY(rect->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect); +} + +void tst_QGraphicsItem::activate() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect = scene.addRect(-10, -10, 20, 20); + QVERIFY(!rect->isActive()); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + + // Non-panel item (active when scene is active). + QVERIFY(rect->isActive()); + + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + rect2->setFlag(QGraphicsItem::ItemIsPanel); + QGraphicsRectItem *rect3 = new QGraphicsRectItem; + rect3->setFlag(QGraphicsItem::ItemIsPanel); + + // Test normal activation. + QVERIFY(!rect2->isActive()); + scene.addItem(rect2); + QVERIFY(rect2->isActive()); // first panel item is activated + scene.addItem(rect3); + QVERIFY(!rect3->isActive()); // second panel item is _not_ activated + rect3->setActive(true); + QVERIFY(rect3->isActive()); + scene.removeItem(rect3); + QVERIFY(!rect3->isActive()); // no panel is active anymore + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + scene.addItem(rect3); + QVERIFY(rect3->isActive()); // second panel item is activated + + // Test pending activation. + scene.removeItem(rect3); + rect2->setActive(true); + QVERIFY(rect2->isActive()); // first panel item is activated + rect3->setActive(true); + QVERIFY(!rect3->isActive()); // not active (yet) + scene.addItem(rect3); + QVERIFY(rect3->isActive()); // now becomes active + + // Test pending deactivation. + scene.removeItem(rect3); + rect3->setActive(false); + scene.addItem(rect3); + QVERIFY(!rect3->isActive()); // doesn't become active + + // Child of panel activation. + rect3->setActive(true); + QGraphicsRectItem *rect4 = new QGraphicsRectItem; + rect4->setFlag(QGraphicsItem::ItemIsPanel); + QGraphicsRectItem *rect5 = new QGraphicsRectItem(rect4); + QGraphicsRectItem *rect6 = new QGraphicsRectItem(rect5); + scene.addItem(rect4); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect3); + scene.removeItem(rect4); + rect6->setActive(true); + scene.addItem(rect4); + QVERIFY(rect4->isActive()); + QVERIFY(rect5->isActive()); + QVERIFY(rect6->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect4); + scene.removeItem(rect4); // no active panel + rect6->setActive(false); + scene.addItem(rect4); + QVERIFY(!rect4->isActive()); + QVERIFY(!rect5->isActive()); + QVERIFY(!rect6->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + + // Controlling auto-activation when the scene changes activation. + rect4->setActive(true); + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!scene.isActive()); + QVERIFY(!rect4->isActive()); + rect4->setActive(false); + QApplication::sendEvent(&scene, &activate); + QVERIFY(scene.isActive()); + QVERIFY(!scene.activePanel()); + QVERIFY(!rect4->isActive()); +} + +void tst_QGraphicsItem::setActivePanelOnInactiveScene() +{ + QGraphicsScene scene; + QGraphicsRectItem *item = scene.addRect(QRectF()); + QGraphicsRectItem *panel = scene.addRect(QRectF()); + panel->setFlag(QGraphicsItem::ItemIsPanel); + + EventSpy itemActivateSpy(&scene, item, QEvent::WindowActivate); + EventSpy itemDeactivateSpy(&scene, item, QEvent::WindowDeactivate); + EventSpy panelActivateSpy(&scene, panel, QEvent::WindowActivate); + EventSpy panelDeactivateSpy(&scene, panel, QEvent::WindowDeactivate); + EventSpy sceneActivationChangeSpy(&scene, QEvent::ActivationChange); + + scene.setActivePanel(panel); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + QCOMPARE(itemActivateSpy.count(), 0); + QCOMPARE(itemDeactivateSpy.count(), 0); + QCOMPARE(panelActivateSpy.count(), 0); + QCOMPARE(panelDeactivateSpy.count(), 0); + QCOMPARE(sceneActivationChangeSpy.count(), 0); +} + +void tst_QGraphicsItem::activationOnShowHide() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsRectItem *rootPanel = scene.addRect(QRectF()); + rootPanel->setFlag(QGraphicsItem::ItemIsPanel); + rootPanel->setActive(true); + + QGraphicsRectItem *subPanel = new QGraphicsRectItem; + subPanel->setFlag(QGraphicsItem::ItemIsPanel); + + // Reparenting onto an active panel auto-activates the child panel. + subPanel->setParentItem(rootPanel); + QVERIFY(subPanel->isActive()); + QVERIFY(!rootPanel->isActive()); + + // Hiding an active child panel will reactivate the parent panel. + subPanel->hide(); + QVERIFY(rootPanel->isActive()); + + // Showing a child panel will auto-activate it. + subPanel->show(); + QVERIFY(subPanel->isActive()); + QVERIFY(!rootPanel->isActive()); + + // Adding an unrelated panel doesn't affect activation. + QGraphicsRectItem *otherPanel = new QGraphicsRectItem; + otherPanel->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(otherPanel); + QVERIFY(subPanel->isActive()); + + // Showing an unrelated panel doesn't affect activation. + otherPanel->hide(); + otherPanel->show(); + QVERIFY(subPanel->isActive()); + + // Add a non-panel item. + QGraphicsRectItem *otherItem = new QGraphicsRectItem; + scene.addItem(otherItem); + otherItem->setActive(true); + QVERIFY(otherItem->isActive()); + + // Reparent a panel onto an active non-panel item. + subPanel->setParentItem(otherItem); + QVERIFY(subPanel->isActive()); + + // Showing a child panel of a non-panel item will activate it. + subPanel->hide(); + QVERIFY(!subPanel->isActive()); + QVERIFY(otherItem->isActive()); + subPanel->show(); + QVERIFY(subPanel->isActive()); + + // Hiding a toplevel active panel will pass activation back + // to the non-panel items. + rootPanel->setActive(true); + rootPanel->hide(); + QVERIFY(!rootPanel->isActive()); + QVERIFY(otherItem->isActive()); +} + +class MoveWhileDying : public QGraphicsRectItem +{ +public: + MoveWhileDying(QGraphicsItem *parent = 0) + : QGraphicsRectItem(parent) + { } + ~MoveWhileDying() + { + foreach (QGraphicsItem *c, childItems()) { + foreach (QGraphicsItem *cc, c->childItems()) { + cc->moveBy(10, 10); + } + c->moveBy(10, 10); + } + if (QGraphicsItem *p = parentItem()) { p->moveBy(10, 10); } + } +}; + +void tst_QGraphicsItem::moveWhileDeleting() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect = new QGraphicsRectItem; + MoveWhileDying *silly = new MoveWhileDying(rect); + QGraphicsRectItem *child = new QGraphicsRectItem(silly); + scene.addItem(rect); + delete rect; // don't crash! + + rect = new QGraphicsRectItem; + silly = new MoveWhileDying(rect); + child = new QGraphicsRectItem(silly); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(125); + + delete rect; + + rect = new QGraphicsRectItem; + rect->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + silly = new MoveWhileDying(rect); + child = new QGraphicsRectItem(silly); + + QTest::qWait(125); + + delete rect; + + rect = new MoveWhileDying; + rect->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + child = new QGraphicsRectItem(rect); + silly = new MoveWhileDying(child); + + QTest::qWait(125); + + delete rect; +} + +class MyRectItem : public QGraphicsWidget +{ + Q_OBJECT +public: + MyRectItem(QGraphicsItem *parent = 0) : QGraphicsWidget(parent) + { + + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->setBrush(brush); + painter->drawRect(boundingRect()); + } + void move() + { + setPos(-100,-100); + topLevel->collidingItems(Qt::IntersectsItemBoundingRect); + } +public: + QGraphicsItem *topLevel; + QBrush brush; +}; + + +void tst_QGraphicsItem::ensureDirtySceneTransform() +{ + QGraphicsScene scene; + + MyRectItem *topLevel = new MyRectItem; + topLevel->setGeometry(0, 0, 100, 100); + topLevel->setPos(-50, -50); + topLevel->brush = QBrush(QColor(Qt::black)); + scene.addItem(topLevel); + + MyRectItem *parent = new MyRectItem; + parent->topLevel = topLevel; + parent->setGeometry(0, 0, 100, 100); + parent->setPos(0, 0); + parent->brush = QBrush(QColor(Qt::magenta)); + parent->setObjectName("parent"); + scene.addItem(parent); + + MyRectItem *child = new MyRectItem(parent); + child->setGeometry(0, 0, 80, 80); + child->setPos(10, 10); + child->setObjectName("child"); + child->brush = QBrush(QColor(Qt::blue)); + + MyRectItem *child2 = new MyRectItem(parent); + child2->setGeometry(0, 0, 80, 80); + child2->setPos(15, 15); + child2->setObjectName("child2"); + child2->brush = QBrush(QColor(Qt::green)); + + MyRectItem *child3 = new MyRectItem(parent); + child3->setGeometry(0, 0, 80, 80); + child3->setPos(20, 20); + child3->setObjectName("child3"); + child3->brush = QBrush(QColor(Qt::gray)); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + //We move the parent + parent->move(); + QApplication::processEvents(); + + //We check if all items moved + QCOMPARE(child->pos(), QPointF(10, 10)); + QCOMPARE(child2->pos(), QPointF(15, 15)); + QCOMPARE(child3->pos(), QPointF(20, 20)); + + QCOMPARE(child->sceneBoundingRect(), QRectF(-90, -90, 80, 80)); + QCOMPARE(child2->sceneBoundingRect(), QRectF(-85, -85, 80, 80)); + QCOMPARE(child3->sceneBoundingRect(), QRectF(-80, -80, 80, 80)); + + QCOMPARE(child->sceneTransform(), QTransform::fromTranslate(-90, -90)); + QCOMPARE(child2->sceneTransform(), QTransform::fromTranslate(-85, -85)); + QCOMPARE(child3->sceneTransform(), QTransform::fromTranslate(-80, -80)); +} + +void tst_QGraphicsItem::focusScope() +{ + // ItemIsFocusScope is an internal feature (for now). + QGraphicsScene scene; + + QGraphicsRectItem *scope3 = new QGraphicsRectItem; + scope3->setData(0, "scope3"); + scope3->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope3->setFocus(); + QVERIFY(!scope3->focusScopeItem()); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + + QGraphicsRectItem *scope2 = new QGraphicsRectItem; + scope2->setData(0, "scope2"); + scope2->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope2->setFocus(); + QVERIFY(!scope2->focusScopeItem()); + scope3->setParentItem(scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)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(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope1->focusScopeItem(), (QGraphicsItem *)scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + + scene.addItem(scope1); + + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + scene.setFocus(); + + QCOMPARE(scope1->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope1->focusScopeItem(), (QGraphicsItem *)scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + + QVERIFY(scope3->hasFocus()); + + scope3->hide(); + QVERIFY(scope2->hasFocus()); + scope2->hide(); + QVERIFY(scope1->hasFocus()); + scope2->show(); + QVERIFY(scope2->hasFocus()); + scope3->show(); + QVERIFY(scope3->hasFocus()); + scope1->hide(); + QVERIFY(!scope3->hasFocus()); + scope1->show(); + QVERIFY(scope3->hasFocus()); + scope3->clearFocus(); + QVERIFY(scope2->hasFocus()); + scope2->clearFocus(); + QVERIFY(scope1->hasFocus()); + scope2->hide(); + scope2->show(); + QVERIFY(!scope2->hasFocus()); + QVERIFY(scope1->hasFocus()); + scope2->setFocus(); + QVERIFY(scope2->hasFocus()); + scope3->setFocus(); + QVERIFY(scope3->hasFocus()); + + QGraphicsRectItem *rect4 = new QGraphicsRectItem; + rect4->setData(0, "rect4"); + rect4->setParentItem(scope3); + + QGraphicsRectItem *rect5 = new QGraphicsRectItem; + rect5->setData(0, "rect5"); + rect5->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + rect5->setFocus(); + rect5->setParentItem(rect4); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)rect5); + QVERIFY(rect5->hasFocus()); + + rect4->setParentItem(0); + QVERIFY(rect5->hasFocus()); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)0); + QVERIFY(!scope3->hasFocus()); + + QGraphicsRectItem *rectA = new QGraphicsRectItem; + QGraphicsRectItem *scopeA = new QGraphicsRectItem(rectA); + scopeA->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scopeA->setFocus(); + QGraphicsRectItem *scopeB = new QGraphicsRectItem(scopeA); + scopeB->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scopeB->setFocus(); + + scene.addItem(rectA); + QVERIFY(rect5->hasFocus()); + QVERIFY(!scopeB->hasFocus()); + + scopeA->setFocus(); + QVERIFY(scopeB->hasFocus()); + QCOMPARE(scopeB->focusItem(), (QGraphicsItem *)scopeB); +} + +void tst_QGraphicsItem::focusScope2() +{ + QGraphicsRectItem *child1 = new QGraphicsRectItem; + child1->setFlags(QGraphicsItem::ItemIsFocusable); + child1->setFocus(); + QCOMPARE(child1->focusItem(), (QGraphicsItem *)child1); + + QGraphicsRectItem *child2 = new QGraphicsRectItem; + child2->setFlags(QGraphicsItem::ItemIsFocusable); + + QGraphicsRectItem *rootFocusScope = new QGraphicsRectItem; + rootFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + rootFocusScope->setFocus(); + QCOMPARE(rootFocusScope->focusItem(), (QGraphicsItem *)rootFocusScope); + + child1->setParentItem(rootFocusScope); + child2->setParentItem(rootFocusScope); + + QCOMPARE(rootFocusScope->focusScopeItem(), (QGraphicsItem *)child1); + QCOMPARE(rootFocusScope->focusItem(), (QGraphicsItem *)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(), (QGraphicsItem *)siblingChild1); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)0); + + QGraphicsItem *root = new QGraphicsRectItem; + rootFocusScope->setParentItem(root); + siblingFocusScope->setParentItem(root); + + QCOMPARE(root->focusItem(), (QGraphicsItem *)child1); + + QGraphicsScene scene; + scene.addItem(root); + + QEvent activate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &activate); + scene.setFocus(); + + QCOMPARE(scene.focusItem(), (QGraphicsItem *)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(), (QGraphicsItem *)siblingChild2); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)0); + + // Set focus on the scope; focus is forwarded to the focus scope item. + siblingFocusScope->setFocus(); + QVERIFY(siblingChild2->hasFocus()); + QVERIFY(siblingChild2->focusItem()); + QCOMPARE(siblingFocusScope->focusScopeItem(), (QGraphicsItem *)siblingChild2); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)siblingChild2); +} + +void tst_QGraphicsItem::stackBefore() +{ + QGraphicsRectItem parent; + QGraphicsRectItem *child1 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child2 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child3 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child4 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child1 << child2 << child3 << child4)); + child1->setData(0, "child1"); + child2->setData(0, "child2"); + child3->setData(0, "child3"); + child4->setData(0, "child4"); + + // Remove and append + child2->setParentItem(0); + child2->setParentItem(&parent); + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child1 << child3 << child4 << child2)); + + // Move child2 before child1 + child2->stackBefore(child1); // 2134 + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child2->stackBefore(child2); // 2134 + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child1->setZValue(1); // 2341 + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child3 << child4 << child1)); + child1->stackBefore(child2); // 2341 + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child2 << child3 << child4 << child1)); + child1->setZValue(0); // 1234 + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child1 << child2 << child3 << child4)); + child4->stackBefore(child1); // 4123 + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child4 << child1 << child2 << child3)); + child4->setZValue(1); // 1234 (4123) + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child1 << child2 << child3 << child4)); + child3->stackBefore(child1); // 3124 (4312) + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child3 << child1 << child2 << child4)); + child4->setZValue(0); // 4312 + QCOMPARE(parent.childItems(), (QList<QGraphicsItem *>() << child4 << child3 << child1 << child2)); + + // Make them all toplevels + child1->setParentItem(0); + child2->setParentItem(0); + child3->setParentItem(0); + child4->setParentItem(0); + + QGraphicsScene scene; + scene.addItem(child1); + scene.addItem(child2); + scene.addItem(child3); + scene.addItem(child4); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), + (QList<QGraphicsItem *>() << child1 << child2 << child3 << child4)); + + // Remove and append + scene.removeItem(child2); + scene.addItem(child2); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child1 << child3 << child4 << child2)); + + // Move child2 before child1 + child2->stackBefore(child1); // 2134 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child2->stackBefore(child2); // 2134 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child1 << child3 << child4)); + child1->setZValue(1); // 2341 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child3 << child4 << child1)); + child1->stackBefore(child2); // 2341 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child2 << child3 << child4 << child1)); + child1->setZValue(0); // 1234 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child1 << child2 << child3 << child4)); + child4->stackBefore(child1); // 4123 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child4 << child1 << child2 << child3)); + child4->setZValue(1); // 1234 (4123) + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child1 << child2 << child3 << child4)); + child3->stackBefore(child1); // 3124 (4312) + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child3 << child1 << child2 << child4)); + child4->setZValue(0); // 4312 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList<QGraphicsItem *>() << child4 << child3 << child1 << child2)); +} + +void tst_QGraphicsItem::QTBUG_4233_updateCachedWithSceneRect() +{ + 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.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&view); + + QTRY_COMPARE(tester->repaints, 1); + + scene.update(); // triggers "updateAll" optimization + qApp->processEvents(); + qApp->processEvents(); // in 4.6 only one processEvents is necessary + + QCOMPARE(tester->repaints, 1); + + scene.update(); // triggers "updateAll" optimization + tester->update(); + qApp->processEvents(); + qApp->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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 0); + + // 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(), (QGraphicsItem *) 0); + + // 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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 0); + + // 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(), (QGraphicsItem *) 0); + + // 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(), (QGraphicsItem *) 0); + } + { + // 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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 0); + + // 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(), (QGraphicsItem *) 0); + + // 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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 0); + + // 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(), (QGraphicsItem *) 0); + + // 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(), (QGraphicsItem *) 0); + } + + { + // 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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 0); + + 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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 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(), (QGraphicsItem *) 0); + } +} + +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); + QApplication::sendEvent(&scene, &windowActivateEvent); + + EventSpy2 rect1Spy(&scene, rect1); + EventSpy2 rect2Spy(&scene, rect2); + + // activate rect1, it should not get focus + rect1->setActive(true); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) 0); + + // focus stays unset when rect2 becomes modal + rect2->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + 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 + sendMouseClick(&scene, QPointF(-25, -25)); + QCOMPARE(rect1->focusItem(), (QGraphicsItem *) 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + 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(), (QGraphicsItem *) rect2); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1); + QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0); + + // clicking on rect1 does *not* give it focus + rect1->setActive(true); + sendMouseClick(&scene, QPointF(-25, -25)); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + 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(), (QGraphicsItem *) 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + 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(), (QGraphicsItem *) rect1); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 1); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + 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); + QApplication::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(), (QGraphicsItem *) rect1child); + + // focus stays on rect1child when rect2 becomes modal + rect2->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) 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(), (QGraphicsItem *) 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); +} + +class ScenePosChangeTester : public ItemChangeTester +{ +public: + ScenePosChangeTester() + { } + ScenePosChangeTester(QGraphicsItem *parent) : ItemChangeTester(parent) + { } +}; + +void tst_QGraphicsItem::scenePosChange() +{ + ScenePosChangeTester* root = new ScenePosChangeTester; + ScenePosChangeTester* child1 = new ScenePosChangeTester(root); + 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); + delete grandChild2; grandChild2 = 0; + 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); +} + +class MyInputContext : public QInputContext +{ +public: + MyInputContext() : nbUpdates(0) {} + ~MyInputContext() {} + + QString identifierName() { return QString(); } + QString language() { return QString(); } + + void reset() {} + + bool isComposing() const { return false; } + + void update() { nbUpdates++; } + + bool nbUpdates; +}; + +class MyInputWidget : public QGraphicsWidget +{ +public: + MyInputWidget() + { + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); + } + void mousePressEvent(QGraphicsSceneMouseEvent *event) + { + event->accept(); + } + + void doUpdateMicroFocus() + { + if (QWidget *fw = QApplication::focusWidget()) { + if (scene()) { + for (int i = 0 ; i < scene()->views().count() ; ++i) { + if (scene()->views().at(i) == fw) { + if (QInputContext *inputContext = fw->inputContext()) { + inputContext->update(); + } + } + } + } + } + } +}; + +void tst_QGraphicsItem::updateMicroFocus() +{ +#if defined Q_OS_WIN || defined Q_OS_MAC + QSKIP("QTBUG-9578", SkipAll); +#endif + QGraphicsScene scene; + QWidget parent; + QGridLayout layout; + parent.setLayout(&layout); + QGraphicsView view(&scene); + QGraphicsView view2(&scene); + layout.addWidget(&view, 0, 0); + layout.addWidget(&view2, 0, 1); + MyInputContext *ic = new MyInputContext; + qApp->setInputContext(ic); + MyInputWidget input; + input.setPos(0, 0); + input.resize(150, 150); + scene.addItem(&input); + input.setFocus(); + parent.show(); + view.setFocus(); + qApp->setAutoSipEnabled(true); + QApplication::setActiveWindow(&parent); + QTest::qWaitForWindowShown(&parent); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&parent)); + //We reset the number of updates that happened previously (initialisation) + ic->nbUpdates = 0; + input.doUpdateMicroFocus(); + QApplication::processEvents(); + QTRY_COMPARE(ic->nbUpdates, 1); +} + +void tst_QGraphicsItem::textItem_shortcuts() +{ + QWidget w; + QVBoxLayout l; + w.setLayout(&l); + 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(); + QTest::qWaitForWindowShown(&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()); +} + +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.setFrameStyle(0); + view.show(); + QTest::qWaitForWindowShown(&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 *) + { + 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(); + QTest::qWaitForWindowShown(&view); + + QApplication::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(), static_cast<QGraphicsRectItem *>(0)); + 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); +} + +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); + + class Testee : public QGraphicsRectItem + { + public: + int touchBeginEventCount; + + Testee() + : QGraphicsRectItem(0, 0, 100, 100) + , touchBeginEventCount(0) + { + setAcceptTouchEvents(true); + setFlag(QGraphicsItem::ItemIsFocusable, false); + } + + bool sceneEvent(QEvent *ev) + { + if (ev->type() == QEvent::TouchBegin) + ++touchBeginEventCount; + + return QGraphicsRectItem::sceneEvent(ev); + } + }; + + Testee *touchEventReceiver = new Testee; + 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.setSceneRect(touchEventReceiver->boundingRect()); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(touchEventReceiver->touchBeginEventCount, 0); + + QTouchEvent::TouchPoint tp(0); + tp.setState(Qt::TouchPointPressed); + tp.setScenePos(view.sceneRect().center()); + tp.setLastScenePos(view.sceneRect().center()); + + QList<QTouchEvent::TouchPoint> touchPoints; + touchPoints << tp; + + sendMousePress(&scene, tp.scenePos()); + QTouchEvent touchBegin(QEvent::TouchBegin, QTouchEvent::TouchScreen, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); + + qApp->sendEvent(&scene, &touchBegin); + QCOMPARE(touchEventReceiver->touchBeginEventCount, expectedCount); +} + +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.show(); + QTest::qWaitForWindowShown(&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) + { + 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.show(); + QTest::qWaitForWindowShown(&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); + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.repaints > 0); + + // test case #1 + view.reset(); + child2->setVisible(false); + child2->setParentItem(child); + + QTRY_VERIFY(view.repaints == 1); + + // test case #2 + view.reset(); + child3->setOpacity(0.0); + child3->setParentItem(child); + + QTRY_VERIFY(view.repaints == 1); + + // test case #3 + view.reset(); + child4->setParentItem(child); + child4->setVisible(false); + + QTRY_VERIFY(view.repaints == 1); +} + +void tst_QGraphicsItem::QT_2653_fullUpdateDiscardingOpacityUpdate() +{ + QGraphicsScene scene(0, 0, 200, 200); + MyGraphicsView view(&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); + + 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); + + if (PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + 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); + 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(); + QTest::qWaitForWindowShown(&origView); + origView.setGeometry(origView.width() + 20, 20, + origView.width(), origView.height()); + + parentGreen->setFlag(QGraphicsItem::ItemIgnoresTransformations); + + origView.reset(); + childYellow->setOpacity(0.0); + + QTRY_COMPARE(origView.repaints, 1); + + view.show(); + + QTest::qWaitForWindowShown(&view); + view.reset(); + origView.reset(); + + childYellow->setOpacity(1.0); + + QTRY_COMPARE(origView.repaints, 1); + QTRY_COMPARE(view.repaints, 1); +} + +void tst_QGraphicsItem::QT_2649_focusScope() +{ + QGraphicsScene *scene = new QGraphicsScene; + + QGraphicsRectItem *subFocusItem = new QGraphicsRectItem; + subFocusItem->setFlags(QGraphicsItem::ItemIsFocusable); + subFocusItem->setFocus(); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + + QGraphicsRectItem *scope = new QGraphicsRectItem; + scope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope->setFocus(); + subFocusItem->setParentItem(scope); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + + QGraphicsRectItem *rootItem = new QGraphicsRectItem; + rootItem->setFlags(QGraphicsItem::ItemIsFocusable); + scope->setParentItem(rootItem); + QCOMPARE(rootItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(rootItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + + scene->addItem(rootItem); + + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(scene, &windowActivate); + scene->setFocus(); + + QCOMPARE(rootItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(rootItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QVERIFY(subFocusItem->hasFocus()); + + scope->hide(); + + QCOMPARE(rootItem->focusItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)0); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)0); + QCOMPARE(rootItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QVERIFY(!subFocusItem->hasFocus()); + + scope->show(); + + QCOMPARE(rootItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(rootItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QVERIFY(subFocusItem->hasFocus()); + + // This should not crash + scope->hide(); + delete scene; +} + +class MyGraphicsItemWithItemChange : public QGraphicsWidget +{ +public: + MyGraphicsItemWithItemChange(QGraphicsItem *parent = 0) : QGraphicsWidget(parent) + {} + + QVariant itemChange(GraphicsItemChange change, const QVariant &value) + { + if (change == QGraphicsItem::ItemSceneHasChanged) { + foreach (QGraphicsView *view, scene()->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() +{ + struct Item : public QGraphicsTextItem + { + int painted; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *wid) + { + painted++; + QGraphicsTextItem::paint(painter, opt, wid); + } + }; + QGraphicsScene scene; + MyGraphicsView view(&scene); + Item *item = new Item; + item->painted = 0; + item->setPlainText("Grandparent"); + Item *item2 = new Item; + item2->setPlainText("parent"); + item2->painted = 0; + Item *item3 = new Item; + item3->setPlainText("child"); + item3->painted = 0; + QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect; + effect->setOpacity(0.5); + item2->setGraphicsEffect(effect); + item3->setParentItem(item2); + item2->setParentItem(item); + scene.addItem(item); + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(view.repaints, 1); + QTRY_COMPARE(item->painted, 1); + QTRY_COMPARE(item2->painted, 1); + QTRY_COMPARE(item3->painted, 1); + item2->update(); + QApplication::processEvents(); + QTRY_COMPARE(item->painted, 2); + QTRY_COMPARE(item2->painted, 2); + QTRY_COMPARE(item3->painted, 2); + item2->update(); + QApplication::processEvents(); + QTRY_COMPARE(item->painted, 3); + QTRY_COMPARE(item2->painted, 3); + QTRY_COMPARE(item3->painted, 3); +} + +void tst_QGraphicsItem::itemDiesDuringDraggingOperation() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + item->setFlag(QGraphicsItem::ItemIsMovable); + item->setAcceptDrops(true); + scene.addItem(item); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&view); + QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter); + dragEnter.setScenePos(item->boundingRect().center()); + QApplication::sendEvent(&scene, &dragEnter); + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove); + event.setScenePos(item->boundingRect().center()); + QApplication::sendEvent(&scene, &event); + QVERIFY(QGraphicsScenePrivate::get(&scene)->dragDropItem == item); + delete item; + QVERIFY(QGraphicsScenePrivate::get(&scene)->dragDropItem == 0); +} + +void tst_QGraphicsItem::QTBUG_12112_focusItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + 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(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&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(0) + { + QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical); + QLatin1String wiseWords("AZ BUKI VEDI"); + QString sentence(wiseWords); + QStringList words = sentence.split(QLatin1Char(' '), QString::SkipEmptyParts); + for (int i = 0; i < words.count(); ++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(0, 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); + + MyWidgetWindow win; + scene.addItem(&win); + + view.show(); + QTest::qWaitForWindowShown(&view); +} + +void tst_QGraphicsItem::QTBUG_20699_focusScopeCrash() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + 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(); + QTest::qWaitForWindowShown(&view); + + fi->setParentItem(fi2); + fi->setFocus(); + fs.setFocus(); + fi->setParentItem(fs2); + fi->setFocus(); + fs2->setFocus(); + fs.setFocus(); + fi->setParentItem(fi2); + fi->setFocus(); + fs.setFocus(); +} + +QTEST_MAIN(tst_QGraphicsItem) +#include "tst_qgraphicsitem.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicsitemanimation/.gitignore b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/.gitignore new file mode 100644 index 0000000000..fe9fe0b8ec --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/.gitignore @@ -0,0 +1 @@ +tst_qgraphicsitemanimation diff --git a/tests/auto/widgets/graphicsview/qgraphicsitemanimation/qgraphicsitemanimation.pro b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/qgraphicsitemanimation.pro new file mode 100644 index 0000000000..5d723da32e --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/qgraphicsitemanimation.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qgraphicsitemanimation.cpp +DEFINES += QT_NO_CAST_TO_ASCII +CONFIG += parallel_test + diff --git a/tests/auto/widgets/graphicsview/qgraphicsitemanimation/tst_qgraphicsitemanimation.cpp b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/tst_qgraphicsitemanimation.cpp new file mode 100644 index 0000000000..f7cf393253 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/tst_qgraphicsitemanimation.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qgraphicsitemanimation.h> +#include <QtCore/qtimeline.h> +#include <QtGui/qmatrix.h> + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QGraphicsItemAnimation : public QObject +{ + Q_OBJECT + +private slots: + void construction(); + void linearMove(); + void linearRotation(); + void checkReturnedLists(); + void overwriteValueForStep(); + void setTimeLine(); +}; + +void tst_QGraphicsItemAnimation::construction() +{ + QGraphicsItemAnimation animation; + QVERIFY(!animation.item()); + QVERIFY(!animation.timeLine()); + QCOMPARE(animation.posAt(0), QPointF()); + QCOMPARE(animation.posAt(0.5), QPointF()); + QCOMPARE(animation.posAt(1), QPointF()); + QCOMPARE(animation.matrixAt(0), QMatrix()); + QCOMPARE(animation.matrixAt(0.5), QMatrix()); + QCOMPARE(animation.matrixAt(1), QMatrix()); + QCOMPARE(animation.rotationAt(0), qreal(0.0)); + QCOMPARE(animation.rotationAt(0.5), qreal(0.0)); + QCOMPARE(animation.rotationAt(1), qreal(0.0)); + QCOMPARE(animation.xTranslationAt(0), qreal(0.0)); + QCOMPARE(animation.xTranslationAt(0.5), qreal(0.0)); + QCOMPARE(animation.xTranslationAt(1), qreal(0.0)); + QCOMPARE(animation.yTranslationAt(0), qreal(0.0)); + QCOMPARE(animation.yTranslationAt(0.5), qreal(0.0)); + QCOMPARE(animation.yTranslationAt(1), qreal(0.0)); + QCOMPARE(animation.verticalScaleAt(0), qreal(1.0)); + QCOMPARE(animation.horizontalScaleAt(0), qreal(1.0)); + QCOMPARE(animation.verticalShearAt(0), qreal(0.0)); + QCOMPARE(animation.horizontalShearAt(0), qreal(0.0)); + animation.clear(); // don't crash +} + +void tst_QGraphicsItemAnimation::linearMove() +{ + QGraphicsItemAnimation animation; + + for (int i = 0; i <= 10; ++i) { + QCOMPARE(animation.posAt(i / 10.0).x(), qreal(0)); + QCOMPARE(animation.posAt(i / 10.0).y(), qreal(0)); + } + + animation.setPosAt(1, QPointF(10, -10)); + + for (int i = 0; i <= 10; ++i) { + QCOMPARE(animation.posAt(i / 10.0).x(), qreal(i)); + QCOMPARE(animation.posAt(i / 10.0).y(), qreal(-i)); + } + + animation.setPosAt(2, QPointF(10, -10)); + + QCOMPARE(animation.posAt(11).x(), qreal(10)); +} + +void tst_QGraphicsItemAnimation::linearRotation() +{ + QGraphicsItemAnimation animation; + animation.setRotationAt(1, 1); + + for (int i = 0; i <= 10; ++i) + QCOMPARE(animation.rotationAt(i / 10.0), qreal(i / 10.0)); +} + +void tst_QGraphicsItemAnimation::checkReturnedLists() +{ + QGraphicsItemAnimation animation; + + animation.setPosAt(1.0, QPointF(10, -10)); + animation.setPosAt(0.5, QPointF(5, -5)); + + animation.setRotationAt(0.3, 2.3); + animation.setTranslationAt(0.3, 15, 15); + animation.setScaleAt(0.3, 2.5, 1.8); + animation.setShearAt(0.3, 5, 5); + + QCOMPARE(animation.posList().at(0), (QPair<qreal, QPointF>(0.5, QPointF(5, -5)))); + QCOMPARE(animation.posList().at(1), (QPair<qreal, QPointF>(1.0, QPointF(10, -10)))); + QCOMPARE(animation.rotationList().at(0), (QPair<qreal, qreal>(0.3, 2.3))); + QCOMPARE(animation.translationList().at(0), (QPair<qreal, QPointF>(0.3, QPointF(15, 15)))); + QCOMPARE(animation.scaleList().at(0), (QPair<qreal, QPointF>(0.3, QPointF(2.5, 1.8)))); + QCOMPARE(animation.shearList().at(0), (QPair<qreal, QPointF>(0.3, QPointF(5, 5)))); + + QCOMPARE(animation.posList().size(), 2); + QCOMPARE(animation.rotationList().size(), 1); + QCOMPARE(animation.translationList().size(), 1); + QCOMPARE(animation.scaleList().size(), 1); + QCOMPARE(animation.shearList().size(), 1); +} + +void tst_QGraphicsItemAnimation::overwriteValueForStep() +{ + QGraphicsItemAnimation animation; + + for (int i=0; i<3; i++){ + animation.setPosAt(0.3, QPointF(3, -3.1)); + animation.setRotationAt(0.3, 2.3); + animation.setTranslationAt(0.3, 15, 15); + animation.setScaleAt(0.3, 2.5, 1.8); + animation.setShearAt(0.3, 5, 5); + + QCOMPARE(animation.posList().size(), 1); + QCOMPARE(animation.rotationList().size(), 1); + QCOMPARE(animation.translationList().size(), 1); + QCOMPARE(animation.scaleList().size(), 1); + QCOMPARE(animation.shearList().size(), 1); + } +} + +void tst_QGraphicsItemAnimation::setTimeLine() +{ + QGraphicsItemAnimation animation; + QCOMPARE(animation.timeLine(), (QTimeLine *)0); + + QPointer<QTimeLine> line1 = new QTimeLine; + animation.setTimeLine(line1); + QCOMPARE(animation.timeLine(), (QTimeLine *)line1); + animation.setTimeLine(line1); + QVERIFY(line1); + QCOMPARE(animation.timeLine(), (QTimeLine *)line1); + + animation.setTimeLine(0); + QCOMPARE(animation.timeLine(), (QTimeLine *)0); + QVERIFY(!line1); + + QTimeLine *line2 = new QTimeLine; + animation.setTimeLine(line2); + QCOMPARE(animation.timeLine(), (QTimeLine *)line2); + + delete line2; + QCOMPARE(animation.timeLine(), (QTimeLine *)0); +} + +QTEST_MAIN(tst_QGraphicsItemAnimation) +#include "tst_qgraphicsitemanimation.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicslayout/.gitignore b/tests/auto/widgets/graphicsview/qgraphicslayout/.gitignore new file mode 100644 index 0000000000..b3b27f68a1 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslayout/.gitignore @@ -0,0 +1 @@ +tst_qgraphicslayout diff --git a/tests/auto/widgets/graphicsview/qgraphicslayout/qgraphicslayout.pro b/tests/auto/widgets/graphicsview/qgraphicslayout/qgraphicslayout.pro new file mode 100644 index 0000000000..ea176c98fe --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslayout/qgraphicslayout.pro @@ -0,0 +1,9 @@ +############################################################ +# Project file for autotest for file qlayout.h +############################################################ + +load(qttest_p4) +QT += widgets +SOURCES += tst_qgraphicslayout.cpp +DEFINES += QT_USE_USING_NAMESPACE +CONFIG += parallel_test diff --git a/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp b/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp new file mode 100644 index 0000000000..3eac04e4e5 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp @@ -0,0 +1,992 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtGui> +#include <QtWidgets> +#include <math.h> + +//TESTED_CLASS=QGraphicsLayout +//TESTED_FILES= + +class tst_QGraphicsLayout : public QObject +{ +Q_OBJECT + +public: + tst_QGraphicsLayout(); + virtual ~tst_QGraphicsLayout(); + +private slots: + void sizeHints(); + void compressLayoutRequest(); + void automaticReparenting(); + void verifyActivate(); + void invalidate(); + void constructors(); + void alternativeLayoutItems(); + void ownership(); +}; + +tst_QGraphicsLayout::tst_QGraphicsLayout() +{ +} + +tst_QGraphicsLayout::~tst_QGraphicsLayout() +{ +} + +void tst_QGraphicsLayout::sizeHints() +{ + + QGraphicsView view; + QGraphicsScene scene; + QGraphicsWidget *window = new QGraphicsWidget(); + scene.addItem(window); + QGraphicsLinearLayout *lout = new QGraphicsLinearLayout(window); + lout->setContentsMargins(0,0,0,0); + QGraphicsWidget *gw = new QGraphicsWidget(window); + gw->setMinimumSize(QSizeF(10,10)); + gw->setPreferredSize(QSizeF(100,100)); + gw->setMaximumSize(QSizeF(500,500)); + lout->addItem(gw); + QCOMPARE(lout->effectiveSizeHint(Qt::MinimumSize), gw->effectiveSizeHint(Qt::MinimumSize)); + QCOMPARE(lout->effectiveSizeHint(Qt::PreferredSize), gw->effectiveSizeHint(Qt::PreferredSize)); + QCOMPARE(lout->effectiveSizeHint(Qt::MaximumSize), gw->effectiveSizeHint(Qt::MaximumSize)); + +} + +enum FunctionType { + SetGeometry = 0, + Invalidate, + NumFunctionTypes +}; + + + +class TestGraphicsWidget : public QGraphicsWidget { +public: + TestGraphicsWidget(QGraphicsWidget *parent = 0) : QGraphicsWidget(parent) + { } + + bool event(QEvent *e) { + ++(m_eventCount[int(e->type())]); + return QGraphicsWidget::event(e); + } + + int eventCount(QEvent::Type type) { + return m_eventCount.value(int(type)); + } + + void clearEventCount() { + m_eventCount.clear(); + } + + void clearCounters() { + m_eventCount.clear(); + functionCount.clear(); + } + + void setGeometry(const QRectF &rect) + { + QGraphicsWidget::setGeometry(rect); + ++(functionCount[SetGeometry]); + } + + void callUpdateGeometry() + { + // updateGeometry() is protected + QGraphicsWidget::updateGeometry(); + } + QMap<FunctionType, int> functionCount; +private: + QMap<int, int> m_eventCount; +}; + +void tst_QGraphicsLayout::compressLayoutRequest() +{ + QGraphicsView view; + QGraphicsScene scene; + TestGraphicsWidget *tw = new TestGraphicsWidget(); + scene.addItem(tw); + view.show(); + + QTest::qWaitForWindowShown(&view); + QGraphicsLinearLayout *lout = new QGraphicsLinearLayout(tw); + for (int i = 0; i < 4; ++i) { + QGraphicsWidget *gw = new QGraphicsWidget(tw); + gw->setPreferredSize(QSizeF(50, 50)); + lout->addItem(gw); + } + QApplication::processEvents(); + QCOMPARE(tw->eventCount(QEvent::LayoutRequest), 1); +} + +void tst_QGraphicsLayout::automaticReparenting() +{ + QGraphicsView view; + QGraphicsScene scene; + { + QGraphicsWidget *w = new QGraphicsWidget(); + QGraphicsLinearLayout *l = new QGraphicsLinearLayout(w); + QGraphicsWidget *w1 = new QGraphicsWidget; + l->addItem(w1); + scene.addItem(w); + QCOMPARE(w1->parentWidget(), w); + delete w; + } + { + QGraphicsWidget *w = new QGraphicsWidget(); + QGraphicsLinearLayout *l = new QGraphicsLinearLayout(w); + QGraphicsWidget *w1 = new QGraphicsWidget; + l->addItem(w1); + scene.addItem(w); + QCOMPARE(w1->parentWidget(), w); + + QGraphicsWidget *ww = new QGraphicsWidget(); + QGraphicsLinearLayout *l1 = new QGraphicsLinearLayout(ww); +#if !defined(Q_OS_MAC) && defined(QT_DEBUG) + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLayout::addChildLayoutItem: QGraphicsWidget \"\"" + " in wrong parent; moved to correct parent"); +#endif + l1->addItem(w1); + QCOMPARE(w1->parentWidget(), ww); + delete w; + } + + QGraphicsWidget *window = new QGraphicsWidget(); + scene.addItem(window); + view.show(); + QGraphicsLinearLayout *l1 = new QGraphicsLinearLayout(); + QGraphicsWidget *w1 = new QGraphicsWidget(); + l1->addItem(w1); + QGraphicsWidget *w2 = new QGraphicsWidget(); + l1->addItem(w2); + QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(0)); + QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(0)); + scene.addItem(w1); + QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(0)); + window->setLayout(l1); + QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window)); + QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window)); + + // Sublayouts + QGraphicsLinearLayout *l2 = new QGraphicsLinearLayout(); + QGraphicsWidget *w3 = new QGraphicsWidget(); + l2->addItem(w3); + QGraphicsWidget *w4 = new QGraphicsWidget(); + l2->addItem(w4); + QGraphicsLinearLayout *l3 = new QGraphicsLinearLayout(); + l2->addItem(l3); + QGraphicsWidget *window2 = new QGraphicsWidget(); + scene.addItem(window2); + window2->setLayout(l2); + + QCOMPARE(w3->parentItem(), static_cast<QGraphicsItem*>(window2)); + QCOMPARE(w4->parentItem(), static_cast<QGraphicsItem*>(window2)); + + // graphics item with another parent + QGraphicsLinearLayout *l5 = new QGraphicsLinearLayout(); + l5->addItem(w1); + l5->addItem(w2); + QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window)); + QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window)); + QGraphicsLinearLayout *l4 = new QGraphicsLinearLayout(); + l4->addItem(l5); + QGraphicsWidget *window3 = new QGraphicsWidget(); + scene.addItem(window3); + window3->setLayout(l4); + + QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window3)); + QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window3)); +} + +class TestLayout : public QGraphicsLinearLayout +{ + public: + TestLayout(QGraphicsLayoutItem *parent = 0) + : QGraphicsLinearLayout(parent) + { + setContentsMargins(0,0,0,0); + setSpacing(0); + } + + void setGeometry(const QRectF &rect) + { + ++(functionCount[SetGeometry]); + QGraphicsLinearLayout::setGeometry(rect); + } + + void invalidate() + { + ++(functionCount[Invalidate]); + QGraphicsLinearLayout::invalidate(); + } + + void clearCounters() { + functionCount.clear(); + } + + QMap<FunctionType, int> functionCount; +}; + +void tst_QGraphicsLayout::verifyActivate() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsWidget *window = new QGraphicsWidget(); + scene.addItem(window); + TestLayout *lout = new TestLayout(window); + QGraphicsWidget *w = new QGraphicsWidget(); + lout->addItem(w); + window->setLayout(lout); + + QCOMPARE(lout->functionCount[SetGeometry], 0); + window->setVisible(false); + QCOMPARE(lout->functionCount[SetGeometry], 0); + window->setVisible(true); + // on polish or the first time a widget is shown, the widget is resized. + QCOMPARE(lout->functionCount[SetGeometry], 1); + +} + +static void clearAllCounters(TestGraphicsWidget *widget) +{ + if (!widget) + return; + widget->clearCounters(); + TestLayout *layout = static_cast<TestLayout *>(widget->layout()); + if (layout) { + layout->clearCounters(); + for (int i = layout->count() - 1; i >=0; --i) { + QGraphicsLayoutItem *item = layout->itemAt(i); + if (item->isLayout()) { + // ### Not used ATM + //TestLayout *lay = static_cast<TestLayout*>(static_cast<QGraphicsLayout*>(item)); + //clearAllCounters(lay); + } else { + TestGraphicsWidget *wid = static_cast<TestGraphicsWidget *>(item); + clearAllCounters(wid); + } + } + } +} + +static void activateAndReset(TestGraphicsWidget *widget) +{ + QApplication::sendPostedEvents(); + QApplication::processEvents(); + if (widget->layout()) + widget->layout()->activate(); + clearAllCounters(widget); +} + + +void tst_QGraphicsLayout::invalidate() +{ + QGraphicsLayout::setInstantInvalidatePropagation(true); + QGraphicsScene scene; + QGraphicsView view(&scene); + + TestGraphicsWidget *a = new TestGraphicsWidget; + a->setData(0, QString("a")); + scene.addItem(a); + TestLayout *alay = new TestLayout(a); + TestGraphicsWidget *b = new TestGraphicsWidget; + b->setData(0, QString("b")); + alay->addItem(b); + TestLayout *blay = new TestLayout(b); + TestGraphicsWidget *e = new TestGraphicsWidget; + e->setData(0, QString("e")); + blay->addItem(e); + + + TestGraphicsWidget *c = new TestGraphicsWidget; + c->setData(0, QString("c")); + alay->addItem(c); + TestLayout *clay = new TestLayout(c); + TestGraphicsWidget *f = new TestGraphicsWidget; + f->setData(0, QString("f")); + clay->addItem(f); + + TestGraphicsWidget *d = new TestGraphicsWidget; + d->setData(0, QString("d")); + alay->addItem(d); + TestLayout *dlay = new TestLayout(d); + TestGraphicsWidget *g = new TestGraphicsWidget; + g->setData(0, QString("g")); + dlay->addItem(g); + + view.show(); + + { + clearAllCounters(a); + + QCoreApplication::sendPostedEvents(); + QCoreApplication::processEvents(); + + alay->activate(); + QCOMPARE(alay->isActivated(), true); + QCOMPARE(blay->isActivated(), true); + QCOMPARE(clay->isActivated(), true); + QCOMPARE(dlay->isActivated(), true); + } + + { + clearAllCounters(a); + e->callUpdateGeometry(); + QCOMPARE(alay->isActivated(), false); + QCOMPARE(blay->isActivated(), false); + QCOMPARE(clay->isActivated(), true); + QCOMPARE(dlay->isActivated(), true); + QCOMPARE(a->eventCount(QEvent::LayoutRequest), 0); + QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0); + QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0); + QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0); + + // should only invalidate ascendants of e + QCOMPARE(blay->functionCount[Invalidate], 1); + QCOMPARE(alay->functionCount[Invalidate], 1); + // not siblings + QCOMPARE(clay->functionCount[Invalidate], 0); + QCOMPARE(dlay->functionCount[Invalidate], 0); + + QApplication::sendPostedEvents(); + QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0); + QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0); + } + + + { + activateAndReset(a); + f->callUpdateGeometry(); + QCOMPARE(alay->isActivated(), false); + QCOMPARE(blay->isActivated(), true); + QCOMPARE(clay->isActivated(), false); + QCOMPARE(dlay->isActivated(), true); + + QCoreApplication::sendPostedEvents(); + QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0); + QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0); + + QCOMPARE(a->functionCount[SetGeometry], 1); + QCOMPARE(alay->functionCount[SetGeometry], 1); + + QCOMPARE(b->functionCount[SetGeometry], 1); + QCOMPARE(c->functionCount[SetGeometry], 1); + QCOMPARE(d->functionCount[SetGeometry], 1); + // Since nothing really changed, blay and dlay don't need + // to be resized. + QCOMPARE(blay->functionCount[SetGeometry], 0); + QCOMPARE(clay->functionCount[SetGeometry], 1); + QCOMPARE(dlay->functionCount[SetGeometry], 0); + + QCOMPARE(f->functionCount[SetGeometry], 1); + + QCOMPARE(a->size(), QSizeF(150, 50)); + } + + { + activateAndReset(a); + f->setPreferredSize(QSizeF(60,50)); + QCOMPARE(alay->isActivated(), false); + QCOMPARE(blay->isActivated(), true); + QCOMPARE(clay->isActivated(), false); + QCOMPARE(dlay->isActivated(), true); + + QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0); + QCoreApplication::sendPostedEvents(); + QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0); + QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0); + + QCOMPARE(a->functionCount[SetGeometry], 1); + QCOMPARE(alay->functionCount[SetGeometry], 1); + + QCOMPARE(b->functionCount[SetGeometry], 1); + QCOMPARE(c->functionCount[SetGeometry], 1); + QCOMPARE(d->functionCount[SetGeometry], 1); + // f actually got wider, need to rearrange its siblings + QCOMPARE(blay->functionCount[SetGeometry], 1); + QCOMPARE(clay->functionCount[SetGeometry], 1); + QCOMPARE(dlay->functionCount[SetGeometry], 1); + + QCOMPARE(e->functionCount[SetGeometry], 1); + QCOMPARE(f->functionCount[SetGeometry], 1); + QCOMPARE(g->functionCount[SetGeometry], 1); + + QVERIFY(e->size().width() < f->size().width()); + QVERIFY(g->size().width() < f->size().width()); + } + + { + // resize f so much that it'll force a resize of the top widget + // this will currently generate two setGeometry() calls on the child layout + // of the top widget. + activateAndReset(a); + f->setPreferredSize(QSizeF()); + f->setMinimumSize(QSizeF(200,50)); + QCOMPARE(alay->isActivated(), false); + QCOMPARE(blay->isActivated(), true); + QCOMPARE(clay->isActivated(), false); + QCOMPARE(dlay->isActivated(), true); + + QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0); + QCoreApplication::sendPostedEvents(); + QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0); + QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0); + + QCOMPARE(a->functionCount[SetGeometry], 1); + + /* well, ideally one call to setGeometry(), but it will currently + * get two calls to setGeometry(): + * 1. The first LayoutRequest will call activate() - that will call + * setGeometry() on the layout. This geometry will be based on + * the widget geometry which is not correct at this moment. + * (it is still 150 wide) + * 2. Next, we check if the widget is top level, and then we call + * parentWidget->resize(parentWidget->size()); + * This will be adjusted to be minimum 200 pixels wide. + * The new size will then be propagated down to the layout + * + */ + QCOMPARE(alay->functionCount[SetGeometry], 2); + + QCOMPARE(b->functionCount[SetGeometry], 2); + QCOMPARE(c->functionCount[SetGeometry], 2); + QCOMPARE(d->functionCount[SetGeometry], 2); + // f actually got wider, need to rearrange its siblings + QCOMPARE(blay->functionCount[SetGeometry], 1); + QCOMPARE(clay->functionCount[SetGeometry], 1); + QCOMPARE(dlay->functionCount[SetGeometry], 1); + + QCOMPARE(e->functionCount[SetGeometry], 1); + QCOMPARE(f->functionCount[SetGeometry], 1); + QCOMPARE(g->functionCount[SetGeometry], 1); + + QVERIFY(e->size().width() < f->size().width()); + QVERIFY(g->size().width() < f->size().width()); + } + + { + f->setPreferredSize(QSizeF()); + f->setMinimumSize(QSizeF()); + a->adjustSize(); + activateAndReset(a); + // update two different leaf widgets, + // eventCount and functionCount should never be >= 2 + e->callUpdateGeometry(); + g->callUpdateGeometry(); + QCOMPARE(alay->isActivated(), false); + QCOMPARE(blay->isActivated(), false); + QCOMPARE(clay->isActivated(), true); + QCOMPARE(dlay->isActivated(), false); + + QCoreApplication::sendPostedEvents(); + QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1); + QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0); + QCOMPARE(d->eventCount(QEvent::LayoutRequest), 1); + + QCOMPARE(a->functionCount[SetGeometry], 1); + QCOMPARE(alay->functionCount[SetGeometry], 1); + + QCOMPARE(b->functionCount[SetGeometry], 1); + QCOMPARE(c->functionCount[SetGeometry], 1); + QCOMPARE(d->functionCount[SetGeometry], 1); + // f actually got wider, need to rearrange its siblings + QCOMPARE(blay->functionCount[SetGeometry], 1); + QCOMPARE(clay->functionCount[SetGeometry], 0); + QCOMPARE(dlay->functionCount[SetGeometry], 1); + + QCOMPARE(e->functionCount[SetGeometry], 1); + QCOMPARE(f->functionCount[SetGeometry], 0); + QCOMPARE(g->functionCount[SetGeometry], 1); + + } + + QGraphicsLayout::setInstantInvalidatePropagation(false); +} + +class Layout : public QGraphicsLayout +{ +public: + Layout(QGraphicsLayoutItem *parentItem = 0) : QGraphicsLayout(parentItem) {} + + void setGeometry(const QRectF &rect) + { + QGraphicsLayout::setGeometry(rect); + } + + int count() const { + return 0; + } + + QGraphicsLayoutItem *itemAt(int index) const { + Q_UNUSED(index); + return 0; + } + + void removeAt(int index) + { + Q_UNUSED(index); + } + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const + { + Q_UNUSED(constraint); + Q_UNUSED(which); + return QSizeF(100,100); + } + +}; + +void tst_QGraphicsLayout::constructors() +{ + // Strange test, but see the fix that was with this submit + QVector<Layout*> layouts; + for (int pass = 0; pass < 5; ++pass) { + Layout *lay = new Layout(); + layouts << lay; + qreal left, top, right, bottom; + lay->getContentsMargins(&left, &top, &right, &bottom); + // Test if the style defaults are sane (should always be ints) + double intpart; + QVERIFY(modf(left, &intpart) == 0.0); + QVERIFY(modf(top, &intpart) == 0.0); + QVERIFY(modf(right, &intpart) == 0.0); + QVERIFY(modf(bottom, &intpart) == 0.0); + + lay->setContentsMargins(1, 2, 4, 8); + lay->getContentsMargins(&left, &top, &right, &bottom); + + QCOMPARE(int(left), 1); + QCOMPARE(int(top), 2); + QCOMPARE(int(right), 4); + QCOMPARE(int(bottom), 8); + } + + qDeleteAll(layouts); +} + +class AnimatedLayoutItem : public QGraphicsLayoutItem { +public: + AnimatedLayoutItem(QGraphicsRectItem *item) + : QGraphicsLayoutItem() + { + setGraphicsItem(item); + } + + void setGeometry(const QRectF &geom); + + QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; + + inline QGraphicsRectItem *rectItem() { + return static_cast<QGraphicsRectItem *>(graphicsItem()); + } + + QRectF m_geom; +private: + AnimatedLayoutItem() {} +}; + +void AnimatedLayoutItem::setGeometry(const QRectF &geom) +{ + QGraphicsLayoutItem::setGeometry(geom); +} + +QSizeF AnimatedLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const +{ + switch (which) { + case Qt::MinimumSize: + return QSizeF(32,32); + case Qt::PreferredSize: + return QSizeF(160,90); + case Qt::MaximumSize: + return QSizeF(1000,1000); + default: + return QSizeF(300, 300); + } +} + +class AnimatedLayout : public QObject, public QGraphicsLinearLayout { + Q_OBJECT +public: + AnimatedLayout(QGraphicsWidget *widget) : QGraphicsLinearLayout(widget), m_timeline(500, this) + { + connect(&m_timeline, SIGNAL(valueChanged(qreal)), this, SLOT(valueChanged(qreal))); + } + + void setGeometry(const QRectF &geom) { + fromGeoms.clear(); + toGeoms.clear(); + for (int i = 0; i < count(); ++i) { + fromGeoms << itemAt(i)->geometry(); + } + + QGraphicsLinearLayout::setGeometry(geom); + + for (int i = 0; i < count(); ++i) { + toGeoms << itemAt(i)->geometry(); + } + m_timeline.start(); + } + +private slots: + void valueChanged(qreal value) { + for (int i = 0; i < fromGeoms.count(); ++i) { + QGraphicsLayoutItem *li = itemAt(i); + QRectF from = fromGeoms.at(i); + QRectF to = toGeoms.at(i); + + QRectF geom(from.topLeft() + (to.topLeft() - from.topLeft()) * value, + from.size() + (to.size() - from.size()) * value); + static_cast<QGraphicsRectItem*>(li->graphicsItem())->setRect(geom); + } + } +private: + QTimeLine m_timeline; + QVector<QRectF> fromGeoms; + QVector<QRectF> toGeoms; +}; + + +void tst_QGraphicsLayout::alternativeLayoutItems() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsWidget *window = new QGraphicsWidget; + scene.addItem(window); + AnimatedLayout *lout = new AnimatedLayout(window); + lout->setContentsMargins(0, 0, 0, 0); + lout->setSpacing(0); + + QGraphicsRectItem *item1 = new QGraphicsRectItem; + AnimatedLayoutItem *li1 = new AnimatedLayoutItem(item1); + lout->addItem(li1); + + QGraphicsRectItem *item2 = new QGraphicsRectItem; + AnimatedLayoutItem *li2 = new AnimatedLayoutItem(item2); + lout->addItem(li2); + + QGraphicsRectItem *item3 = new QGraphicsRectItem; + AnimatedLayoutItem *li3 = new AnimatedLayoutItem(item3); + lout->addItem(li3); + + window->setLayout(lout); + + window->setGeometry(0, 0, 99, 99); + view.setSceneRect(QRectF(-10, -10, 110, 110)); + view.resize(150, 150); + view.show(); + + QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li1->graphicsItem())->rect(), QRectF( 0, 0, 33, 99)); + QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li2->graphicsItem())->rect(), QRectF(33, 0, 33, 99)); + QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li3->graphicsItem())->rect(), QRectF(66, 0, 33, 99)); + + lout->setOrientation(Qt::Vertical); + + QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li1->graphicsItem())->rect(), QRectF(0, 0, 99, 33)); + QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li2->graphicsItem())->rect(), QRectF(0, 33, 99, 33)); + QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li3->graphicsItem())->rect(), QRectF(0, 66, 99, 33)); + +} + +class CustomLayoutItem : public QGraphicsLayoutItem { +public: + CustomLayoutItem(QSet<QGraphicsLayoutItem*> *destructedSet) + : QGraphicsLayoutItem() + { + m_destructedSet = destructedSet; + setOwnedByLayout(true); + } + + QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; + + ~CustomLayoutItem() { + m_destructedSet->insert(this); + } +private: + QSet<QGraphicsLayoutItem*> *m_destructedSet; +}; + +QSizeF CustomLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const +{ + switch (which) { + case Qt::MinimumSize: + return QSizeF(32,32); + case Qt::PreferredSize: + return QSizeF(160,90); + case Qt::MaximumSize: + return QSizeF(1000,1000); + default: + return QSizeF(300, 300); + } +} + +class CustomGraphicsWidget : public QGraphicsWidget { +public: + CustomGraphicsWidget(QSet<QGraphicsLayoutItem*> *destructedSet = 0) + : QGraphicsWidget() + { + m_destructedSet = destructedSet; + } + + QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * = 0) + { + const QRect r = option->rect.adjusted(0, 0, -1, -1); + painter->drawLine(r.topLeft(), r.bottomRight()); + painter->drawLine(r.bottomLeft(), r.topRight()); + painter->drawRect(r); + } + + ~CustomGraphicsWidget() { + if (m_destructedSet) + m_destructedSet->insert(this); + } +private: + QSet<QGraphicsLayoutItem*> *m_destructedSet; +}; + +QSizeF CustomGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const +{ + switch (which) { + case Qt::MinimumSize: + return QSizeF(32,32); + case Qt::PreferredSize: + return QSizeF(160,90); + case Qt::MaximumSize: + return QSizeF(1000,1000); + default: + return QSizeF(300, 300); + } +} + +static bool compareSets(const QSet<QGraphicsLayoutItem*> &actual, const QSet<QGraphicsLayoutItem*> &expected) +{ + if (actual != expected) { + qDebug() << "actual:" << actual << "expected:" << expected; + return false; + } + return true; +} + +class CustomLayout : public QGraphicsLayout +{ +public : +CustomLayout(QGraphicsLayoutItem *parent) + : QGraphicsLayout(parent) +{ +} + + +~CustomLayout() +{ +} + +int count() const +{ + return items.count(); +} + +QGraphicsLayoutItem* itemAt(int index) const +{ + return items.at(index); +} + + +void removeAt(int index) +{ + items.removeAt(index); +} + +void addItem(QGraphicsLayoutItem *item) +{ + insertItem(items.count(), item); +} + +void insertItem(int index, QGraphicsLayoutItem *item) +{ + index = qBound(0, index, items.count()); + + item->setParentLayoutItem(this); + + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + updateParentWidget(widget); + + + if (index == items.count()) { + items.append(item); + } else { + items.insert(index, item); + } + + updateGeometry(); + activate(); +} + +void updateParentWidget(QGraphicsWidget *item) +{ + QGraphicsLayoutItem *parentItem = parentLayoutItem(); + while (parentItem && parentItem->isLayout()) { + parentItem = parentItem->parentLayoutItem(); + } + + if (parentItem) { + item->setParentItem(static_cast<QGraphicsWidget*>(parentItem)); + } +} + +QSizeF sizeHint(Qt::SizeHint /* which */, const QSizeF & /* constraint */) const +{ + return QSizeF(50,50); +} + +QList<QGraphicsLayoutItem*> items; + +}; + +void tst_QGraphicsLayout::ownership() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + { + QGraphicsLinearLayout *lay = new QGraphicsLinearLayout; + QSet<QGraphicsLayoutItem*> destructedSet; + CustomLayoutItem *li1 = new CustomLayoutItem(&destructedSet); + lay->addItem(li1); + CustomLayoutItem *li2 = new CustomLayoutItem(&destructedSet); + lay->addItem(li2); + CustomLayoutItem *li3 = new CustomLayoutItem(&destructedSet); + lay->addItem(li3); + destructedSet.clear(); + + delete lay; + QSet<QGraphicsLayoutItem*> expected; + expected << li1 << li2 << li3; + QVERIFY(compareSets(destructedSet, expected)); + } + + { + QGraphicsWidget *window = new QGraphicsWidget; + QGraphicsLinearLayout *lay = new QGraphicsLinearLayout; + QSet<QGraphicsLayoutItem*> destructedSet; + CustomGraphicsWidget *li1 = new CustomGraphicsWidget(&destructedSet); + lay->addItem(li1); + CustomGraphicsWidget *li2 = new CustomGraphicsWidget(&destructedSet); + lay->addItem(li2); + CustomGraphicsWidget *li3 = new CustomGraphicsWidget(&destructedSet); + lay->addItem(li3); + window->setLayout(lay); + scene.addItem(window); + + destructedSet.clear(); + window->setLayout(0); + QVERIFY(destructedSet.count() == 0); + delete window; + } + + { + QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window); + QGraphicsLinearLayout *lay = new QGraphicsLinearLayout; + + CustomGraphicsWidget *li1 = new CustomGraphicsWidget; + lay->addItem(li1); + + QGraphicsLinearLayout *li2 = new QGraphicsLinearLayout; + CustomGraphicsWidget *li2_1 = new CustomGraphicsWidget; + li2->addItem(li2_1); + CustomGraphicsWidget *li2_2 = new CustomGraphicsWidget; + li2->addItem(li2_2); + CustomGraphicsWidget *li2_3 = new CustomGraphicsWidget; + li2->addItem(li2_3); + lay->addItem(li2); + + CustomGraphicsWidget *li3 = new CustomGraphicsWidget; + lay->addItem(li3); + + window->setLayout(lay); + scene.addItem(window); + view.resize(500, 200); + view.show(); + + for (int i = li2->count(); i > 0; --i) { + QCOMPARE(li2->count(), i); + delete li2->itemAt(0); + } + + for (int i = lay->count(); i > 0; --i) { + QCOMPARE(lay->count(), i); + delete lay->itemAt(0); + } + + delete window; + } + + { + QGraphicsWidget *top = new QGraphicsWidget; + QGraphicsWidget *w = new QGraphicsWidget; + QGraphicsWidget *w2 = new QGraphicsWidget; + CustomLayout *layout = new CustomLayout(top); + layout->addItem(w); + layout->addItem(w2); + top->setLayout(layout); + delete top; + //don't crash after that. + } +} + +QTEST_MAIN(tst_QGraphicsLayout) +#include "tst_qgraphicslayout.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicslayoutitem/.gitignore b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/.gitignore new file mode 100644 index 0000000000..55a8d18acb --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/.gitignore @@ -0,0 +1 @@ +tst_qgraphicslayoutitem diff --git a/tests/auto/widgets/graphicsview/qgraphicslayoutitem/qgraphicslayoutitem.pro b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/qgraphicslayoutitem.pro new file mode 100644 index 0000000000..ed9adf87fd --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/qgraphicslayoutitem.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qgraphicslayoutitem.cpp +CONFIG += parallel_test + diff --git a/tests/auto/widgets/graphicsview/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp new file mode 100644 index 0000000000..a8b6c0854e --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qgraphicslayoutitem.h> +#include <float.h> +#include <limits.h> + +class tst_QGraphicsLayoutItem : public QObject { +Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qgraphicslayoutitem(); + + void contentsRect(); + void effectiveSizeHint_data(); + void effectiveSizeHint(); + void getContentsMargins(); + void isLayout_data(); + void isLayout(); + void maximumSize(); + void minimumSize(); + void parentLayoutItem_data(); + void parentLayoutItem(); + void preferredSize(); + void setMaximumSize_data(); + void setMaximumSize(); + void setMinimumSize_data(); + void setMinimumSize(); + void setPreferredSize_data(); + void setPreferredSize(); + void setSizePolicy_data(); + void setPreferredSize2(); + void setSizePolicy(); +}; + +// Subclass that exposes the protected functions. +class SubQGraphicsLayoutItem : public QGraphicsLayoutItem { +public: + SubQGraphicsLayoutItem(QGraphicsLayoutItem *par = 0, bool layout = false) + : QGraphicsLayoutItem(par, layout), updateGeometryCalled(0) + {} + + // QGraphicsLayoutItem::geometry is a pure virtual function + QRectF geometry() const + { return QRectF(); } + + // QGraphicsLayoutItem::setGeometry is a pure virtual function + void setGeometry(QRectF const& rect) + { Q_UNUSED(rect); } + + // QGraphicsLayoutItem::sizeHint is a pure virtual function + QSizeF sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const + { Q_UNUSED(which); Q_UNUSED(constraint); return QSizeF(); } + + void updateGeometry() + { updateGeometryCalled++; QGraphicsLayoutItem::updateGeometry(); } + int updateGeometryCalled; + +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsLayoutItem::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsLayoutItem::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsLayoutItem::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsLayoutItem::cleanup() +{ +} + +void tst_QGraphicsLayoutItem::qgraphicslayoutitem() +{ + SubQGraphicsLayoutItem layoutItem; + layoutItem.contentsRect(); + layoutItem.effectiveSizeHint(Qt::MinimumSize); + layoutItem.geometry(); + QCOMPARE(layoutItem.isLayout(), false); + layoutItem.maximumSize(); + layoutItem.minimumSize(); + QCOMPARE(layoutItem.parentLayoutItem(), (QGraphicsLayoutItem*)0); + layoutItem.preferredSize(); + layoutItem.sizePolicy(); + layoutItem.sizeHint(Qt::MinimumSize); +} + +// QRectF contentsRect() const public +void tst_QGraphicsLayoutItem::contentsRect() +{ + SubQGraphicsLayoutItem layoutItem; + QRectF f = layoutItem.contentsRect(); + QCOMPARE(f, QRectF(QPoint(), QSizeF(0, 0))); +} +Q_DECLARE_METATYPE(Qt::SizeHint) +void tst_QGraphicsLayoutItem::effectiveSizeHint_data() +{ + QTest::addColumn<Qt::SizeHint>("sizeHint"); + QTest::addColumn<QSizeF>("constraint"); + for (int i = 0; i < 15; ++i) { + QTestData &data = QTest::newRow(QString("%1").arg(i).toLatin1()); + switch(i % 5) { + case 0: data << Qt::MinimumSize; break; + case 1: data << Qt::PreferredSize; break; + case 2: data << Qt::MaximumSize; break; + case 3: data << Qt::MinimumDescent; break; + case 4: data << Qt::NSizeHints; break; + } + switch(i % 3) { + case 0: data << QSizeF(-1, -1); break; + case 1: data << QSizeF(0, 0); break; + case 2: data << QSizeF(10, 10); break; + } + } +} + +// QSizeF effectiveSizeHint(Qt::SizeHint which, QSizeF const& constraint = QSize()) const public +void tst_QGraphicsLayoutItem::effectiveSizeHint() +{ + QFETCH(Qt::SizeHint, sizeHint); + QFETCH(QSizeF, constraint); + SubQGraphicsLayoutItem layoutItem; + QSizeF r = layoutItem.effectiveSizeHint(sizeHint, constraint); + if (constraint.width() != -1) + QCOMPARE(r.width(), constraint.width()); + if (constraint.height() != -1) + QCOMPARE(r.height(), constraint.height()); +} + +// void getContentsMargins(qreal* left, qreal* top, qreal* right, qreal* bottom) const public +void tst_QGraphicsLayoutItem::getContentsMargins() +{ + SubQGraphicsLayoutItem layoutItem; + qreal left; + qreal top; + qreal right; + qreal bottom; + layoutItem.getContentsMargins(&left, &top, &right, &bottom); + QCOMPARE(left, (qreal)0); + QCOMPARE(top, (qreal)0); + QCOMPARE(right, (qreal)0); + QCOMPARE(bottom, (qreal)0); +} + +void tst_QGraphicsLayoutItem::isLayout_data() +{ + QTest::addColumn<bool>("isLayout"); + QTest::newRow("no") << false; + QTest::newRow("yes") << true; +} + +// bool isLayout() const public +void tst_QGraphicsLayoutItem::isLayout() +{ + QFETCH(bool, isLayout); + SubQGraphicsLayoutItem layoutItem(0, isLayout); + QCOMPARE(layoutItem.isLayout(), isLayout); +} + +// QSizeF maximumSize() const public +void tst_QGraphicsLayoutItem::maximumSize() +{ + SubQGraphicsLayoutItem layoutItem; + QCOMPARE(layoutItem.maximumSize(), QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); + // layoutItem.effectiveSizeHint(Qt::MaximumSize); +} + +// QSizeF minimumSize() const public +void tst_QGraphicsLayoutItem::minimumSize() +{ + SubQGraphicsLayoutItem layoutItem; + QCOMPARE(layoutItem.minimumSize(), QSizeF(0, 0)); + // layoutItem.effectiveSizeHint(Qt::MinimumSize); +} + +void tst_QGraphicsLayoutItem::parentLayoutItem_data() +{ + QTest::addColumn<bool>("parent"); + QTest::newRow("no") << false; + QTest::newRow("yes") << true; +} + +// QGraphicsLayoutItem* parentLayoutItem() const public +void tst_QGraphicsLayoutItem::parentLayoutItem() +{ + QFETCH(bool, parent); + SubQGraphicsLayoutItem parentLayout; + SubQGraphicsLayoutItem layoutItem(parent ? &parentLayout : 0); + QCOMPARE(layoutItem.parentLayoutItem(), static_cast<QGraphicsLayoutItem*>( parent ? &parentLayout : 0)); +} + +// QSizeF preferredSize() const public +void tst_QGraphicsLayoutItem::preferredSize() +{ + SubQGraphicsLayoutItem layoutItem; + QCOMPARE(layoutItem.preferredSize(), QSizeF(0, 0)); + // layoutItem.effectiveSizeHint(Qt::PreferredSize)); +} + +void tst_QGraphicsLayoutItem::setMaximumSize_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<QSizeF>("outputSize"); + QTest::newRow("-1") << QSizeF(-1, -1) << QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + QTest::newRow("0") << QSizeF(0, 0) << QSizeF(0, 0); + QTest::newRow("10") << QSizeF(10, 10) << QSizeF(10, 10); +} + +// void setMaximumSize(QSizeF const& size) public +void tst_QGraphicsLayoutItem::setMaximumSize() +{ + QFETCH(QSizeF, size); + QFETCH(QSizeF, outputSize); + SubQGraphicsLayoutItem layoutItem; + QSizeF oldSize = layoutItem.maximumSize(); + layoutItem.setMaximumSize(size); + if (size.isValid()) + QCOMPARE(layoutItem.updateGeometryCalled, (oldSize == size) ? 0 : 1); + else + QVERIFY(!layoutItem.updateGeometryCalled); + layoutItem.setMinimumSize(1, 1); + + QVERIFY(layoutItem.maximumSize().width() <= outputSize.width()); + QVERIFY(layoutItem.maximumSize().height() <= outputSize.height()); + QVERIFY(layoutItem.minimumSize().width() <= outputSize.width()); + QVERIFY(layoutItem.minimumSize().height() <= outputSize.height()); + QVERIFY(layoutItem.preferredSize().width() <= outputSize.width()); + QVERIFY(layoutItem.preferredSize().height() <= outputSize.height()); +} + +void tst_QGraphicsLayoutItem::setMinimumSize_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<QSizeF>("outputSize"); + QTest::newRow("-1") << QSizeF(-1, -1) << QSizeF(0, 0); + QTest::newRow("0") << QSizeF(0, 0) << QSizeF(0, 0); + QTest::newRow("10") << QSizeF(10, 10) << QSizeF(10, 10); +} + +// void setMinimumSize(QSizeF const& size) public +void tst_QGraphicsLayoutItem::setMinimumSize() +{ + QFETCH(QSizeF, size); + QFETCH(QSizeF, outputSize); + SubQGraphicsLayoutItem layoutItem; + QSizeF oldSize = layoutItem.minimumSize(); + + layoutItem.setMinimumSize(size); + if (size.isValid()) { + QEXPECT_FAIL("0", "updateGeometry() is called when it doesn't have to be.", Continue); + QCOMPARE(layoutItem.updateGeometryCalled, (oldSize == size) ? 0 : 1); + } else { + QVERIFY(!layoutItem.updateGeometryCalled); + } + layoutItem.setMaximumSize(5, 5); + QEXPECT_FAIL("10", "layoutItem.maximumSize().width() < size.width()", Abort); + QVERIFY(layoutItem.maximumSize().width() >= size.width()); + QVERIFY(layoutItem.maximumSize().height() >= size.height()); + QVERIFY(layoutItem.minimumSize().width() >= size.width()); + QVERIFY(layoutItem.minimumSize().height() >= size.height()); + QVERIFY(layoutItem.preferredSize().width() >= size.width()); + QVERIFY(layoutItem.preferredSize().height() >= size.height()); +} + +void tst_QGraphicsLayoutItem::setPreferredSize_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::newRow("-1") << QSizeF(-1, -1); + QTest::newRow("0") << QSizeF(0, 0); + QTest::newRow("10") << QSizeF(10, 10); +} + +// void setPreferredSize(QSizeF const& size) public +void tst_QGraphicsLayoutItem::setPreferredSize() +{ + QFETCH(QSizeF, size); + SubQGraphicsLayoutItem layoutItem; + QSizeF oldSize = layoutItem.preferredSize(); + layoutItem.setPreferredSize(size); + if (size.isValid()) + QCOMPARE(layoutItem.preferredSize(), size); + + if (size.isValid()) { + QEXPECT_FAIL("0", "updateGeometry() is called when it doesn't have to be.", Continue); + QCOMPARE(layoutItem.updateGeometryCalled, (oldSize == size) ? 0 : 1); + } else { + QVERIFY(!layoutItem.updateGeometryCalled); + } +} + +void tst_QGraphicsLayoutItem::setPreferredSize2() +{ + SubQGraphicsLayoutItem layoutItem; + layoutItem.setPreferredSize(QSizeF(30, -1)); + QCOMPARE(layoutItem.preferredWidth(), qreal(30)); +} + +void tst_QGraphicsLayoutItem::setSizePolicy_data() +{ + QTest::addColumn<QSizePolicy>("policy"); + QTest::newRow("default") << QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed, QSizePolicy::DefaultType); + QTest::newRow("rand") << QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); +} + +// void setSizePolicy(QSizePolicy const& policy) public +void tst_QGraphicsLayoutItem::setSizePolicy() +{ + QFETCH(QSizePolicy, policy); + SubQGraphicsLayoutItem layoutItem; + QSizePolicy defaultPolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType); + QCOMPARE(layoutItem.sizePolicy(), defaultPolicy); + + layoutItem.setSizePolicy(policy); + QCOMPARE(layoutItem.sizePolicy(), policy); + QCOMPARE(layoutItem.updateGeometryCalled, (defaultPolicy == policy) ? 0 : 1); +} + +QTEST_MAIN(tst_QGraphicsLayoutItem) +#include "tst_qgraphicslayoutitem.moc" + diff --git a/tests/auto/widgets/graphicsview/qgraphicslinearlayout/.gitignore b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/.gitignore new file mode 100644 index 0000000000..95c7dac979 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/.gitignore @@ -0,0 +1 @@ +tst_qgraphicslinearlayout diff --git a/tests/auto/widgets/graphicsview/qgraphicslinearlayout/qgraphicslinearlayout.pro b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/qgraphicslinearlayout.pro new file mode 100644 index 0000000000..1f7ff0cc6e --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/qgraphicslinearlayout.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qgraphicslinearlayout.cpp +CONFIG += parallel_test + diff --git a/tests/auto/widgets/graphicsview/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp new file mode 100644 index 0000000000..873a711127 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp @@ -0,0 +1,1650 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qgraphicslinearlayout.h> +#include <qgraphicsproxywidget.h> +#include <qgraphicswidget.h> +#include <qgraphicsscene.h> +#include <qgraphicsview.h> +#include <qapplication.h> +#include <qplastiquestyle.h> + +class tst_QGraphicsLinearLayout : public QObject { +Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qgraphicslinearlayout_data(); + void qgraphicslinearlayout(); + + void alignment_data(); + void alignment(); + void count_data(); + void count(); + void dump_data(); + void dump(); + void geometry_data(); + void geometry(); + void insertItem_data(); + void insertItem(); + void insertStretch_data(); + void insertStretch(); + void invalidate_data(); + void invalidate(); + void itemAt_data(); + void itemAt(); + void itemAt_visualOrder(); + void orientation_data(); + void orientation(); + void removeAt_data(); + void removeAt(); + void removeItem_data(); + void removeItem(); + void setGeometry_data(); + void setGeometry(); + void setSpacing_data(); + void setSpacing(); + void setItemSpacing_data(); + void setItemSpacing(); + void itemSpacing(); + void setStretchFactor_data(); + void setStretchFactor(); + void testStretch(); + void defaultStretchFactors_data(); + void defaultStretchFactors(); + void sizeHint_data(); + void sizeHint(); + void updateGeometry(); + void layoutDirection(); + void removeLayout(); + void avoidRecursionInInsertItem(); + void styleInfoLeak(); + void testAlignmentInLargerLayout(); + void testOffByOneInLargerLayout(); + void testDefaultAlignment(); + void combineSizePolicies(); + + // Task specific tests + void task218400_insertStretchCrash(); +}; + +// Subclass that exposes the protected functions. +class SubQGraphicsLinearLayout : public QGraphicsLinearLayout { +public: + SubQGraphicsLinearLayout(Qt::Orientation orientation = Qt::Horizontal) : QGraphicsLinearLayout(orientation), + graphicsSceneResize(0), + layoutRequest(0), + layoutDirectionChange(0) + { } + + void widgetEvent(QEvent *e) + { + switch (e->type()) { + case QEvent::GraphicsSceneResize: + graphicsSceneResize++; + break; + case QEvent::LayoutRequest: + layoutRequest++; + break; + case QEvent::LayoutDirectionChange: + layoutDirectionChange++; + break; + default: + break; + } + + QGraphicsLinearLayout::widgetEvent(e); + } + + int graphicsSceneResize; + int layoutRequest; + int layoutDirectionChange; +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsLinearLayout::initTestCase() +{ + // since the style will influence the results, we have to ensure + // that the tests are run using the same style on all platforms +#if defined (Q_WS_WINCE) + QApplication::setStyle(new QWindowsStyle); +#else + QApplication::setStyle(new QPlastiqueStyle); +#endif +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsLinearLayout::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsLinearLayout::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsLinearLayout::cleanup() +{ +} + +class RectWidget : public QGraphicsWidget +{ +public: + RectWidget(QGraphicsItem *parent = 0, const QBrush &brush = QBrush()) : QGraphicsWidget(parent){ m_brush = brush;} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + Q_UNUSED(option); + Q_UNUSED(widget); + painter->setBrush(m_brush); + painter->drawRoundRect(rect()); + } + + void setSizeHint(Qt::SizeHint which, const QSizeF &size) { + m_sizeHints[which] = size; + updateGeometry(); + } + + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const { + if (m_sizeHints[which].isValid()) { + return m_sizeHints[which]; + } + return QGraphicsWidget::sizeHint(which, constraint); + } + + QSizeF m_sizeHints[Qt::NSizeHints]; + QBrush m_brush; +}; + + +Q_DECLARE_METATYPE(Qt::Orientation) +void tst_QGraphicsLinearLayout::qgraphicslinearlayout_data() +{ + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::newRow("vertical") << Qt::Vertical; + QTest::newRow("horizontal") << Qt::Horizontal; +} + +void tst_QGraphicsLinearLayout::qgraphicslinearlayout() +{ + QFETCH(Qt::Orientation, orientation); + SubQGraphicsLinearLayout layout(orientation); + QVERIFY(layout.isLayout()); + + qApp->processEvents(); + QCOMPARE(layout.graphicsSceneResize, 0); + QCOMPARE(layout.layoutRequest, 0); + QCOMPARE(layout.layoutDirectionChange, 0); + + layout.setOrientation(Qt::Vertical); + layout.orientation(); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLinearLayout::insertItem: cannot insert null item"); + QCOMPARE(layout.count(), 0); + layout.addItem(0); + QCOMPARE(layout.count(), 0); + layout.addStretch(0); + QCOMPARE(layout.count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLinearLayout::insertItem: cannot insert null item"); + layout.insertItem(0, 0); + layout.insertStretch(0, 0); + layout.removeItem(0); + QCOMPARE(layout.count(), 0); + layout.setSpacing(0); + layout.spacing(); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLinearLayout::setStretchFactor: cannot assign a stretch factor to a null item"); + layout.setStretchFactor(0, 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLinearLayout::setStretchFactor: cannot return a stretch factor for a null item"); + layout.stretchFactor(0); + layout.setAlignment(0, Qt::AlignHCenter); + QCOMPARE(layout.alignment(0), 0); + layout.setGeometry(QRectF()); + layout.geometry(); + QCOMPARE(layout.count(), 0); + layout.invalidate(); + layout.sizeHint(Qt::MinimumSize, QSizeF()); +} + +Q_DECLARE_METATYPE(Qt::AlignmentFlag) +void tst_QGraphicsLinearLayout::alignment_data() +{ + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<QSize>("newSize"); + QTest::newRow("h-defaultsize") << Qt::Horizontal << QSize(); + QTest::newRow("v-defaultsize") << Qt::Vertical << QSize(); + QTest::newRow("h-300") << Qt::Horizontal << QSize(300,100); + QTest::newRow("v-300") << Qt::Vertical << QSize(100, 300); +} + +void tst_QGraphicsLinearLayout::alignment() +{ + QFETCH(Qt::Orientation, orientation); + QFETCH(QSize, newSize); + + //if (alignment == Qt::AlignAbsolute) + // QApplication::setLayoutDirection(Qt::RightToLeft); + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setSceneRect(0, 0, 320, 240); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout &layout = *(new SubQGraphicsLinearLayout(orientation)); + scene.addItem(widget); + widget->setLayout(&layout); + + static const Qt::Alignment alignmentsToTest[] = { + (Qt::Alignment)0, + Qt::AlignLeft, + Qt::AlignRight, + Qt::AlignHCenter, + Qt::AlignTop, + Qt::AlignBottom, + Qt::AlignVCenter, + Qt::AlignCenter, + (Qt::Alignment)0, + Qt::AlignLeft, + Qt::AlignRight, + Qt::AlignHCenter, + Qt::AlignTop, + Qt::AlignBottom, + Qt::AlignVCenter, + Qt::AlignCenter + }; + + int i; + bool addWidget = true; + for (size_t i = 0; i < sizeof(alignmentsToTest)/sizeof(Qt::Alignment); ++i) { + QGraphicsLayoutItem *loutItem; + Qt::Alignment align = alignmentsToTest[i]; + if (!align && i > 0) + addWidget = false; + if (addWidget) + loutItem = new RectWidget(widget, QBrush(Qt::blue)); + else { + SubQGraphicsLinearLayout *lay = new SubQGraphicsLinearLayout(Qt::Vertical); + lay->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType); + lay->setContentsMargins(0,0,0,0); + QGraphicsWidget *w = new RectWidget(widget, QBrush(Qt::red)); + if (align) { + w->setMinimumSize(QSizeF(10,10)); + w->setMaximumSize(QSizeF(10,10)); + } else { + w->setMinimumSize(QSizeF(50,50)); + w->setMaximumSize(QSizeF(50,50)); + } + lay->addItem(w); + loutItem = lay; + } + if (align) { + loutItem->setMinimumSize(QSizeF(10,10)); + loutItem->setMaximumSize(QSizeF(10,10)); + } else { + loutItem->setMinimumSize(QSizeF(50,50)); + loutItem->setMaximumSize(QSizeF(50,50)); + } + layout.addItem(loutItem); + layout.setAlignment(loutItem, align); + + } + layout.setContentsMargins(0,0,0,0); + int spacing = 1; + layout.setSpacing(spacing); + if (newSize.isValid()) + widget->resize(newSize); + view.show(); + widget->show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + int x = 0; + int y = 0; + for (i = 0; i < layout.count(); ++i) { + QGraphicsLayoutItem *item = layout.itemAt(i); + Qt::Alignment align = layout.alignment(item); + + int w = 10; + int h = 10; + switch(align) { + case Qt::AlignLeft: + break; + case Qt::AlignRight: + if (orientation == Qt::Vertical) + x += 40; + break; + case Qt::AlignHCenter: + if (orientation == Qt::Vertical) + x += 20; + break; + case Qt::AlignTop: + break; + case Qt::AlignBottom: + if (orientation == Qt::Horizontal) + y += 40; + break; + case Qt::AlignVCenter: + if (orientation == Qt::Horizontal) + y += 20; + break; + case Qt::AlignCenter: + if (orientation == Qt::Horizontal) + y += 20; + else + x += 20; + break; + case 0: + w = 50; + h = 50; + break; + default: + break; + } + QRectF expectedGeometry(x, y, w, h); + QCOMPARE(item->geometry(), expectedGeometry); + if (orientation == Qt::Horizontal) { + x += w; + y = 0; + x += spacing; + } else { + x = 0; + y += h; + y += spacing; + } + } +} + +void tst_QGraphicsLinearLayout::count_data() +{ + QTest::addColumn<int>("itemCount"); + QTest::addColumn<int>("layoutCount"); + QTest::newRow("0, 0") << 0 << 0; + QTest::newRow("0, 5") << 0 << 5; + QTest::newRow("5, 0") << 5 << 0; + QTest::newRow("5, 5") << 5 << 5; +} + +// int count() const public +void tst_QGraphicsLinearLayout::count() +{ + QFETCH(int, itemCount); + QFETCH(int, layoutCount); + + SubQGraphicsLinearLayout layout; + QCOMPARE(layout.count(), 0); + + for (int i = 0; i < itemCount; ++i) + layout.addItem(new QGraphicsWidget); + QCOMPARE(layout.count(), itemCount); + + for (int i = 0; i < layoutCount; ++i) + layout.addItem(new SubQGraphicsLinearLayout); + QCOMPARE(layout.count(), itemCount + layoutCount); + + // see also removeAt() +} + +void tst_QGraphicsLinearLayout::dump_data() +{ + QTest::addColumn<int>("itemCount"); + QTest::addColumn<int>("layoutCount"); + for (int i = -1; i < 3; ++i) { + QTest::newRow(QString("%1, 0, 0").arg(i).toLatin1()) << 0 << 0; + QTest::newRow(QString("%1, 0, 5").arg(i).toLatin1()) << 5 << 5; + QTest::newRow(QString("%1, 5, 0").arg(i).toLatin1()) << 5 << 5; + QTest::newRow(QString("%1, 5, 5").arg(i).toLatin1()) << 5 << 5; + } +} + +// void dump(int indent = 0) const public +void tst_QGraphicsLinearLayout::dump() +{ + QFETCH(int, itemCount); + QFETCH(int, layoutCount); + SubQGraphicsLinearLayout layout; + for (int i = 0; i < itemCount; ++i) + layout.addItem(new QGraphicsWidget); + for (int i = 0; i < layoutCount; ++i) + layout.addItem(new SubQGraphicsLinearLayout); +} + +void tst_QGraphicsLinearLayout::geometry_data() +{ + QTest::addColumn<int>("itemCount"); + QTest::addColumn<int>("layoutCount"); + QTest::addColumn<int>("itemSpacing"); + QTest::addColumn<int>("spacing"); + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<QRectF>("rect"); + + QTest::newRow("null") << 0 << 0 << 0 << 0 << Qt::Horizontal << QRectF(); + + QTest::newRow("one item") << 1 << 0 << 0 << 0 << Qt::Horizontal << QRectF(0, 0, 10, 10); + QTest::newRow("one layout") << 0 << 1 << 0 << 0 << Qt::Horizontal << QRectF(0, 0, 10, 10); + QTest::newRow("two h") << 1 << 1 << 0 << 0 << Qt::Horizontal << QRectF(0, 0, 20, 10); + QTest::newRow("two v") << 1 << 1 << 0 << 0 << Qt::Vertical << QRectF(0, 0, 10, 20); + + QTest::newRow("two w/itemspacing") << 1 << 1 << 5 << 0 << Qt::Horizontal << QRectF(0, 0, 25, 10); + QTest::newRow("two w/spacing") << 1 << 1 << 8 << 0 << Qt::Horizontal << QRectF(0, 0, 28, 10); + + QTest::newRow("two w/itemspacing v") << 1 << 1 << 5 << 0 << Qt::Vertical << QRectF(0, 0, 10, 25); + QTest::newRow("two w/spacing v") << 1 << 1 << 8 << 0 << Qt::Vertical << QRectF(0, 0, 10, 28); +} + +// QRectF geometry() const public +void tst_QGraphicsLinearLayout::geometry() +{ + QFETCH(int, itemCount); + QFETCH(int, layoutCount); + QFETCH(int, itemSpacing); + QFETCH(int, spacing); + QFETCH(Qt::Orientation, orientation); + QFETCH(QRectF, rect); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout &layout = *(new SubQGraphicsLinearLayout(orientation)); + scene.addItem(widget); + widget->setLayout(&layout); + widget->setContentsMargins(0, 0, 0, 0); + for (int i = 0; i < itemCount; ++i) + layout.addItem(new QGraphicsWidget); + for (int i = 0; i < layoutCount; ++i) + layout.addItem(new SubQGraphicsLinearLayout); + + for (int i = 0; i < layout.count(); ++i) { + QGraphicsLayoutItem *item = layout.itemAt(i); + item->setMaximumSize(10, 10); + item->setMinimumSize(10, 10); + } + layout.setItemSpacing(0, itemSpacing); + layout.setSpacing(spacing); + layout.setContentsMargins(0, 0, 0, 0); + + widget->show(); + view.show(); + QApplication::processEvents(); + QCOMPARE(layout.geometry(), rect); + delete widget; +} + +void tst_QGraphicsLinearLayout::insertItem_data() +{ + QTest::addColumn<int>("itemCount"); + QTest::addColumn<int>("layoutCount"); + QTest::addColumn<int>("insertItemAt"); + QTest::addColumn<bool>("isWidget"); + for (int i = -1; i < 4; ++i) { + for (int j = 0; j < 2; ++j) { + QTest::newRow(QString("0, 0, %1 %2").arg(i).arg(j).toLatin1()) << 0 << 0 << i << (bool)j; + QTest::newRow(QString("1, 0, %1 %2").arg(i).arg(j).toLatin1()) << 1 << 0 << i << (bool)j; + QTest::newRow(QString("0, 1, %1 %2").arg(i).arg(j).toLatin1()) << 0 << 1 << i << (bool)j; + QTest::newRow(QString("2, 2, %1 %2").arg(i).arg(j).toLatin1()) << 2 << 2 << i << (bool)j; + } + } +} + +// void insertItem(int index, QGraphicsLayoutItem* item) public +void tst_QGraphicsLinearLayout::insertItem() +{ + QFETCH(int, itemCount); + QFETCH(int, layoutCount); + QFETCH(int, insertItemAt); + QFETCH(bool, isWidget); + if (insertItemAt > layoutCount + itemCount) + return; + + SubQGraphicsLinearLayout layout; + for (int i = 0; i < itemCount; ++i) + layout.addItem(new QGraphicsWidget); + for (int i = 0; i < layoutCount; ++i) + layout.addItem(new SubQGraphicsLinearLayout); + + QGraphicsLayoutItem *item = 0; + if (isWidget) + item = new QGraphicsWidget; + else + item = new SubQGraphicsLinearLayout; + + QSizeF oldSizeHint = layout.sizeHint(Qt::PreferredSize, QSizeF()); + layout.insertItem(insertItemAt, item); + QCOMPARE(layout.count(), itemCount + layoutCount + 1); + + if (insertItemAt >= 0 && (itemCount + layoutCount >= 0)) { + QCOMPARE(layout.itemAt(insertItemAt), item); + } + + layout.activate(); + QSizeF newSizeHint = layout.sizeHint(Qt::PreferredSize, QSizeF()); + if (!isWidget && layout.count() == 1) + QCOMPARE(oldSizeHint.width(), newSizeHint.width()); + else if (itemCount + layoutCount > 0) + QVERIFY(oldSizeHint.width() < newSizeHint.width()); +} + +void tst_QGraphicsLinearLayout::insertStretch_data() +{ + QTest::addColumn<int>("itemCount"); + QTest::addColumn<int>("layoutCount"); + QTest::addColumn<int>("insertItemAt"); + QTest::addColumn<int>("stretch"); + for (int i = -1; i < 4; ++i) { + for (int j = 0; j < 2; ++j) { + QTest::newRow(QString("0, 0, %1 %2").arg(i).arg(j).toLatin1()) << 0 << 0 << i << j; + QTest::newRow(QString("1, 0, %1 %2").arg(i).arg(j).toLatin1()) << 1 << 0 << i << j; + QTest::newRow(QString("0, 1, %1 %2").arg(i).arg(j).toLatin1()) << 0 << 1 << i << j; + QTest::newRow(QString("2, 2, %1 %2").arg(i).arg(j).toLatin1()) << 2 << 2 << i << j; + } + } +} + +// void insertStretch(int index, int stretch = 1) public +void tst_QGraphicsLinearLayout::insertStretch() +{ + QFETCH(int, itemCount); + QFETCH(int, layoutCount); + QFETCH(int, insertItemAt); + QFETCH(int, stretch); + if (insertItemAt > layoutCount + itemCount) + return; + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout *layout = new SubQGraphicsLinearLayout; + scene.addItem(widget); + + QList<QGraphicsWidget *>items; + QGraphicsWidget *item = 0; + for (int i = 0; i < itemCount; ++i) { + item = new RectWidget; + item->setMinimumSize(10, 10); + item->setPreferredSize(25, 25); + item->setMaximumSize(50, 50); + layout->addItem(item); + } + for (int i = 0; i < layoutCount; ++i) { + item = new RectWidget; + item->setMinimumSize(10, 10); + item->setPreferredSize(25, 25); + item->setMaximumSize(50, 50); + SubQGraphicsLinearLayout *sublayout = new SubQGraphicsLinearLayout; + sublayout->addItem(item); + layout->addItem(sublayout); + } + widget->setLayout(layout); + layout->insertStretch(insertItemAt, stretch); + QCOMPARE(layout->count(), itemCount + layoutCount); + + layout->activate(); + view.show(); + widget->show(); + + int prevStretch = -2; + int prevWidth = -2; + widget->resize((layoutCount + itemCount) * 25 + 25, 25); + for (int i = 0; i < layout->count(); ++i) { + if (QGraphicsLayoutItem *item = layout->itemAt(i)) { + if (prevStretch != -2) { + if (layout->stretchFactor(item) >= prevStretch) { + QVERIFY(item->geometry().width() >= prevWidth); + } else { + QVERIFY(item->geometry().width() < prevWidth); + } + } + prevStretch = layout->stretchFactor(item); + prevWidth = (int)(item->geometry().width()); + } + } + + //QTest::qWait(1000); + delete widget; +} + +void tst_QGraphicsLinearLayout::invalidate_data() +{ + QTest::addColumn<int>("count"); + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("3") << 3; +} + +// void invalidate() public +void tst_QGraphicsLinearLayout::invalidate() +{ + QFETCH(int, count); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout &layout = *(new SubQGraphicsLinearLayout); + scene.addItem(widget); + widget->setLayout(&layout); + widget->setContentsMargins(0, 0, 0, 0); + layout.setContentsMargins(0, 0, 0, 0); + view.show(); + widget->show(); + //QTest::qWait(1000); + QTest::qWaitForWindowShown(&view); + qApp->processEvents(); + layout.layoutRequest = 0; + + layout.setContentsMargins(1, 2, 3, 4); + QApplication::sendPostedEvents(0, 0); + QCOMPARE(layout.layoutRequest, 1); + + layout.setOrientation(Qt::Vertical); + QApplication::sendPostedEvents(0, 0); + QCOMPARE(layout.layoutRequest, 2); + + for (int i = 0; i < count; ++i) + layout.invalidate(); // Event is compressed, should only get one layoutrequest + QApplication::sendPostedEvents(0, 0); + QCOMPARE(layout.layoutRequest, count ? 3 : 2); + delete widget; +} + +void tst_QGraphicsLinearLayout::itemAt_data() +{ + QTest::addColumn<int>("index"); + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; +} + +// QGraphicsLayoutItem* itemAt(int index) const public +void tst_QGraphicsLinearLayout::itemAt() +{ + // see also the insertItem() etc tests + QFETCH(int, index); + SubQGraphicsLinearLayout layout; + for (int i = 0; i < 3; ++i) + layout.addItem(new QGraphicsWidget); + + QVERIFY(layout.itemAt(index) != 0); +} + +void tst_QGraphicsLinearLayout::itemAt_visualOrder() +{ + QGraphicsLinearLayout *l = new QGraphicsLinearLayout; + + QGraphicsWidget *w1 = new QGraphicsWidget; + l->addItem(w1); + + QGraphicsWidget *w3 = new QGraphicsWidget; + l->addItem(w3); + + QGraphicsWidget *w0 = new QGraphicsWidget; + l->insertItem(0, w0); + + QGraphicsWidget *w2 = new QGraphicsWidget; + l->insertItem(2, w2); + + QCOMPARE(l->itemAt(0), static_cast<QGraphicsLayoutItem*>(w0)); + QCOMPARE(l->itemAt(1), static_cast<QGraphicsLayoutItem*>(w1)); + QCOMPARE(l->itemAt(2), static_cast<QGraphicsLayoutItem*>(w2)); + QCOMPARE(l->itemAt(3), static_cast<QGraphicsLayoutItem*>(w3)); +} + +void tst_QGraphicsLinearLayout::orientation_data() +{ + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::newRow("vertical") << Qt::Vertical; + QTest::newRow("horizontal") << Qt::Horizontal; +} + +// Qt::Orientation orientation() const public +void tst_QGraphicsLinearLayout::orientation() +{ + QFETCH(Qt::Orientation, orientation); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + Qt::Orientation initialOrientation = (orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical); + SubQGraphicsLinearLayout &layout = *(new SubQGraphicsLinearLayout(initialOrientation)); + scene.addItem(widget); + widget->setLayout(&layout); + widget->setContentsMargins(0, 0, 0, 0); + layout.setContentsMargins(0, 0, 0, 0); + int i; + int itemCount = 3; + for (i = 0; i < itemCount; ++i) + layout.addItem(new RectWidget); + QCOMPARE(layout.orientation(), initialOrientation); + QList<qreal> positions; + + view.show(); + widget->show(); + qApp->processEvents(); + + for (i = 0; i < itemCount; ++i) { + QGraphicsWidget *item = static_cast<QGraphicsWidget*>(layout.itemAt(i)); + qreal pos; + if (initialOrientation == Qt::Horizontal) + pos = item->pos().x(); + else + pos = item->pos().y(); + positions.append(pos); + + } + + layout.setOrientation(orientation); + QCOMPARE(layout.orientation(), orientation); + // important to resize to preferredsize when orientation is switched + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); + qApp->processEvents(); + for (i = 0; i < positions.count(); ++i) { + QGraphicsWidget *item = static_cast<QGraphicsWidget*>(layout.itemAt(i)); + if (initialOrientation == Qt::Horizontal) + QCOMPARE(item->pos().y(), positions.at(i)); + else + QCOMPARE(item->pos().x(), positions.at(i)); + } +} + +void tst_QGraphicsLinearLayout::removeAt_data() +{ + QTest::addColumn<int>("itemCount"); + QTest::addColumn<int>("layoutCount"); + QTest::addColumn<int>("removeItemAt"); + QTest::addColumn<Qt::Orientation>("orientation"); + for (int i = -1; i < 4; ++i) { + for (int k = 0; k < 2; ++k) { + Qt::Orientation orientation = (k == 0) ? Qt::Vertical : Qt::Horizontal; + QTest::newRow(QString("0, 0, %1").arg(i).toLatin1()) << 0 << 0 << i << orientation; + QTest::newRow(QString("1, 0, %1").arg(i).toLatin1()) << 1 << 0 << i << orientation; + QTest::newRow(QString("0, 1, %1").arg(i).toLatin1()) << 0 << 1 << i << orientation; + QTest::newRow(QString("2, 2, %1").arg(i).toLatin1()) << 2 << 2 << i << orientation; + } + } +} + +// void removeAt(int index) public +void tst_QGraphicsLinearLayout::removeAt() +{ + QFETCH(int, itemCount); + QFETCH(int, layoutCount); + QFETCH(int, removeItemAt); + QFETCH(Qt::Orientation, orientation); + if (removeItemAt >= layoutCount + itemCount) + return; + + SubQGraphicsLinearLayout layout(orientation); + for (int i = 0; i < itemCount; ++i) + layout.addItem(new QGraphicsWidget); + for (int i = 0; i < layoutCount; ++i) + layout.addItem(new SubQGraphicsLinearLayout); + QSizeF oldSizeHint = layout.sizeHint(Qt::PreferredSize, QSizeF()); + + QGraphicsLayoutItem *w = 0; + if (removeItemAt >= 0 && removeItemAt < layout.count()) + w = layout.itemAt(removeItemAt); + if (w) { + QGraphicsLayoutItem *wParent = w->parentLayoutItem(); + QCOMPARE(wParent, static_cast<QGraphicsLayoutItem *>(&layout)); + layout.removeAt(removeItemAt); + wParent = w->parentLayoutItem(); + QCOMPARE(wParent, static_cast<QGraphicsLayoutItem *>(0)); + delete w; + } + QCOMPARE(layout.count(), itemCount + layoutCount - (w ? 1 : 0)); + + layout.activate(); + QSizeF newSizeHint = layout.sizeHint(Qt::PreferredSize, QSizeF()); + if (orientation == Qt::Horizontal) + QVERIFY(oldSizeHint.width() >= newSizeHint.width()); + else + QVERIFY(oldSizeHint.height() >= newSizeHint.height()); +} + +void tst_QGraphicsLinearLayout::removeItem_data() +{ + QTest::addColumn<int>("itemCount"); + QTest::addColumn<int>("layoutCount"); + QTest::addColumn<int>("removeItemAt"); + for (int i = -1; i < 4; ++i) { + QTest::newRow(QString("0, 0, %1").arg(i).toLatin1()) << 0 << 0 << i; + QTest::newRow(QString("1, 0, %1").arg(i).toLatin1()) << 1 << 0 << i; + QTest::newRow(QString("0, 1, %1").arg(i).toLatin1()) << 0 << 1 << i; + QTest::newRow(QString("2, 2, %1").arg(i).toLatin1()) << 2 << 2 << i; + } +} + +// void removeItem(QGraphicsLayoutItem* item) public +void tst_QGraphicsLinearLayout::removeItem() +{ + QFETCH(int, itemCount); + QFETCH(int, layoutCount); + QFETCH(int, removeItemAt); + if (removeItemAt >= layoutCount + itemCount) + return; + + SubQGraphicsLinearLayout layout; + for (int i = 0; i < itemCount; ++i) + layout.addItem(new QGraphicsWidget); + for (int i = 0; i < layoutCount; ++i) + layout.addItem(new SubQGraphicsLinearLayout); + + QGraphicsLayoutItem *w = 0; + if (removeItemAt >= 0 && removeItemAt < layout.count()) + w = layout.itemAt(removeItemAt); + QSizeF oldSizeHint = layout.sizeHint(Qt::PreferredSize, QSizeF()); + if (w) { + layout.removeItem(w); + delete w; + } + QCOMPARE(layout.count(), itemCount + layoutCount - (w ? 1 : 0)); + + layout.activate(); + QSizeF newSizeHint = layout.sizeHint(Qt::PreferredSize, QSizeF()); + QVERIFY(oldSizeHint.width() >= newSizeHint.width()); +} + +void tst_QGraphicsLinearLayout::setGeometry_data() +{ + QTest::addColumn<QRectF>("rect"); + QTest::newRow("null") << QRectF(); + QTest::newRow("small") << QRectF(0, 0, 10, 10); +} + +// void setGeometry(QRectF const& rect) public +void tst_QGraphicsLinearLayout::setGeometry() +{ + QFETCH(QRectF, rect); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout &layout = *(new SubQGraphicsLinearLayout); + scene.addItem(widget); + widget->setLayout(&layout); + widget->setContentsMargins(0, 0, 0, 0); + layout.setContentsMargins(0, 0, 0, 0); + layout.setMaximumSize(100, 100); + view.show(); + widget->show(); + QApplication::processEvents(); + widget->setGeometry(rect); + QCOMPARE(layout.geometry(), rect); + // see also geometry() + delete widget; +} + +void tst_QGraphicsLinearLayout::setSpacing_data() +{ + QTest::addColumn<qreal>("spacing"); + QTest::newRow("0") << (qreal)0; + QTest::newRow("5") << (qreal)5; + QTest::newRow("3.3") << (qreal)3.3; + QTest::newRow("-4.3") << (qreal)4.3; +} + +// void setSpacing(qreal spacing) public +void tst_QGraphicsLinearLayout::setSpacing() +{ + QFETCH(qreal, spacing); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout &layout = *(new SubQGraphicsLinearLayout); + scene.addItem(widget); + widget->setLayout(&layout); + layout.setContentsMargins(0, 0, 0, 0); + + qreal oldSpacing = layout.spacing(); + if (oldSpacing != -1) { + for (int i = 0; i < 3; ++i) + layout.addItem(new QGraphicsWidget); + QSizeF oldSizeHint = layout.sizeHint(Qt::PreferredSize); + + layout.setSpacing(spacing); + QCOMPARE(layout.spacing(), spacing); + + view.show(); + widget->show(); + QApplication::processEvents(); + QSizeF newSizeHint = layout.sizeHint(Qt::PreferredSize); + + QCOMPARE(oldSizeHint.width() - oldSpacing * 2, newSizeHint.width() - spacing * 2); + } else { + QSKIP("This style uses non-uniform spacings (layoutSpacingImplementation() is reimplemented)", SkipAll); + } + delete widget; +} + +void tst_QGraphicsLinearLayout::setItemSpacing_data() +{ + QTest::addColumn<int>("index"); + QTest::addColumn<int>("spacing"); + + QTest::newRow("0 at 0") << 0 << 0; + QTest::newRow("10 at 0") << 0 << 10; + QTest::newRow("10 at 1") << 1 << 10; + QTest::newRow("10 at the end") << 4 << 10; +} + +void tst_QGraphicsLinearLayout::setItemSpacing() +{ + QFETCH(int, index); + QFETCH(int, spacing); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout *layout = new SubQGraphicsLinearLayout; + scene.addItem(widget); + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + for (int i = 0; i < 5; ++i) { + QGraphicsWidget *w = new QGraphicsWidget; + layout->addItem(w); + } + QSizeF oldSizeHint = layout->sizeHint(Qt::PreferredSize); + qreal oldSpacing = 0; + if (index < layout->count() - 1) + oldSpacing = layout->spacing(); + else + spacing = 0; + + layout->setItemSpacing(index, spacing); + view.show(); + QApplication::processEvents(); + QSizeF newSizeHint = layout->sizeHint(Qt::PreferredSize); + if (oldSpacing >= 0) { + QCOMPARE(newSizeHint.width() - spacing, oldSizeHint.width() - oldSpacing); + } else { + QSKIP("This style uses non-uniform spacings (layoutSpacingImplementation() is reimplemented)", SkipAll); + } + delete widget; +} + +void tst_QGraphicsLinearLayout::itemSpacing() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout *layout = new SubQGraphicsLinearLayout; + scene.addItem(widget); + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + for (int i = 0; i < 5; ++i) { + QGraphicsWidget *w = new QGraphicsWidget; + layout->addItem(w); + } + + // Check defaults + qreal defaultSpacing = layout->spacing(); + if (defaultSpacing >= 0) { + QCOMPARE(layout->itemSpacing(0), defaultSpacing); + } else { + // all widgets are the same, so the spacing should be uniform + QCOMPARE(layout->itemSpacing(0), layout->itemSpacing(1)); + } + + layout->setItemSpacing(1, 42); + QCOMPARE(layout->itemSpacing(1), qreal(42)); + + // try to unset + layout->setItemSpacing(1, -1); + QCOMPARE(layout->itemSpacing(1), defaultSpacing); + + delete widget; +} + +/** + * The stretch factors are not applied linearly, but they are used together with both the preferred size, maximum size to form the + * internal effective stretch factor. + * There is only need to apply stretch factors if the size of the layout is different than the layouts preferred size. + * (If the size of the layout is the preferred size, then all items should get their preferred sizes. + * However, imagine this use case: + * Layout + * +----------+----------+----------+ + * name | A | B | C | + * stretch | 1 | 2 | 3 | + * sizehints|[5,10,50] |[5,10,50] |[5,10,50] | + * +----------+----------+----------+ + * + * layout->resize(120, h) + * + * In QLayout, C would become 50, B would become 50 and A would get 20. When scaling a layout this would give a jerky feeling, since + * the item with the highest stretch factor will first resize. When that has reached its maximum the next candidate for stretch will + * resize, and finally, item with the lowest stretch factor will resize. + * In QGraphicsLinearLayout we try to scale all items so that they all reach their maximum at the same time. This means that + * their relative sizes are not proportional to their stretch factors. + */ + +typedef QList<int> IntList; +Q_DECLARE_METATYPE(IntList) +Q_DECLARE_METATYPE(qreal) + +void tst_QGraphicsLinearLayout::setStretchFactor_data() +{ + QTest::addColumn<qreal>("totalSize"); + QTest::addColumn<IntList>("stretches"); + + QTest::newRow(QString("60 [1,2]").toLatin1()) << qreal(60.0) << (IntList() << 1 << 2); + QTest::newRow(QString("60 [1,2,3]").toLatin1()) << qreal(60.0) << (IntList() << 1 << 2 << 3); + QTest::newRow(QString("120 [1,2,3,6]").toLatin1()) << qreal(120.0) << (IntList() << 1 << 2 << 3 << 6); +} + +// void setStretchFactor(QGraphicsLayoutItem* item, int stretch) public +void tst_QGraphicsLinearLayout::setStretchFactor() +{ + QFETCH(qreal, totalSize); + QFETCH(IntList, stretches); + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout &layout = *(new SubQGraphicsLinearLayout); + scene.addItem(widget); + widget->setLayout(&layout); + layout.setContentsMargins(0, 0, 0, 0); + layout.setSpacing(0.0); + widget->setContentsMargins(0, 0, 0, 0); + + + int i; + for (i = 0; i < stretches.count(); ++i) { + QGraphicsWidget *item = new RectWidget(widget); + item->setMinimumSize(5,5); + item->setPreferredSize(10,5); + item->setMaximumSize(50,5); + layout.addItem(item); + layout.setStretchFactor(item, stretches.at(i)); + } + + widget->resize(totalSize, 10); + QApplication::processEvents(); + + view.show(); + widget->show(); + + qreal firstStretch = -1; + qreal firstExtent = -1.; + qreal sumExtent = 0; + for (i = 0; i < stretches.count(); ++i) { + QGraphicsWidget *item = static_cast<QGraphicsWidget*>(layout.itemAt(i)); + qreal extent = item->size().width(); + qreal stretch = (qreal)stretches.at(i); + if (firstStretch != -1 && firstExtent != -1) { + // The resulting widths does not correspond linearly to the stretch factors. + if (stretch == firstStretch) + QCOMPARE(extent, firstExtent); + else if (stretch > firstStretch) + QVERIFY(extent > firstExtent); + else + QVERIFY(extent < firstExtent); + } else { + firstStretch = (qreal)stretch; + firstExtent = extent; + } + sumExtent+= extent; + } + QCOMPARE(sumExtent, totalSize); + + delete widget; +} + +void tst_QGraphicsLinearLayout::testStretch() +{ + QGraphicsScene scene; + QGraphicsView *view = new QGraphicsView(&scene); + Q_UNUSED(view); + QGraphicsWidget *form = new QGraphicsWidget(0, Qt::Window); + + scene.addItem(form); + form->setMinimumSize(600, 600); + form->setMaximumSize(600, 600); + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Horizontal, form); + QGraphicsWidget *w1 = new RectWidget; + w1->setPreferredSize(100,100); + w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + QGraphicsWidget *w2 = new RectWidget; + w2->setPreferredSize(200,200); + w2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + layout->addItem(w1); + layout->addStretch(2); + layout->addItem(w2); + QCOMPARE(layout->count(), 2); + QVERIFY(layout->itemAt(0) == w1); + QVERIFY(layout->itemAt(1) == w2); + layout->activate(); + + //view->setSceneRect(-50, -50, 800, 800); + //view->show(); + //QTest::qWaitForWindowShown(view); + //QTest::qWait(5000); + QCOMPARE(form->geometry().size(), QSizeF(600,600)); + QCOMPARE(w1->geometry(), QRectF(0, 0, 100, 100)); + QCOMPARE(w2->geometry(), QRectF(400, 0, 200, 200)); +} + +void tst_QGraphicsLinearLayout::defaultStretchFactors_data() +{ + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<int>("count"); + QTest::addColumn<IntList>("preferredSizeHints"); + QTest::addColumn<IntList>("stretches"); + QTest::addColumn<IntList>("ignoreFlag"); + QTest::addColumn<QSizeF>("newSize"); + QTest::addColumn<IntList>("expectedSizes"); + + QTest::newRow("hor") << Qt::Horizontal << 3 + << (IntList() << 20 << 40 << 60) + << (IntList()) + << (IntList()) + << QSizeF() + << (IntList() << 20 << 40 << 60); + + QTest::newRow("ver") << Qt::Vertical << 3 + << (IntList() << 20 << 40 << 60) + << (IntList()) + << (IntList()) + << QSizeF() + << (IntList() << 20 << 40 << 60); + + QTest::newRow("hor,ignore123") << Qt::Horizontal << 3 + << (IntList() << 20 << 40 << 60) + << (IntList()) + << (IntList() << 1 << 1 << 1) + << QSizeF() + << (IntList() << 0 << 0 << 0); + + QTest::newRow("hor,ignore23") << Qt::Horizontal << 3 + << (IntList() << 10 << 10 << 10) + << (IntList()) + << (IntList() << 0 << 1 << 1) + << QSizeF(200, 50) + << (IntList()); //### stretches are not linear. + + QTest::newRow("hor,ignore2") << Qt::Horizontal << 3 + << (IntList() << 10 << 10 << 10) + << (IntList()) + << (IntList() << 0 << 1 << 0) + << QSizeF() + << (IntList() << 10 << 0 << 10); + +} + +void tst_QGraphicsLinearLayout::defaultStretchFactors() +{ + QFETCH(Qt::Orientation, orientation); + QFETCH(int, count); + QFETCH(IntList, preferredSizeHints); + QFETCH(IntList, stretches); + QFETCH(IntList, ignoreFlag); + QFETCH(QSizeF, newSize); + QFETCH(IntList, expectedSizes); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout *layout = new SubQGraphicsLinearLayout(orientation); + scene.addItem(widget); + widget->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0.0); + widget->setContentsMargins(0, 0, 0, 0); + + int i; + for (i = 0; i < count; ++i) { + RectWidget *item = new RectWidget(widget); + layout->addItem(item); + if (preferredSizeHints.value(i, -1) >= 0) { + item->setSizeHint(Qt::PreferredSize, QSizeF(preferredSizeHints.at(i), preferredSizeHints.at(i))); + } + if (stretches.value(i, -1) >= 0) { + layout->setStretchFactor(item, stretches.at(i)); + } + if (ignoreFlag.value(i, 0) != 0) { + QSizePolicy sp = item->sizePolicy(); + if (orientation == Qt::Horizontal) + sp.setHorizontalPolicy(QSizePolicy::Policy(sp.horizontalPolicy() | QSizePolicy::IgnoreFlag)); + else + sp.setVerticalPolicy(QSizePolicy::Policy(sp.verticalPolicy() | QSizePolicy::IgnoreFlag)); + item->setSizePolicy(sp); + } + } + + QApplication::processEvents(); + + widget->show(); + view.show(); + view.resize(400,300); + if (newSize.isValid()) + widget->resize(newSize); + + QApplication::processEvents(); + for (i = 0; i < count; ++i) { + QSizeF itemSize = layout->itemAt(i)->geometry().size(); + if (orientation == Qt::Vertical) + itemSize.transpose(); + if (i < expectedSizes.count()) + QCOMPARE(itemSize.width(), qreal(expectedSizes.at(i))); + } + + delete widget; +} + +Q_DECLARE_METATYPE(Qt::SizeHint) +void tst_QGraphicsLinearLayout::sizeHint_data() +{ + QTest::addColumn<Qt::SizeHint>("which"); + QTest::addColumn<QSizeF>("constraint"); + QTest::addColumn<qreal>("spacing"); + QTest::addColumn<qreal>("layoutMargin"); + QTest::addColumn<QSizeF>("sizeHint"); + + QTest::newRow("minimumSize") << Qt::MinimumSize << QSizeF() << qreal(0.0) << qreal(0.0) << QSizeF(30, 10); + QTest::newRow("preferredSize") << Qt::PreferredSize << QSizeF() << qreal(0.0) << qreal(0.0) << QSizeF(75, 25); + QTest::newRow("maximumSize") << Qt::MaximumSize << QSizeF() << qreal(0.0) << qreal(0.0) << QSizeF(150, 50); + QTest::newRow("minimumSize, spacing=3") << Qt::MinimumSize << QSizeF() << qreal(3.0) << qreal(0.0) << QSizeF(30 + 2*3, 10); + QTest::newRow("minimumSize, spacing=3, layoutMargin=10") << Qt::MinimumSize << QSizeF() << qreal(3.0) << qreal(10.0) << QSizeF(30 + 2*3 + 2*10, 10 + 2*10); + QTest::newRow("minimumSize, spacing=0, layoutMargin=7") << Qt::MinimumSize << QSizeF() << qreal(0.0) << qreal(7.0) << QSizeF(30 + 0 + 2*7, 10 + 2*7); +} + +// QSizeF sizeHint(Qt::SizeHint which, QSizeF const& constraint) const public +void tst_QGraphicsLinearLayout::sizeHint() +{ + QFETCH(Qt::SizeHint, which); + QFETCH(QSizeF, constraint); + QFETCH(qreal, spacing); + QFETCH(qreal, layoutMargin); + QFETCH(QSizeF, sizeHint); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout &layout = *(new SubQGraphicsLinearLayout); + scene.addItem(widget); + widget->setLayout(&layout); + layout.setContentsMargins(layoutMargin, layoutMargin, layoutMargin, layoutMargin); + layout.setSpacing(spacing); + for (int i = 0; i < 3; ++i) { + QGraphicsWidget *item = new QGraphicsWidget(widget); + item->setMinimumSize(10, 10); + item->setPreferredSize(25, 25); + item->setMaximumSize(50, 50); + layout.addItem(item); + } + QApplication::processEvents(); + QCOMPARE(layout.sizeHint(which, constraint), sizeHint); + delete widget; +} + +void tst_QGraphicsLinearLayout::updateGeometry() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window); + QGraphicsWidget *w1 = new QGraphicsWidget(window); + w1->setMinimumSize(100, 40); + SubQGraphicsLinearLayout *layout = new SubQGraphicsLinearLayout; + layout->addItem(w1); + scene.addItem(window); + window->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + QCOMPARE(w1->parentLayoutItem(), static_cast<QGraphicsLayoutItem*>(layout)); + QCOMPARE(layout->parentLayoutItem(), static_cast<QGraphicsLayoutItem*>(window)); + + view.show(); + QApplication::processEvents(); + QCOMPARE(window->size().toSize(), QSize(100, 50)); + w1->setMinimumSize(110, 60); + QApplication::processEvents(); + QCOMPARE(window->size().toSize(), QSize(110, 60)); + QApplication::processEvents(); + + { + delete window; + window = new QGraphicsWidget(0, Qt::Window); + SubQGraphicsLinearLayout *layout2a = new SubQGraphicsLinearLayout; + QGraphicsWidget *w1 = new QGraphicsWidget(window); + w1->setMinimumSize(110, 50); + layout2a->addItem(w1); + SubQGraphicsLinearLayout *layout2 = new SubQGraphicsLinearLayout; + layout2->addItem(layout2a); + layout2->setContentsMargins(1, 1, 1, 1); + layout2a->setContentsMargins(1, 1, 1, 1); + window->setLayout(layout2); + QApplication::processEvents(); + QCOMPARE(w1->parentLayoutItem(), static_cast<QGraphicsLayoutItem*>(layout2a)); + QCOMPARE(layout2a->parentLayoutItem(), static_cast<QGraphicsLayoutItem*>(layout2)); + QCOMPARE(layout2->parentLayoutItem(), static_cast<QGraphicsLayoutItem*>(window)); + QCOMPARE(window->size().toSize(), QSize(114, 54)); + QApplication::processEvents(); + w1->setMinimumSize(120, 60); + QApplication::processEvents(); + QCOMPARE(window->size().toSize(), QSize(124, 64)); + } + + { + delete window; + window = new QGraphicsWidget(0, Qt::Window); + scene.addItem(window); + window->show(); + QGraphicsWidget *w1 = new QGraphicsWidget(window); + w1->setMinimumSize(100, 50); + SubQGraphicsLinearLayout *layout2a = new SubQGraphicsLinearLayout; + layout2a->addItem(w1); + SubQGraphicsLinearLayout *layout2 = new SubQGraphicsLinearLayout; + layout2->addItem(layout2a); + layout2a->setContentsMargins(1, 1, 1, 1); + window->setLayout(layout2); + QApplication::processEvents(); + qreal left, top, right, bottom; + layout2->getContentsMargins(&left, &top, &right, &bottom); + QCOMPARE(window->size().toSize(), QSize(102 +left + right, 52 + top + bottom)); + } + + { + delete window; + window = new QGraphicsWidget(0, Qt::Window); + scene.addItem(window); + QGraphicsWidget *w1 = new QGraphicsWidget(window); + w1->setMinimumSize(100, 50); + window->setLayout(0); + SubQGraphicsLinearLayout *layout2a = new SubQGraphicsLinearLayout; + layout2a->addItem(w1); + SubQGraphicsLinearLayout *layout2 = new SubQGraphicsLinearLayout; + layout2->addItem(layout2a); + window->resize(200, 80); + window->setLayout(layout2); + window->show(); + QApplication::processEvents(); + QCOMPARE(window->size().toSize(), QSize(200, 80)); + } + +} + +void tst_QGraphicsLinearLayout::layoutDirection() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window); + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout; + layout->setContentsMargins(1, 2, 3, 4); + layout->setSpacing(6); + + RectWidget *w1 = new RectWidget; + w1->setPreferredSize(20, 20); + w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + layout->addItem(w1); + RectWidget *w2 = new RectWidget; + w2->setPreferredSize(20, 20); + w2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + layout->addItem(w2); + + scene.addItem(window); + window->setLayout(layout); + view.show(); + window->resize(50, 20); + window->setLayoutDirection(Qt::LeftToRight); + QApplication::processEvents(); + QCOMPARE(w1->geometry().left(), 1.0); + QCOMPARE(w1->geometry().right(), 21.0); + QCOMPARE(w2->geometry().left(), 27.0); + QCOMPARE(w2->geometry().right(), 47.0); + + window->setLayoutDirection(Qt::RightToLeft); + QApplication::processEvents(); + QCOMPARE(w1->geometry().right(), 49.0); + QCOMPARE(w1->geometry().left(), 29.0); + QCOMPARE(w2->geometry().right(), 23.0); + QCOMPARE(w2->geometry().left(), 3.0); + + delete window; +} + +void tst_QGraphicsLinearLayout::removeLayout() +{ + QGraphicsScene scene; + RectWidget *textEdit = new RectWidget; + RectWidget *pushButton = new RectWidget; + scene.addItem(textEdit); + scene.addItem(pushButton); + + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout; + layout->addItem(textEdit); + layout->addItem(pushButton); + + QGraphicsWidget *form = new QGraphicsWidget; + form->setLayout(layout); + scene.addItem(form); + + QGraphicsView view(&scene); + view.show(); + QTest::qWait(20); + + QRectF r1 = textEdit->geometry(); + QRectF r2 = pushButton->geometry(); + form->setLayout(0); + //documentation of QGraphicsWidget::setLayout: + //If layout is 0, the widget is left without a layout. Existing subwidgets' geometries will remain unaffected. + QCOMPARE(textEdit->geometry(), r1); + QCOMPARE(pushButton->geometry(), r2); +} + +void tst_QGraphicsLinearLayout::avoidRecursionInInsertItem() +{ + QGraphicsWidget window(0, Qt::Window); + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(&window); + QCOMPARE(layout->count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLinearLayout::insertItem: cannot insert itself"); + layout->insertItem(0, layout); + QCOMPARE(layout->count(), 0); +} + +void tst_QGraphicsLinearLayout::styleInfoLeak() +{ + QGraphicsLinearLayout layout; + layout.spacing(); +} + +void tst_QGraphicsLinearLayout::task218400_insertStretchCrash() +{ + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsWidget *a = scene->addWidget(new QWidget); + QGraphicsWidget *b = scene->addWidget(new QWidget); + + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout; + layout->addItem(a); + layout->addItem(b); + layout->insertStretch(0); // inserts gap in item grid in the layout engine + + QGraphicsWidget *form = new QGraphicsWidget; + form->setLayout(layout); // crash +} + +void tst_QGraphicsLinearLayout::testAlignmentInLargerLayout() +{ + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsWidget *form = new QGraphicsWidget; + scene->addItem(form); + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Vertical, form); + layout->setSpacing(0); + layout->setContentsMargins(0,0,0,0); + + QGraphicsWidget *a = new QGraphicsWidget; + a->setMaximumSize(100,100); + layout->addItem(a); + + QCOMPARE(form->maximumSize(), QSizeF(100,100)); + QCOMPARE(layout->maximumSize(), QSizeF(100,100)); + layout->setMinimumSize(QSizeF(200,200)); + layout->setMaximumSize(QSizeF(200,200)); + + layout->setAlignment(a, Qt::AlignCenter); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(50,50,100,100)); + + layout->setAlignment(a, Qt::AlignRight | Qt::AlignBottom); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(100,100,100,100)); + + layout->setAlignment(a, Qt::AlignHCenter | Qt::AlignTop); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(50,0,100,100)); + + QGraphicsWidget *b = new QGraphicsWidget; + b->setMaximumSize(100,100); + layout->addItem(b); + + layout->setAlignment(a, Qt::AlignCenter); + layout->setAlignment(b, Qt::AlignCenter); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(50,0,100,100)); + QCOMPARE(b->geometry(), QRectF(50,100,100,100)); + + layout->setAlignment(a, Qt::AlignRight | Qt::AlignBottom); + layout->setAlignment(b, Qt::AlignLeft | Qt::AlignTop); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(100,0,100,100)); + QCOMPARE(b->geometry(), QRectF(0,100,100,100)); +} + +void tst_QGraphicsLinearLayout::testOffByOneInLargerLayout() { + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsWidget *form = new QGraphicsWidget; + scene->addItem(form); + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Vertical, form); + layout->setSpacing(0); + layout->setContentsMargins(0,0,0,0); + + QGraphicsWidget *a = new QGraphicsWidget; + QGraphicsWidget *b = new QGraphicsWidget; + a->setMaximumSize(100,100); + b->setMaximumSize(100,100); + layout->addItem(a); + layout->addItem(b); + + layout->setAlignment(a, Qt::AlignRight | Qt::AlignBottom); + layout->setAlignment(b, Qt::AlignLeft | Qt::AlignTop); + layout->setMinimumSize(QSizeF(101,201)); + layout->setMaximumSize(QSizeF(101,201)); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(1,0.5,100,100)); + QCOMPARE(b->geometry(), QRectF(0,100.5,100,100)); + + layout->setMinimumSize(QSizeF(100,200)); + layout->setMaximumSize(QSizeF(100,200)); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(0,0,100,100)); + QCOMPARE(b->geometry(), QRectF(0,100,100,100)); + + layout->setMinimumSize(QSizeF(99,199)); + layout->setMaximumSize(QSizeF(99,199)); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(0,0,99,99.5)); + QCOMPARE(b->geometry(), QRectF(0,99.5,99,99.5)); +} +void tst_QGraphicsLinearLayout::testDefaultAlignment() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Vertical, widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QGraphicsWidget *w = new QGraphicsWidget; + w->setMinimumSize(50,50); + w->setMaximumSize(50,50); + layout->addItem(w); + + //Default alignment should be to the top-left + QCOMPARE(layout->alignment(w), 0); + + //First, check by forcing the layout to be bigger + layout->setMinimumSize(100,100); + layout->activate(); + QCOMPARE(layout->geometry(), QRectF(0,0,100,100)); + QCOMPARE(w->geometry(), QRectF(0,0,50,50)); + layout->setMinimumSize(-1,-1); + + //Second, check by adding a larger item in the column + QGraphicsWidget *w2 = new QGraphicsWidget; + w2->setMinimumSize(100,100); + w2->setMaximumSize(100,100); + layout->addItem(w2); + layout->activate(); + QCOMPARE(layout->geometry(), QRectF(0,0,100,150)); + QCOMPARE(w->geometry(), QRectF(0,0,50,50)); + QCOMPARE(w2->geometry(), QRectF(0,50,100,100)); +} + +void tst_QGraphicsLinearLayout::combineSizePolicies() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Horizontal, widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QGraphicsWidget *w1 = new QGraphicsWidget; + w1->setMaximumSize(200,200); + w1->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + layout->addItem(w1); + + QGraphicsWidget *w2 = new QGraphicsWidget; + w2->setPreferredSize(50,50); + w2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + layout->addItem(w2); + QCOMPARE(layout->maximumHeight(), qreal(200)); + + // now remove the fixed vertical size policy, and set instead the maximum height to 50 + // this should in effect give the same maximumHeight + w2->setMaximumHeight(50); + w2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + QCOMPARE(layout->maximumHeight(), qreal(200)); +} + +QTEST_MAIN(tst_QGraphicsLinearLayout) +#include "tst_qgraphicslinearlayout.moc" + diff --git a/tests/auto/widgets/graphicsview/qgraphicsobject/qgraphicsobject.pro b/tests/auto/widgets/graphicsview/qgraphicsobject/qgraphicsobject.pro new file mode 100644 index 0000000000..5232ec8372 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsobject/qgraphicsobject.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +QT += widgets +QT += core-private + +SOURCES += tst_qgraphicsobject.cpp +CONFIG += parallel_test diff --git a/tests/auto/widgets/graphicsview/qgraphicsobject/tst_qgraphicsobject.cpp b/tests/auto/widgets/graphicsview/qgraphicsobject/tst_qgraphicsobject.cpp new file mode 100644 index 0000000000..85e36b74b2 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsobject/tst_qgraphicsobject.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qgraphicsitem.h> +#include <qgraphicsscene.h> +#include <qgraphicssceneevent.h> +#include <qgraphicsview.h> +#include <qstyleoption.h> +#include <private/qobject_p.h> + +class tst_QGraphicsObject : public QObject { + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void pos(); + void x(); + void y(); + void z(); + void opacity(); + void enabled(); + void visible(); + void deleted(); +}; + + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsObject::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsObject::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsObject::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsObject::cleanup() +{ +} + + +class MyGraphicsObject : public QGraphicsObject +{ +public: + MyGraphicsObject() : QGraphicsObject() {} + virtual QRectF boundingRect() const { return QRectF(); } + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) {} +}; + +void tst_QGraphicsObject::pos() +{ + MyGraphicsObject object; + QSignalSpy xSpy(&object, SIGNAL(xChanged())); + QSignalSpy ySpy(&object, SIGNAL(yChanged())); + QVERIFY(object.pos() == QPointF(0, 0)); + object.setPos(10, 10); + QCOMPARE(xSpy.count(), 1); + QCOMPARE(ySpy.count(), 1); + + QVERIFY(object.pos() == QPointF(10,10)); + + object.setPos(10, 10); + QCOMPARE(xSpy.count(), 1); + QCOMPARE(ySpy.count(), 1); + + object.setProperty("pos", QPointF(0, 0)); + QCOMPARE(xSpy.count(), 2); + QCOMPARE(ySpy.count(), 2); + QVERIFY(object.property("pos") == QPointF(0,0)); + + object.setProperty("pos", QPointF(10, 0)); + QCOMPARE(xSpy.count(), 3); + QCOMPARE(ySpy.count(), 2); + QVERIFY(object.property("pos") == QPointF(10,0)); + + object.setProperty("pos", QPointF(10, 10)); + QCOMPARE(xSpy.count(), 3); + QCOMPARE(ySpy.count(), 3); + QVERIFY(object.property("pos") == QPointF(10, 10)); +} + +void tst_QGraphicsObject::x() +{ + MyGraphicsObject object; + QSignalSpy xSpy(&object, SIGNAL(xChanged())); + QSignalSpy ySpy(&object, SIGNAL(yChanged())); + QVERIFY(object.pos() == QPointF(0, 0)); + object.setX(10); + QCOMPARE(xSpy.count(), 1); + QCOMPARE(ySpy.count(), 0); + + QVERIFY(object.pos() == QPointF(10, 0)); + QVERIFY(object.x() == 10); + + object.setX(10); + QCOMPARE(xSpy.count(), 1); + QCOMPARE(ySpy.count(), 0); + + object.setProperty("x", 0); + QCOMPARE(xSpy.count(), 2); + QCOMPARE(ySpy.count(), 0); + QVERIFY(object.property("x") == 0); +} + +void tst_QGraphicsObject::y() +{ + MyGraphicsObject object; + QSignalSpy xSpy(&object, SIGNAL(xChanged())); + QSignalSpy ySpy(&object, SIGNAL(yChanged())); + QVERIFY(object.pos() == QPointF(0, 0)); + object.setY(10); + QCOMPARE(xSpy.count(), 0); + QCOMPARE(ySpy.count(), 1); + + QVERIFY(object.pos() == QPointF(0, 10)); + QVERIFY(object.y() == 10); + + object.setY(10); + QCOMPARE(xSpy.count(), 0); + QCOMPARE(ySpy.count(), 1); + + object.setProperty("y", 0); + QCOMPARE(xSpy.count(), 0); + QCOMPARE(ySpy.count(), 2); + QVERIFY(object.property("y") == 0); +} + +void tst_QGraphicsObject::z() +{ + MyGraphicsObject object; + QSignalSpy zSpy(&object, SIGNAL(zChanged())); + QVERIFY(object.zValue() == 0); + object.setZValue(10); + QCOMPARE(zSpy.count(), 1); + + QVERIFY(object.zValue() == 10); + + object.setZValue(10); + QCOMPARE(zSpy.count(), 1); + + object.setProperty("z", 0); + QCOMPARE(zSpy.count(), 2); + QVERIFY(object.property("z") == 0); +} + +void tst_QGraphicsObject::opacity() +{ + MyGraphicsObject object; + QSignalSpy spy(&object, SIGNAL(opacityChanged())); + QVERIFY(object.opacity() == 1.); + object.setOpacity(0); + QCOMPARE(spy.count(), 1); + + QVERIFY(object.opacity() == 0.); + + object.setOpacity(0); + QCOMPARE(spy.count(), 1); + + object.setProperty("opacity", .5); + QCOMPARE(spy.count(), 2); + QVERIFY(object.property("opacity") == .5); +} + +void tst_QGraphicsObject::enabled() +{ + MyGraphicsObject object; + QSignalSpy spy(&object, SIGNAL(enabledChanged())); + QVERIFY(object.isEnabled() == true); + object.setEnabled(false); + QCOMPARE(spy.count(), 1); + + QVERIFY(object.isEnabled() == false); + + object.setEnabled(false); + QCOMPARE(spy.count(), 1); + + object.setProperty("enabled", true); + QCOMPARE(spy.count(), 2); + QVERIFY(object.property("enabled") == true); +} + +void tst_QGraphicsObject::visible() +{ + MyGraphicsObject object; + QSignalSpy spy(&object, SIGNAL(visibleChanged())); + QVERIFY(object.isVisible() == true); + object.setVisible(false); + QCOMPARE(spy.count(), 1); + + QVERIFY(object.isVisible() == false); + + object.setVisible(false); + QCOMPARE(spy.count(), 1); + + object.setProperty("visible", true); + QCOMPARE(spy.count(), 2); + QVERIFY(object.property("visible") == true); +} + +class DeleteTester : public QGraphicsObject +{ +public: + DeleteTester(bool *w, bool *pw, QGraphicsItem *parent = 0) + : QGraphicsObject(parent), wasDeleted(w), parentWasDeleted(pw) + { } + + ~DeleteTester() + { + *wasDeleted = QObjectPrivate::get(this)->wasDeleted; + if (QGraphicsItem *p = parentItem()) { + if (QGraphicsObject *o = p->toGraphicsObject()) + *parentWasDeleted = QObjectPrivate::get(o)->wasDeleted; + } + } + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0) + { } + QRectF boundingRect() const + { return QRectF(); } + + bool *wasDeleted; + bool *parentWasDeleted; +}; + +void tst_QGraphicsObject::deleted() +{ + bool item1_parentWasDeleted = false; + bool item1_wasDeleted = false; + bool item2_parentWasDeleted = false; + bool item2_wasDeleted = false; + DeleteTester *item1 = new DeleteTester(&item1_wasDeleted, &item1_parentWasDeleted); + DeleteTester *item2 = new DeleteTester(&item2_wasDeleted, &item2_parentWasDeleted, item1); + Q_UNUSED(item2); + delete item1; + + QVERIFY(!item1_wasDeleted); // destructor not called yet + QVERIFY(!item1_parentWasDeleted); // no parent + QVERIFY(!item2_wasDeleted); // destructor not called yet + QVERIFY(item2_parentWasDeleted); +} + +QTEST_MAIN(tst_QGraphicsObject) +#include "tst_qgraphicsobject.moc" + diff --git a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/.gitignore b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/.gitignore new file mode 100644 index 0000000000..1e92419fd0 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/.gitignore @@ -0,0 +1 @@ +tst_qgraphicspixmapitem diff --git a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/qgraphicspixmapitem.pro b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/qgraphicspixmapitem.pro new file mode 100644 index 0000000000..6b1ad34057 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/qgraphicspixmapitem.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qgraphicspixmapitem.cpp +CONFIG += parallel_test + diff --git a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp new file mode 100644 index 0000000000..b2dccb0b09 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp @@ -0,0 +1,427 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qgraphicsscene.h> +#include <qgraphicsitem.h> + +class tst_QGraphicsPixmapItem : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qgraphicspixmapitem_data(); + void qgraphicspixmapitem(); + void boundingRect_data(); + void boundingRect(); + void contains_data(); + void contains(); + void isObscuredBy_data(); + void isObscuredBy(); + void offset_data(); + void offset(); + void opaqueArea_data(); + void opaqueArea(); + void pixmap_data(); + void pixmap(); + void setPixmap_data(); + void setPixmap(); + void setShapeMode_data(); + void setShapeMode(); + void setTransformationMode_data(); + void setTransformationMode(); + void shape_data(); + void shape(); + void extension_data(); + void extension(); + void setExtension_data(); + void setExtension(); + void supportsExtension_data(); + void supportsExtension(); +}; + +// Subclass that exposes the protected functions. +class SubQGraphicsPixmapItem : public QGraphicsPixmapItem +{ +public: + enum Extension { + UserExtension = QGraphicsItem::UserExtension + }; + SubQGraphicsPixmapItem(QGraphicsItem *parent = 0) : QGraphicsPixmapItem(parent) + { + } + + SubQGraphicsPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent = 0) : QGraphicsPixmapItem(pixmap, parent) + { + } + + QVariant call_extension(QVariant const& variant) const + { return SubQGraphicsPixmapItem::extension(variant); } + + void call_setExtension(Extension extension, QVariant const& variant) + { return SubQGraphicsPixmapItem::setExtension((QGraphicsItem::Extension)extension, variant); } + + bool call_supportsExtension(Extension extension) const + { return SubQGraphicsPixmapItem::supportsExtension((QGraphicsItem::Extension)extension); } +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsPixmapItem::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsPixmapItem::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsPixmapItem::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsPixmapItem::cleanup() +{ +} + +void tst_QGraphicsPixmapItem::qgraphicspixmapitem_data() +{ +} + +void tst_QGraphicsPixmapItem::qgraphicspixmapitem() +{ + SubQGraphicsPixmapItem item; + item.boundingRect(); + item.contains(QPoint()); + item.isObscuredBy(0); + item.opaqueArea(); + //item.paint(); + QCOMPARE(item.offset(), QPointF()); + QCOMPARE(item.pixmap(), QPixmap()); + QCOMPARE(item.shapeMode(), QGraphicsPixmapItem::MaskShape); + QCOMPARE(item.transformationMode(), Qt::FastTransformation); + item.setOffset(0, 0); + item.setOffset(QPointF(0, 0)); + item.setPixmap(QPixmap()); + item.setShapeMode(QGraphicsPixmapItem::MaskShape); + item.setTransformationMode(Qt::FastTransformation); + item.shape(); + item.type(); + item.call_extension(QVariant()); + item.call_setExtension(SubQGraphicsPixmapItem::UserExtension, QVariant()); + item.call_supportsExtension(SubQGraphicsPixmapItem::UserExtension); +} + +void tst_QGraphicsPixmapItem::boundingRect_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::addColumn<QRectF>("boundingRect"); + QTest::newRow("null") << QPixmap() << QRectF(); + QTest::newRow("10x10") << QPixmap(10, 10) << QRectF(0, 0, 10, 10); +} + +// public QRectF boundingRect() const +void tst_QGraphicsPixmapItem::boundingRect() +{ + QFETCH(QPixmap, pixmap); + QFETCH(QRectF, boundingRect); + + SubQGraphicsPixmapItem item(pixmap); + QCOMPARE(item.boundingRect(), boundingRect); +} + +void tst_QGraphicsPixmapItem::contains_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::addColumn<QPointF>("point"); + QTest::addColumn<bool>("contains"); + QTest::newRow("null") << QPixmap() << QPointF() << false; + QTest::newRow("10x10, 100x100") << QPixmap(10, 10) << QPointF(100, 100) << false; + QTest::newRow("10x10, 5x5") << QPixmap(10, 10) << QPointF(5, 5) << true; + QTest::newRow("border-1") << QPixmap(10, 10) << QPointF(10.5, 10.5) << false; + QTest::newRow("border-2") << QPixmap(10, 10) << QPointF(-0.5, -0.5) << false; +} + +// public bool contains(QPointF const& point) const +void tst_QGraphicsPixmapItem::contains() +{ + QFETCH(QPixmap, pixmap); + QFETCH(QPointF, point); + QFETCH(bool, contains); + + SubQGraphicsPixmapItem item(pixmap); + QCOMPARE(item.contains(point), contains); +} + +void tst_QGraphicsPixmapItem::isObscuredBy_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::addColumn<QPixmap>("otherPixmap"); + QTest::addColumn<bool>("isObscuredBy"); + QTest::newRow("null") << QPixmap() << QPixmap() << false; + QTest::newRow("(10, 10) vs. (5, 5)") << QPixmap(10, 10) << QPixmap(5, 5) << false; + QTest::newRow("(5, 5) vs. (10, 10)") << QPixmap(5, 5) << QPixmap(10, 10) << true; + QTest::newRow("(10, 10) vs. (10, 10)") << QPixmap(10, 10) << QPixmap(10, 10) << false; + QTest::newRow("(9, 9) vs. (10, 10)") << QPixmap(8, 8) << QPixmap(10, 10) << true; + QTest::newRow("(10, 10) vs. (9, 9)") << QPixmap(10, 10) << QPixmap(8, 8) << false; +} + +// public bool isObscuredBy(QGraphicsItem const* item) const +void tst_QGraphicsPixmapItem::isObscuredBy() +{ + QFETCH(QPixmap, pixmap); + QFETCH(QPixmap, otherPixmap); + QFETCH(bool, isObscuredBy); + pixmap.fill(); + otherPixmap.fill(); + + SubQGraphicsPixmapItem *item = new SubQGraphicsPixmapItem(pixmap); + SubQGraphicsPixmapItem *otherItem = new SubQGraphicsPixmapItem(otherPixmap); + + item->setOffset(-pixmap.width() / 2.0, -pixmap.height() / 2.0); + otherItem->setOffset(-otherPixmap.width() / 2.0, -otherPixmap.height() / 2.0); + + QGraphicsScene scene; + scene.addItem(item); + scene.addItem(otherItem); + otherItem->setZValue(1); + + QCOMPARE(item->isObscuredBy(otherItem), isObscuredBy); +} + +void tst_QGraphicsPixmapItem::offset_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::addColumn<QPointF>("offset"); + QTest::newRow("null") << QPixmap() << QPointF(); + QTest::newRow("10x10, 1x1") << QPixmap(10, 10) << QPointF(1, 1); +} + +// public QPointF offset() const +void tst_QGraphicsPixmapItem::offset() +{ + QFETCH(QPixmap, pixmap); + QFETCH(QPointF, offset); + + SubQGraphicsPixmapItem item(pixmap); + item.setOffset(offset); + QCOMPARE(item.offset(), offset); + + // ### test actual painting and compare pixmap with offseted one? +} + +Q_DECLARE_METATYPE(QPainterPath) +void tst_QGraphicsPixmapItem::opaqueArea_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::addColumn<QPainterPath>("opaqueArea"); + QTest::newRow("null") << QPixmap() << QPainterPath(); + // Currently QGraphicsPixmapItem just calls QGraphicsItem test there +} + +// public QPainterPath opaqueArea() const +void tst_QGraphicsPixmapItem::opaqueArea() +{ + QFETCH(QPixmap, pixmap); + QFETCH(QPainterPath, opaqueArea); + + SubQGraphicsPixmapItem item; + QCOMPARE(item.opaqueArea(), opaqueArea); +} + +void tst_QGraphicsPixmapItem::pixmap_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::newRow("null") << QPixmap(); + QTest::newRow("10x10") << QPixmap(10, 10); +} + +// public QPixmap pixmap() const +void tst_QGraphicsPixmapItem::pixmap() +{ + QFETCH(QPixmap, pixmap); + + SubQGraphicsPixmapItem item(pixmap); + QCOMPARE(item.pixmap(), pixmap); +} + +void tst_QGraphicsPixmapItem::setPixmap_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::newRow("null") << QPixmap(); + QTest::newRow("10x10") << QPixmap(10, 10); +} + +// public void setPixmap(QPixmap const& pixmap) +void tst_QGraphicsPixmapItem::setPixmap() +{ + QFETCH(QPixmap, pixmap); + + SubQGraphicsPixmapItem item; + item.setPixmap(pixmap); + QCOMPARE(item.pixmap(), pixmap); +} + +Q_DECLARE_METATYPE(QGraphicsPixmapItem::ShapeMode) +void tst_QGraphicsPixmapItem::setShapeMode_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::addColumn<QGraphicsPixmapItem::ShapeMode>("mode"); + QTest::newRow("MaskShape") << QPixmap() << QGraphicsPixmapItem::MaskShape; + QTest::newRow("BoundingRectShape") << QPixmap() << QGraphicsPixmapItem::BoundingRectShape; + QTest::newRow("HeuristicMaskShape") << QPixmap() << QGraphicsPixmapItem::HeuristicMaskShape; +} + +// public void setShapeMode(QGraphicsPixmapItem::ShapeMode mode) +void tst_QGraphicsPixmapItem::setShapeMode() +{ + QFETCH(QPixmap, pixmap); + QFETCH(QGraphicsPixmapItem::ShapeMode, mode); + + SubQGraphicsPixmapItem item(pixmap); + item.setShapeMode(mode); + QCOMPARE(item.shapeMode(), mode); +} + +Q_DECLARE_METATYPE(Qt::TransformationMode) +void tst_QGraphicsPixmapItem::setTransformationMode_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::addColumn<Qt::TransformationMode>("mode"); + QTest::newRow("FastTransformation") << QPixmap() << Qt::FastTransformation; + QTest::newRow("SmoothTransformation") << QPixmap() << Qt::SmoothTransformation; +} + +// public void setTransformationMode(Qt::TransformationMode mode) +void tst_QGraphicsPixmapItem::setTransformationMode() +{ + QFETCH(QPixmap, pixmap); + QFETCH(Qt::TransformationMode, mode); + + SubQGraphicsPixmapItem item(pixmap); + item.setTransformationMode(mode); + QCOMPARE(item.transformationMode(), mode); +} + +void tst_QGraphicsPixmapItem::shape_data() +{ + QTest::addColumn<QPixmap>("pixmap"); + QTest::addColumn<QPainterPath>("shape"); + QTest::newRow("null") << QPixmap() << QPainterPath(); + // ### what does a normal shape look like? +} + +// public QPainterPath shape() const +void tst_QGraphicsPixmapItem::shape() +{ + QFETCH(QPixmap, pixmap); + QFETCH(QPainterPath, shape); + + SubQGraphicsPixmapItem item(pixmap); + QCOMPARE(item.shape(), shape); +} + +Q_DECLARE_METATYPE(SubQGraphicsPixmapItem::Extension) +Q_DECLARE_METATYPE(QVariant) +void tst_QGraphicsPixmapItem::extension_data() +{ + QTest::addColumn<QVariant>("variant"); + QTest::addColumn<QVariant>("extension"); + QTest::newRow("null") << QVariant() << QVariant(); +} + +// protected QVariant extension(QVariant const& variant) const +void tst_QGraphicsPixmapItem::extension() +{ + QFETCH(QVariant, variant); + QFETCH(QVariant, extension); + + SubQGraphicsPixmapItem item; + QCOMPARE(item.call_extension(variant), extension); +} + +void tst_QGraphicsPixmapItem::setExtension_data() +{ + QTest::addColumn<SubQGraphicsPixmapItem::Extension>("extension"); + QTest::addColumn<QVariant>("variant"); + QTest::newRow("null") << SubQGraphicsPixmapItem::UserExtension << QVariant(); +} + +// protected void setExtension(QGraphicsItem::Extension extension, QVariant const& variant) +void tst_QGraphicsPixmapItem::setExtension() +{ + QFETCH(SubQGraphicsPixmapItem::Extension, extension); + QFETCH(QVariant, variant); + + SubQGraphicsPixmapItem item; + item.call_setExtension(extension, variant); +} + +void tst_QGraphicsPixmapItem::supportsExtension_data() +{ + QTest::addColumn<SubQGraphicsPixmapItem::Extension>("extension"); + QTest::addColumn<bool>("supportsExtension"); + QTest::newRow("null") << SubQGraphicsPixmapItem::UserExtension << false; +} + +// protected bool supportsExtension(QGraphicsItem::Extension extension) const +void tst_QGraphicsPixmapItem::supportsExtension() +{ + QFETCH(SubQGraphicsPixmapItem::Extension, extension); + QFETCH(bool, supportsExtension); + + SubQGraphicsPixmapItem item; + QCOMPARE(item.call_supportsExtension(extension), supportsExtension); +} + +QTEST_MAIN(tst_QGraphicsPixmapItem) +#include "tst_qgraphicspixmapitem.moc" + diff --git a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/.gitignore b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/.gitignore new file mode 100644 index 0000000000..595076024b --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/.gitignore @@ -0,0 +1 @@ +tst_qgraphicspolygonitem diff --git a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/qgraphicspolygonitem.pro b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/qgraphicspolygonitem.pro new file mode 100644 index 0000000000..2aa16751e6 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/qgraphicspolygonitem.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qgraphicspolygonitem.cpp +CONFIG += parallel_test + diff --git a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp new file mode 100644 index 0000000000..061b3eda81 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qgraphicsitem.h> + +Q_DECLARE_METATYPE(QPolygonF) + +class tst_QGraphicsPolygonItem : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qgraphicspolygonitem_data(); + void qgraphicspolygonitem(); + void boundingRect_data(); + void boundingRect(); + void contains_data(); + void contains(); + void fillRule_data(); + void fillRule(); + void isObscuredBy_data(); + void isObscuredBy(); + void opaqueArea_data(); + void opaqueArea(); + void polygon_data(); + void polygon(); + void shape_data(); + void shape(); + void extension_data(); + void extension(); + void setExtension_data(); + void setExtension(); + void supportsExtension_data(); + void supportsExtension(); +}; + +// Subclass that exposes the protected functions. +class SubQGraphicsPolygonItem : public QGraphicsPolygonItem +{ +public: + enum Extension { + UserExtension = QGraphicsItem::UserExtension + }; + + SubQGraphicsPolygonItem(QGraphicsItem *parent = 0) : QGraphicsPolygonItem(parent) + { + } + + SubQGraphicsPolygonItem(const QPolygonF &polygon, QGraphicsItem *parent = 0) : QGraphicsPolygonItem(polygon, parent) + { + } + + QVariant call_extension(QVariant const& variant) const + { return SubQGraphicsPolygonItem::extension(variant); } + + void call_setExtension(SubQGraphicsPolygonItem::Extension extension, QVariant const& variant) + { return SubQGraphicsPolygonItem::setExtension((QGraphicsItem::Extension)extension, variant); } + + bool call_supportsExtension(SubQGraphicsPolygonItem::Extension extension) const + { return SubQGraphicsPolygonItem::supportsExtension((QGraphicsItem::Extension)extension); } +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsPolygonItem::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsPolygonItem::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsPolygonItem::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsPolygonItem::cleanup() +{ +} + +void tst_QGraphicsPolygonItem::qgraphicspolygonitem_data() +{ +} + +void tst_QGraphicsPolygonItem::qgraphicspolygonitem() +{ + SubQGraphicsPolygonItem item; + + item.boundingRect(); + item.contains(QPoint()); + item.isObscuredBy(0); + item.opaqueArea(); + //item.paint(); + item.shape(); + item.type(); + item.call_extension(QVariant()); + item.call_setExtension(SubQGraphicsPolygonItem::UserExtension, QVariant()); + item.call_supportsExtension(SubQGraphicsPolygonItem::UserExtension); + item.fillRule(); + item.polygon(); + item.setFillRule(Qt::OddEvenFill); + item.setPolygon(QPolygonF()); +} + +void tst_QGraphicsPolygonItem::boundingRect_data() +{ + QTest::addColumn<QPolygonF>("polygon"); + QTest::addColumn<QRectF>("boundingRect"); + QTest::newRow("null") << QPolygonF() << QRectF(); + QPolygonF example; + example << QPointF(10.4, 20.5) << QPointF(20.2, 30.2); + QTest::newRow("example") << example << example.boundingRect(); + // ### set pen width? +} + +// public QRectF boundingRect() const +void tst_QGraphicsPolygonItem::boundingRect() +{ + QFETCH(QPolygonF, polygon); + QFETCH(QRectF, boundingRect); + + SubQGraphicsPolygonItem item(polygon); + QCOMPARE(item.boundingRect(), boundingRect); +} + +void tst_QGraphicsPolygonItem::contains_data() +{ + QTest::addColumn<QPolygonF>("polygon"); + QTest::addColumn<QPointF>("point"); + QTest::addColumn<bool>("contains"); + QTest::newRow("null") << QPolygonF() << QPointF() << false; +} + +// public bool contains(QPointF const& point) const +void tst_QGraphicsPolygonItem::contains() +{ + QFETCH(QPolygonF, polygon); + QFETCH(QPointF, point); + QFETCH(bool, contains); + + SubQGraphicsPolygonItem item(polygon); + + QCOMPARE(item.contains(point), contains); +} + +Q_DECLARE_METATYPE(Qt::FillRule) +void tst_QGraphicsPolygonItem::fillRule_data() +{ + QTest::addColumn<QPolygonF>("polygon"); + QTest::addColumn<Qt::FillRule>("fillRule"); + QTest::newRow("OddEvenFill") << QPolygonF() << Qt::OddEvenFill; + QTest::newRow("WindingFill") << QPolygonF() << Qt::WindingFill; +} + +// public Qt::FillRule fillRule() const +void tst_QGraphicsPolygonItem::fillRule() +{ + QFETCH(QPolygonF, polygon); + QFETCH(Qt::FillRule, fillRule); + + SubQGraphicsPolygonItem item(polygon); + + item.setFillRule(fillRule); + QCOMPARE(item.fillRule(), fillRule); + // ### Check that the painting is different? +} + +void tst_QGraphicsPolygonItem::isObscuredBy_data() +{ + QTest::addColumn<QPolygonF>("polygon"); + QTest::addColumn<QPolygonF>("otherPolygon"); + QTest::addColumn<bool>("isObscuredBy"); + QTest::newRow("null") << QPolygonF() << QPolygonF() << false; + //QTest::newRow("ontop-inside") << QPixmap(10, 10) << QPixmap(5, 5) << false; + //QTest::newRow("ontop-larger") << QPixmap(10, 10) << QPixmap(11, 11) << true; +} + +// public bool isObscuredBy(QGraphicsItem const* item) const +void tst_QGraphicsPolygonItem::isObscuredBy() +{ + QFETCH(QPolygonF, polygon); + QFETCH(QPolygonF, otherPolygon); + QFETCH(bool, isObscuredBy); + SubQGraphicsPolygonItem item(polygon); + SubQGraphicsPolygonItem otherItem(otherPolygon); + QCOMPARE(item.isObscuredBy(&otherItem), isObscuredBy); +} + +Q_DECLARE_METATYPE(QPainterPath) +void tst_QGraphicsPolygonItem::opaqueArea_data() +{ + QTest::addColumn<QPolygonF>("polygon"); + QTest::addColumn<QPainterPath>("opaqueArea"); + QTest::newRow("null") << QPolygonF() << QPainterPath(); + // Currently QGraphicsPolygonItem just calls QGraphicsItem test there +} + +// public QPainterPath opaqueArea() const +void tst_QGraphicsPolygonItem::opaqueArea() +{ + QFETCH(QPolygonF, polygon); + QFETCH(QPainterPath, opaqueArea); + + SubQGraphicsPolygonItem item(polygon); + QCOMPARE(item.opaqueArea(), opaqueArea); +} + +void tst_QGraphicsPolygonItem::polygon_data() +{ + QTest::addColumn<QPolygonF>("polygon"); + QTest::newRow("null") << QPolygonF(); + QPolygonF example; + example << QPointF(10.4, 20.5) << QPointF(20.2, 30.2); + QTest::newRow("example") << example; +} + +// public QPolygonF polygon() const +void tst_QGraphicsPolygonItem::polygon() +{ + QFETCH(QPolygonF, polygon); + + SubQGraphicsPolygonItem item; + item.setPolygon(polygon); + QCOMPARE(item.polygon(), polygon); +} + +void tst_QGraphicsPolygonItem::shape_data() +{ + QTest::addColumn<QPainterPath>("shape"); + QTest::newRow("null") << QPainterPath(); + // ### what should a normal shape look like? +} + +// public QPainterPath shape() const +void tst_QGraphicsPolygonItem::shape() +{ + QFETCH(QPainterPath, shape); + + SubQGraphicsPolygonItem item; + QCOMPARE(item.shape(), shape); +} + +Q_DECLARE_METATYPE(QVariant) +void tst_QGraphicsPolygonItem::extension_data() +{ + QTest::addColumn<QVariant>("variant"); + QTest::addColumn<QVariant>("extension"); + QTest::newRow("null") << QVariant() << QVariant(); +} + +// protected QVariant extension(QVariant const& variant) const +void tst_QGraphicsPolygonItem::extension() +{ + QFETCH(QVariant, variant); + QFETCH(QVariant, extension); + + SubQGraphicsPolygonItem item; + + QCOMPARE(item.call_extension(variant), extension); +} + +Q_DECLARE_METATYPE(SubQGraphicsPolygonItem::Extension) +void tst_QGraphicsPolygonItem::setExtension_data() +{ + QTest::addColumn<SubQGraphicsPolygonItem::Extension>("extension"); + QTest::addColumn<QVariant>("variant"); + QTest::newRow("null") << SubQGraphicsPolygonItem::Extension() << QVariant(); +} + +// protected void setExtension(SubQGraphicsPolygonItem::Extension extension, QVariant const& variant) +void tst_QGraphicsPolygonItem::setExtension() +{ + QFETCH(SubQGraphicsPolygonItem::Extension, extension); + QFETCH(QVariant, variant); + + SubQGraphicsPolygonItem item; + item.call_setExtension(extension, variant); +} + +void tst_QGraphicsPolygonItem::supportsExtension_data() +{ + QTest::addColumn<SubQGraphicsPolygonItem::Extension>("extension"); + QTest::addColumn<bool>("supportsExtension"); + QTest::newRow("null") << SubQGraphicsPolygonItem::Extension() << false; +} + +// protected bool supportsExtension(SubQGraphicsPolygonItem::Extension extension) const +void tst_QGraphicsPolygonItem::supportsExtension() +{ + QFETCH(SubQGraphicsPolygonItem::Extension, extension); + QFETCH(bool, supportsExtension); + + SubQGraphicsPolygonItem item; + QCOMPARE(item.call_supportsExtension(extension), supportsExtension); +} + +QTEST_MAIN(tst_QGraphicsPolygonItem) +#include "tst_qgraphicspolygonitem.moc" + diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/.gitignore b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/.gitignore new file mode 100644 index 0000000000..530452478b --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/.gitignore @@ -0,0 +1 @@ +tst_qgraphicsproxywidget diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/qgraphicsproxywidget.pro b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/qgraphicsproxywidget.pro new file mode 100644 index 0000000000..a649ae1a3c --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/qgraphicsproxywidget.pro @@ -0,0 +1,8 @@ +load(qttest_p4) + +QT += widgets widgets-private +QT += core-private gui-private + +SOURCES += tst_qgraphicsproxywidget.cpp + +contains(QT_CONFIG,xcb):qpa:CONFIG+=insignificant_test # QTBUG-20756 crashes on qpa, xcb diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp new file mode 100644 index 0000000000..78c545e25a --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -0,0 +1,3649 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtGui> +#include <QtWidgets> +#include <private/qgraphicsproxywidget_p.h> +#include <private/qlayoutengine_p.h> // qSmartMin functions... +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) +#include <QMacStyle> +#endif +#ifdef Q_WS_X11 +#include <private/qt_x11_p.h> +#endif + +static void sendMouseMove(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::NoButton) +{ + QMouseEvent event(QEvent::MouseMove, point, widget->mapToGlobal(point), button, button, 0); + QApplication::sendEvent(widget, &event); +} + +/* + Notes: + + 1) The proxy and the widget geometries are linked. + proxy resize => widget resize => stop (no livelock) + widget resize => proxy resize => stop (no livelock) + + 2) As far as possible, the properties are linked. + proxy enable => widget enable => stop + widget disabled => proxy disabled => stop + + 3) Windowed state is linked + Windowed proxy state => windowed widget state => stop + Windowed widget state => windowed proxy state => stop +*/ + +class EventSpy : public QObject +{ +public: + EventSpy(QObject *receiver) + { + receiver->installEventFilter(this); + } + + QMap<QEvent::Type, int> counts; + +protected: + bool eventFilter(QObject *, QEvent *event) + { + ++counts[event->type()]; + return false; + } +}; + +class tst_QGraphicsProxyWidget : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qgraphicsproxywidget_data(); + void qgraphicsproxywidget(); + void paint(); + void paint_2(); + void setWidget_data(); + void setWidget(); + void eventFilter_data(); + void eventFilter(); + void focusInEvent_data(); + void focusInEvent(); + void focusInEventNoWidget(); + void focusNextPrevChild_data(); + void focusNextPrevChild(); + void focusOutEvent_data(); + void focusOutEvent(); +#if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR)) + void hoverEnterLeaveEvent_data(); + void hoverEnterLeaveEvent(); +#endif + void hoverMoveEvent_data(); + void hoverMoveEvent(); + void keyPressEvent_data(); + void keyPressEvent(); + void keyReleaseEvent_data(); + void keyReleaseEvent(); + void mouseDoubleClickEvent_data(); + void mouseDoubleClickEvent(); + void mousePressReleaseEvent_data(); + void mousePressReleaseEvent(); + void resizeEvent_data(); + void resizeEvent(); + void paintEvent(); + void wheelEvent(); + void sizeHint_data(); + void sizeHint(); + void sizePolicy(); + void minimumSize(); + void maximumSize(); + void scrollUpdate(); + void setWidget_simple(); + void setWidget_ownership(); + void resize_simple_data(); + void resize_simple(); + void symmetricMove(); + void symmetricResize(); + void symmetricEnabled(); + void symmetricVisible(); + void tabFocus_simpleWidget(); + void tabFocus_simpleTwoWidgets(); + void tabFocus_complexWidget(); + void tabFocus_complexTwoWidgets(); + void setFocus_simpleWidget(); + void setFocus_simpleTwoWidgets(); + void setFocus_complexTwoWidgets(); + void popup_basic(); + void popup_subwidget(); +#if !defined(QT_NO_CURSOR) && (!defined(Q_OS_WINCE) || defined(GWES_ICONCURS)) + void changingCursor_basic(); +#endif + void tooltip_basic(); + void childPos_data(); + void childPos(); + void autoShow(); + void windowOpacity(); + void stylePropagation(); + void palettePropagation(); + void fontPropagation(); + void dontCrashWhenDie(); + void createProxyForChildWidget(); + void actionsContextMenu(); + void actionsContextMenu_data(); + void deleteProxyForChildWidget(); + void bypassGraphicsProxyWidget_data(); + void bypassGraphicsProxyWidget(); + void dragDrop(); + void windowFlags_data(); + void windowFlags(); + void comboboxWindowFlags(); + void updateAndDelete(); + void inputMethod(); + void clickFocus(); + void windowFrameMargins(); + void QTBUG_6986_sendMouseEventToAlienWidget(); +}; + +// Subclass that exposes the protected functions. +class SubQGraphicsProxyWidget : public QGraphicsProxyWidget +{ + +public: + SubQGraphicsProxyWidget(QGraphicsItem *parent = 0) : QGraphicsProxyWidget(parent), + paintCount(0), keyPress(0), focusOut(0) + {} + + bool call_eventFilter(QObject* object, QEvent* event) + { return SubQGraphicsProxyWidget::eventFilter(object, event); } + + void call_focusInEvent(QFocusEvent* event) + { return SubQGraphicsProxyWidget::focusInEvent(event); } + + bool call_focusNextPrevChild(bool next) + { return SubQGraphicsProxyWidget::focusNextPrevChild(next); } + + void call_focusOutEvent(QFocusEvent* event) + { return SubQGraphicsProxyWidget::focusOutEvent(event); } + + void call_hideEvent(QHideEvent* event) + { return SubQGraphicsProxyWidget::hideEvent(event); } + + void call_hoverEnterEvent(QGraphicsSceneHoverEvent* event) + { return SubQGraphicsProxyWidget::hoverEnterEvent(event); } + + void call_hoverLeaveEvent(QGraphicsSceneHoverEvent* event) + { return SubQGraphicsProxyWidget::hoverLeaveEvent(event); } + + void call_hoverMoveEvent(QGraphicsSceneHoverEvent* event) + { return SubQGraphicsProxyWidget::hoverMoveEvent(event); } + + void call_keyPressEvent(QKeyEvent* event) + { return SubQGraphicsProxyWidget::keyPressEvent(event); } + + void call_keyReleaseEvent(QKeyEvent* event) + { return SubQGraphicsProxyWidget::keyReleaseEvent(event); } + + void call_mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) + { return SubQGraphicsProxyWidget::mouseDoubleClickEvent(event); } + + void call_mouseMoveEvent(QGraphicsSceneMouseEvent* event) + { return SubQGraphicsProxyWidget::mouseMoveEvent(event); } + + void call_mousePressEvent(QGraphicsSceneMouseEvent* event) + { return SubQGraphicsProxyWidget::mousePressEvent(event); } + + void call_mouseReleaseEvent(QGraphicsSceneMouseEvent* event) + { return SubQGraphicsProxyWidget::mouseReleaseEvent(event); } + + void call_resizeEvent(QGraphicsSceneResizeEvent* event) + { return SubQGraphicsProxyWidget::resizeEvent(event); } + + QSizeF call_sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const + { return SubQGraphicsProxyWidget::sizeHint(which, constraint); } + + void call_showEvent(QShowEvent* event) + { return SubQGraphicsProxyWidget::showEvent(event); } + + void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) { + paintCount++; + QGraphicsProxyWidget::paint(painter, option, widget); + } + + void focusOutEvent(QFocusEvent *event) + { + focusOut++; + QGraphicsProxyWidget::focusOutEvent(event); + } + + bool eventFilter(QObject *object, QEvent *event) { + if (event->type() == QEvent::KeyPress && object == widget()) + keyPress++; + return QGraphicsProxyWidget::eventFilter(object, event); + } + int paintCount; + int keyPress; + int focusOut; +}; + +class WheelWidget : public QWidget +{ +public: + WheelWidget() : wheelEventCalled(false) { setFocusPolicy(Qt::WheelFocus); } + + virtual void wheelEvent(QWheelEvent *event) { event->accept(); wheelEventCalled = true; } + + bool wheelEventCalled; +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsProxyWidget::initTestCase() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsProxyWidget::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsProxyWidget::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsProxyWidget::cleanup() +{ +} + +void tst_QGraphicsProxyWidget::qgraphicsproxywidget_data() +{ +} + +void tst_QGraphicsProxyWidget::qgraphicsproxywidget() +{ + SubQGraphicsProxyWidget proxy; + proxy.paint(0, 0, 0); + proxy.setWidget(0); + QVERIFY(proxy.type() == QGraphicsProxyWidget::Type); + QVERIFY(!proxy.widget()); + QEvent event(QEvent::None); + proxy.call_eventFilter(0, &event); + QFocusEvent focusEvent(QEvent::FocusIn); + focusEvent.ignore(); + proxy.call_focusInEvent(&focusEvent); + QCOMPARE(focusEvent.isAccepted(), false); + QCOMPARE(proxy.call_focusNextPrevChild(false), false); + QCOMPARE(proxy.call_focusNextPrevChild(true), false); + proxy.call_focusOutEvent(&focusEvent); + QHideEvent hideEvent; + proxy.call_hideEvent(&hideEvent); + QGraphicsSceneHoverEvent hoverEvent; + proxy.call_hoverEnterEvent(&hoverEvent); + proxy.call_hoverLeaveEvent(&hoverEvent); + proxy.call_hoverMoveEvent(&hoverEvent); + QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier); + proxy.call_keyPressEvent(&keyEvent); + proxy.call_keyReleaseEvent(&keyEvent); + QGraphicsSceneMouseEvent mouseEvent; + proxy.call_mouseDoubleClickEvent(&mouseEvent); + proxy.call_mouseMoveEvent(&mouseEvent); + proxy.call_mousePressEvent(&mouseEvent); + proxy.call_mouseReleaseEvent(&mouseEvent); + QGraphicsSceneResizeEvent resizeEvent; + proxy.call_resizeEvent(&resizeEvent); + QShowEvent showEvent; + proxy.call_showEvent(&showEvent); + proxy.call_sizeHint(Qt::PreferredSize, QSizeF()); +} + +// public void paint(QPainter* painter, QStyleOptionGraphicsItem const* option, QWidget* widget) +void tst_QGraphicsProxyWidget::paint() +{ + SubQGraphicsProxyWidget proxy; + proxy.paint(0, 0, 0); +} + +class MyProxyWidget : public QGraphicsProxyWidget +{ +public: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + // Make sure QGraphicsProxyWidget::paint does not modify the render hints set on the painter. + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform + | QPainter::NonCosmeticDefaultPen | QPainter::TextAntialiasing); + const QPainter::RenderHints oldRenderHints = painter->renderHints(); + QGraphicsProxyWidget::paint(painter, option, widget); + QCOMPARE(painter->renderHints(), oldRenderHints); + } +}; + +void tst_QGraphicsProxyWidget::paint_2() +{ + MyProxyWidget *proxyWidget = new MyProxyWidget; + proxyWidget->setWidget(new QLineEdit); + + QGraphicsScene scene; + scene.addItem(proxyWidget); + scene.setSceneRect(scene.itemsBoundingRect()); + + // Trigger repaint. + QPixmap pixmap(scene.sceneRect().toRect().size()); + QPainter painter(&pixmap); + scene.render(&painter); +} + +void tst_QGraphicsProxyWidget::setWidget_data() +{ + QTest::addColumn<bool>("widgetExists"); + QTest::addColumn<bool>("insertWidget"); + QTest::addColumn<bool>("hasParent"); + QTest::addColumn<bool>("proxyHasParent"); + + QTest::newRow("setWidget(0)") << false << false << false << false; + QTest::newRow("setWidget(widget)") << false << true << false << false; + QTest::newRow("setWidget(widgetWParent)") << false << true << true << false; + QTest::newRow("setWidget(1), setWidget(0)") << true << false << false << false; + QTest::newRow("setWidget(1), setWidget(widget)") << true << true << false << false; + QTest::newRow("setWidget(1), setWidget(widgetWParent)") << true << true << true << false; + QTest::newRow("p setWidget(0)") << false << false << false << true; + QTest::newRow("p setWidget(widget)") << false << true << false << true; + QTest::newRow("p setWidget(widgetWParent)") << false << true << true << true; + QTest::newRow("p setWidget(1), setWidget(0)") << true << false << false << true; + QTest::newRow("p setWidget(1), setWidget(widget)") << true << true << false << true; + QTest::newRow("p setWidget(1), setWidget(widgetWParent)") << true << true << true << true; +} + +// public void setWidget(QWidget* widget) +void tst_QGraphicsProxyWidget::setWidget() +{ + QFETCH(bool, widgetExists); + QFETCH(bool, insertWidget); + QFETCH(bool, hasParent); + QFETCH(bool, proxyHasParent); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QPointer<SubQGraphicsProxyWidget> proxy = new SubQGraphicsProxyWidget; + SubQGraphicsProxyWidget parentProxy; + scene.addItem(proxy); + scene.addItem(&parentProxy); + if (proxyHasParent) + proxy->setParent(&parentProxy); + QPointer<QWidget> existingSubWidget = new QWidget; + proxy->setVisible(false); + proxy->setEnabled(false); + + if (widgetExists) { + existingSubWidget->setAttribute(Qt::WA_QuitOnClose, true); + proxy->setWidget(existingSubWidget); + } + + QWidget *widget = new QWidget; +#ifndef QT_NO_CURSOR + widget->setCursor(Qt::IBeamCursor); +#endif + widget->setPalette(QPalette(Qt::magenta)); + widget->setLayoutDirection(Qt::RightToLeft); + QCleanlooksStyle cleanlooksStyle; + widget->setStyle(&cleanlooksStyle); + widget->setFont(QFont("Times")); + widget->setVisible(true); + QApplication::setActiveWindow(widget); + widget->activateWindow(); + widget->setEnabled(true); + widget->resize(325, 241); + widget->setMinimumSize(100, 200); + widget->setMaximumSize(1000, 2000); + widget->setFocusPolicy(Qt::TabFocus); + widget->setContentsMargins(10, 29, 19, 81); + widget->setFocus(Qt::TabFocusReason); + widget->setAcceptDrops(true); + QTRY_VERIFY(widget->hasFocus()); + QVERIFY(widget->isActiveWindow()); + + QWidget parentWidget; + if (hasParent) + widget->setParent(&parentWidget); + + QWidget *subWidget = insertWidget ? widget : 0; + bool shouldBeInsertable = !hasParent && subWidget; + if (shouldBeInsertable) + subWidget->setAttribute(Qt::WA_QuitOnClose, true); + + proxy->setWidget(subWidget); + + if (shouldBeInsertable) { + QCOMPARE(proxy->widget(), subWidget); + QVERIFY(subWidget->testAttribute(Qt::WA_DontShowOnScreen)); + QVERIFY(!subWidget->testAttribute(Qt::WA_QuitOnClose)); + QCOMPARE(proxy->acceptHoverEvents(), true); +#ifndef QT_NO_CURSOR + QVERIFY(proxy->hasCursor()); + + // These should match + QCOMPARE(proxy->cursor().shape(), widget->cursor().shape()); +#endif + //###QCOMPARE(proxy->palette(), widget->palette()); + QCOMPARE(proxy->layoutDirection(), widget->layoutDirection()); + QCOMPARE(proxy->style(), widget->style()); + QCOMPARE(proxy->isVisible(), widget->isVisible()); + QCOMPARE(proxy->isEnabled(), widget->isEnabled()); + QVERIFY(proxy->flags() & QGraphicsItem::ItemIsFocusable); + QCOMPARE(proxy->effectiveSizeHint(Qt::MinimumSize).toSize(), + qSmartMinSize(widget)); + QCOMPARE(proxy->minimumSize().toSize(), + qSmartMinSize(widget) ); + QCOMPARE(proxy->maximumSize().toSize(), QSize(1000, 2000)); + QCOMPARE(proxy->effectiveSizeHint(Qt::PreferredSize).toSize(), + qSmartMinSize(widget)); + QCOMPARE(proxy->size().toSize(), widget->size()); + QCOMPARE(proxy->rect().toRect(), widget->rect()); + QCOMPARE(proxy->focusPolicy(), Qt::WheelFocus); + QVERIFY(proxy->acceptDrops()); + QCOMPARE(proxy->acceptsHoverEvents(), true); // to get widget enter events + int left, top, right, bottom; + widget->getContentsMargins(&left, &top, &right, &bottom); + qreal rleft, rtop, rright, rbottom; + proxy->getContentsMargins(&rleft, &rtop, &rright, &rbottom); + QCOMPARE((qreal)left, rleft); + QCOMPARE((qreal)top, rtop); + QCOMPARE((qreal)right, rright); + QCOMPARE((qreal)bottom, rbottom); + } else { + // proxy shouldn't mess with the widget if it can't insert it. + QCOMPARE(proxy->widget(), (QWidget*)0); + QCOMPARE(proxy->acceptHoverEvents(), false); + if (subWidget) { + QVERIFY(!subWidget->testAttribute(Qt::WA_DontShowOnScreen)); + QVERIFY(subWidget->testAttribute(Qt::WA_QuitOnClose)); + // reset + subWidget->setAttribute(Qt::WA_QuitOnClose, false); + } + } + + if (widgetExists) { + QCOMPARE(existingSubWidget->parent(), static_cast<QObject*>(0)); + QVERIFY(!existingSubWidget->testAttribute(Qt::WA_DontShowOnScreen)); + QVERIFY(!existingSubWidget->testAttribute(Qt::WA_QuitOnClose)); + } + + if (hasParent) + widget->setParent(0); + + delete widget; + if (shouldBeInsertable) + QVERIFY(!proxy); + delete existingSubWidget; + if (!shouldBeInsertable) { + QVERIFY(proxy); + delete proxy; + } + QVERIFY(!proxy); +} + +Q_DECLARE_METATYPE(QEvent::Type) +void tst_QGraphicsProxyWidget::eventFilter_data() +{ + QTest::addColumn<QEvent::Type>("eventType"); + QTest::addColumn<bool>("fromObject"); // big grin evil + QTest::newRow("none") << QEvent::None << false; + for (int i = 0; i < 2; ++i) { + bool fromObject = (i == 0); + QTest::newRow(QString("resize %1").arg(fromObject).toLatin1()) << QEvent::Resize << fromObject; + QTest::newRow(QString("move %1").arg(fromObject).toLatin1()) << QEvent::Move << fromObject; + QTest::newRow(QString("hide %1").arg(fromObject).toLatin1()) << QEvent::Hide << fromObject; + QTest::newRow(QString("show %1").arg(fromObject).toLatin1()) << QEvent::Show << fromObject; + QTest::newRow(QString("enabled %1").arg(fromObject).toLatin1()) << QEvent::EnabledChange << fromObject; + QTest::newRow(QString("focusIn %1").arg(fromObject).toLatin1()) << QEvent::FocusIn << fromObject; + QTest::newRow(QString("focusOut %1").arg(fromObject).toLatin1()) << QEvent::FocusOut << fromObject; + QTest::newRow(QString("keyPress %1").arg(fromObject).toLatin1()) << QEvent::KeyPress << fromObject; + } +} + +// protected bool eventFilter(QObject* object, QEvent* event) +void tst_QGraphicsProxyWidget::eventFilter() +{ + QFETCH(QEvent::Type, eventType); + QFETCH(bool, fromObject); + + QGraphicsScene scene; + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + scene.addItem(proxy); + + QWidget *widget = new QWidget(0, Qt::FramelessWindowHint); + widget->setFocusPolicy(Qt::TabFocus); + widget->resize(10, 10); + widget->show(); + proxy->setWidget(widget); + proxy->show(); + + // mirror whatever is happening to the widget + // don't get in a loop + switch (eventType) { + case QEvent::None: { + QEvent event(QEvent::None); + proxy->call_eventFilter(widget, &event); + break; + } + case QEvent::Resize: { + QSize oldSize = widget->size(); + QSize newSize = QSize(100, 100); + if (fromObject) { + widget->resize(newSize); + } else { + proxy->resize(newSize); + } + QCOMPARE(proxy->size().toSize(), newSize); + QCOMPARE(widget->size(), newSize); + break; + } + case QEvent::Move: { + QPoint oldPoint = widget->pos(); + QPoint newPoint = QPoint(100, 100); + if (fromObject) { + widget->move(newPoint); + } else { + proxy->setPos(newPoint); + } + QCOMPARE(proxy->pos().toPoint(), newPoint); + QCOMPARE(widget->pos(), newPoint); + break; + } + case QEvent::Hide: { + // A hide event can only come from a widget + if (fromObject) { + widget->setFocus(Qt::TabFocusReason); + widget->hide(); + } else { + QHideEvent event; + proxy->call_eventFilter(widget, &event); + } + QCOMPARE(proxy->isVisible(), false); + break; + } + case QEvent::Show: { + // A show event can either come from a widget or somewhere else + widget->hide(); + if (fromObject) { + widget->show(); + } else { + QShowEvent event; + proxy->call_eventFilter(widget, &event); + } + QCOMPARE(proxy->isVisible(), true); + break; + } + case QEvent::EnabledChange: { + widget->setEnabled(false); + proxy->setEnabled(false); + if (fromObject) { + widget->setEnabled(true); + QCOMPARE(proxy->isEnabled(), true); + widget->setEnabled(false); + QCOMPARE(proxy->isEnabled(), false); + } else { + QEvent event(QEvent::EnabledChange); + proxy->call_eventFilter(widget, &event); + // match the widget not the event + QCOMPARE(proxy->isEnabled(), false); + } + break; + } + case QEvent::FocusIn: { + if (fromObject) { + widget->setFocus(Qt::TabFocusReason); + QVERIFY(proxy->hasFocus()); + } + break; + } + case QEvent::FocusOut: { + widget->setFocus(Qt::TabFocusReason); + QVERIFY(proxy->hasFocus()); + QVERIFY(widget->hasFocus()); + if (fromObject) { + widget->clearFocus(); + QVERIFY(!proxy->hasFocus()); + } + break; + } + case QEvent::KeyPress: { + if (fromObject) { + QTest::keyPress(widget, Qt::Key_A, Qt::NoModifier); + } else { + QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier); + proxy->call_eventFilter(widget, &event); + } + QCOMPARE(proxy->keyPress, 1); + break; + } + default: + break; + } +} + +void tst_QGraphicsProxyWidget::focusInEvent_data() +{ + QTest::addColumn<bool>("widgetHasFocus"); + QTest::addColumn<bool>("widgetCanHaveFocus"); + QTest::newRow("no focus, can't get") << false << false; + QTest::newRow("no focus, can get") << false << true; + QTest::newRow("has focus, can't get") << true << false; + QTest::newRow("has focus, can get") << true << true; + // ### add test for widget having a focusNextPrevChild +} + +// protected void focusInEvent(QFocusEvent* event) +void tst_QGraphicsProxyWidget::focusInEvent() +{ + // ### This test is just plain old broken + QFETCH(bool, widgetHasFocus); + QFETCH(bool, widgetCanHaveFocus); + + QGraphicsScene scene; + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setEnabled(true); + scene.addItem(proxy); + proxy->setVisible(true); + + QWidget *widget = new QWidget; + widget->resize(100, 100); + if (widgetCanHaveFocus) + widget->setFocusPolicy(Qt::WheelFocus); + widget->show(); + + if (widgetHasFocus) + widget->setFocus(Qt::TabFocusReason); + + proxy->setWidget(widget); + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // <- shouldn't need to do this + + // ### This test is just plain old broken - sending a focus in event + // does not cause items to gain input focus. The widget has focus + // because the proxy has focus, not because it got this event. + + QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); + event.ignore(); + proxy->call_focusInEvent(&event); + QTRY_COMPARE(widget->hasFocus(), widgetCanHaveFocus); +} + +void tst_QGraphicsProxyWidget::focusInEventNoWidget() +{ + QGraphicsView view; + QGraphicsScene scene(&view); + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setEnabled(true); + scene.addItem(proxy); + proxy->setVisible(true); + view.show(); + + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // <- shouldn't need to do this + QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); + event.ignore(); + //should not crash + proxy->call_focusInEvent(&event); +} + +void tst_QGraphicsProxyWidget::focusNextPrevChild_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::addColumn<bool>("hasScene"); + QTest::addColumn<bool>("next"); + QTest::addColumn<bool>("focusNextPrevChild"); + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + for (int k = 0; k < 2; ++k) { + bool next = (i == 0); + bool hasWidget = (j == 0); + bool hasScene = (k == 0); + bool result = hasScene && hasWidget; + QString name = QString("Forward: %1, hasWidget: %2, hasScene: %3, result: %4").arg(next).arg(hasWidget).arg(hasScene).arg(result); + QTest::newRow(name.toLatin1()) << hasWidget << hasScene << next << result; + } + } + } +} + +// protected bool focusNextPrevChild(bool next) +void tst_QGraphicsProxyWidget::focusNextPrevChild() +{ + QFETCH(bool, next); + QFETCH(bool, focusNextPrevChild); + QFETCH(bool, hasWidget); + QFETCH(bool, hasScene); + + // If a widget has its own focusNextPrevChild we need to respect it + // otherwise respect the scene + // Respect the widget over the scene! + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + + QLabel *widget = new QLabel; + // I can't believe designer adds this much junk! + widget->setText("<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\"> p, li { white-space: pre-wrap; } </style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\"> <p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><a href=\"http://www.slashdot.org\"><span style=\" text-decoration: underline; color:#0000ff;\">old</span></a> foo <a href=\"http://www.reddit.org\"><span style=\" text-decoration: underline; color:#0000ff;\">new</span></a></p></body></html>"); + widget->setTextInteractionFlags(Qt::TextBrowserInteraction); + + if (hasWidget) + proxy->setWidget(widget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + if (hasScene) { + scene.addItem(proxy); + proxy->show(); + + // widget should take precedence over scene so make scene.focusNextPrevChild return false + // so we know that a true can only come from the widget + if (!(hasWidget && hasScene)) { + QGraphicsTextItem *item = new QGraphicsTextItem("Foo"); + item->setTextInteractionFlags(Qt::TextBrowserInteraction); + scene.addItem(item); + item->setPos(50, 40); + } + scene.setFocusItem(proxy); + QVERIFY(proxy->hasFocus()); + } + + QCOMPARE(proxy->call_focusNextPrevChild(next), focusNextPrevChild); + + if (!hasScene) + delete proxy; +} + +void tst_QGraphicsProxyWidget::focusOutEvent_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::addColumn<bool>("call"); + QTest::newRow("no widget, focus to other widget") << false << false; + QTest::newRow("no widget, focusOutCalled") << false << true; + QTest::newRow("widget, focus to other widget") << true << false; + QTest::newRow("widget, focusOutCalled") << true << true; +} + +// protected void focusOutEvent(QFocusEvent* event) +void tst_QGraphicsProxyWidget::focusOutEvent() +{ + QFETCH(bool, hasWidget); + QFETCH(bool, call); + + QGraphicsScene scene; + QGraphicsView view(&scene); + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + scene.addItem(proxy); + view.show(); + QApplication::setActiveWindow(&view); + view.activateWindow(); + view.setFocus(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.isVisible()); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + QWidget *widget = new QWidget; + widget->setFocusPolicy(Qt::WheelFocus); + if (hasWidget) + proxy->setWidget(widget); + proxy->show(); + proxy->setFocus(); + QVERIFY(proxy->hasFocus()); + QEXPECT_FAIL("widget, focus to other widget", "Widget should have focus but doesn't", Continue); + QEXPECT_FAIL("widget, focusOutCalled", "Widget should have focus but doesn't", Continue); + QCOMPARE(widget->hasFocus(), hasWidget); + + if (!call) { + QWidget *other = new QLineEdit(&view); + other->show(); + QApplication::processEvents(); + QTRY_VERIFY(other->isVisible()); + other->setFocus(); + QTRY_VERIFY(other->hasFocus()); + qApp->processEvents(); + QTRY_COMPARE(proxy->hasFocus(), false); + QVERIFY(proxy->focusOut); + QCOMPARE(widget->hasFocus(), false); + } else { + { + /* + ### Test doesn't make sense + QFocusEvent focusEvent(QEvent::FocusOut); + proxy->call_focusOutEvent(&focusEvent); + QCOMPARE(focusEvent.isAccepted(), hasWidget); + qApp->processEvents(); + QCOMPARE(proxy->paintCount, hasWidget ? 1 : 0); + */ + } + { + /* + ### Test doesn't make sense + proxy->setFlag(QGraphicsItem::ItemIsFocusable, false); + QFocusEvent focusEvent(QEvent::FocusOut); + proxy->call_focusOutEvent(&focusEvent); + QCOMPARE(focusEvent.isAccepted(), hasWidget); + qApp->processEvents(); + QCOMPARE(proxy->paintCount, 0); + */ + } + } +} + +class EventLogger : public QWidget +{ +public: + EventLogger() : QWidget(), enterCount(0), leaveCount(0), moveCount(0), + hoverEnter(0), hoverLeave(0), hoverMove(0) + { + installEventFilter(this); + } + + void enterEvent(QEvent *event) + { + enterCount++; + QWidget::enterEvent(event); + } + + void leaveEvent(QEvent *event ) + { + leaveCount++; + QWidget::leaveEvent(event); + } + + void mouseMoveEvent(QMouseEvent *event) + { + event->setAccepted(true); + moveCount++; + QWidget::mouseMoveEvent(event); + } + + int enterCount; + int leaveCount; + int moveCount; + + int hoverEnter; + int hoverLeave; + int hoverMove; +protected: + bool eventFilter(QObject *object, QEvent *event) + { + switch (event->type()) { + case QEvent::HoverEnter: + hoverEnter++; + break; + case QEvent::HoverLeave: + hoverLeave++; + break; + case QEvent::HoverMove: + hoverMove++; + break; + default: + break; + } + return QWidget::eventFilter(object, event); + } +}; + +// protected void hoverEnterEvent(QGraphicsSceneHoverEvent* event) +#if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR)) +void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::addColumn<bool>("hoverEnabled"); + QTest::newRow("widget, no hover") << true << false; + QTest::newRow("no widget, no hover") << false << false; + QTest::newRow("widget, hover") << true << true; + QTest::newRow("no widget, hover") << false << true; +} + +void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent() +{ + QFETCH(bool, hasWidget); + QFETCH(bool, hoverEnabled); + + // proxy should translate this into events that the widget would expect + + QGraphicsScene scene; + QGraphicsView view(&scene); + //do not let the window manager move the window while we are moving the mouse on it + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + EventLogger *widget = new EventLogger; + widget->resize(50, 50); + widget->setAttribute(Qt::WA_Hover, hoverEnabled); + widget->setMouseTracking(true); + view.resize(100, 100); + if (hasWidget) + proxy->setWidget(widget); + proxy->setPos(50, 0); + scene.addItem(proxy); + QTest::qWait(30); + QTest::mouseMove(&view, QPoint(10, 10)); + QTest::qWait(30); + + // in + QTest::mouseMove(&view, QPoint(50, 50)); + QTRY_COMPARE(widget->testAttribute(Qt::WA_UnderMouse), hasWidget ? true : false); + // ### this attribute isn't supported + QCOMPARE(widget->enterCount, hasWidget ? 1 : 0); + QCOMPARE(widget->hoverEnter, (hasWidget && hoverEnabled) ? 1 : 0); + // does not work on all platforms + //QCOMPARE(widget->moveCount, 0); + + // out + QTest::mouseMove(&view, QPoint(10, 10)); + // QTRY_COMPARE(widget->testAttribute(Qt::WA_UnderMouse), false); + // ### this attribute isn't supported + QTRY_COMPARE(widget->leaveCount, hasWidget ? 1 : 0); + QTRY_COMPARE(widget->hoverLeave, (hasWidget && hoverEnabled) ? 1 : 0); + // does not work on all platforms + //QCOMPARE(widget->moveCount, 0); + + if (!hasWidget) + delete widget; +} +#endif + +void tst_QGraphicsProxyWidget::hoverMoveEvent_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::addColumn<bool>("hoverEnabled"); + QTest::addColumn<bool>("mouseTracking"); + QTest::addColumn<bool>("mouseDown"); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + for (int k = 0; k < 2; ++k) { + for (int l = 0; l < 2; ++l) { + bool hasWidget = (i == 0); + bool hoverEnabled = (j == 0); + bool mouseTracking = (k == 0); + bool mouseDown = (l == 0); + QString name = QString("hasWidget:%1, hover:%2, mouseTracking:%3, mouseDown: %4").arg(hasWidget).arg(hoverEnabled).arg(mouseTracking).arg(mouseDown); + QTest::newRow(name.toLatin1()) << hasWidget << hoverEnabled << mouseTracking << mouseDown; + } + } + } + } +} + +// protected void hoverMoveEvent(QGraphicsSceneHoverEvent* event) +void tst_QGraphicsProxyWidget::hoverMoveEvent() +{ + QFETCH(bool, hasWidget); + QFETCH(bool, hoverEnabled); + QFETCH(bool, mouseTracking); + QFETCH(bool, mouseDown); + + QSKIP("Ambiguous test...", SkipAll); + + // proxy should translate the move events to what the widget would expect + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + EventLogger *widget = new EventLogger; + widget->resize(50, 50); + widget->setAttribute(Qt::WA_Hover, hoverEnabled); + if (mouseTracking) + widget->setMouseTracking(true); + view.resize(100, 100); + if (hasWidget) + proxy->setWidget(widget); + proxy->setPos(50, 0); + scene.addItem(proxy); + + // in + QTest::mouseMove(&view, QPoint(50, 50)); + QTest::qWait(12); + + if (mouseDown) + QTest::mousePress(view.viewport(), Qt::LeftButton); + + // move a little bit + QTest::mouseMove(&view, QPoint(60, 60)); + QTRY_COMPARE(widget->hoverEnter, (hasWidget && hoverEnabled) ? 1 : 0); + QCOMPARE(widget->moveCount, (hasWidget && mouseTracking) || (hasWidget && mouseDown) ? 1 : 0); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::keyPressEvent_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void keyPressEvent(QKeyEvent* event) +void tst_QGraphicsProxyWidget::keyPressEvent() +{ + QFETCH(bool, hasWidget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + view.viewport()->setFocus(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + + QLineEdit *widget = new QLineEdit; + widget->resize(50, 50); + view.resize(100, 100); + if (hasWidget) { + proxy->setWidget(widget); + proxy->show(); + } + proxy->setPos(50, 0); + scene.addItem(proxy); + proxy->setFocus(); + + QTest::keyPress(view.viewport(), 'x'); + + QTRY_COMPARE(widget->text(), hasWidget ? QString("x") : QString()); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::keyReleaseEvent_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void keyReleaseEvent(QKeyEvent* event) +void tst_QGraphicsProxyWidget::keyReleaseEvent() +{ + QFETCH(bool, hasWidget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + QPushButton *widget = new QPushButton; + QSignalSpy spy(widget, SIGNAL(clicked())); + widget->resize(50, 50); + view.resize(100, 100); + if (hasWidget) { + proxy->setWidget(widget); + proxy->show(); + } + proxy->setPos(50, 0); + scene.addItem(proxy); + proxy->setFocus(); + + QTest::keyPress(view.viewport(), Qt::Key_Space); + QTRY_COMPARE(spy.count(), 0); + QTest::keyRelease(view.viewport(), Qt::Key_Space); + QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::mouseDoubleClickEvent_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +void tst_QGraphicsProxyWidget::mouseDoubleClickEvent() +{ + QFETCH(bool, hasWidget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + QLineEdit *widget = new QLineEdit; + widget->setText("foo"); + widget->resize(50, 50); + view.resize(100, 100); + if (hasWidget) { + proxy->setWidget(widget); + proxy->show(); + } + proxy->setPos(50, 0); + scene.addItem(proxy); + proxy->setFocus(); + + QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + + QTRY_COMPARE(widget->selectedText(), hasWidget ? QString("foo") : QString()); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::mousePressReleaseEvent_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void mousePressEvent(QGraphicsSceneMouseEvent* event) +void tst_QGraphicsProxyWidget::mousePressReleaseEvent() +{ + QFETCH(bool, hasWidget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + QPushButton *widget = new QPushButton; + QSignalSpy spy(widget, SIGNAL(clicked())); + widget->resize(50, 50); + view.resize(100, 100); + if (hasWidget) { + proxy->setWidget(widget); + proxy->show(); + } + proxy->setPos(50, 0); + scene.addItem(proxy); + proxy->setFocus(); + + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + QTRY_COMPARE(spy.count(), 0); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::resizeEvent_data() +{ + QTest::addColumn<bool>("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void resizeEvent(QGraphicsSceneResizeEvent* event) +void tst_QGraphicsProxyWidget::resizeEvent() +{ + QFETCH(bool, hasWidget); + + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + if (hasWidget) + proxy.setWidget(widget); + + QSize newSize(100, 100); + QGraphicsSceneResizeEvent event; + event.setOldSize(QSize(10, 10)); + event.setNewSize(newSize); + proxy.call_resizeEvent(&event); + if (hasWidget) + QCOMPARE(widget->size(), newSize); + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::paintEvent() +{ + //we test that calling update on a widget inside a QGraphicsView is triggering a repaint + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.isActiveWindow()); + + SubQGraphicsProxyWidget proxy; + + QWidget *w = new QWidget; + //showing the widget here seems to create a bug in Graphics View + //this bug prevents the widget from being updated + + w->show(); + QTest::qWaitForWindowShown(w); + QApplication::processEvents(); + QTest::qWait(30); + proxy.setWidget(w); + scene.addItem(&proxy); + + //make sure we flush all the paint events + QTRY_VERIFY(proxy.paintCount > 1); + QTest::qWait(30); + proxy.paintCount = 0; + + w->update(); + QTRY_COMPARE(proxy.paintCount, 1); //the widget should have been painted now +} + + +void tst_QGraphicsProxyWidget::wheelEvent() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + WheelWidget *wheelWidget = new WheelWidget(); + wheelWidget->setFixedSize(400, 400); + + QGraphicsProxyWidget *proxy = scene.addWidget(wheelWidget); + proxy->setVisible(true); + + QGraphicsSceneWheelEvent event(QEvent::GraphicsSceneWheel); + event.setScenePos(QPoint(50, 50)); + event.setAccepted(false); + wheelWidget->wheelEventCalled = false; + + QApplication::sendEvent(&scene, &event); + + QVERIFY(event.isAccepted()); + QVERIFY(wheelWidget->wheelEventCalled); +} + +Q_DECLARE_METATYPE(Qt::SizeHint) +void tst_QGraphicsProxyWidget::sizeHint_data() +{ + QTest::addColumn<Qt::SizeHint>("which"); + QTest::addColumn<QSizeF>("constraint"); + QTest::addColumn<QSizeF>("sizeHint"); + QTest::addColumn<bool>("hasWidget"); + + for (int i = 0; i < 2; ++i) { + bool hasWidget = (i == 0); + // ### What should these do? + QTest::newRow("min") << Qt::MinimumSize << QSizeF() << QSizeF() << hasWidget; + QTest::newRow("pre") << Qt::PreferredSize << QSizeF() << QSizeF() << hasWidget; + QTest::newRow("max") << Qt::MaximumSize << QSizeF() << QSizeF() << hasWidget; + QTest::newRow("mindes") << Qt::MinimumDescent << QSizeF() << QSizeF() << hasWidget; + QTest::newRow("nsize") << Qt::NSizeHints << QSizeF() << QSizeF() << hasWidget; + } +} + +// protected QSizeF sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const +void tst_QGraphicsProxyWidget::sizeHint() +{ + QFETCH(Qt::SizeHint, which); + QFETCH(QSizeF, constraint); + QFETCH(QSizeF, sizeHint); + QFETCH(bool, hasWidget); + QSKIP("Broken test", SkipAll); + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + if (hasWidget) + proxy.setWidget(widget); + QCOMPARE(proxy.call_sizeHint(which, constraint), sizeHint); + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::sizePolicy() +{ + for (int p = 0; p < 2; ++p) { + bool hasWidget = (p == 0 ? true : false); + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + QSizePolicy proxyPol(QSizePolicy::Maximum, QSizePolicy::Expanding); + proxy.setSizePolicy(proxyPol); + QSizePolicy widgetPol(QSizePolicy::Fixed, QSizePolicy::Minimum); + widget->setSizePolicy(widgetPol); + + QCOMPARE(proxy.sizePolicy(), proxyPol); + QCOMPARE(widget->sizePolicy(), widgetPol); + if (hasWidget) { + proxy.setWidget(widget); + QCOMPARE(proxy.sizePolicy(), widgetPol); + } else { + QCOMPARE(proxy.sizePolicy(), proxyPol); + } + QCOMPARE(widget->sizePolicy(), widgetPol); + + proxy.setSizePolicy(widgetPol); + widget->setSizePolicy(proxyPol); + if (hasWidget) + QCOMPARE(proxy.sizePolicy(), proxyPol); + else + QCOMPARE(proxy.sizePolicy(), widgetPol); + } +} + +void tst_QGraphicsProxyWidget::minimumSize() +{ + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + QSize minSize(50, 50); + widget->setMinimumSize(minSize); + proxy.resize(30, 30); + widget->resize(30,30); + QCOMPARE(proxy.size(), QSizeF(30, 30)); + proxy.setWidget(widget); + QCOMPARE(proxy.size().toSize(), minSize); + QCOMPARE(proxy.minimumSize().toSize(), minSize); + widget->setMinimumSize(70, 70); + QCOMPARE(proxy.minimumSize(), QSizeF(70, 70)); + QCOMPARE(proxy.size(), QSizeF(70, 70)); +} + +void tst_QGraphicsProxyWidget::maximumSize() +{ + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + QSize maxSize(150, 150); + widget->setMaximumSize(maxSize); + proxy.resize(200, 200); + widget->resize(200,200); + QCOMPARE(proxy.size(), QSizeF(200, 200)); + proxy.setWidget(widget); + QCOMPARE(proxy.size().toSize(), maxSize); + QCOMPARE(proxy.maximumSize().toSize(), maxSize); + widget->setMaximumSize(70, 70); + QCOMPARE(proxy.maximumSize(), QSizeF(70, 70)); + QCOMPARE(proxy.size(), QSizeF(70, 70)); +} + +class View : public QGraphicsView +{ +public: + View(QGraphicsScene *scene, QWidget *parent = 0) + : QGraphicsView(scene, parent), npaints(0) + { } + QRegion paintEventRegion; + int npaints; +protected: + void paintEvent(QPaintEvent *event) + { + ++npaints; + paintEventRegion += event->region(); + QGraphicsView::paintEvent(event); + } +}; + +class ScrollWidget : public QWidget +{ + Q_OBJECT +public: + ScrollWidget() : npaints(0) + { + resize(200, 200); + } + QRegion paintEventRegion; + int npaints; + +public slots: + void updateScroll() + { + update(0, 0, 200, 10); + scroll(0, 10, QRect(0, 0, 100, 20)); + } + +protected: + void paintEvent(QPaintEvent *event) + { + ++npaints; + paintEventRegion += event->region(); + QPainter painter(this); + painter.fillRect(event->rect(), Qt::blue); + } +}; + +void tst_QGraphicsProxyWidget::scrollUpdate() +{ + ScrollWidget *widget = new ScrollWidget; + + QGraphicsScene scene; + scene.addWidget(widget); + + View view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.npaints >= 1); + QTest::qWait(20); + widget->paintEventRegion = QRegion(); + widget->npaints = 0; + view.paintEventRegion = QRegion(); + view.npaints = 0; + QTimer::singleShot(0, widget, SLOT(updateScroll())); + QTRY_COMPARE(view.npaints, 2); + // QRect(0, 0, 200, 12) is the first update, expanded (-2, -2, 2, 2) + // QRect(0, 12, 102, 10) is the scroll update, expanded (-2, -2, 2, 2), + // intersected with the above update. + QCOMPARE(view.paintEventRegion.rects(), + QVector<QRect>() << QRect(0, 0, 200, 12) << QRect(0, 12, 102, 10)); + QCOMPARE(widget->npaints, 2); + QCOMPARE(widget->paintEventRegion.rects(), + QVector<QRect>() << QRect(0, 0, 200, 12) << QRect(0, 12, 102, 10)); +} + +void tst_QGraphicsProxyWidget::setWidget_simple() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + + QVERIFY(lineEdit->testAttribute(Qt::WA_DontShowOnScreen)); + // Size hints + // ### size hints are tested in a different test + // QCOMPARE(proxy.effectiveSizeHint(Qt::MinimumSize).toSize(), lineEdit->minimumSizeHint()); + // QCOMPARE(proxy.effectiveSizeHint(Qt::MaximumSize).toSize(), lineEdit->maximumSize()); + // QCOMPARE(proxy.effectiveSizeHint(Qt::PreferredSize).toSize(), lineEdit->sizeHint()); + QCOMPARE(proxy.size().toSize(), lineEdit->minimumSizeHint().expandedTo(lineEdit->size())); + QRect rect = lineEdit->rect(); + rect.setSize(rect.size().expandedTo(lineEdit->minimumSizeHint())); + QCOMPARE(proxy.rect().toRect(), rect); + + // Properties + // QCOMPARE(proxy.focusPolicy(), lineEdit->focusPolicy()); + // QCOMPARE(proxy.palette(), lineEdit->palette()); +#ifndef QT_NO_CURSOR + QCOMPARE(proxy.cursor().shape(), lineEdit->cursor().shape()); +#endif + QCOMPARE(proxy.layoutDirection(), lineEdit->layoutDirection()); + QCOMPARE(proxy.style(), lineEdit->style()); + QCOMPARE(proxy.font(), lineEdit->font()); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); +} + +void tst_QGraphicsProxyWidget::setWidget_ownership() +{ + QPointer<QLineEdit> lineEdit = new QLineEdit; + QPointer<QLineEdit> lineEdit2 = new QLineEdit; + QVERIFY(lineEdit); + { + // Create a proxy and transfer ownership to it + QGraphicsProxyWidget proxy; + proxy.setWidget(lineEdit); + QCOMPARE(proxy.widget(), (QWidget *)lineEdit); + + // Remove the widget without destroying it. + proxy.setWidget(0); + QVERIFY(!proxy.widget()); + QVERIFY(lineEdit); + + // Assign the widget again and switch to another widget. + proxy.setWidget(lineEdit); + proxy.setWidget(lineEdit2); + QCOMPARE(proxy.widget(), (QWidget *)lineEdit2); + + // Assign the first widget, and destroy the proxy. + proxy.setWidget(lineEdit); + } + QVERIFY(!lineEdit); + QVERIFY(lineEdit2); + + QGraphicsScene scene; + QPointer<QGraphicsProxyWidget> proxy = scene.addWidget(lineEdit2); + + delete lineEdit2; + QVERIFY(!proxy); +} + +void tst_QGraphicsProxyWidget::resize_simple_data() +{ + QTest::addColumn<QSizeF>("size"); + + QTest::newRow("200, 200") << QSizeF(200, 200); +#if !defined(QT_ARCH_ARM) && !defined(Q_OS_WINCE) + QTest::newRow("1000, 1000") << QSizeF(1000, 1000); + // Since 4.5, 10000x10000 runs out of memory. + // QTest::newRow("10000, 10000") << QSizeF(10000, 10000); +#endif +} + +void tst_QGraphicsProxyWidget::resize_simple() +{ + QFETCH(QSizeF, size); + + QGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + widget->setGeometry(0, 0, (int)size.width(), (int)size.height()); + proxy.setWidget(widget); + widget->show(); + QCOMPARE(widget->pos(), QPoint()); + + // The proxy resizes itself, the line edit follows + proxy.resize(size); + QCOMPARE(proxy.size(), size); + QCOMPARE(proxy.size().toSize(), widget->size()); + + // The line edit resizes itself, the proxy follows (no loopback/live lock) + QSize doubleSize = size.toSize() * 2; + widget->resize(doubleSize); + QCOMPARE(widget->size(), doubleSize); + QCOMPARE(widget->size(), proxy.size().toSize()); +} + +void tst_QGraphicsProxyWidget::symmetricMove() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + lineEdit->show(); + + proxy.setPos(10, 10); + QCOMPARE(proxy.pos(), QPointF(10, 10)); + QCOMPARE(lineEdit->pos(), QPoint(10, 10)); + + lineEdit->move(5, 5); + QCOMPARE(proxy.pos(), QPointF(5, 5)); + QCOMPARE(lineEdit->pos(), QPoint(5, 5)); +} + +void tst_QGraphicsProxyWidget::symmetricResize() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + lineEdit->show(); + + proxy.resize(256, 256); + QCOMPARE(proxy.size(), QSizeF(256, 256)); + QCOMPARE(lineEdit->size(), QSize(256, 256)); + + lineEdit->resize(512, 512); + QCOMPARE(proxy.size(), QSizeF(512, 512)); + QCOMPARE(lineEdit->size(), QSize(512, 512)); +} + +void tst_QGraphicsProxyWidget::symmetricVisible() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + lineEdit->show(); + + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + + proxy.hide(); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + proxy.show(); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + lineEdit->hide(); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + lineEdit->show(); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + } + +void tst_QGraphicsProxyWidget::symmetricEnabled() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + lineEdit->show(); + + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + proxy.setEnabled(false); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + proxy.setEnabled(true); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + lineEdit->setEnabled(false); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + lineEdit->setEnabled(true); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); +} + +void tst_QGraphicsProxyWidget::tabFocus_simpleWidget() +{ + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + editProxy->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWaitForWindowShown(&window); + + leftDial->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit); + + // Tab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(!leftDial->hasFocus()); + QTRY_VERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(editProxy->hasFocus()); + QVERIFY(edit->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0); + + // Tab into right dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QTRY_VERIFY(rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + + // Backtab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QTRY_VERIFY(scene.hasFocus()); + QVERIFY(editProxy->hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(!rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + + // Backtab into left dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QTRY_VERIFY(leftDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2); + + delete view; +} + +void tst_QGraphicsProxyWidget::tabFocus_simpleTwoWidgets() +{ + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QLineEdit *edit2 = new QLineEdit; + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + editProxy->show(); + QGraphicsProxyWidget *editProxy2 = scene.addWidget(edit2); + editProxy2->show(); + editProxy2->setPos(0, editProxy->rect().height() * 1.1); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWaitForWindowShown(&window); + + leftDial->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit); + EventSpy eventSpy2(edit2); + + // Tab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(editProxy->hasFocus()); + QVERIFY(edit->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0); + + // Tab into second line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(editProxy2->hasFocus()); + QVERIFY(edit2->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0); + + // Tab into right dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(!editProxy2->hasFocus()); + QVERIFY(!edit2->hasFocus()); + QVERIFY(rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(editProxy2->hasFocus()); + QVERIFY(edit2->hasFocus()); + QVERIFY(!rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 1 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(editProxy->hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(!editProxy2->hasFocus()); + QVERIFY(!edit2->hasFocus()); + QVERIFY(!rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 2); + + // Backtab into left dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(leftDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2); + + delete view; +} + +void tst_QGraphicsProxyWidget::tabFocus_complexWidget() +{ + QGraphicsScene scene; + + QLineEdit *edit1 = new QLineEdit; + edit1->setText("QLineEdit 1"); + QLineEdit *edit2 = new QLineEdit; + edit2->setText("QLineEdit 2"); + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addWidget(edit1); + vlayout->addWidget(edit2); + + QGroupBox *box = new QGroupBox("QGroupBox"); + box->setCheckable(true); + box->setChecked(true); + box->setLayout(vlayout); + + QGraphicsProxyWidget *proxy = scene.addWidget(box); + proxy->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWaitForWindowShown(&window); + + leftDial->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit1); + EventSpy eventSpy2(edit2); + EventSpy eventSpyBox(box); + + // Tab into group box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(proxy->hasFocus()); + QVERIFY(box->hasFocus()); + + // Tab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + edit1->hasFocus(); + QVERIFY(!box->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0); + + // Tab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + edit2->hasFocus(); + QVERIFY(!edit1->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0); + + // Tab into right dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!edit2->hasFocus()); + rightDial->hasFocus(); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!rightDial->hasFocus()); + edit2->hasFocus(); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 1 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!edit2->hasFocus()); + edit1->hasFocus(); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + + // Backtab into line box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!edit1->hasFocus()); + box->hasFocus(); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2); + + // Backtab into left dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!box->hasFocus()); + leftDial->hasFocus(); + + delete view; +} + +void tst_QGraphicsProxyWidget::tabFocus_complexTwoWidgets() +{ + // ### add event spies to this test. + QGraphicsScene scene; + + QLineEdit *edit1 = new QLineEdit; + edit1->setText("QLineEdit 1"); + QLineEdit *edit2 = new QLineEdit; + edit2->setText("QLineEdit 2"); + QFontComboBox *fontComboBox = new QFontComboBox; + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addWidget(edit1); + vlayout->addWidget(fontComboBox); + vlayout->addWidget(edit2); + + QGroupBox *box = new QGroupBox("QGroupBox"); + box->setCheckable(true); + box->setChecked(true); + box->setLayout(vlayout); + + QLineEdit *edit1_2 = new QLineEdit; + edit1_2->setText("QLineEdit 1_2"); + QLineEdit *edit2_2 = new QLineEdit; + edit2_2->setText("QLineEdit 2_2"); + QFontComboBox *fontComboBox2 = new QFontComboBox; + vlayout = new QVBoxLayout; + vlayout->addWidget(edit1_2); + vlayout->addWidget(fontComboBox2); + vlayout->addWidget(edit2_2); + + QGroupBox *box_2 = new QGroupBox("QGroupBox 2"); + box_2->setCheckable(true); + box_2->setChecked(true); + box_2->setLayout(vlayout); + + QGraphicsProxyWidget *proxy = scene.addWidget(box); + proxy->show(); + + QGraphicsProxyWidget *proxy_2 = scene.addWidget(box_2); + proxy_2->setPos(proxy->boundingRect().width() * 1.2, 0); + proxy_2->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + view->setRenderHint(QPainter::Antialiasing); + view->rotate(45); + view->scale(0.5, 0.5); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWaitForWindowShown(&window); + QTRY_COMPARE(QApplication::activeWindow(), &window); + + leftDial->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit1); + EventSpy eventSpy2(edit2); + EventSpy eventSpy3(fontComboBox); + EventSpy eventSpy1_2(edit1_2); + EventSpy eventSpy2_2(edit2_2); + EventSpy eventSpy2_3(fontComboBox2); + EventSpy eventSpyBox(box); + + // Tab into group box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(proxy->hasFocus()); + QVERIFY(box->hasFocus()); + + // Tab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + edit1->hasFocus(); + QVERIFY(!box->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0); + + // Tab to the font combobox + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + fontComboBox->hasFocus(); + QVERIFY(!edit2->hasFocus()); + QCOMPARE(eventSpy3.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy3.counts[QEvent::FocusOut], 0); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + + // Tab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + edit2->hasFocus(); + QVERIFY(!edit1->hasFocus()); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0); + QCOMPARE(eventSpy3.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + + // Tab into right box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!edit2->hasFocus()); + box_2->hasFocus(); + + // Tab into right top line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!box_2->hasFocus()); + edit1_2->hasFocus(); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 0); + + // Tab into right font combobox + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!edit1_2->hasFocus()); + fontComboBox2->hasFocus(); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_3.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2_3.counts[QEvent::FocusOut], 0); + + // Tab into right bottom line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!edit1_2->hasFocus()); + edit2_2->hasFocus(); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_3.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2_3.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 0); + + // Tab into right dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QVERIFY(!edit2->hasFocus()); + rightDial->hasFocus(); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!rightDial->hasFocus()); + edit2_2->hasFocus(); + + // Backtab into the right font combobox + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!edit2_2->hasFocus()); + fontComboBox2->hasFocus(); + + // Backtab into line edit 1 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!edit2_2->hasFocus()); + edit1_2->hasFocus(); + + // Backtab into line box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!edit1_2->hasFocus()); + box_2->hasFocus(); + + // Backtab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!rightDial->hasFocus()); + edit2->hasFocus(); + + // Backtab into the font combobox + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!edit2->hasFocus()); + fontComboBox->hasFocus(); + + // Backtab into line edit 1 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!fontComboBox->hasFocus()); + edit1->hasFocus(); + + // Backtab into line box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!edit1->hasFocus()); + box->hasFocus(); + + // Backtab into left dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QVERIFY(!box->hasFocus()); + leftDial->hasFocus(); + + delete view; +} + +void tst_QGraphicsProxyWidget::setFocus_simpleWidget() +{ + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + editProxy->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWaitForWindowShown(&window); + QTRY_COMPARE(QApplication::activeWindow(), &window); + + leftDial->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit); + + // Calling setFocus for the line edit when the view doesn't have input + // focus does not steal focus from the dial. The edit, proxy and scene + // have focus but only in their own little space. + edit->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(leftDial->hasFocus()); + + // Clearing focus is a local operation. + edit->clearFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(!view->hasFocus()); + QVERIFY(leftDial->hasFocus()); + + view->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(!edit->hasFocus()); + + scene.clearFocus(); + QVERIFY(!scene.hasFocus()); + + edit->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(editProxy->hasFocus()); + + // Symmetry + editProxy->clearFocus(); + QVERIFY(!edit->hasFocus()); + + delete view; +} + +void tst_QGraphicsProxyWidget::setFocus_simpleTwoWidgets() +{ + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + editProxy->show(); + QLineEdit *edit2 = new QLineEdit; + QGraphicsProxyWidget *edit2Proxy = scene.addWidget(edit2); + edit2Proxy->show(); + edit2Proxy->setPos(editProxy->boundingRect().width() * 1.01, 0); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWaitForWindowShown(&window); + QTRY_COMPARE(QApplication::activeWindow(), &window); + + leftDial->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit); + + view->setFocus(); + QVERIFY(!edit->hasFocus()); + + edit->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(editProxy->hasFocus()); + + edit2->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(edit2->hasFocus()); + QVERIFY(edit2Proxy->hasFocus()); + + delete view; +} + +void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets() +{ + // ### add event spies to this test. + QGraphicsScene scene; + + QLineEdit *edit1 = new QLineEdit; + edit1->setText("QLineEdit 1"); + QLineEdit *edit2 = new QLineEdit; + edit2->setText("QLineEdit 2"); + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addWidget(edit1); + vlayout->addWidget(edit2); + + QGroupBox *box = new QGroupBox("QGroupBox"); + box->setCheckable(true); + box->setChecked(true); + box->setLayout(vlayout); + + QLineEdit *edit1_2 = new QLineEdit; + edit1_2->setText("QLineEdit 1_2"); + QLineEdit *edit2_2 = new QLineEdit; + edit2_2->setText("QLineEdit 2_2"); + vlayout = new QVBoxLayout; + vlayout->addWidget(edit1_2); + vlayout->addWidget(edit2_2); + + QGroupBox *box_2 = new QGroupBox("QGroupBox 2"); + box_2->setCheckable(true); + box_2->setChecked(true); + box_2->setLayout(vlayout); + + QGraphicsProxyWidget *proxy = scene.addWidget(box); + proxy->show(); + + QGraphicsProxyWidget *proxy_2 = scene.addWidget(box_2); + proxy_2->setPos(proxy->boundingRect().width() * 1.2, 0); + proxy_2->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWaitForWindowShown(&window); + QTRY_COMPARE(QApplication::activeWindow(), &window); + + leftDial->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit1); + EventSpy eventSpy2(edit2); + EventSpy eventSpyBox(box); + EventSpy eventSpy_2(edit1_2); + EventSpy eventSpy2_2(edit2_2); + EventSpy eventSpyBox_2(box_2); + + view->setFocus(); + + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 0); + + edit1->setFocus(); + QApplication::processEvents(); + QVERIFY(scene.hasFocus()); + QVERIFY(edit1->hasFocus()); + QVERIFY(!box->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 0); + + edit2_2->setFocus(); + QApplication::processEvents(); + QVERIFY(!edit1->hasFocus()); + QVERIFY(!box_2->hasFocus()); + QVERIFY(edit2_2->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 0); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0); + + box->setFocus(); + QApplication::processEvents(); + QVERIFY(!edit2_2->hasFocus()); + QVERIFY(!edit1->hasFocus()); + QVERIFY(box->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 0); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0); + + edit2_2->setFocus(); + QApplication::processEvents(); + QVERIFY(edit2_2->hasFocus()); + QVERIFY(!edit1->hasFocus()); + QVERIFY(!box->hasFocus()); + QVERIFY(!box_2->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0); + + delete view; +} + +void tst_QGraphicsProxyWidget::popup_basic() +{ + // ProxyWidget should automatically create proxy's when the widget creates a child + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsView view(scene); + view.setAlignment(Qt::AlignLeft | Qt::AlignTop); + view.setGeometry(0, 100, 480, 500); + view.show(); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QComboBox *box = new QComboBox; + box->setGeometry(0, 0, 320, 40); + box->addItems(QStringList() << "monday" << "tuesday" << "wednesday" + << "thursday" << "saturday" << "sunday"); + QCOMPARE(proxy->childItems().count(), 0); + proxy->setWidget(box); + proxy->show(); + scene->addItem(proxy); + + QCOMPARE(box->pos(), QPoint()); + QCOMPARE(proxy->pos(), QPointF()); + + QTest::qWaitForWindowShown(&view); + QTest::qWait(125); + QApplication::processEvents(); + + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + + QTRY_COMPARE(box->pos(), QPoint()); + + QCOMPARE(proxy->childItems().count(), 1); + QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(proxy->childItems())[0]; + QVERIFY(child->isWidget()); + QVERIFY(child->widget()); + QStyleOptionComboBox opt; + opt.initFrom(box); + opt.editable = box->isEditable(); + if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt)) + QSKIP("Does not work due to SH_Combobox_Popup", SkipAll); + QCOMPARE(child->widget()->parent(), static_cast<QObject*>(box)); + + QTRY_COMPARE(proxy->pos(), QPointF(box->pos())); + QCOMPARE(child->x(), qreal(box->x())); + QCOMPARE(child->y(), qreal(box->rect().bottom())); +#ifndef Q_OS_WIN + // The popup's coordinates on Windows are in global space, + // regardless. + QCOMPARE(child->widget()->x(), box->x()); + QCOMPARE(child->widget()->y(), box->rect().bottom()); + QCOMPARE(child->geometry().toRect(), child->widget()->geometry()); +#endif + QTest::qWait(12); +} + +void tst_QGraphicsProxyWidget::popup_subwidget() +{ + QGroupBox *groupBox = new QGroupBox; + groupBox->setTitle("GroupBox"); + groupBox->setCheckable(true); + + QComboBox *box = new QComboBox; + box->addItems(QStringList() << "monday" << "tuesday" << "wednesday" + << "thursday" << "saturday" << "sunday"); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(new QLineEdit("QLineEdit")); + layout->addWidget(box); + layout->addWidget(new QLineEdit("QLineEdit")); + groupBox->setLayout(layout); + + QGraphicsScene scene; + QGraphicsProxyWidget *groupBoxProxy = scene.addWidget(groupBox); + groupBox->show(); + groupBox->move(10000, 10000); + QCOMPARE(groupBox->pos(), QPoint(10000, 10000)); + QCOMPARE(groupBoxProxy->pos(), QPointF(10000, 10000)); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + box->showPopup(); + + QVERIFY(!groupBoxProxy->childItems().isEmpty()); + + QStyleOptionComboBox opt; + opt.initFrom(box); + opt.editable = box->isEditable(); + if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt)) + QSKIP("Does not work due to SH_Combobox_Popup", SkipAll); + QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(groupBoxProxy->childItems())[0]; + QWidget *popup = child->widget(); + QCOMPARE(popup->parent(), static_cast<QObject*>(box)); + QCOMPARE(popup->x(), box->mapToGlobal(QPoint()).x()); + QCOMPARE(popup->y(), QRect(box->mapToGlobal(QPoint()), box->size()).bottom()); + QCOMPARE(popup->size(), child->size().toSize()); +} + +#if !defined(QT_NO_CURSOR) && (!defined(Q_OS_WINCE) || defined(GWES_ICONCURS)) +void tst_QGraphicsProxyWidget::changingCursor_basic() +{ + // Confirm that mouse events are working properly by checking that + // when moving the mouse over a line edit it will change the cursor into the I + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QLineEdit *widget = new QLineEdit; + proxy->setWidget(widget); + proxy->show(); + scene.addItem(proxy); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + QTRY_VERIFY(view.isActiveWindow()); + + // in + QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + sendMouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + + // out + QTest::mouseMove(view.viewport(), QPoint(1, 1)); + sendMouseMove(view.viewport(), QPoint(1, 1)); + QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::ArrowCursor); +} +#endif + +void tst_QGraphicsProxyWidget::tooltip_basic() +{ + QString toolTip = "Qt rocks!"; + QString toolTip2 = "Qt rocks even more!"; + + QPushButton *button = new QPushButton("button"); + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget; + QGraphicsProxyWidgetPrivate *proxyd = static_cast<QGraphicsProxyWidgetPrivate *>(QGraphicsItemPrivate::get(proxy)); + proxy->setWidget(button); + proxyd->lastWidgetUnderMouse = button; // force widget under mouse + + QVERIFY(button->toolTip().isEmpty()); + QVERIFY(proxy->toolTip().isEmpty()); + // Check that setting the tooltip on the proxy also set it on the widget + proxy->setToolTip(toolTip); + QCOMPARE(proxy->toolTip(), toolTip); + QCOMPARE(button->toolTip(), toolTip); + // Check that setting the tooltip on the widget also set it on the proxy + button->setToolTip(toolTip2); + QCOMPARE(proxy->toolTip(), toolTip2); + QCOMPARE(button->toolTip(), toolTip2); + + QGraphicsScene scene; + scene.addItem(proxy); + + QGraphicsView view(&scene); + view.setFixedSize(200, 200); + view.show(); + QTest::qWaitForWindowShown(&view); + { + QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(), + view.viewport()->mapToGlobal(view.viewport()->rect().topLeft())); + QApplication::sendEvent(view.viewport(), &helpEvent); + QTest::qWait(350); + + bool foundView = false; + bool foundTipLabel = false; + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + if (widget == &view) + foundView = true; + if (widget->inherits("QTipLabel")) + foundTipLabel = true; + } + QVERIFY(foundView); + QVERIFY(!foundTipLabel); + } + + { + QHelpEvent helpEvent(QEvent::ToolTip, view.mapFromScene(proxy->boundingRect().center()), + view.viewport()->mapToGlobal(view.mapFromScene(proxy->boundingRect().center()))); + QApplication::sendEvent(view.viewport(), &helpEvent); + QTest::qWait(350); + + bool foundView = false; + bool foundTipLabel = false; + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + if (widget == &view) + foundView = true; + if (widget->inherits("QTipLabel")) + foundTipLabel = true; + } + QVERIFY(foundView); + QVERIFY(foundTipLabel); + } +} + +void tst_QGraphicsProxyWidget::childPos_data() +{ + QTest::addColumn<bool>("moveCombo"); + QTest::addColumn<QPoint>("comboPos"); + QTest::addColumn<QPointF>("proxyPos"); + QTest::addColumn<QPointF>("menuPos"); + + QTest::newRow("0") << true << QPoint() << QPointF() << QPointF(); + QTest::newRow("1") << true << QPoint(10, 0) << QPointF(10, 0) << QPointF(); + QTest::newRow("2") << true << QPoint(100, 0) << QPointF(100, 0) << QPointF(); + QTest::newRow("3") << true << QPoint(1000, 0) << QPointF(1000, 0) << QPointF(); + QTest::newRow("4") << true << QPoint(10000, 0) << QPointF(10000, 0) << QPointF(); + QTest::newRow("5") << true << QPoint(-10000, 0) << QPointF(-10000, 0) << QPointF(); + QTest::newRow("6") << true << QPoint(-1000, 0) << QPointF(-1000, 0) << QPointF(); + QTest::newRow("7") << true << QPoint(-100, 0) << QPointF(-100, 0) << QPointF(); + QTest::newRow("8") << true << QPoint(-10, 0) << QPointF(-10, 0) << QPointF(); + QTest::newRow("0-") << false << QPoint() << QPointF() << QPointF(); + QTest::newRow("1-") << false << QPoint(10, 0) << QPointF(10, 0) << QPointF(); + QTest::newRow("2-") << false << QPoint(100, 0) << QPointF(100, 0) << QPointF(); + QTest::newRow("3-") << false << QPoint(1000, 0) << QPointF(1000, 0) << QPointF(); + QTest::newRow("4-") << false << QPoint(10000, 0) << QPointF(10000, 0) << QPointF(); + QTest::newRow("5-") << false << QPoint(-10000, 0) << QPointF(-10000, 0) << QPointF(); + QTest::newRow("6-") << false << QPoint(-1000, 0) << QPointF(-1000, 0) << QPointF(); + QTest::newRow("7-") << false << QPoint(-100, 0) << QPointF(-100, 0) << QPointF(); + QTest::newRow("8-") << false << QPoint(-10, 0) << QPointF(-10, 0) << QPointF(); +} + +void tst_QGraphicsProxyWidget::childPos() +{ +#ifdef Q_OS_IRIX + QSKIP("This test is not reliable on IRIX.", SkipAll); +#endif + QFETCH(bool, moveCombo); + QFETCH(QPoint, comboPos); + QFETCH(QPointF, proxyPos); + QFETCH(QPointF, menuPos); + + QGraphicsScene scene; + + QComboBox *box = new QComboBox; + box->addItem("Item 1"); + box->addItem("Item 2"); + box->addItem("Item 3"); + box->addItem("Item 4"); + + if (moveCombo) + box->move(comboPos); + + QGraphicsProxyWidget *proxy = scene.addWidget(box); + proxy->show(); + QVERIFY(proxy->isVisible()); + QVERIFY(box->isVisible()); + + if (!moveCombo) + proxy->setPos(proxyPos); + + QCOMPARE(proxy->pos(), proxyPos); + QCOMPARE(box->pos(), comboPos); + + for (int i = 0; i < 2; ++i) { + box->showPopup(); + QApplication::processEvents(); + QApplication::processEvents(); + + QWidget *menu = 0; + foreach (QObject *child, box->children()) { + if ((menu = qobject_cast<QWidget *>(child))) + break; + } + QVERIFY(menu); + QVERIFY(menu->isVisible()); + QVERIFY(menu->testAttribute(Qt::WA_DontShowOnScreen)); + + QCOMPARE(proxy->childItems().size(), 1); + QGraphicsProxyWidget *proxyChild = 0; + foreach (QGraphicsItem *child, proxy->childItems()) { + if (child->isWidget() && (proxyChild = qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(child)))) + break; + } + QVERIFY(proxyChild); + QVERIFY(proxyChild->isVisible()); + qreal expectedXPosition = 0.0; +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) + // The Mac style wants the popup to show up at QPoint(4 - 11, 1). + // See QMacStyle::subControlRect SC_ComboBoxListBoxPopup. + if (qobject_cast<QMacStyle *>(QApplication::style())) + expectedXPosition = qreal(4 - 11); +#endif + QCOMPARE(proxyChild->pos().x(), expectedXPosition); + menu->hide(); + } +} + +void tst_QGraphicsProxyWidget::autoShow() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsProxyWidget *proxy1 = scene.addWidget(new QPushButton("Button1")); + + QPushButton *button2 = new QPushButton("Button2"); + button2->hide(); + QGraphicsProxyWidget *proxy2 = new QGraphicsProxyWidget(); + proxy2->setWidget(button2); + scene.addItem(proxy2); + + view.show(); + QApplication::processEvents(); + + QCOMPARE(proxy1->isVisible(), true); + QCOMPARE(proxy2->isVisible(), false); + +} + +Q_DECLARE_METATYPE(QList<QRectF>) +void tst_QGraphicsProxyWidget::windowOpacity() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QWidget *widget = new QWidget; + widget->resize(100, 100); + QGraphicsProxyWidget *proxy = scene.addWidget(widget); + proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache); + + QApplication::setActiveWindow(&view); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::sendPostedEvents(); + QTRY_VERIFY(view.isActiveWindow()); + + qRegisterMetaType<QList<QRectF> >("QList<QRectF>"); + QSignalSpy signalSpy(&scene, SIGNAL(changed(const QList<QRectF> &))); + + EventSpy eventSpy(widget); + QVERIFY(widget->isVisible()); + + widget->setWindowOpacity(0.5); + QApplication::processEvents(); + + // Make sure setWindowOpacity triggers an update on the scene, + // and not on the widget or the proxy itself. The entire proxy needs an update + // in case it has a window decoration. Update: QGraphicsItem::CacheMode is + // disabled on platforms without alpha channel support in QPixmap (e.g., + // X11 without XRender). + int paints = 0; +#ifdef Q_WS_X11 + paints = !X11->use_xrender; +#endif + QTRY_COMPARE(eventSpy.counts[QEvent::UpdateRequest], 0); + QTRY_COMPARE(eventSpy.counts[QEvent::Paint], paints); + + QCOMPARE(signalSpy.count(), 1); + const QList<QVariant> arguments = signalSpy.takeFirst(); + const QList<QRectF> updateRects = qvariant_cast<QList<QRectF> >(arguments.at(0)); + QCOMPARE(updateRects.size(), 1); + QCOMPARE(updateRects.at(0), proxy->sceneBoundingRect()); +} + +void tst_QGraphicsProxyWidget::stylePropagation() +{ + QPointer<QWindowsStyle> windowsStyle = new QWindowsStyle; + + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget proxy; + proxy.setWidget(edit); + + EventSpy editSpy(edit); + EventSpy proxySpy(&proxy); + + // Widget to proxy + QCOMPARE(proxy.style(), QApplication::style()); + edit->setStyle(windowsStyle); + QCOMPARE(editSpy.counts[QEvent::StyleChange], 1); + QCOMPARE(proxySpy.counts[QEvent::StyleChange], 1); + QCOMPARE(proxy.style(), (QStyle *)windowsStyle); + edit->setStyle(0); + QCOMPARE(editSpy.counts[QEvent::StyleChange], 2); + QCOMPARE(proxySpy.counts[QEvent::StyleChange], 2); + QCOMPARE(proxy.style(), QApplication::style()); + QCOMPARE(edit->style(), QApplication::style()); + QVERIFY(windowsStyle); // not deleted + + // Proxy to widget + proxy.setStyle(windowsStyle); + QCOMPARE(editSpy.counts[QEvent::StyleChange], 3); + QCOMPARE(proxySpy.counts[QEvent::StyleChange], 3); + QCOMPARE(edit->style(), (QStyle *)windowsStyle); + proxy.setStyle(0); + QCOMPARE(editSpy.counts[QEvent::StyleChange], 4); + QCOMPARE(proxySpy.counts[QEvent::StyleChange], 4); + QCOMPARE(proxy.style(), QApplication::style()); + QCOMPARE(edit->style(), QApplication::style()); + QVERIFY(windowsStyle); // not deleted + + delete windowsStyle; +} + +void tst_QGraphicsProxyWidget::palettePropagation() +{ + // Construct a palette with an unlikely setup + QPalette lineEditPalette = QApplication::palette("QLineEdit"); + QPalette palette = lineEditPalette; + palette.setBrush(QPalette::Text, Qt::red); + + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget proxy; + proxy.setWidget(edit); + + EventSpy editSpy(edit); + EventSpy proxySpy(&proxy); + + // Widget to proxy (no change) + QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + edit->setPalette(palette); + QCOMPARE(editSpy.counts[QEvent::PaletteChange], 1); + QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0); + QVERIFY(edit->testAttribute(Qt::WA_SetPalette)); + QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette)); + QCOMPARE(proxy.palette(), QPalette()); + edit->setPalette(QPalette()); + QCOMPARE(editSpy.counts[QEvent::PaletteChange], 2); + QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0); + QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette)); + QCOMPARE(proxy.palette(), QPalette()); + + // Proxy to widget + proxy.setPalette(palette); + QVERIFY(proxy.testAttribute(Qt::WA_SetPalette)); + QCOMPARE(editSpy.counts[QEvent::PaletteChange], 3); + QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 1); + QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + QCOMPARE(edit->palette(), palette); + QCOMPARE(edit->palette(), proxy.palette()); + QCOMPARE(edit->palette().color(QPalette::Text), QColor(Qt::red)); + proxy.setPalette(QPalette()); + QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette)); + QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + QCOMPARE(editSpy.counts[QEvent::PaletteChange], 4); + QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 2); + QCOMPARE(edit->palette(), lineEditPalette); +} + +void tst_QGraphicsProxyWidget::fontPropagation() +{ + // Construct a font with an unlikely setup + QGraphicsScene scene; + QFont lineEditFont = QApplication::font("QLineEdit"); + QFont font = lineEditFont; + font.setPointSize(43); + + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget proxy; + proxy.setWidget(edit); + + scene.addItem(&proxy); + EventSpy editSpy(edit); + EventSpy proxySpy(&proxy); + + // Widget to proxy (no change) + QVERIFY(!edit->testAttribute(Qt::WA_SetFont)); + edit->setFont(font); + QCOMPARE(editSpy.counts[QEvent::FontChange], 1); + QCOMPARE(proxySpy.counts[QEvent::FontChange], 0); + QVERIFY(edit->testAttribute(Qt::WA_SetFont)); + QVERIFY(!proxy.testAttribute(Qt::WA_SetFont)); + QCOMPARE(proxy.font(), lineEditFont); + edit->setFont(QFont()); + QCOMPARE(editSpy.counts[QEvent::FontChange], 2); + QCOMPARE(proxySpy.counts[QEvent::FontChange], 0); + QVERIFY(!edit->testAttribute(Qt::WA_SetFont)); + QVERIFY(!proxy.testAttribute(Qt::WA_SetFont)); + QCOMPARE(proxy.font(), lineEditFont); + + // Proxy to widget + proxy.setFont(font); + QApplication::processEvents(); // wait for QEvent::Polish + QVERIFY(proxy.testAttribute(Qt::WA_SetFont)); + QCOMPARE(editSpy.counts[QEvent::FontChange], 3); + QCOMPARE(proxySpy.counts[QEvent::FontChange], 1); + QVERIFY(!edit->testAttribute(Qt::WA_SetFont)); + QCOMPARE(edit->font(), font); + QCOMPARE(edit->font(), proxy.font()); + QCOMPARE(edit->font().pointSize(), 43); + proxy.setFont(QFont()); + QVERIFY(!proxy.testAttribute(Qt::WA_SetFont)); + QVERIFY(!edit->testAttribute(Qt::WA_SetFont)); + QCOMPARE(editSpy.counts[QEvent::FontChange], 4); + QCOMPARE(proxySpy.counts[QEvent::FontChange], 2); + QCOMPARE(edit->font(), lineEditFont); + + proxy.setFont(font); + QLineEdit *edit2 = new QLineEdit; + proxy.setWidget(edit2); + delete edit; + QCOMPARE(edit2->font().pointSize(), 43); +} + +class MainWidget : public QMainWindow +{ +Q_OBJECT +public: + MainWidget() : QMainWindow() { + view = new QGraphicsView(this); + scene = new QGraphicsScene(this); + item = new QGraphicsWidget(); + widget = new QGraphicsProxyWidget(item); + layout = new QGraphicsLinearLayout(item); + layout->addItem(widget); + item->setLayout(layout); + button = new QPushButton("Push me"); + widget->setWidget(button); + setCentralWidget(view); + scene->addItem(item); + view->setScene(scene); + scene->setFocusItem(item); + layout->activate(); + } + QGraphicsView* view; + QGraphicsScene* scene; + QGraphicsWidget * item; + QGraphicsLinearLayout * layout; + QGraphicsProxyWidget * widget; + QPushButton * button; +}; + +void tst_QGraphicsProxyWidget::dontCrashWhenDie() +{ + MainWidget *w = new MainWidget(); + w->show(); + QTest::qWaitForWindowShown(w); + QTest::qWait(100); + QTest::mouseMove(w->view->viewport(), w->view->mapFromScene(w->widget->mapToScene(w->widget->boundingRect().center()))); + delete w->item; + + QApplication::processEvents(); + delete w; +} + +void tst_QGraphicsProxyWidget::createProxyForChildWidget() +{ + QGraphicsScene scene; + + QLineEdit *edit1 = new QLineEdit; + edit1->setText("QLineEdit 1"); + QLineEdit *edit2 = new QLineEdit; + edit2->setText("QLineEdit 2"); + QCheckBox *checkbox = new QCheckBox("QCheckBox"); + QVBoxLayout *vlayout = new QVBoxLayout; + + vlayout->addWidget(edit1); + vlayout->addWidget(edit2); + vlayout->addWidget(checkbox); + vlayout->insertStretch(-1); + + QGroupBox *box = new QGroupBox("QGroupBox"); + box->setCheckable(true); + box->setChecked(true); + box->setLayout(vlayout); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(box); + layout->addWidget(rightDial); + window.setLayout(layout); + + QVERIFY(window.graphicsProxyWidget() == 0); + QVERIFY(checkbox->graphicsProxyWidget() == 0); + + QGraphicsProxyWidget *windowProxy = scene.addWidget(&window); + QGraphicsView view(&scene); + view.show(); + view.resize(500,500); + + QVERIFY(window.graphicsProxyWidget() == windowProxy); + QVERIFY(box->graphicsProxyWidget() == 0); + QVERIFY(checkbox->graphicsProxyWidget() == 0); + + QPointer<QGraphicsProxyWidget> checkboxProxy = windowProxy->createProxyForChildWidget(checkbox); + + QGraphicsProxyWidget *boxProxy = box->graphicsProxyWidget(); + + QVERIFY(boxProxy); + QVERIFY(checkbox->graphicsProxyWidget() == checkboxProxy); + QVERIFY(checkboxProxy->parentItem() == boxProxy); + QVERIFY(boxProxy->parentItem() == windowProxy); + + QVERIFY(checkboxProxy->mapToScene(QPointF()) == checkbox->mapTo(&window, QPoint())); + QVERIFY(checkboxProxy->size() == checkbox->size()); + QVERIFY(boxProxy->size() == box->size()); + + window.resize(500,500); + QVERIFY(windowProxy->size() == QSize(500,500)); + QVERIFY(checkboxProxy->mapToScene(QPointF()) == checkbox->mapTo(&window, QPoint())); + QVERIFY(checkboxProxy->size() == checkbox->size()); + QVERIFY(boxProxy->size() == box->size()); + + QTest::qWait(10); + + + QSignalSpy spy(checkbox, SIGNAL(clicked())); + + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8)))); + QTRY_COMPARE(spy.count(), 0); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8)))); + QTRY_COMPARE(spy.count(), 1); + + + + boxProxy->setWidget(0); + + QVERIFY(checkbox->graphicsProxyWidget() == 0); + QVERIFY(box->graphicsProxyWidget() == 0); + QVERIFY(checkboxProxy == 0); + + delete boxProxy; +} + +class ContextMenuWidget : public QWidget +{ + Q_OBJECT +public: + ContextMenuWidget() + : embeddedPopup(false), + gotContextMenuEvent(false) + { } + bool embeddedPopup; + bool gotContextMenuEvent; +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::ContextMenu) + QTimer::singleShot(0, this, SLOT(checkMenu())); + return QWidget::event(event); + } + void contextMenuEvent(QContextMenuEvent *) + { + gotContextMenuEvent = true; + } + +private slots: + void checkMenu() + { + if (qFindChild<QMenu *>(this)) + embeddedPopup = true; + hide(); + } +}; + +void tst_QGraphicsProxyWidget::actionsContextMenu_data() +{ + QTest::addColumn<bool>("actionsContextMenu"); + QTest::addColumn<bool>("hasFocus"); + + QTest::newRow("without actionsContextMenu and with focus") << false << true; + QTest::newRow("without actionsContextMenu and without focus") << false << false; + QTest::newRow("with actionsContextMenu and focus") << true << true; + QTest::newRow("with actionsContextMenu without focus") << true << false; +} + +void tst_QGraphicsProxyWidget::actionsContextMenu() +{ + QFETCH(bool, hasFocus); + QFETCH(bool, actionsContextMenu); + + ContextMenuWidget *widget = new ContextMenuWidget; + if (actionsContextMenu) { + widget->addAction(new QAction("item 1", widget)); + widget->addAction(new QAction("item 2", widget)); + widget->addAction(new QAction("item 3", widget)); + widget->setContextMenuPolicy(Qt::ActionsContextMenu); + } + QGraphicsScene scene; + + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + view.setFocus(); + QTRY_VERIFY(view.hasFocus()); + + if (hasFocus) + scene.addWidget(widget)->setFocus(); + else + scene.addWidget(widget)->clearFocus(); + + QApplication::processEvents(); + + QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, + view.viewport()->rect().center(), + view.viewport()->mapToGlobal(view.viewport()->rect().center())); + contextMenuEvent.accept(); + qApp->sendEvent(view.viewport(), &contextMenuEvent); + + if (hasFocus) { + if (actionsContextMenu) { + //actionsContextMenu embedded popup but no contextMenuEvent (widget has focus) + QVERIFY(widget->embeddedPopup); + QVERIFY(!widget->gotContextMenuEvent); + } else { + //no embedded popup but contextMenuEvent (widget has focus) + QVERIFY(!widget->embeddedPopup); + QVERIFY(widget->gotContextMenuEvent); + } + } else { + //qgraphicsproxywidget doesn't have the focus, the widget must not receive any contextMenuEvent and must not create any QMenu + QVERIFY(!widget->embeddedPopup); + QVERIFY(!widget->gotContextMenuEvent); + } + +} + + +void tst_QGraphicsProxyWidget::deleteProxyForChildWidget() +{ + QDialog dialog; + dialog.resize(320, 120); + dialog.move(80, 40); + + QGraphicsScene scene; + scene.setSceneRect(QRectF(0, 0, 640, 480)); + QGraphicsView view(&scene); + QComboBox *combo = new QComboBox; + combo->addItems(QStringList() << "red" << "green" << "blue" << "white" << "black" << "yellow" << "cyan" << "magenta"); + dialog.setLayout(new QVBoxLayout); + dialog.layout()->addWidget(combo); + + QGraphicsProxyWidget *proxy = scene.addWidget(&dialog); + view.show(); + + proxy->setWidget(0); + //just don't crash + QApplication::processEvents(); + delete combo; +} + +void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget_data() +{ + QTest::addColumn<bool>("bypass"); + + QTest::newRow("autoembed") << false; + QTest::newRow("bypass") << true; +} + +void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget() +{ + QFETCH(bool, bypass); + + QWidget *widget = new QWidget; + widget->resize(100, 100); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + QGraphicsProxyWidget *proxy = scene.addWidget(widget); + + QCOMPARE(proxy->widget(), widget); + QVERIFY(proxy->childItems().isEmpty()); + + Qt::WindowFlags flags; + flags |= Qt::Dialog; + if (bypass) + flags |= Qt::BypassGraphicsProxyWidget; + QFileDialog *dialog = new QFileDialog(widget, flags); + dialog->setOption(QFileDialog::DontUseNativeDialog, true); + dialog->show(); + + QCOMPARE(proxy->childItems().size(), bypass ? 0 : 1); + if (!bypass) + QCOMPARE(((QGraphicsProxyWidget *)proxy->childItems().first())->widget(), (QWidget *)dialog); + + dialog->hide(); + QApplication::processEvents(); + delete dialog; + delete widget; +} + +static void makeDndEvent(QGraphicsSceneDragDropEvent *event, QGraphicsView *view, const QPointF &pos) +{ + event->setScenePos(pos); + event->setScreenPos(view->mapToGlobal(view->mapFromScene(pos))); + event->setButtons(Qt::LeftButton); + event->setModifiers(0); + event->setPossibleActions(Qt::CopyAction); + event->setProposedAction(Qt::CopyAction); + event->setDropAction(Qt::CopyAction); + event->setWidget(view->viewport()); + event->setSource(view->viewport()); + event->ignore(); +} + +void tst_QGraphicsProxyWidget::dragDrop() +{ + QPushButton *button = new QPushButton; // acceptDrops(false) + QLineEdit *edit = new QLineEdit; // acceptDrops(true) + + QGraphicsScene scene; + QGraphicsProxyWidget *buttonProxy = scene.addWidget(button); + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + QVERIFY(buttonProxy->acceptDrops()); + QVERIFY(editProxy->acceptDrops()); + + button->setGeometry(0, 0, 100, 50); + edit->setGeometry(0, 60, 100, 50); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + QMimeData data; + data.setText("hei"); + { + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragEnter); + makeDndEvent(&event, &view, QPointF(50, 25)); + event.setMimeData(&data); + qApp->sendEvent(&scene, &event); + QVERIFY(event.isAccepted()); + } + { + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove); + makeDndEvent(&event, &view, QPointF(50, 25)); + event.setMimeData(&data); + qApp->sendEvent(&scene, &event); + QVERIFY(!event.isAccepted()); + } + { + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove); + makeDndEvent(&event, &view, QPointF(50, 75)); + event.setMimeData(&data); + qApp->sendEvent(&scene, &event); + QVERIFY(event.isAccepted()); + } + { + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDrop); + makeDndEvent(&event, &view, QPointF(50, 75)); + event.setMimeData(&data); + qApp->sendEvent(&scene, &event); + QVERIFY(event.isAccepted()); + } + QCOMPARE(edit->text(), QString("hei")); +} + +void tst_QGraphicsProxyWidget::windowFlags_data() +{ + QTest::addColumn<int>("proxyFlags"); + QTest::addColumn<int>("widgetFlags"); + QTest::addColumn<int>("resultingProxyFlags"); + QTest::addColumn<int>("resultingWidgetFlags"); + + QTest::newRow("proxy(0) widget(0)") << 0 << 0 << 0 << int(Qt::Window); + QTest::newRow("proxy(window)") << int(Qt::Window) << 0 << int(Qt::Window) << int(Qt::Window); + QTest::newRow("proxy(window) widget(window)") << int(Qt::Window) << int(Qt::Window) << int(Qt::Window) << int(Qt::Window); + QTest::newRow("proxy(0) widget(window)") << int(0) << int(Qt::Window) << int(0) << int(Qt::Window); +} + +void tst_QGraphicsProxyWidget::windowFlags() +{ + QFETCH(int, proxyFlags); + QFETCH(int, widgetFlags); + QFETCH(int, resultingProxyFlags); + QFETCH(int, resultingWidgetFlags); + Qt::WindowFlags proxyWFlags = Qt::WindowFlags(proxyFlags); + Qt::WindowFlags widgetWFlags = Qt::WindowFlags(widgetFlags); + Qt::WindowFlags resultingProxyWFlags = Qt::WindowFlags(resultingProxyFlags); + Qt::WindowFlags resultingWidgetWFlags = Qt::WindowFlags(resultingWidgetFlags); + + QGraphicsProxyWidget proxy(0, proxyWFlags); + QVERIFY((proxy.windowFlags() & proxyWFlags) == proxyWFlags); + + QWidget *widget = new QWidget(0, widgetWFlags); + QVERIFY((widget->windowFlags() & widgetWFlags) == widgetWFlags); + + proxy.setWidget(widget); + + if (resultingProxyFlags == 0) + QVERIFY(!proxy.windowFlags()); + else + QVERIFY((proxy.windowFlags() & resultingProxyWFlags) == resultingProxyWFlags); + QVERIFY((widget->windowFlags() & resultingWidgetWFlags) == resultingWidgetWFlags); +} + +void tst_QGraphicsProxyWidget::comboboxWindowFlags() +{ + QComboBox *comboBox = new QComboBox; + comboBox->addItem("Item 1"); + comboBox->addItem("Item 2"); + comboBox->addItem("Item 3"); + QWidget *embedWidget = comboBox; + + QGraphicsScene scene; + QGraphicsProxyWidget *proxy = scene.addWidget(embedWidget); + proxy->setWindowFlags(Qt::Window); + QVERIFY(embedWidget->isWindow()); + QVERIFY(proxy->isWindow()); + + comboBox->showPopup(); + + QCOMPARE(proxy->childItems().size(), 1); + QGraphicsItem *popupProxy = proxy->childItems().first(); + QVERIFY(popupProxy->isWindow()); + QVERIFY((static_cast<QGraphicsWidget *>(popupProxy)->windowFlags() & Qt::Popup) == Qt::Popup); +} + +void tst_QGraphicsProxyWidget::updateAndDelete() +{ + QGraphicsScene scene; + QGraphicsProxyWidget *proxy = scene.addWidget(new QPushButton("Hello World")); + View view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTRY_VERIFY(view.npaints > 0); + + const QRect itemDeviceBoundingRect = proxy->deviceTransform(view.viewportTransform()) + .mapRect(proxy->boundingRect()).toRect(); + const QRegion expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2); + + view.npaints = 0; + view.paintEventRegion = QRegion(); + + // Update and hide. + proxy->update(); + proxy->hide(); + QTRY_COMPARE(view.npaints, 1); + QCOMPARE(view.paintEventRegion, expectedRegion); + + proxy->show(); + QTest::qWait(50); + view.npaints = 0; + view.paintEventRegion = QRegion(); + + // Update and delete. + proxy->update(); + delete proxy; + QTRY_COMPARE(view.npaints, 1); + QCOMPARE(view.paintEventRegion, expectedRegion); +} + +class InputMethod_LineEdit : public QLineEdit +{ + bool event(QEvent *e) + { + if (e->type() == QEvent::InputMethod) + ++inputMethodEvents; + return QLineEdit::event(e); + } +public: + int inputMethodEvents; +}; + +void tst_QGraphicsProxyWidget::inputMethod() +{ + QGraphicsScene scene; + + // check that the proxy is initialized with the correct input method sensitivity + for (int i = 0; i < 2; ++i) + { + QLineEdit *lineEdit = new QLineEdit; + lineEdit->setAttribute(Qt::WA_InputMethodEnabled, !!i); + QGraphicsProxyWidget *proxy = scene.addWidget(lineEdit); + QCOMPARE(!!(proxy->flags() & QGraphicsItem::ItemAcceptsInputMethod), !!i); + } + + // check that input method events are only forwarded to widgets with focus + for (int i = 0; i < 2; ++i) + { + InputMethod_LineEdit *lineEdit = new InputMethod_LineEdit; + lineEdit->setAttribute(Qt::WA_InputMethodEnabled, true); + QGraphicsProxyWidget *proxy = scene.addWidget(lineEdit); + + if (i) + lineEdit->setFocus(); + + lineEdit->inputMethodEvents = 0; + QInputMethodEvent event; + qApp->sendEvent(proxy, &event); + QCOMPARE(lineEdit->inputMethodEvents, i); + } + + scene.clear(); + QGraphicsView view(&scene); + QWidget *w = new QWidget; + w->setLayout(new QVBoxLayout(w)); + QLineEdit *lineEdit = new QLineEdit; + lineEdit->setEchoMode(QLineEdit::Password); + w->layout()->addWidget(lineEdit); + lineEdit->setAttribute(Qt::WA_InputMethodEnabled, true); + QGraphicsProxyWidget *proxy = scene.addWidget(w); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(!(proxy->flags() & QGraphicsItem::ItemAcceptsInputMethod)); + lineEdit->setFocus(); + QVERIFY((proxy->flags() & QGraphicsItem::ItemAcceptsInputMethod)); +} + +void tst_QGraphicsProxyWidget::clickFocus() +{ + QGraphicsScene scene; + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + QGraphicsProxyWidget *proxy = scene.addWidget(new QLineEdit); + + QGraphicsView view(&scene); + + { + EventSpy proxySpy(proxy); + EventSpy widgetSpy(proxy->widget()); + + view.setFrameStyle(0); + view.resize(300, 300); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + QVERIFY(!proxy->hasFocus()); + QVERIFY(!proxy->widget()->hasFocus()); + + QCOMPARE(proxySpy.counts[QEvent::FocusIn], 0); + QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0); + QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 0); + QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0); + + QPointF lineEditCenter = proxy->mapToScene(proxy->boundingRect().center()); + // Spontaneous mouse click sets focus on a clickable widget. + for (int retry = 0; retry < 50 && !proxy->hasFocus(); retry++) + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QVERIFY(proxy->hasFocus()); + QVERIFY(proxy->widget()->hasFocus()); + QCOMPARE(proxySpy.counts[QEvent::FocusIn], 1); + QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 1); + + scene.setFocusItem(0); + QVERIFY(!proxy->hasFocus()); + QVERIFY(!proxy->widget()->hasFocus()); + QCOMPARE(proxySpy.counts[QEvent::FocusOut], 1); + QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 1); + + // Non-spontaneous mouse click sets focus if the widget has been clicked before + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(lineEditCenter); + event.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + QVERIFY(proxy->hasFocus()); + QVERIFY(proxy->widget()->hasFocus()); + QCOMPARE(proxySpy.counts[QEvent::FocusIn], 2); + QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 2); + } + } + + scene.setFocusItem(0); + proxy->setWidget(new QLineEdit); // resets focusWidget + + { + QPointF lineEditCenter = proxy->mapToScene(proxy->boundingRect().center()); + EventSpy proxySpy(proxy); + EventSpy widgetSpy(proxy->widget()); + QVERIFY(!proxy->hasFocus()); + QVERIFY(!proxy->widget()->hasFocus()); + QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0); + QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0); + + // Non-spontaneous mouse click does not set focus on the embedded widget. + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(lineEditCenter); + event.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + QVERIFY(!proxy->hasFocus()); + QVERIFY(!proxy->widget()->hasFocus()); + QCOMPARE(proxySpy.counts[QEvent::FocusIn], 0); + QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 0); + } + + scene.setFocusItem(0); + QVERIFY(!proxy->hasFocus()); + QVERIFY(!proxy->widget()->hasFocus()); + QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0); + QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0); + + // Spontaneous click on non-clickable widget does not give focus. + proxy->widget()->setFocusPolicy(Qt::NoFocus); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QVERIFY(!proxy->hasFocus()); + QVERIFY(!proxy->widget()->hasFocus()); + + // Multiple clicks should only result in one FocusIn. + proxy->widget()->setFocusPolicy(Qt::StrongFocus); + scene.setFocusItem(0); + QVERIFY(!proxy->hasFocus()); + QVERIFY(!proxy->widget()->hasFocus()); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QVERIFY(proxy->hasFocus()); + QVERIFY(proxy->widget()->hasFocus()); + QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(proxySpy.counts[QEvent::FocusIn], 1); + } +} + +void tst_QGraphicsProxyWidget::windowFrameMargins() +{ + // Make sure the top margin is non-zero when passing Qt::Window. + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, Qt::Window); + + qreal left, top, right, bottom; + proxy->getWindowFrameMargins(&left, &top, &right, &bottom); + QVERIFY(top > 0); + + proxy->setWidget(new QPushButton("testtest")); + proxy->getWindowFrameMargins(&left, &top, &right, &bottom); + QVERIFY(top > 0); + + QGraphicsScene scene; + scene.addItem(proxy); + proxy->getWindowFrameMargins(&left, &top, &right, &bottom); + QVERIFY(top > 0); + + proxy->unsetWindowFrameMargins(); + proxy->getWindowFrameMargins(&left, &top, &right, &bottom); + QVERIFY(top > 0); +} + +class HoverButton : public QPushButton +{ +public: + HoverButton(QWidget *parent = 0) : QPushButton(parent), hoverLeaveReceived(false) + {} + + bool hoverLeaveReceived; + + bool event(QEvent* e) + { + if(QEvent::HoverLeave == e->type()) + hoverLeaveReceived = true; + return QPushButton::event(e); + } +}; + +class Scene : public QGraphicsScene +{ +Q_OBJECT +public: + Scene() { + QWidget *background = new QWidget; + background->setGeometry(0, 0, 500, 500); + hoverButton = new HoverButton; + hoverButton->setParent(background); + hoverButton->setText("Second button"); + hoverButton->setGeometry(10, 10, 200, 50); + addWidget(background); + + QPushButton *hideButton = new QPushButton("I'm a button with a very very long text"); + hideButton->setGeometry(10, 10, 400, 50); + topButton = addWidget(hideButton); + connect(hideButton, SIGNAL(clicked()), this, SLOT(hideButton())); + topButton->setFocus(); + } + + QGraphicsProxyWidget *topButton; + HoverButton *hoverButton; + +public slots: + void hideButton() { + QCursor::setPos(600,600); + topButton->hide(); + } +}; + +void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget() +{ +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(QT_NO_CURSOR) + QSKIP("Test case unstable on this platform", SkipAll); +#endif + QGraphicsView view; + Scene scene; + view.setScene(&scene); + view.resize(600, 600); + QApplication::setActiveWindow(&view); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), &view); + QCursor::setPos(view.mapToGlobal(view.mapFromScene(scene.topButton->boundingRect().center()))); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(scene.topButton->scenePos())); + QTRY_COMPARE(scene.hoverButton->hoverLeaveReceived, true); +} + +QTEST_MAIN(tst_QGraphicsProxyWidget) +#include "tst_qgraphicsproxywidget.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/.gitignore b/tests/auto/widgets/graphicsview/qgraphicsscene/.gitignore new file mode 100644 index 0000000000..df1097ad74 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/.gitignore @@ -0,0 +1 @@ +tst_qgraphicsscene diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/Ash_European.jpg b/tests/auto/widgets/graphicsview/qgraphicsscene/Ash_European.jpg Binary files differnew file mode 100644 index 0000000000..8581d322ba --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/Ash_European.jpg diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/graphicsScene_selection.data b/tests/auto/widgets/graphicsview/qgraphicsscene/graphicsScene_selection.data Binary files differnew file mode 100644 index 0000000000..8ff3feebb1 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/graphicsScene_selection.data diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/images.qrc b/tests/auto/widgets/graphicsview/qgraphicsscene/images.qrc new file mode 100644 index 0000000000..cac10bef82 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/images.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>Ash_European.jpg</file> +</qresource> +</RCC> diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/qgraphicsscene.pro b/tests/auto/widgets/graphicsview/qgraphicsscene/qgraphicsscene.pro new file mode 100644 index 0000000000..75b4d84fb4 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/qgraphicsscene.pro @@ -0,0 +1,20 @@ +load(qttest_p4) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qgraphicsscene.cpp +RESOURCES += images.qrc +win32:!wince*: LIBS += -lUser32 + +!wince*:DEFINES += SRCDIR=\\\"$$PWD\\\" +DEFINES += QT_NO_CAST_TO_ASCII + +wince* { + rootFiles.files = Ash_European.jpg graphicsScene_selection.data + rootFiles.path = . + renderFiles.files = testData\\render\\* + renderFiles.path = testData\\render + DEPLOYMENT += rootFiles renderFiles + DEFINES += SRCDIR=\\\".\\\" +} + +contains(QT_CONFIG,xcb):qpa:CONFIG+=insignificant_test # QTBUG-20756 crashes on qpa, xcb diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-45-deg-left.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-45-deg-left.png Binary files differnew file mode 100644 index 0000000000..526b897c1c --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-45-deg-left.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-45-deg-right.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-45-deg-right.png Binary files differnew file mode 100644 index 0000000000..83563872c1 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-45-deg-right.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-scale-2x.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-scale-2x.png Binary files differnew file mode 100644 index 0000000000..a929621bca --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-scale-2x.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-translate-0-50.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-translate-0-50.png Binary files differnew file mode 100644 index 0000000000..aeaf20ae41 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-translate-0-50.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-translate-50-0.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-translate-50-0.png Binary files differnew file mode 100644 index 0000000000..ed80dba118 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-translate-50-0.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed-clip-ellipse.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed-clip-ellipse.png Binary files differnew file mode 100644 index 0000000000..9b401b41c7 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed-clip-ellipse.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed-clip-rect.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed-clip-rect.png Binary files differnew file mode 100644 index 0000000000..1c5969870d --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed-clip-rect.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed.png Binary files differnew file mode 100644 index 0000000000..3592744bda --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-all-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-bottomleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-bottomleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..ab15e7228b --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-bottomleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-bottomright-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-bottomright-untransformed.png Binary files differnew file mode 100644 index 0000000000..82e25762fa --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-bottomright-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..934ee4679b --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-topright-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-topright-untransformed.png Binary files differnew file mode 100644 index 0000000000..aced485c75 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/all-topright-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottom-bottomright-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottom-bottomright-untransformed.png Binary files differnew file mode 100644 index 0000000000..5df8ab0612 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottom-bottomright-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottom-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottom-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..bb04881763 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottom-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomleft-all-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomleft-all-untransformed.png Binary files differnew file mode 100644 index 0000000000..907dec647d --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomleft-all-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomleft-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomleft-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..d37313c06d --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomleft-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomright-all-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomright-all-untransformed.png Binary files differnew file mode 100644 index 0000000000..9fbafe48d5 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomright-all-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomright-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomright-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..1d0253796e --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/bottomright-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/left-bottomright-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/left-bottomright-untransformed.png Binary files differnew file mode 100644 index 0000000000..bcfda9968b --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/left-bottomright-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/left-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/left-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..b443a4f991 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/left-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/right-bottomright-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/right-bottomright-untransformed.png Binary files differnew file mode 100644 index 0000000000..e6922e0920 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/right-bottomright-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/right-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/right-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..a74f218876 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/right-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/top-bottomright-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/top-bottomright-untransformed.png Binary files differnew file mode 100644 index 0000000000..6c51907a6d --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/top-bottomright-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/top-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/top-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..0a37133e31 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/top-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topleft-all-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topleft-all-untransformed.png Binary files differnew file mode 100644 index 0000000000..edc7dc9ce6 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topleft-all-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topleft-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topleft-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..e37051f7d2 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topleft-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topright-all-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topright-all-untransformed.png Binary files differnew file mode 100644 index 0000000000..a185d14ebd --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topright-all-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topright-topleft-untransformed.png b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topright-topleft-untransformed.png Binary files differnew file mode 100644 index 0000000000..42e07f11ea --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/testData/render/topright-topleft-untransformed.png diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp new file mode 100644 index 0000000000..9ac1573b2a --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp @@ -0,0 +1,4710 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#if defined(Q_OS_WINCE) +#include <ceconfig.h> +#endif + +#include <QtGui> +#include <QtWidgets> +#include <private/qgraphicsscene_p.h> +#include <private/qgraphicssceneindex_p.h> +#include <math.h> +#include "../../../gui/painting/qpathclipper/pathcompare.h" + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +#include <windows.h> +#define Q_CHECK_PAINTEVENTS \ + if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \ + QSKIP("The Graphics View doesn't get the paint events", SkipSingle); +#else +#define Q_CHECK_PAINTEVENTS +#endif + +//TESTED_CLASS= +//TESTED_FILES= + +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<QRectF>) +Q_DECLARE_METATYPE(QMatrix) +Q_DECLARE_METATYPE(QPainterPath) +Q_DECLARE_METATYPE(QPointF) +Q_DECLARE_METATYPE(QRectF) +Q_DECLARE_METATYPE(Qt::AspectRatioMode) +Q_DECLARE_METATYPE(Qt::ItemSelectionMode) + +static const int randomX[] = {276, 40, 250, 864, -56, 426, 855, 825, 184, 955, -798, -804, 773, + 282, 489, 686, 780, -220, 50, 749, -856, -205, 81, 492, -819, 518, + 895, 57, -559, 788, -965, 68, -442, -247, -339, -648, 292, 891, + -865, 462, 864, 673, 640, 523, 194, 500, -727, 307, -243, 320, + -545, 415, 448, 341, -619, 652, 892, -16, -14, -659, -101, -934, + 532, 356, 824, 132, 160, 130, 104, 886, -179, -174, 543, -644, 60, + -470, -354, -728, 689, 682, -587, -694, -221, -741, 37, 372, -289, + 741, -300, 858, -320, 729, -602, -956, -544, -403, 203, 398, 284, + -972, -572, -946, 81, 51, -403, -580, 867, 546, 565, -580, -484, + 659, 982, -518, -976, 423, -800, 659, -297, 712, 938, -19, -16, + 824, -252, 197, 321, -837, 824, 136, 226, -980, -909, -826, -479, + -835, -503, -828, -901, -810, -641, -548, -179, 194, 749, -296, 539, + -37, -599, -235, 121, 35, -230, -915, 789, 764, -622, -382, -90, -701, + 676, -407, 998, 267, 913, 817, -748, -370, -162, -797, 19, -556, 933, + -670, -101, -765, -941, -17, 360, 31, 960, 509, 933, -35, 974, -924, + -734, 589, 963, 724, 794, 843, 16, -272, -811, 721, 99, -122, 216, + -404, 158, 787, -443, -437, -337, 383, -342, 538, -641, 791, 637, + -848, 397, 820, 109, 11, 45, 809, 591, 933, 961, 625, -140, -592, + -694, -969, 317, 293, 777, -18, -282, 835, -455, -708, -407, -204, + 748, 347, -501, -545, 292, -362, 176, 546, -573, -38, -854, -395, + 560, -624, -940, -971, 66, -910, 782, 985}; + +static const int randomY[] = {603, 70, -318, 843, 450, -637, 199, -527, 407, 964, -54, 620, -207, + -736, -700, -476, -706, -142, 837, 621, 522, -98, 232, 292, -267, 900, + 615, -356, -415, 783, 290, 462, -857, -314, 677, 36, 772, 424, -72, + -121, 547, -533, 537, -656, 289, 508, 914, 601, 434, 588, -779, -714, + -368, 628, -276, 432, -1, -929, 638, -36, 253, -922, -943, 979, -34, + -268, -193, 601, 686, -330, 165, 98, 75, -691, -605, 617, 773, 617, + 619, 238, -42, -405, 17, 384, -472, -846, 520, 110, 591, -217, 936, + -373, 731, 734, 810, 961, 881, 939, 379, -905, -137, 437, 298, 688, + -71, -204, 573, -120, -821, 489, -722, -926, 529, -113, -243, 543, + 868, -301, -781, -549, -842, -489, -80, -910, -928, 51, -91, 324, + 204, -92, 867, 723, 248, 709, -357, 591, -365, -379, 266, -649, -95, + 205, 551, 355, -631, 79, -186, 795, -7, -225, 46, -410, 665, -874, + -618, 845, -548, 443, 471, -644, 606, -607, 59, -619, 288, -244, 529, + 690, 349, -738, -611, -879, -642, 801, -178, 823, -748, -552, -247, + -223, -408, 651, -62, 949, -795, 171, -107, -210, -207, -842, -86, + 436, 528, 366, -178, 245, -695, 665, 613, -948, 667, -620, -979, -949, + 905, 181, -412, -467, -437, -774, 750, -10, 54, 205, -674, -290, -924, + -361, -463, 912, -702, 622, -542, 220, 115, 832, 451, -38, -952, -230, + -588, 864, 234, 225, -303, 493, 246, 153, 338, -378, 377, -819, 140, 136, + 467, -849, -326, -533, 166, 252, -994, -699, 904, -566, 621, -752}; + +class HoverItem : public QGraphicsRectItem +{ +public: + HoverItem() + : QGraphicsRectItem(QRectF(-10, -10, 20, 20)), isHovered(false) + { setAcceptsHoverEvents(true); } + + bool isHovered; + +protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) + { + isHovered = (option->state & QStyle::State_MouseOver); + + painter->setOpacity(0.75); + painter->setPen(Qt::NoPen); + painter->setBrush(Qt::darkGray); + painter->drawRoundRect(boundingRect().adjusted(3, 3, -3, -3), Qt::darkGray); + painter->setPen(Qt::black); + if (isHovered) { + painter->setBrush(QColor(Qt::blue).light(120)); + } else { + painter->setBrush(Qt::gray); + } + painter->drawRoundRect(boundingRect().adjusted(0, 0, -5, -5)); + } +}; + +class EventSpy : public QGraphicsWidget +{ + Q_OBJECT +public: + EventSpy(QObject *watched, QEvent::Type type) + : _count(0), spied(type) + { + watched->installEventFilter(this); + } + + EventSpy(QGraphicsScene *scene, QGraphicsItem *watched, QEvent::Type type) + : _count(0), spied(type) + { + scene->addItem(this); + watched->installSceneEventFilter(this); + } + + int count() const { return _count; } + +protected: + bool eventFilter(QObject *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + int _count; + QEvent::Type spied; +}; + +class tst_QGraphicsScene : public QObject +{ + Q_OBJECT +public slots: + void initTestCase(); + +private slots: + void construction(); + void sceneRect(); + void itemIndexMethod(); + void bspTreeDepth(); + void itemsBoundingRect_data(); + void itemsBoundingRect(); + void items(); + void items_QPointF_data(); + void items_QPointF(); + void items_QRectF(); + void items_QRectF_2_data(); + void items_QRectF_2(); + void items_QPolygonF(); + void items_QPolygonF_2(); + void items_QPainterPath(); + void items_QPainterPath_2(); + void selection(); + void selectionChanged(); + void selectionChanged2(); + void addItem(); + void addEllipse(); + void addLine(); + void addPath(); + void addPixmap(); + void addRect(); + void addText(); +#if !defined(Q_OS_WINCE) || defined(GWES_ICONCURS) + void removeItem(); +#endif + void clear(); + void focusItem(); + void focusItemLostFocus(); + void setFocusItem(); + void setFocusItem_inactive(); + void mouseGrabberItem(); + void hoverEvents_siblings(); + void hoverEvents_parentChild(); + void createItemGroup(); + void mouseEventPropagation(); + void mouseEventPropagation_ignore(); + void mouseEventPropagation_focus(); + void mouseEventPropagation_doubleclick(); + void mouseEventPropagation_mouseMove(); +#ifndef QT_NO_DRAGANDDROP + void dragAndDrop_simple(); + void dragAndDrop_disabledOrInvisible(); + void dragAndDrop_propagate(); +#endif + void render_data(); + void render(); + void renderItemsWithNegativeWidthOrHeight(); + void contextMenuEvent(); + void contextMenuEvent_ItemIgnoresTransformations(); + void update(); + void update2(); + void views(); + void event(); + void eventsToDisabledItems(); + void exposedRect(); + void tabFocus_emptyScene(); + void tabFocus_sceneWithFocusableItems(); + void tabFocus_sceneWithFocusWidgets(); + void tabFocus_sceneWithNestedFocusWidgets(); + void style(); + void sorting_data(); + void sorting(); + void changedSignal_data(); + void changedSignal(); + void stickyFocus_data(); + void stickyFocus(); + void sendEvent(); + void inputMethod_data(); + void inputMethod(); + void dispatchHoverOnPress(); + void initialFocus_data(); + void initialFocus(); + void polishItems(); + void polishItems2(); + void isActive(); + void siblingIndexAlwaysValid(); + void removeFullyTransparentItem(); + void zeroScale(); + + // task specific tests below me + void task139710_bspTreeCrash(); + void task139782_containsItemBoundingRect(); + void task176178_itemIndexMethodBreaksSceneRect(); + void task160653_selectionChanged(); + void task250680_childClip(); + void taskQTBUG_5904_crashWithDeviceCoordinateCache(); + void taskQT657_paintIntoCacheWithTransparentParts(); + void taskQTBUG_7863_paintIntoCacheWithTransparentParts(); + void taskQT_3674_doNotCrash(); + void taskQTBUG_15977_renderWithDeviceCoordinateCache(); + void taskQTBUG_16401_focusItem(); +}; + +void tst_QGraphicsScene::initTestCase() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +void tst_QGraphicsScene::construction() +{ + QGraphicsScene scene; + QCOMPARE(scene.itemsBoundingRect(), QRectF()); + QVERIFY(scene.items().isEmpty()); + QVERIFY(scene.items(QPointF()).isEmpty()); + QVERIFY(scene.items(QRectF()).isEmpty()); + QVERIFY(scene.items(QPolygonF()).isEmpty()); + QVERIFY(scene.items(QPainterPath()).isEmpty()); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsScene::collidingItems: cannot find collisions for null item"); + QVERIFY(scene.collidingItems(0).isEmpty()); + QVERIFY(!scene.itemAt(QPointF())); + QVERIFY(scene.selectedItems().isEmpty()); + QVERIFY(!scene.focusItem()); +} + +void tst_QGraphicsScene::sceneRect() +{ + QGraphicsScene scene; + QSignalSpy sceneRectChanged(&scene, SIGNAL(sceneRectChanged(QRectF))); + QCOMPARE(scene.sceneRect(), QRectF()); + QCOMPARE(sceneRectChanged.count(), 0); + + QGraphicsItem *item = scene.addRect(QRectF(0, 0, 10, 10)); + item->setPos(-5, -5); + QCOMPARE(sceneRectChanged.count(), 0); + + QCOMPARE(scene.itemAt(0, 0), item); + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)0); + QCOMPARE(sceneRectChanged.count(), 0); + QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 10, 10)); + QCOMPARE(sceneRectChanged.count(), 1); + QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); + + item->setPos(0, 0); + QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 15, 15)); + QCOMPARE(sceneRectChanged.count(), 2); + QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); + + scene.setSceneRect(-100, -100, 10, 10); + QCOMPARE(sceneRectChanged.count(), 3); + QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); + + QCOMPARE(scene.itemAt(0, 0), item); + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)0); + QCOMPARE(scene.sceneRect(), QRectF(-100, -100, 10, 10)); + item->setPos(10, 10); + QCOMPARE(scene.sceneRect(), QRectF(-100, -100, 10, 10)); + QCOMPARE(sceneRectChanged.count(), 3); + QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); + + scene.setSceneRect(QRectF()); + + QCOMPARE(scene.itemAt(10, 10), item); + QCOMPARE(scene.itemAt(20, 20), (QGraphicsItem *)0); + QCOMPARE(sceneRectChanged.count(), 4); + QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 25, 25)); + QCOMPARE(sceneRectChanged.count(), 5); + QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); +} + +void tst_QGraphicsScene::itemIndexMethod() +{ + QGraphicsScene scene; + QCOMPARE(scene.itemIndexMethod(), QGraphicsScene::BspTreeIndex); + +#ifdef QT_ARCH_ARM + const int minY = -500; + const int maxY = 500; + const int minX = -500; + const int maxX = 500; +#else + const int minY = -1000; + const int maxY = 2000; + const int minX = -1000; + const int maxX = 2000; +#endif + + QList<QGraphicsItem *> items; + for (int y = minY; y < maxY; y += 100) { + for (int x = minX; x < maxX; x += 100) { + QGraphicsItem *item = scene.addRect(QRectF(0, 0, 10, 10)); + item->setPos(x, y); + QCOMPARE(scene.itemAt(x, y), item); + items << item; + } + } + + int n = 0; + for (int y = minY; y < maxY; y += 100) { + for (int x = minX; x < maxX; x += 100) + QCOMPARE(scene.itemAt(x, y), items.at(n++)); + } + + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + QCOMPARE(scene.itemIndexMethod(), QGraphicsScene::NoIndex); + + n = 0; + for (int y = minY; y < maxY; y += 100) { + for (int x = minX; x < maxX; x += 100) + QCOMPARE(scene.itemAt(x, y), items.at(n++)); + } + + scene.setItemIndexMethod(QGraphicsScene::BspTreeIndex); + QCOMPARE(scene.itemIndexMethod(), QGraphicsScene::BspTreeIndex); + + n = 0; + for (int y = minY; y < maxY; y += 100) { + for (int x = minX; x < maxX; x += 100) + QCOMPARE(scene.itemAt(x, y), items.at(n++)); + } +} + +void tst_QGraphicsScene::bspTreeDepth() +{ + QGraphicsScene scene; + QCOMPARE(scene.itemIndexMethod(), QGraphicsScene::BspTreeIndex); + QCOMPARE(scene.bspTreeDepth(), 0); + scene.setBspTreeDepth(1); + QCOMPARE(scene.bspTreeDepth(), 1); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsScene::setBspTreeDepth: invalid depth -1 ignored; must be >= 0"); + scene.setBspTreeDepth(-1); + QCOMPARE(scene.bspTreeDepth(), 1); +} + +void tst_QGraphicsScene::items() +{ +#ifdef QT_ARCH_ARM + const int minY = -500; + const int maxY = 500; + const int minX = -500; + const int maxX = 500; +#else + const int minY = -1000; + const int maxY = 2000; + const int minX = -1000; + const int maxX = 2000; +#endif + + { + QGraphicsScene scene; + + QList<QGraphicsItem *> items; + for (int y = minY; y < maxY; y += 100) { + for (int x = minX; x < maxX; x += 100) + items << scene.addRect(QRectF(0, 0, 10, 10)); + } + QCOMPARE(scene.items().size(), items.size()); + scene.itemAt(0, 0); // trigger indexing + + scene.removeItem(items.at(5)); + delete items.at(5); + QVERIFY(!scene.items().contains(0)); + delete items.at(7); + QVERIFY(!scene.items().contains(0)); + } + { + QGraphicsScene scene; + QGraphicsLineItem *l1 = scene.addLine(-5, 0, 5, 0); + QGraphicsLineItem *l2 = scene.addLine(0, -5, 0, 5); + QVERIFY(!l1->sceneBoundingRect().intersects(l2->sceneBoundingRect())); + QVERIFY(!l2->sceneBoundingRect().intersects(l1->sceneBoundingRect())); + QList<QGraphicsItem *> items; + items<<l1<<l2; + QCOMPARE(scene.items().size(), items.size()); + QVERIFY(scene.items(-1, -1, 2, 2).contains(l1)); + QVERIFY(scene.items(-1, -1, 2, 2).contains(l2)); + } +} + +void tst_QGraphicsScene::itemsBoundingRect_data() +{ + QTest::addColumn<QList<QRectF> >("rects"); + QTest::addColumn<QMatrix>("matrix"); + QTest::addColumn<QRectF>("boundingRect"); + + QMatrix transformationMatrix; + transformationMatrix.translate(50, -50); + transformationMatrix.scale(2, 2); + transformationMatrix.rotate(90); + + QTest::newRow("none") + << QList<QRectF>() + << QMatrix() + << QRectF(); + QTest::newRow("{{0, 0, 10, 10}}") + << (QList<QRectF>() << QRectF(0, 0, 10, 10)) + << QMatrix() + << QRectF(0, 0, 10, 10); + QTest::newRow("{{-10, -10, 10, 10}}") + << (QList<QRectF>() << QRectF(-10, -10, 10, 10)) + << QMatrix() + << QRectF(-10, -10, 10, 10); + QTest::newRow("{{-1000, -1000, 1, 1}, {-10, -10, 10, 10}}") + << (QList<QRectF>() << QRectF(-1000, -1000, 1, 1) << QRectF(-10, -10, 10, 10)) + << QMatrix() + << QRectF(-1000, -1000, 1000, 1000); + QTest::newRow("transformed {{0, 0, 10, 10}}") + << (QList<QRectF>() << QRectF(0, 0, 10, 10)) + << transformationMatrix + << QRectF(30, -50, 20, 20); + QTest::newRow("transformed {{-10, -10, 10, 10}}") + << (QList<QRectF>() << QRectF(-10, -10, 10, 10)) + << transformationMatrix + << QRectF(50, -70, 20, 20); + QTest::newRow("transformed {{-1000, -1000, 1, 1}, {-10, -10, 10, 10}}") + << (QList<QRectF>() << QRectF(-1000, -1000, 1, 1) << QRectF(-10, -10, 10, 10)) + << transformationMatrix + << QRectF(50, -2050, 2000, 2000); + + QList<QRectF> all; + for (int i = 0; i < 256; ++i) + all << QRectF(randomX[i], randomY[i], 10, 10); + QTest::newRow("all") + << all + << QMatrix() + << QRectF(-980, -994, 1988, 1983); + QTest::newRow("transformed all") + << all + << transformationMatrix + << QRectF(-1928, -2010, 3966, 3976); +} + +void tst_QGraphicsScene::itemsBoundingRect() +{ + QFETCH(QList<QRectF>, rects); + QFETCH(QMatrix, matrix); + QFETCH(QRectF, boundingRect); + + QGraphicsScene scene; + + foreach (QRectF rect, rects) { + QPainterPath path; + path.addRect(rect); + scene.addPath(path)->setMatrix(matrix); + } + + QCOMPARE(scene.itemsBoundingRect(), boundingRect); +} + +void tst_QGraphicsScene::items_QPointF_data() +{ + QTest::addColumn<QList<QRectF> >("items"); + QTest::addColumn<QPointF>("point"); + QTest::addColumn<QList<int> >("itemsAtPoint"); + + QTest::newRow("empty") + << QList<QRectF>() + << QPointF() + << QList<int>(); + QTest::newRow("1") + << (QList<QRectF>() << QRectF(0, 0, 10, 10)) + << QPointF(0, 0) + << (QList<int>() << 0); + QTest::newRow("2") + << (QList<QRectF>() << QRectF(0, 0, 10, 10)) + << QPointF(5, 5) + << (QList<int>() << 0); + QTest::newRow("3") + << (QList<QRectF>() << QRectF(0, 0, 10, 10)) + << QPointF(9.9, 9.9) + << (QList<int>() << 0); + QTest::newRow("3.5") + << (QList<QRectF>() << QRectF(0, 0, 10, 10)) + << QPointF(10, 10) + << QList<int>(); + QTest::newRow("4") + << (QList<QRectF>() << QRectF(0, 0, 10, 10) << QRectF(9.9, 9.9, 10, 10)) + << QPointF(9.9, 9.9) + << (QList<int>() << 1 << 0); + QTest::newRow("4.5") + << (QList<QRectF>() << QRectF(0, 0, 10, 10) << QRectF(10, 10, 10, 10)) + << QPointF(10, 10) + << (QList<int>() << 1); + QTest::newRow("5") + << (QList<QRectF>() << QRectF(5, 5, 10, 10) << QRectF(10, 10, 10, 10)) + << QPointF(10, 10) + << (QList<int>() << 1 << 0); + QTest::newRow("6") + << (QList<QRectF>() << QRectF(5, 5, 10, 10) << QRectF(10, 10, 10, 10) << QRectF(0, 0, 20, 30)) + << QPointF(10, 10) + << (QList<int>() << 2 << 1 << 0); +} + +void tst_QGraphicsScene::items_QPointF() +{ + QFETCH(QList<QRectF>, items); + QFETCH(QPointF, point); + QFETCH(QList<int>, itemsAtPoint); + + QGraphicsScene scene; + + int n = 0; + QList<QGraphicsItem *> addedItems; + foreach(QRectF rect, items) { + QPainterPath path; + path.addRect(0, 0, rect.width(), rect.height()); + + QGraphicsItem *item = scene.addPath(path); + item->setZValue(n++); + item->setPos(rect.topLeft()); + addedItems << item; + } + + QList<int> itemIndexes; + foreach (QGraphicsItem *item, scene.items(point)) + itemIndexes << addedItems.indexOf(item); + + QCOMPARE(itemIndexes, itemsAtPoint); +} + +void tst_QGraphicsScene::items_QRectF() +{ + QGraphicsScene scene; + QGraphicsItem *item1 = scene.addRect(QRectF(-10, -10, 10, 10)); + QGraphicsItem *item2 = scene.addRect(QRectF(10, -10, 10, 10)); + QGraphicsItem *item3 = scene.addRect(QRectF(10, 10, 10, 10)); + QGraphicsItem *item4 = scene.addRect(QRectF(-10, 10, 10, 10)); + + item1->setZValue(0); + item2->setZValue(1); + item3->setZValue(2); + item4->setZValue(3); + + QCOMPARE(scene.items(QRectF(-10, -10, 10, 10)), QList<QGraphicsItem *>() << item1); + QCOMPARE(scene.items(QRectF(10, -10, 10, 10)), QList<QGraphicsItem *>() << item2); + QCOMPARE(scene.items(QRectF(10, 10, 10, 10)), QList<QGraphicsItem *>() << item3); + QCOMPARE(scene.items(QRectF(-10, 10, 10, 10)), QList<QGraphicsItem *>() << item4); + QCOMPARE(scene.items(QRectF(-10, -10, 1, 1)), QList<QGraphicsItem *>() << item1); + QCOMPARE(scene.items(QRectF(10, -10, 1, 1)), QList<QGraphicsItem *>() << item2); + QCOMPARE(scene.items(QRectF(10, 10, 1, 1)), QList<QGraphicsItem *>() << item3); + QCOMPARE(scene.items(QRectF(-10, 10, 1, 1)), QList<QGraphicsItem *>() << item4); + + QCOMPARE(scene.items(QRectF(-10, -10, 40, 10)), QList<QGraphicsItem *>() << item2 << item1); + QCOMPARE(scene.items(QRectF(-10, 10, 40, 10)), QList<QGraphicsItem *>() << item4 << item3); + + item1->setZValue(3); + item2->setZValue(2); + item3->setZValue(1); + item4->setZValue(0); + + QCOMPARE(scene.items(QRectF(-10, -10, 40, 10)), QList<QGraphicsItem *>() << item1 << item2); + QCOMPARE(scene.items(QRectF(-10, 10, 40, 10)), QList<QGraphicsItem *>() << item3 << item4); +} + +void tst_QGraphicsScene::items_QRectF_2_data() +{ + QTest::addColumn<QRectF>("ellipseRect"); + QTest::addColumn<QRectF>("sceneRect"); + QTest::addColumn<Qt::ItemSelectionMode>("selectionMode"); + QTest::addColumn<bool>("contained"); + QTest::addColumn<bool>("containedRotated"); + + // None of the rects contain the ellipse's shape nor bounding rect + QTest::newRow("1") << QRectF(0, 0, 100, 100) << QRectF(1, 1, 10, 10) << Qt::ContainsItemShape << false << false; + QTest::newRow("2") << QRectF(0, 0, 100, 100) << QRectF(1, 89, 10, 10) << Qt::ContainsItemShape << false << false; + QTest::newRow("3") << QRectF(0, 0, 100, 100) << QRectF(89, 1, 10, 10) << Qt::ContainsItemShape << false << false; + QTest::newRow("4") << QRectF(0, 0, 100, 100) << QRectF(89, 89, 10, 10) << Qt::ContainsItemShape << false << false; + QTest::newRow("5") << QRectF(0, 0, 100, 100) << QRectF(1, 1, 10, 10) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("6") << QRectF(0, 0, 100, 100) << QRectF(1, 89, 10, 10) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("7") << QRectF(0, 0, 100, 100) << QRectF(89, 1, 10, 10) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("8") << QRectF(0, 0, 100, 100) << QRectF(89, 89, 10, 10) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("9") << QRectF(0, 0, 100, 100) << QRectF(0, 0, 50, 50) << Qt::ContainsItemShape << false << false; + QTest::newRow("10") << QRectF(0, 0, 100, 100) << QRectF(0, 50, 50, 50) << Qt::ContainsItemShape << false << false; + QTest::newRow("11") << QRectF(0, 0, 100, 100) << QRectF(50, 0, 50, 50) << Qt::ContainsItemShape << false << false; + QTest::newRow("12") << QRectF(0, 0, 100, 100) << QRectF(50, 50, 50, 50) << Qt::ContainsItemShape << false << false; + QTest::newRow("13") << QRectF(0, 0, 100, 100) << QRectF(0, 0, 50, 50) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("14") << QRectF(0, 0, 100, 100) << QRectF(0, 50, 50, 50) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("15") << QRectF(0, 0, 100, 100) << QRectF(50, 0, 50, 50) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("16") << QRectF(0, 0, 100, 100) << QRectF(50, 50, 50, 50) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("17") << QRectF(0, 0, 100, 100) << QRectF(-50, -50, 100, 100) << Qt::ContainsItemShape << false << false; + QTest::newRow("18") << QRectF(0, 0, 100, 100) << QRectF(0, -50, 100, 100) << Qt::ContainsItemShape << false << false; + QTest::newRow("19") << QRectF(0, 0, 100, 100) << QRectF(-50, 0, 100, 100) << Qt::ContainsItemShape << false << false; + QTest::newRow("20") << QRectF(0, 0, 100, 100) << QRectF(0, 0, 100, 100) << Qt::ContainsItemShape << false << false; + QTest::newRow("21") << QRectF(0, 0, 100, 100) << QRectF(-50, -50, 100, 100) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("22") << QRectF(0, 0, 100, 100) << QRectF(0, -50, 100, 100) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("23") << QRectF(0, 0, 100, 100) << QRectF(-50, 0, 100, 100) << Qt::ContainsItemBoundingRect << false << false; + + // The rect is the same as the ellipse's bounding rect + QTest::newRow("24") << QRectF(0, 0, 100, 100) << QRectF(0, 0, 100, 100) << Qt::ContainsItemBoundingRect << false << false; + + // None intersects with the item's shape, but they all intersects with the + // item's bounding rect. + QTest::newRow("25") << QRectF(0, 0, 100, 100) << QRectF(1, 1, 10, 10) << Qt::IntersectsItemShape << false << false; + QTest::newRow("26") << QRectF(0, 0, 100, 100) << QRectF(1, 89, 10, 10) << Qt::IntersectsItemShape << false << true; + QTest::newRow("27") << QRectF(0, 0, 100, 100) << QRectF(89, 1, 10, 10) << Qt::IntersectsItemShape << false << false; + QTest::newRow("28") << QRectF(0, 0, 100, 100) << QRectF(89, 89, 10, 10) << Qt::IntersectsItemShape << false << false; + QTest::newRow("29") << QRectF(0, 0, 100, 100) << QRectF(1, 1, 10, 10) << Qt::IntersectsItemBoundingRect << true << true; + QTest::newRow("30") << QRectF(0, 0, 100, 100) << QRectF(1, 89, 10, 10) << Qt::IntersectsItemBoundingRect << true << true; + QTest::newRow("31") << QRectF(0, 0, 100, 100) << QRectF(89, 1, 10, 10) << Qt::IntersectsItemBoundingRect << true << false; + QTest::newRow("32") << QRectF(0, 0, 100, 100) << QRectF(89, 89, 10, 10) << Qt::IntersectsItemBoundingRect << true << false; + + // This rect does not contain the shape nor the bounding rect + QTest::newRow("33") << QRectF(0, 0, 100, 100) << QRectF(5, 5, 90, 90) << Qt::ContainsItemShape << false << false; + QTest::newRow("34") << QRectF(0, 0, 100, 100) << QRectF(5, 5, 90, 90) << Qt::ContainsItemBoundingRect << false << false; + + // It will, however, intersect with both + QTest::newRow("35") << QRectF(0, 0, 100, 100) << QRectF(5, 5, 90, 90) << Qt::IntersectsItemShape << true << true; + QTest::newRow("36") << QRectF(0, 0, 100, 100) << QRectF(5, 5, 90, 90) << Qt::IntersectsItemBoundingRect << true << true; + + // A rect that contains the whole ellipse will both contain and intersect + // with both the ellipse's shape and bounding rect. + QTest::newRow("37") << QRectF(0, 0, 100, 100) << QRectF(-5, -5, 110, 110) << Qt::IntersectsItemBoundingRect << true << true; + QTest::newRow("38") << QRectF(0, 0, 100, 100) << QRectF(-5, -5, 110, 110) << Qt::IntersectsItemShape << true << true; + QTest::newRow("39") << QRectF(0, 0, 100, 100) << QRectF(-5, -5, 110, 110) << Qt::ContainsItemBoundingRect << true << false; + QTest::newRow("40") << QRectF(0, 0, 100, 100) << QRectF(-5, -5, 110, 110) << Qt::ContainsItemShape << true << false; + + // A rect that is fully contained within the ellipse will intersect only + QTest::newRow("41") << QRectF(0, 0, 100, 100) << QRectF(40, 40, 20, 20) << Qt::ContainsItemShape << false << false; + QTest::newRow("42") << QRectF(0, 0, 100, 100) << QRectF(40, 40, 20, 20) << Qt::ContainsItemBoundingRect << false << false; + QTest::newRow("43") << QRectF(0, 0, 100, 100) << QRectF(40, 40, 20, 20) << Qt::IntersectsItemShape << true << true; + QTest::newRow("44") << QRectF(0, 0, 100, 100) << QRectF(40, 40, 20, 20) << Qt::IntersectsItemBoundingRect << true << true; +} + +void tst_QGraphicsScene::items_QRectF_2() +{ + QFETCH(QRectF, ellipseRect); + QFETCH(QRectF, sceneRect); + QFETCH(Qt::ItemSelectionMode, selectionMode); + QFETCH(bool, contained); + QFETCH(bool, containedRotated); + + QGraphicsScene scene; + QGraphicsItem *item = scene.addEllipse(ellipseRect); + + QCOMPARE(!scene.items(sceneRect, selectionMode).isEmpty(), contained); + item->rotate(45); + QCOMPARE(!scene.items(sceneRect, selectionMode).isEmpty(), containedRotated); +} + +void tst_QGraphicsScene::items_QPolygonF() +{ + QGraphicsScene scene; + QGraphicsItem *item1 = scene.addRect(QRectF(-10, -10, 10, 10)); + QGraphicsItem *item2 = scene.addRect(QRectF(10, -10, 10, 10)); + QGraphicsItem *item3 = scene.addRect(QRectF(10, 10, 10, 10)); + QGraphicsItem *item4 = scene.addRect(QRectF(-10, 10, 10, 10)); + + item1->setZValue(0); + item2->setZValue(1); + item3->setZValue(2); + item4->setZValue(3); + + QPolygonF poly1(item1->boundingRect()); + QPolygonF poly2(item2->boundingRect()); + QPolygonF poly3(item3->boundingRect()); + QPolygonF poly4(item4->boundingRect()); + + QCOMPARE(scene.items(poly1), QList<QGraphicsItem *>() << item1); + QCOMPARE(scene.items(poly2), QList<QGraphicsItem *>() << item2); + QCOMPARE(scene.items(poly3), QList<QGraphicsItem *>() << item3); + QCOMPARE(scene.items(poly4), QList<QGraphicsItem *>() << item4); + + poly1 = QPolygonF(QRectF(-10, -10, 1, 1)); + poly2 = QPolygonF(QRectF(10, -10, 1, 1)); + poly3 = QPolygonF(QRectF(10, 10, 1, 1)); + poly4 = QPolygonF(QRectF(-10, 10, 1, 1)); + + QCOMPARE(scene.items(poly1), QList<QGraphicsItem *>() << item1); + QCOMPARE(scene.items(poly2), QList<QGraphicsItem *>() << item2); + QCOMPARE(scene.items(poly3), QList<QGraphicsItem *>() << item3); + QCOMPARE(scene.items(poly4), QList<QGraphicsItem *>() << item4); + + poly1 = QPolygonF(QRectF(-10, -10, 40, 10)); + poly2 = QPolygonF(QRectF(-10, 10, 40, 10)); + + QCOMPARE(scene.items(poly1), QList<QGraphicsItem *>() << item2 << item1); + QCOMPARE(scene.items(poly2), QList<QGraphicsItem *>() << item4 << item3); + + item1->setZValue(3); + item2->setZValue(2); + item3->setZValue(1); + item4->setZValue(0); + + QCOMPARE(scene.items(poly1), QList<QGraphicsItem *>() << item1 << item2); + QCOMPARE(scene.items(poly2), QList<QGraphicsItem *>() << item3 << item4); +} + +void tst_QGraphicsScene::items_QPolygonF_2() +{ + QGraphicsScene scene; + QGraphicsItem *ellipse = scene.addEllipse(QRectF(0, 0, 100, 100)); + + // None of the rects contain the ellipse's shape nor bounding rect + QVERIFY(scene.items(QPolygonF(QRectF(1, 1, 10, 10)), Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(1, 89, 10, 10)), Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(89, 1, 10, 10)), Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(89, 89, 10, 10)), Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(1, 1, 10, 10)), Qt::ContainsItemBoundingRect).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(1, 89, 10, 10)), Qt::ContainsItemBoundingRect).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(89, 1, 10, 10)), Qt::ContainsItemBoundingRect).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(89, 89, 10, 10)), Qt::ContainsItemBoundingRect).isEmpty()); + + // None intersects with the item's shape, but they all intersects with the + // item's bounding rect. + QVERIFY(scene.items(QPolygonF(QRectF(1, 1, 10, 10)), Qt::IntersectsItemShape).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(1, 89, 10, 10)), Qt::IntersectsItemShape).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(89, 1, 10, 10)), Qt::IntersectsItemShape).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(89, 89, 10, 10)), Qt::IntersectsItemShape).isEmpty()); + QCOMPARE(scene.items(QPolygonF(QRectF(1, 1, 10, 10)), Qt::IntersectsItemBoundingRect).first(), ellipse); + QCOMPARE(scene.items(QPolygonF(QRectF(1, 89, 10, 10)), Qt::IntersectsItemBoundingRect).first(), ellipse); + QCOMPARE(scene.items(QPolygonF(QRectF(89, 1, 10, 10)), Qt::IntersectsItemBoundingRect).first(), ellipse); + QCOMPARE(scene.items(QPolygonF(QRectF(89, 89, 10, 10)), Qt::IntersectsItemBoundingRect).first(), ellipse); + + // This rect does not contain the shape nor the bounding rect + QVERIFY(scene.items(QPolygonF(QRectF(5, 5, 90, 90)), Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(QPolygonF(QRectF(5, 5, 90, 90)), Qt::ContainsItemBoundingRect).isEmpty()); + + // It will, however, intersect with both + QCOMPARE(scene.items(QPolygonF(QRectF(5, 5, 90, 90)), Qt::IntersectsItemShape).first(), ellipse); + QCOMPARE(scene.items(QPolygonF(QRectF(5, 5, 90, 90)), Qt::IntersectsItemBoundingRect).first(), ellipse); + + // A rect that contains the whole ellipse will both contain and intersect + // with both the ellipse's shape and bounding rect. + QCOMPARE(scene.items(QPolygonF(QRectF(-5, -5, 110, 110)), Qt::IntersectsItemShape).first(), ellipse); + QCOMPARE(scene.items(QPolygonF(QRectF(-5, -5, 110, 110)), Qt::ContainsItemShape).first(), ellipse); + QCOMPARE(scene.items(QPolygonF(QRectF(-5, -5, 110, 110)), Qt::IntersectsItemBoundingRect).first(), ellipse); + QCOMPARE(scene.items(QPolygonF(QRectF(-5, -5, 110, 110)), Qt::ContainsItemBoundingRect).first(), ellipse); +} + +void tst_QGraphicsScene::items_QPainterPath() +{ + QGraphicsScene scene; + QGraphicsItem *item1 = scene.addRect(QRectF(-10, -10, 10, 10)); + QGraphicsItem *item2 = scene.addRect(QRectF(10, -10, 10, 10)); + QGraphicsItem *item3 = scene.addRect(QRectF(10, 10, 10, 10)); + QGraphicsItem *item4 = scene.addRect(QRectF(-10, 10, 10, 10)); + + item1->setZValue(0); + item2->setZValue(1); + item3->setZValue(2); + item4->setZValue(3); + + QPainterPath path1; path1.addEllipse(item1->boundingRect()); + QPainterPath path2; path2.addEllipse(item2->boundingRect()); + QPainterPath path3; path3.addEllipse(item3->boundingRect()); + QPainterPath path4; path4.addEllipse(item4->boundingRect()); + + QCOMPARE(scene.items(path1), QList<QGraphicsItem *>() << item1); + QCOMPARE(scene.items(path2), QList<QGraphicsItem *>() << item2); + QCOMPARE(scene.items(path3), QList<QGraphicsItem *>() << item3); + QCOMPARE(scene.items(path4), QList<QGraphicsItem *>() << item4); + + path1 = QPainterPath(); path1.addEllipse(QRectF(-10, -10, 1, 1)); + path2 = QPainterPath(); path2.addEllipse(QRectF(10, -10, 1, 1)); + path3 = QPainterPath(); path3.addEllipse(QRectF(10, 10, 1, 1)); + path4 = QPainterPath(); path4.addEllipse(QRectF(-10, 10, 1, 1)); + + QCOMPARE(scene.items(path1), QList<QGraphicsItem *>() << item1); + QCOMPARE(scene.items(path2), QList<QGraphicsItem *>() << item2); + QCOMPARE(scene.items(path3), QList<QGraphicsItem *>() << item3); + QCOMPARE(scene.items(path4), QList<QGraphicsItem *>() << item4); + + path1 = QPainterPath(); path1.addRect(QRectF(-10, -10, 40, 10)); + path2 = QPainterPath(); path2.addRect(QRectF(-10, 10, 40, 10)); + + QCOMPARE(scene.items(path1), QList<QGraphicsItem *>() << item2 << item1); + QCOMPARE(scene.items(path2), QList<QGraphicsItem *>() << item4 << item3); + + item1->setZValue(3); + item2->setZValue(2); + item3->setZValue(1); + item4->setZValue(0); + + QCOMPARE(scene.items(path1), QList<QGraphicsItem *>() << item1 << item2); + QCOMPARE(scene.items(path2), QList<QGraphicsItem *>() << item3 << item4); +} + +void tst_QGraphicsScene::items_QPainterPath_2() +{ + QGraphicsScene scene; + QGraphicsItem *ellipse = scene.addEllipse(QRectF(0, 0, 100, 100)); + + QPainterPath p1; p1.addRect(QRectF(1, 1, 10, 10)); + QPainterPath p2; p2.addRect(QRectF(1, 89, 10, 10)); + QPainterPath p3; p3.addRect(QRectF(89, 1, 10, 10)); + QPainterPath p4; p4.addRect(QRectF(89, 89, 10, 10)); + + // None of the rects contain the ellipse's shape nor bounding rect + QVERIFY(scene.items(p1, Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(p2, Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(p3, Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(p4, Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(p1, Qt::ContainsItemBoundingRect).isEmpty()); + QVERIFY(scene.items(p2, Qt::ContainsItemBoundingRect).isEmpty()); + QVERIFY(scene.items(p3, Qt::ContainsItemBoundingRect).isEmpty()); + QVERIFY(scene.items(p4, Qt::ContainsItemBoundingRect).isEmpty()); + + // None intersects with the item's shape, but they all intersects with the + // item's bounding rect. + QVERIFY(scene.items(p1, Qt::IntersectsItemShape).isEmpty()); + QVERIFY(scene.items(p2, Qt::IntersectsItemShape).isEmpty()); + QVERIFY(scene.items(p3, Qt::IntersectsItemShape).isEmpty()); + QVERIFY(scene.items(p4, Qt::IntersectsItemShape).isEmpty()); + QCOMPARE(scene.items(p1, Qt::IntersectsItemBoundingRect).first(), ellipse); + QCOMPARE(scene.items(p2, Qt::IntersectsItemBoundingRect).first(), ellipse); + QCOMPARE(scene.items(p3, Qt::IntersectsItemBoundingRect).first(), ellipse); + QCOMPARE(scene.items(p4, Qt::IntersectsItemBoundingRect).first(), ellipse); + + QPainterPath p5; + p5.addRect(QRectF(5, 5, 90, 90)); + + // This rect does not contain the shape nor the bounding rect + QVERIFY(scene.items(p5, Qt::ContainsItemShape).isEmpty()); + QVERIFY(scene.items(p5, Qt::ContainsItemBoundingRect).isEmpty()); + + // It will, however, intersect with both + QCOMPARE(scene.items(p5, Qt::IntersectsItemShape).first(), ellipse); + QCOMPARE(scene.items(p5, Qt::IntersectsItemBoundingRect).first(), ellipse); + + QPainterPath p6; + p6.addRect(QRectF(-5, -5, 110, 110)); + + // A rect that contains the whole ellipse will both contain and intersect + // with both the ellipse's shape and bounding rect. + QCOMPARE(scene.items(p6, Qt::IntersectsItemShape).first(), ellipse); + QCOMPARE(scene.items(p6, Qt::ContainsItemShape).first(), ellipse); + QCOMPARE(scene.items(p6, Qt::IntersectsItemBoundingRect).first(), ellipse); + QCOMPARE(scene.items(p6, Qt::ContainsItemBoundingRect).first(), ellipse); +} + +void tst_QGraphicsScene::selection() +{ + // ### This test is difficult to make work for all platforms; instead, a + // hand crafted data set would make it stable. Its behavior is thoroughly + // covered by other tests. Todo: Fix this test. + + /* + QGraphicsScene scene; + QMap<QGraphicsItem *, int> itemIndexes; + for (int i = 0; i < 256; ++i) { + QPainterPath path; + path.addRect(randomX[i], randomY[i], 25, 25); + + QGraphicsPathItem *pathItem = scene.addPath(path); + pathItem->setFlag(QGraphicsItem::ItemIsSelectable); + itemIndexes.insert(pathItem, i); + } + +#if 0 + // Write data + QFile::remove("graphicsScene_selection.data"); + QFile file("graphicsScene_selection.data"); + if (!file.open(QFile::WriteOnly)) + QFAIL("Unable to generate data file graphicsScene_selection.data"); + QDataStream stream(&file); + for (qreal y = -1000; y < 1000; y += 33) { + for (qreal x = -1000; x < 1000; x += 33) { + for (qreal size = 1; size < 200; size += 33) { + QPainterPath path; + path.addRect(QRectF(x, y, size, size)); + scene.setSelectionArea(path); + QCOMPARE(scene.selectionArea(), path); + + QList<int> indexes; + foreach (QGraphicsItem *item, scene.selectedItems()) + indexes << itemIndexes.value(item); + + stream << x << y << size << indexes; + } + } + } +#else + // Read data + QFile file("graphicsScene_selection.data"); + if (!file.open(QFile::ReadOnly)) + QFAIL("Unable to load data file graphicsScene_selection.data"); + + QDataStream stream(&file); + + while (!stream.atEnd()) { + QList<int> expectedIndexes; + + qreal x, y, size; + stream >> x >> y >> size >> expectedIndexes; + + QPainterPath path; + path.addRect(QRectF(x, y, size, size)); + scene.setSelectionArea(path); + QCOMPARE(scene.selectionArea(), path); + + QList<int> indexes; + foreach (QGraphicsItem *item, scene.selectedItems()) + indexes << itemIndexes.value(item); + + qSort(indexes); + qSort(expectedIndexes); + + QCOMPARE(indexes, expectedIndexes); + + scene.clearSelection(); + QVERIFY(scene.selectedItems().isEmpty()); + } +#endif + */ +} + +class CustomView : public QGraphicsView +{ +public: + CustomView() : repaints(0) + { } + + int repaints; +protected: + void paintEvent(QPaintEvent *event) + { + ++repaints; + QGraphicsView::paintEvent(event); + } +}; + +void tst_QGraphicsScene::selectionChanged() +{ + QGraphicsScene scene(0, 0, 1000, 1000); + QSignalSpy spy(&scene, SIGNAL(selectionChanged())); + QCOMPARE(spy.count(), 0); + + QPainterPath path; + path.addRect(scene.sceneRect()); + QCOMPARE(scene.selectionArea(), QPainterPath()); + scene.setSelectionArea(path); + QCOMPARE(scene.selectionArea(), path); + QCOMPARE(spy.count(), 0); // selection didn't change + QVERIFY(scene.selectedItems().isEmpty()); + + QGraphicsItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); + QCOMPARE(spy.count(), 0); // selection didn't change + + rect->setSelected(true); + QVERIFY(!rect->isSelected()); + QCOMPARE(spy.count(), 0); // selection didn't change, item isn't selectable + + rect->setFlag(QGraphicsItem::ItemIsSelectable); + rect->setSelected(true); + QVERIFY(rect->isSelected()); + QCOMPARE(spy.count(), 1); // selection changed + QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>() << rect); + + rect->setSelected(false); + QVERIFY(!rect->isSelected()); + QCOMPARE(spy.count(), 2); // selection changed + QVERIFY(scene.selectedItems().isEmpty()); + + QGraphicsEllipseItem *parentItem = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100)); + QGraphicsEllipseItem *childItem = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100), parentItem); + QGraphicsEllipseItem *grandChildItem = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100), childItem); + grandChildItem->setFlag(QGraphicsItem::ItemIsSelectable); + grandChildItem->setSelected(true); + grandChildItem->setSelected(false); + grandChildItem->setSelected(true); + scene.addItem(parentItem); + + QCOMPARE(spy.count(), 3); // the grandchild was added, so the selection changed once + + scene.removeItem(parentItem); + QCOMPARE(spy.count(), 4); // the grandchild was removed, so the selection changed + + rect->setSelected(true); + QCOMPARE(spy.count(), 5); // the rect was reselected, so the selection changed + + scene.clearSelection(); + QCOMPARE(spy.count(), 6); // the scene selection was cleared + + rect->setSelected(true); + QCOMPARE(spy.count(), 7); // the rect was reselected, so the selection changed + + rect->setFlag(QGraphicsItem::ItemIsSelectable, false); + QCOMPARE(spy.count(), 8); // the rect was unselected, so the selection changed + + rect->setSelected(true); + QCOMPARE(spy.count(), 8); // the rect is not longer selectable, so the selection does not change + + + rect->setFlag(QGraphicsItem::ItemIsSelectable, true); + rect->setSelected(true); + QCOMPARE(spy.count(), 9); // the rect is again selectable, so the selection changed + + delete rect; + QCOMPARE(spy.count(), 10); // a selected item was deleted; selection changed +} + +void tst_QGraphicsScene::selectionChanged2() +{ + QGraphicsScene scene; + QSignalSpy spy(&scene, SIGNAL(selectionChanged())); + + QGraphicsItem *item1 = scene.addRect(0, 0, 100, 100); + QGraphicsItem *item2 = scene.addRect(100, 100, 100, 100); + item1->setFlag(QGraphicsItem::ItemIsSelectable); + item2->setFlag(QGraphicsItem::ItemIsSelectable); + + QCOMPARE(spy.count(), 0); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(QPointF(50, 50)); + event.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + } + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setScenePos(QPointF(50, 50)); + event.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + } + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + QCOMPARE(spy.count(), 1); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(QPointF(150, 150)); + event.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + } + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setScenePos(QPointF(150, 150)); + event.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + } + QVERIFY(!item1->isSelected()); + QVERIFY(item2->isSelected()); + QCOMPARE(spy.count(), 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(QPointF(50, 50)); + event.setButton(Qt::LeftButton); + event.setModifiers(Qt::ControlModifier); + qApp->sendEvent(&scene, &event); + } + QVERIFY(!item1->isSelected()); + QVERIFY(item2->isSelected()); + QCOMPARE(spy.count(), 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setScenePos(QPointF(50, 50)); + event.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + } + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + QCOMPARE(spy.count(), 3); +} + +void tst_QGraphicsScene::addItem() +{ + Q_CHECK_PAINTEVENTS + { + // 1) Create item, then scene, then add item + QGraphicsItem *path = new QGraphicsEllipseItem(QRectF(-10, -10, 20, 20)); + QGraphicsScene scene; + + CustomView view; + view.setScene(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + qApp->processEvents(); + view.repaints = 0; + + scene.addItem(path); + + // Adding an item should always issue a repaint. + qApp->processEvents(); // <- delayed update is called + qApp->processEvents(); // <- scene schedules pending update + qApp->processEvents(); // <- pending update is sent to view + QVERIFY(view.repaints > 0); + view.repaints = 0; + + QCOMPARE(scene.itemAt(0, 0), path); + + QGraphicsItem *path2 = new QGraphicsEllipseItem(QRectF(-10, -10, 20, 20)); + path2->setPos(100, 100); + + QCOMPARE(scene.itemAt(0, 0), path); + QCOMPARE(scene.itemAt(100, 100), (QGraphicsItem *)0); + scene.addItem(path2); + + // Adding an item should always issue a repaint. + qApp->processEvents(); // <- delayed update is called + qApp->processEvents(); // <- scene schedules pending update + qApp->processEvents(); // <- pending update is sent to view + QVERIFY(view.repaints > 0); + + QCOMPARE(scene.itemAt(100, 100), path2); + } + { + // 2) Create scene, then item, then add item + QGraphicsScene scene; + QGraphicsItem *path = new QGraphicsEllipseItem(QRectF(-10, -10, 20, 20)); + scene.addItem(path); + + QGraphicsItem *path2 = new QGraphicsEllipseItem(QRectF(-10, -10, 20, 20)); + path2->setPos(100, 100); + scene.addItem(path2); + + QCOMPARE(scene.itemAt(0, 0), path); + QCOMPARE(scene.itemAt(100, 100), path2); + } +} + +void tst_QGraphicsScene::addEllipse() +{ + QGraphicsScene scene; + QGraphicsEllipseItem *ellipse = scene.addEllipse(QRectF(-10, -10, 20, 20), + QPen(Qt::red), QBrush(Qt::blue)); + QCOMPARE(ellipse->pos(), QPointF()); + QCOMPARE(ellipse->pen(), QPen(Qt::red)); + QCOMPARE(ellipse->brush(), QBrush(Qt::blue)); + QCOMPARE(ellipse->rect(), QRectF(-10, -10, 20, 20)); + QCOMPARE(scene.itemAt(0, 0), (QGraphicsItem *)ellipse); + QCOMPARE(scene.itemAt(-10, -10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(-9.9, 0), (QGraphicsItem *)ellipse); + QCOMPARE(scene.itemAt(-10, 10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(0, -9.9), (QGraphicsItem *)ellipse); + QCOMPARE(scene.itemAt(0, 9.9), (QGraphicsItem *)ellipse); + QCOMPARE(scene.itemAt(10, -10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(9.9, 0), (QGraphicsItem *)ellipse); + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)0); +} + +void tst_QGraphicsScene::addLine() +{ + QGraphicsScene scene; + QPen pen(Qt::red); + pen.setWidthF(1.0); + QGraphicsLineItem *line = scene.addLine(QLineF(-10, -10, 20, 20), + pen); + QCOMPARE(line->pos(), QPointF()); + QCOMPARE(line->pen(), pen); + QCOMPARE(line->line(), QLineF(-10, -10, 20, 20)); + QCOMPARE(scene.itemAt(0, 0), (QGraphicsItem *)line); + QCOMPARE(scene.itemAt(-10, -10), (QGraphicsItem *)line); + QCOMPARE(scene.itemAt(-9.9, 0), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(-10, 10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(0, -9.9), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(0, 9.9), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(10, -10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(9.9, 0), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)line); +} + +void tst_QGraphicsScene::addPath() +{ + QGraphicsScene scene; + QPainterPath p; + p.addEllipse(QRectF(-10, -10, 20, 20)); + p.addEllipse(QRectF(-10, 20, 20, 20)); + + QGraphicsPathItem *path = scene.addPath(p, QPen(Qt::red), QBrush(Qt::blue)); + QCOMPARE(path->pos(), QPointF()); + QCOMPARE(path->pen(), QPen(Qt::red)); + QCOMPARE(path->path(), p); + QCOMPARE(path->brush(), QBrush(Qt::blue)); + QCOMPARE(scene.itemAt(0, 0), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(-9.9, 0), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(9.9, 0), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(0, -9.9), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(0, 9.9), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(0, 30), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(-9.9, 30), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(9.9, 30), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(0, 20.1), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(0, 39.9), (QGraphicsItem *)path); + QCOMPARE(scene.itemAt(-10, -10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(10, -10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(-10, 10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(-10, 20), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(10, 20), (QGraphicsItem *)0); +if (sizeof(qreal) != sizeof(double)) + QWARN("Skipping test because of rounding errors when qreal != double"); +else + QCOMPARE(scene.itemAt(-10, 30), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(10.1, 30), (QGraphicsItem *)0); +} + +void tst_QGraphicsScene::addPixmap() +{ + QGraphicsScene scene; + QPixmap pix(":/Ash_European.jpg"); + QGraphicsPixmapItem *pixmap = scene.addPixmap(pix); + + QCOMPARE(pixmap->pos(), QPointF()); + QCOMPARE(pixmap->pixmap(), pix); + QCOMPARE(scene.itemAt(0, 0), (QGraphicsItem *)pixmap); + QCOMPARE(scene.itemAt(pix.width() - 1, 0), (QGraphicsItem *)pixmap); + QCOMPARE(scene.itemAt(0, pix.height() - 1), (QGraphicsItem *)pixmap); + QCOMPARE(scene.itemAt(pix.width() - 1, pix.height() - 1), (QGraphicsItem *)pixmap); + QCOMPARE(scene.itemAt(-1, -1), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(pix.width() - 1, -1), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(-1, pix.height() - 1), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(pix.width(), pix.height()), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(0, pix.height()), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(pix.width(), 0), (QGraphicsItem *)0); +} + +void tst_QGraphicsScene::addRect() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect = scene.addRect(QRectF(-10, -10, 20, 20), + QPen(Qt::red), QBrush(Qt::blue)); + QCOMPARE(rect->pos(), QPointF()); + QCOMPARE(rect->pen(), QPen(Qt::red)); + QCOMPARE(rect->brush(), QBrush(Qt::blue)); + QCOMPARE(rect->rect(), QRectF(-10, -10, 20, 20)); + QCOMPARE(scene.itemAt(0, 0), (QGraphicsItem *)rect); + QCOMPARE(scene.itemAt(-10, -10), (QGraphicsItem *)rect); + QCOMPARE(scene.itemAt(-9.9, 0), (QGraphicsItem *)rect); + QCOMPARE(scene.itemAt(-10, 10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(0, -9.9), (QGraphicsItem *)rect); + QCOMPARE(scene.itemAt(0, 9.9), (QGraphicsItem *)rect); + QCOMPARE(scene.itemAt(10, -10), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(9.9, 0), (QGraphicsItem *)rect); + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)0); +} + +void tst_QGraphicsScene::addText() +{ + QGraphicsScene scene; + QGraphicsTextItem *text = scene.addText("Qt", QFont()); + QCOMPARE(text->pos(), QPointF()); + QCOMPARE(text->toPlainText(), QString("Qt")); + QCOMPARE(text->font(), QFont()); +} + +#if !defined(Q_OS_WINCE) || defined(GWES_ICONCURS) +void tst_QGraphicsScene::removeItem() +{ + QGraphicsScene scene; + QGraphicsItem *item = scene.addRect(QRectF(0, 0, 10, 10)); + QCOMPARE(scene.itemAt(0, 0), item); // forces indexing + scene.removeItem(item); + QCOMPARE(scene.itemAt(0, 0), (QGraphicsItem *)0); + delete item; + + QGraphicsItem *item2 = scene.addRect(QRectF(0, 0, 10, 10)); + item2->setFlag(QGraphicsItem::ItemIsSelectable); + QCOMPARE(scene.itemAt(0, 0), item2); + + // Removing a selected item + QVERIFY(scene.selectedItems().isEmpty()); + item2->setSelected(true); + QVERIFY(scene.selectedItems().contains(item2)); + scene.removeItem(item2); + QVERIFY(scene.selectedItems().isEmpty()); + + // Check that we are in a state that can receive paint events + // (i.e., not logged out on Windows). + Q_CHECK_PAINTEVENTS + + // Removing a hovered item + HoverItem *hoverItem = new HoverItem; + scene.addItem(hoverItem); + scene.setSceneRect(-50, -50, 100, 100); + + QGraphicsView view(&scene); + view.setFixedSize(150, 150); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::mouseMove(view.viewport(), QPoint(-1, -1)); + { + QMouseEvent moveEvent(QEvent::MouseMove, view.mapFromScene(hoverItem->scenePos() + QPointF(20, 20)), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &moveEvent); + } + qApp->processEvents(); // update + qApp->processEvents(); // draw + QVERIFY(!hoverItem->isHovered); + + { + QTest::qWait(250); + QTest::mouseMove(view.viewport(), view.mapFromScene(hoverItem->scenePos()), Qt::NoButton); + QTest::qWait(10); + QMouseEvent moveEvent(QEvent::MouseMove, view.mapFromScene(hoverItem->scenePos()), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &moveEvent); + } + qApp->processEvents(); // update + qApp->processEvents(); // draw + QVERIFY(hoverItem->isHovered); + + scene.removeItem(hoverItem); + hoverItem->setAcceptsHoverEvents(false); + scene.addItem(hoverItem); + qApp->processEvents(); // <- delayed update is called + qApp->processEvents(); // <- scene schedules pending update + qApp->processEvents(); // <- pending update is sent to view + QVERIFY(!hoverItem->isHovered); +} +#endif + +void tst_QGraphicsScene::focusItem() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QVERIFY(!scene.focusItem()); + QGraphicsItem *item = scene.addText("Qt"); + QVERIFY(!scene.focusItem()); + item->setFocus(); + QVERIFY(!scene.focusItem()); + item->setFlag(QGraphicsItem::ItemIsFocusable); + QVERIFY(!scene.focusItem()); + item->setFocus(); + QCOMPARE(scene.focusItem(), item); + + QFocusEvent focusOut(QEvent::FocusOut); + QApplication::sendEvent(&scene, &focusOut); + + QVERIFY(!scene.focusItem()); + + QFocusEvent focusIn(QEvent::FocusIn); + QApplication::sendEvent(&scene, &focusIn); + QCOMPARE(scene.focusItem(), item); + + QGraphicsItem *item2 = scene.addText("Qt"); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + QCOMPARE(scene.focusItem(), item); + + item2->setFocus(); + QCOMPARE(scene.focusItem(), item2); + item->setFocus(); + QCOMPARE(scene.focusItem(), item); + + item2->setFocus(); + QCOMPARE(scene.focusItem(), item2); + QApplication::sendEvent(&scene, &focusOut); + QVERIFY(!scene.hasFocus()); + QVERIFY(!scene.focusItem()); + QApplication::sendEvent(&scene, &focusIn); + QCOMPARE(scene.focusItem(), item2); + + QApplication::sendEvent(&scene, &focusOut); + + QVERIFY(!scene.focusItem()); + scene.removeItem(item2); + delete item2; + + QApplication::sendEvent(&scene, &focusIn); + QVERIFY(!scene.focusItem()); +} + +class FocusItem : public QGraphicsTextItem +{ +protected: + void focusOutEvent(QFocusEvent *) + { + QVERIFY(!scene()->focusItem()); + } +}; + +void tst_QGraphicsScene::focusItemLostFocus() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + FocusItem *item = new FocusItem; + item->setTextInteractionFlags(Qt::TextEditorInteraction); + scene.addItem(item); + + item->setFocus(); + QCOMPARE(scene.focusItem(), (QGraphicsItem *)item); + item->clearFocus(); +} + +class ClearTestItem : public QGraphicsRectItem +{ +public: + ClearTestItem(QGraphicsItem *parent = 0) : QGraphicsRectItem(parent) {} + ~ClearTestItem() { qDeleteAll(items); } + QList<QGraphicsItem *> items; +}; + +void tst_QGraphicsScene::clear() +{ + QGraphicsScene scene; + scene.clear(); + QVERIFY(scene.items().isEmpty()); + scene.addRect(0, 0, 100, 100); + QCOMPARE(scene.sceneRect(), QRectF(0, 0, 100, 100)); + scene.clear(); + QVERIFY(scene.items().isEmpty()); + QCOMPARE(scene.sceneRect(), QRectF(0, 0, 100, 100)); + + ClearTestItem *firstItem = new ClearTestItem; + QGraphicsItem *secondItem = new QGraphicsRectItem; + firstItem->items += secondItem; + + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + scene.addItem(firstItem); + scene.addItem(secondItem); + QCOMPARE(scene.items().at(0), (QGraphicsItem*)firstItem); + QCOMPARE(scene.items().at(1), secondItem); + + ClearTestItem *thirdItem = new ClearTestItem(firstItem); + QGraphicsItem *forthItem = new QGraphicsRectItem(firstItem); + thirdItem->items += forthItem; + + // must not crash even if firstItem deletes secondItem + scene.clear(); + QVERIFY(scene.items().isEmpty()); +} + +void tst_QGraphicsScene::setFocusItem() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsItem *item = scene.addText("Qt"); + QVERIFY(!scene.focusItem()); + QVERIFY(!scene.hasFocus()); + scene.setFocusItem(item); + QVERIFY(!scene.hasFocus()); + QVERIFY(!scene.focusItem()); + item->setFlag(QGraphicsItem::ItemIsFocusable); + + for (int i = 0; i < 3; ++i) { + scene.setFocusItem(item); + QVERIFY(scene.hasFocus()); + QCOMPARE(scene.focusItem(), item); + QVERIFY(item->hasFocus()); + } + + QGraphicsItem *item2 = scene.addText("Qt"); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + + scene.setFocusItem(item2); + QVERIFY(!item->hasFocus()); + QVERIFY(item2->hasFocus()); + + scene.setFocusItem(item); + QVERIFY(item->hasFocus()); + QVERIFY(!item2->hasFocus()); + + scene.clearFocus(); + QVERIFY(!item->hasFocus()); + QVERIFY(!item2->hasFocus()); + + scene.setFocus(); + QVERIFY(item->hasFocus()); + QVERIFY(!item2->hasFocus()); + + scene.setFocusItem(0); + QVERIFY(!item->hasFocus()); + QVERIFY(!item2->hasFocus()); + + scene.setFocus(); + QVERIFY(!item->hasFocus()); + QVERIFY(!item2->hasFocus()); +} + +void tst_QGraphicsScene::setFocusItem_inactive() +{ + QGraphicsScene scene; + QGraphicsItem *item = scene.addText("Qt"); + QVERIFY(!scene.focusItem()); + QVERIFY(!scene.hasFocus()); + scene.setFocusItem(item); + QVERIFY(!scene.hasFocus()); + QVERIFY(!scene.focusItem()); + item->setFlag(QGraphicsItem::ItemIsFocusable); + + for (int i = 0; i < 3; ++i) { + scene.setFocusItem(item); + QCOMPARE(scene.focusItem(), item); + QVERIFY(!item->hasFocus()); + } + +} + + +void tst_QGraphicsScene::mouseGrabberItem() +{ + QGraphicsScene scene; + QVERIFY(!scene.mouseGrabberItem()); + + QGraphicsItem *item = scene.addRect(QRectF(-10, -10, 20, 20)); + item->setFlag(QGraphicsItem::ItemIsMovable); + item->setZValue(1); + + QGraphicsItem *item2 = scene.addRect(QRectF(-10, -10, 20, 20)); + item2->setFlag(QGraphicsItem::ItemIsMovable); + item2->setZValue(0); + + for (int i = 0; i < 3; ++i) { + item->setPos(0, 0); + item2->setPos(0, 0); + item->setZValue((i & 1) ? 0 : 1); + item2->setZValue((i & 1) ? 1 : 0); + QGraphicsItem *topMostItem = (i & 1) ? item2 : item; + + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(0, 0)); + pressEvent.setScreenPos(QPoint(100, 100)); + + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(scene.mouseGrabberItem(), topMostItem); + + for (int i = 0; i < 1000; ++i) { + QGraphicsSceneMouseEvent moveEvent(QEvent::GraphicsSceneMouseMove); + moveEvent.setButtons(Qt::LeftButton); + moveEvent.setScenePos(QPointF(i * 10, i * 10)); + moveEvent.setScreenPos(QPoint(100 + i * 10, 100 + i * 10)); + QApplication::sendEvent(&scene, &moveEvent); + QCOMPARE(scene.mouseGrabberItem(), topMostItem); + + // Geometrical changes should not affect the mouse grabber. + item->setZValue(rand() % 500); + item2->setZValue(rand() % 500); + item->setPos(rand() % 50000, rand() % 50000); + item2->setPos(rand() % 50000, rand() % 50000); + } + + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + releaseEvent.setScenePos(QPointF(10000, 10000)); + releaseEvent.setScreenPos(QPoint(1000000, 1000000)); + QApplication::sendEvent(&scene, &releaseEvent); + QVERIFY(!scene.mouseGrabberItem()); + } + + // Structural change: deleting the mouse grabber + item->setPos(0, 0); + item->setZValue(1); + item2->setPos(0, 0); + item2->setZValue(0); + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(0, 0)); + pressEvent.setScreenPos(QPoint(100, 100)); + + QGraphicsSceneMouseEvent moveEvent(QEvent::GraphicsSceneMouseMove); + moveEvent.setButtons(Qt::LeftButton); + moveEvent.setScenePos(QPointF(0, 0)); + moveEvent.setScreenPos(QPoint(100, 100)); + + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &moveEvent); + QCOMPARE(scene.mouseGrabberItem(), item); + item->setVisible(false); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(scene.mouseGrabberItem(), item2); + item2->setVisible(false); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QApplication::sendEvent(&scene, &moveEvent); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + item2->setVisible(true); + QApplication::sendEvent(&scene, &moveEvent); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &moveEvent); + QCOMPARE(scene.mouseGrabberItem(), item2); + scene.removeItem(item2); + delete item2; + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); +} + +void tst_QGraphicsScene::hoverEvents_siblings() +{ + Q_CHECK_PAINTEVENTS + + QGraphicsScene scene; + QGraphicsItem *lastItem = 0; + QList<HoverItem *> items; + for (int i = 0; i < 15; ++i) { + QGraphicsItem *item = new HoverItem; + scene.addItem(item); + items << (HoverItem *)item; + if (lastItem) { + item->setPos(lastItem->pos() + QPointF(sin(i / 3.0) * 17, cos(i / 3.0) * 17)); + } + item->setZValue(i); + lastItem = item; + } + + QGraphicsView view(&scene); + view.setRenderHint(QPainter::Antialiasing, true); +#if defined(Q_OS_WINCE) + view.setMinimumSize(230, 200); +#else + view.setMinimumSize(400, 300); +#endif + view.rotate(10); + view.scale(1.7, 1.7); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + qApp->setActiveWindow(&view); + view.activateWindow(); + QTest::qWait(70); + + QCursor::setPos(view.mapToGlobal(QPoint(-5, -5))); + + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); + mouseEvent.setScenePos(QPointF(-1000, -1000)); + QApplication::sendEvent(&scene, &mouseEvent); + + QTest::qWait(50); + + for (int j = 1; j >= 0; --j) { + int i = j ? 0 : 14; + forever { + if (j) + QVERIFY(!items.at(i)->isHovered); + else + QVERIFY(!items.at(i)->isHovered); + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); + mouseEvent.setScenePos(items.at(i)->mapToScene(0, 0)); + QApplication::sendEvent(&scene, &mouseEvent); + + qApp->processEvents(); // this posts updates from the scene to the view + qApp->processEvents(); // which trigger a repaint here + + QTRY_VERIFY(items.at(i)->isHovered); + if (j && i > 0) + QVERIFY(!items.at(i - 1)->isHovered); + if (!j && i < 14) + QVERIFY(!items.at(i + 1)->isHovered); + i += j ? 1 : -1; + if ((j && i == 15) || (!j && i == -1)) + break; + } + + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); + mouseEvent.setScenePos(QPointF(-1000, -1000)); + QApplication::sendEvent(&scene, &mouseEvent); + + qApp->processEvents(); // this posts updates from the scene to the view + qApp->processEvents(); // which trigger a repaint here + } +} + +void tst_QGraphicsScene::hoverEvents_parentChild() +{ + Q_CHECK_PAINTEVENTS + + QGraphicsScene scene; + QGraphicsItem *lastItem = 0; + QList<HoverItem *> items; + for (int i = 0; i < 15; ++i) { + QGraphicsItem *item = new HoverItem; + scene.addItem(item); + items << (HoverItem *)item; + if (lastItem) { + item->setParentItem(lastItem); + item->setPos(sin(i / 3.0) * 17, cos(i / 3.0) * 17); + } + lastItem = item; + } + + QGraphicsView view(&scene); + view.setRenderHint(QPainter::Antialiasing, true); +#if defined(Q_OS_WINCE) + view.setMinimumSize(230, 200); +#else + view.setMinimumSize(400, 300); +#endif + view.rotate(10); + view.scale(1.7, 1.7); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(70); + + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); + mouseEvent.setScenePos(QPointF(-1000, -1000)); + QApplication::sendEvent(&scene, &mouseEvent); + + for (int j = 1; j >= 0; --j) { + int i = j ? 0 : 14; + forever { + if (j) { + QVERIFY(!items.at(i)->isHovered); + } else { + if (i == 14) + QVERIFY(!items.at(13)->isHovered); + } + mouseEvent.setScenePos(items.at(i)->mapToScene(0, 0)); + QApplication::sendEvent(&scene, &mouseEvent); + + qApp->processEvents(); // this posts updates from the scene to the view + qApp->processEvents(); // which trigger a repaint here + + QTRY_VERIFY(items.at(i)->isHovered); + if (i < 14) + QVERIFY(!items.at(i + 1)->isHovered); + i += j ? 1 : -1; + if ((j && i == 15) || (!j && i == -1)) + break; + } + + mouseEvent.setScenePos(QPointF(-1000, -1000)); + QApplication::sendEvent(&scene, &mouseEvent); + + qApp->processEvents(); // this posts updates from the scene to the view + qApp->processEvents(); // which trigger a repaint here + } +} + +void tst_QGraphicsScene::createItemGroup() +{ + QGraphicsScene scene; + + QList<QGraphicsItem *> children1; + children1 << scene.addRect(QRectF(-10, -10, 20, 20)); + children1 << scene.addRect(QRectF(-10, -10, 20, 20)); + children1 << scene.addRect(QRectF(-10, -10, 20, 20)); + children1 << scene.addRect(QRectF(-10, -10, 20, 20)); + + QList<QGraphicsItem *> children2; + children2 << scene.addRect(QRectF(-10, -10, 20, 20)); + children2 << scene.addRect(QRectF(-10, -10, 20, 20)); + children2 << scene.addRect(QRectF(-10, -10, 20, 20)); + children2 << scene.addRect(QRectF(-10, -10, 20, 20)); + + QList<QGraphicsItem *> children3; + children3 << scene.addRect(QRectF(-10, -10, 20, 20)); + children3 << scene.addRect(QRectF(-10, -10, 20, 20)); + children3 << scene.addRect(QRectF(-10, -10, 20, 20)); + children3 << scene.addRect(QRectF(-10, -10, 20, 20)); + + // All items in children1 are children of parent1 + QGraphicsItem *parent1 = scene.addRect(QRectF(-10, -10, 20, 20)); + foreach (QGraphicsItem *item, children1) + item->setParentItem(parent1); + + QGraphicsItemGroup *group = scene.createItemGroup(children1); + QCOMPARE(group->parentItem(), parent1); + QCOMPARE(children1.first()->parentItem(), (QGraphicsItem *)group); + scene.destroyItemGroup(group); + QCOMPARE(children1.first()->parentItem(), parent1); + group = scene.createItemGroup(children1); + QCOMPARE(group->parentItem(), parent1); + QCOMPARE(children1.first()->parentItem(), (QGraphicsItem *)group); + scene.destroyItemGroup(group); + QCOMPARE(children1.first()->parentItem(), parent1); + + // All items in children2 are children of parent2 + QGraphicsItem *parent2 = scene.addRect(QRectF(-10, -10, 20, 20)); + foreach (QGraphicsItem *item, children2) + item->setParentItem(parent2); + + // Now make parent2 a child of parent1, so all children2 are also children + // of parent1. + parent2->setParentItem(parent1); + + // The children2 group should still have parent2 as their common ancestor. + group = scene.createItemGroup(children2); + QCOMPARE(group->parentItem(), parent2); + QCOMPARE(children2.first()->parentItem(), (QGraphicsItem *)group); + scene.destroyItemGroup(group); + QCOMPARE(children2.first()->parentItem(), parent2); + + // But the set of both children2 and children1 share only parent1. + group = scene.createItemGroup(children2 + children1); + QCOMPARE(group->parentItem(), parent1); + QCOMPARE(children1.first()->parentItem(), (QGraphicsItem *)group); + QCOMPARE(children2.first()->parentItem(), (QGraphicsItem *)group); + scene.destroyItemGroup(group); + QCOMPARE(children1.first()->parentItem(), parent1); + QCOMPARE(children2.first()->parentItem(), parent1); + + // Fixup the parent-child chain + foreach (QGraphicsItem *item, children2) + item->setParentItem(parent2); + + // These share no common parent + group = scene.createItemGroup(children3); + QCOMPARE(group->parentItem(), (QGraphicsItem *)0); + scene.destroyItemGroup(group); + + // Make children3 children of parent3 + QGraphicsItem *parent3 = scene.addRect(QRectF(-10, -10, 20, 20)); + foreach (QGraphicsItem *item, children3) + item->setParentItem(parent3); + + // These should have parent3 as a parent + group = scene.createItemGroup(children3); + QCOMPARE(group->parentItem(), parent3); + scene.destroyItemGroup(group); + + // Now make them all children of parent1 + parent3->setParentItem(parent1); + + group = scene.createItemGroup(children3); + QCOMPARE(group->parentItem(), parent3); + scene.destroyItemGroup(group); + + group = scene.createItemGroup(children2); + QCOMPARE(group->parentItem(), parent2); + scene.destroyItemGroup(group); + + group = scene.createItemGroup(children1); + QCOMPARE(group->parentItem(), parent1); + scene.destroyItemGroup(group); + + QGraphicsItemGroup *emptyGroup = scene.createItemGroup(QList<QGraphicsItem *>()); + QCOMPARE(emptyGroup->children(), QList<QGraphicsItem *>()); + QVERIFY(!emptyGroup->parentItem()); + QCOMPARE(emptyGroup->scene(), &scene); +} + +class EventTester : public QGraphicsEllipseItem +{ +public: + EventTester() + : QGraphicsEllipseItem(QRectF(-10, -10, 20, 20)), ignoreMouse(false) + { } + + bool ignoreMouse; + QList<QEvent::Type> eventTypes; + +protected: + bool sceneEvent(QEvent *event) + { + eventTypes << QEvent::Type(event->type()); + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + if (ignoreMouse) { + event->ignore(); + return true; + } + default: + break; + } + + return QGraphicsEllipseItem::sceneEvent(event); + } +}; + +void tst_QGraphicsScene::mouseEventPropagation() +{ + EventTester *a = new EventTester; + EventTester *b = new EventTester; + EventTester *c = new EventTester; + EventTester *d = new EventTester; + b->setParentItem(a); + c->setParentItem(b); + d->setParentItem(c); + + a->setFlag(QGraphicsItem::ItemIsMovable); + b->setFlag(QGraphicsItem::ItemIsMovable); + c->setFlag(QGraphicsItem::ItemIsMovable); + d->setFlag(QGraphicsItem::ItemIsMovable); + + a->setData(0, "A"); + b->setData(0, "B"); + c->setData(0, "C"); + d->setData(0, "D"); + + // scene -> a -> b -> c -> d + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(a); + + // Prepare some events + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(0, 0)); + QGraphicsSceneMouseEvent moveEvent(QEvent::GraphicsSceneMouseMove); + moveEvent.setButton(Qt::LeftButton); + moveEvent.setScenePos(QPointF(0, 0)); + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + releaseEvent.setButton(Qt::LeftButton); + releaseEvent.setScenePos(QPointF(0, 0)); + + // Send a press + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(d->eventTypes.size(), 2); + QCOMPARE(d->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(d->eventTypes.at(1), QEvent::GraphicsSceneMousePress); + QCOMPARE(c->eventTypes.size(), 0); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)d); + + // Send a move + QApplication::sendEvent(&scene, &moveEvent); + QCOMPARE(d->eventTypes.size(), 3); + QCOMPARE(d->eventTypes.at(2), QEvent::GraphicsSceneMouseMove); + QCOMPARE(c->eventTypes.size(), 0); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)d); + + // Send a release + QApplication::sendEvent(&scene, &releaseEvent); + QCOMPARE(d->eventTypes.size(), 5); + QCOMPARE(d->eventTypes.at(3), QEvent::GraphicsSceneMouseRelease); + QCOMPARE(d->eventTypes.at(4), QEvent::UngrabMouse); + QCOMPARE(c->eventTypes.size(), 0); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + d->setAcceptedMouseButtons(Qt::RightButton); + + // Send a press + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(d->eventTypes.size(), 5); + QCOMPARE(c->eventTypes.size(), 2); + QCOMPARE(c->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(c->eventTypes.at(1), QEvent::GraphicsSceneMousePress); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)c); + + // Send another press, with a button that isn't actually accepted + QApplication::sendEvent(&scene, &pressEvent); + pressEvent.setButton(Qt::RightButton); + QCOMPARE(d->eventTypes.size(), 5); + QCOMPARE(c->eventTypes.size(), 3); + QCOMPARE(c->eventTypes.at(2), QEvent::GraphicsSceneMousePress); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)c); + + // Send a move + QApplication::sendEvent(&scene, &moveEvent); + QCOMPARE(d->eventTypes.size(), 5); + QCOMPARE(c->eventTypes.size(), 4); + QCOMPARE(c->eventTypes.at(3), QEvent::GraphicsSceneMouseMove); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)c); + + // Send a release + QApplication::sendEvent(&scene, &releaseEvent); + QCOMPARE(d->eventTypes.size(), 5); + QCOMPARE(c->eventTypes.size(), 6); + QCOMPARE(c->eventTypes.at(4), QEvent::GraphicsSceneMouseRelease); + QCOMPARE(c->eventTypes.at(5), QEvent::UngrabMouse); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + // Disabled items eat events. c should not get this. + d->setEnabled(false); + d->setAcceptedMouseButtons(Qt::RightButton); + + // Send a right press. This disappears in d. + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(d->eventTypes.size(), 5); + QCOMPARE(c->eventTypes.size(), 6); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + // Send a left press. This goes to c. + pressEvent.setButton(Qt::LeftButton); + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(d->eventTypes.size(), 5); + QCOMPARE(c->eventTypes.size(), 8); + QCOMPARE(c->eventTypes.at(6), QEvent::GrabMouse); + QCOMPARE(c->eventTypes.at(7), QEvent::GraphicsSceneMousePress); + QCOMPARE(b->eventTypes.size(), 0); + QCOMPARE(a->eventTypes.size(), 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)c); + + // Clicking outside the items removes the mouse grabber +} + +void tst_QGraphicsScene::mouseEventPropagation_ignore() +{ + EventTester *a = new EventTester; + EventTester *b = new EventTester; + EventTester *c = new EventTester; + EventTester *d = new EventTester; + b->setParentItem(a); + c->setParentItem(b); + d->setParentItem(c); + + a->setFlags(QGraphicsItem::ItemIsMovable); + b->setFlags(QGraphicsItem::ItemIsMovable); + c->setFlags(QGraphicsItem::ItemIsMovable); + d->setFlags(QGraphicsItem::ItemIsMovable); + + // scene -> a -> b -> c -> d + QGraphicsScene scene; + scene.addItem(a); + + // Prepare some events + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(0, 0)); + + b->ignoreMouse = true; + c->ignoreMouse = true; + d->ignoreMouse = true; + + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(a->eventTypes.size(), 2); + QCOMPARE(a->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(a->eventTypes.at(1), QEvent::GraphicsSceneMousePress); + QCOMPARE(b->eventTypes.size(), 3); + QCOMPARE(b->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(b->eventTypes.at(1), QEvent::GraphicsSceneMousePress); + QCOMPARE(b->eventTypes.at(2), QEvent::UngrabMouse); + QCOMPARE(c->eventTypes.size(), 3); + QCOMPARE(c->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(c->eventTypes.at(1), QEvent::GraphicsSceneMousePress); + QCOMPARE(c->eventTypes.at(2), QEvent::UngrabMouse); + QCOMPARE(d->eventTypes.size(), 3); + QCOMPARE(d->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(d->eventTypes.at(1), QEvent::GraphicsSceneMousePress); + QCOMPARE(d->eventTypes.at(2), QEvent::UngrabMouse); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)a); + + a->ignoreMouse = true; + + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(a->eventTypes.size(), 3); + QCOMPARE(a->eventTypes.at(2), QEvent::GraphicsSceneMousePress); + QCOMPARE(b->eventTypes.size(), 3); + QCOMPARE(c->eventTypes.size(), 3); + QCOMPARE(d->eventTypes.size(), 3); + + QVERIFY(!pressEvent.isAccepted()); +} + +void tst_QGraphicsScene::mouseEventPropagation_focus() +{ + EventTester *a = new EventTester; + EventTester *b = new EventTester; + EventTester *c = new EventTester; + EventTester *d = new EventTester; + b->setParentItem(a); + c->setParentItem(b); + d->setParentItem(c); + + a->setFlag(QGraphicsItem::ItemIsMovable); + b->setFlag(QGraphicsItem::ItemIsMovable); + c->setFlag(QGraphicsItem::ItemIsMovable); + d->setFlag(QGraphicsItem::ItemIsMovable); + + // scene -> a -> b -> c -> d + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(a); + + // Prepare some events + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(0, 0)); + + a->setFlag(QGraphicsItem::ItemIsFocusable); + QVERIFY(!a->hasFocus()); + + QApplication::sendEvent(&scene, &pressEvent); + + QVERIFY(a->hasFocus()); + QCOMPARE(a->eventTypes.size(), 1); + QCOMPARE(a->eventTypes.first(), QEvent::FocusIn); + QCOMPARE(d->eventTypes.size(), 2); + QCOMPARE(d->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(d->eventTypes.at(1), QEvent::GraphicsSceneMousePress); +} + +void tst_QGraphicsScene::mouseEventPropagation_doubleclick() +{ + EventTester *a = new EventTester; + EventTester *b = new EventTester; + a->setFlags(QGraphicsItem::ItemIsMovable); + b->setFlags(QGraphicsItem::ItemIsMovable); + + a->setPos(-50, 0); + b->setPos(50, 0); + + QGraphicsScene scene; + scene.addItem(a); + scene.addItem(b); + + // Prepare some events + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(0, 0)); + QGraphicsSceneMouseEvent doubleClickEvent(QEvent::GraphicsSceneMouseDoubleClick); + doubleClickEvent.setButton(Qt::LeftButton); + doubleClickEvent.setScenePos(QPointF(0, 0)); + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + releaseEvent.setButton(Qt::LeftButton); + releaseEvent.setScenePos(QPointF(0, 0)); + + // Send press to A + pressEvent.setScenePos(a->mapToScene(0, 0)); + QApplication::sendEvent(&scene, &pressEvent); + QCOMPARE(a->eventTypes.size(), 2); + QCOMPARE(a->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(a->eventTypes.at(1), QEvent::GraphicsSceneMousePress); + + // Send release to A + releaseEvent.setScenePos(a->mapToScene(0, 0)); + QApplication::sendEvent(&scene, &releaseEvent); + QCOMPARE(a->eventTypes.size(), 4); + QCOMPARE(a->eventTypes.at(2), QEvent::GraphicsSceneMouseRelease); + QCOMPARE(a->eventTypes.at(3), QEvent::UngrabMouse); + + // Send doubleclick to B + doubleClickEvent.setScenePos(b->mapToScene(0, 0)); + QApplication::sendEvent(&scene, &doubleClickEvent); + QCOMPARE(a->eventTypes.size(), 4); + QCOMPARE(b->eventTypes.size(), 2); + QCOMPARE(b->eventTypes.at(0), QEvent::GrabMouse); + QCOMPARE(b->eventTypes.at(1), QEvent::GraphicsSceneMousePress); + + // Send release to B + releaseEvent.setScenePos(b->mapToScene(0, 0)); + QApplication::sendEvent(&scene, &releaseEvent); + QCOMPARE(a->eventTypes.size(), 4); + QCOMPARE(b->eventTypes.size(), 4); + QCOMPARE(b->eventTypes.at(2), QEvent::GraphicsSceneMouseRelease); + QCOMPARE(b->eventTypes.at(3), QEvent::UngrabMouse); +} + +class Scene : public QGraphicsScene +{ +public: + QList<QPointF> mouseMovePoints; + +protected: + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) + { + mouseMovePoints << event->scenePos(); + } +}; + +void tst_QGraphicsScene::mouseEventPropagation_mouseMove() +{ + Scene scene; + scene.addRect(QRectF(5, 0, 12, 12)); + scene.addRect(QRectF(15, 0, 12, 12))->setAcceptsHoverEvents(true); + for (int i = 0; i < 30; ++i) { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setScenePos(QPointF(i, 5)); + QApplication::sendEvent(&scene, &event); + } + + QCOMPARE(scene.mouseMovePoints.size(), 30); + for (int i = 0; i < 30; ++i) + QCOMPARE(scene.mouseMovePoints.at(i), QPointF(i, 5)); +} + +class DndTester : public QGraphicsEllipseItem +{ +public: + DndTester(const QRectF &rect) + : QGraphicsEllipseItem(rect), lastEvent(0), + ignoresDragEnter(false), ignoresDragMove(false) + + { + } + + ~DndTester() + { + delete lastEvent; + } + + QGraphicsSceneDragDropEvent *lastEvent; + QList<QEvent::Type> eventList; + bool ignoresDragEnter; + bool ignoresDragMove; + +protected: + void dragEnterEvent(QGraphicsSceneDragDropEvent *event) + { + storeLastEvent(event); + event->setAccepted(!ignoresDragEnter); + if (!ignoresDragEnter) + event->setDropAction(Qt::IgnoreAction); + eventList << event->type(); + } + + void dragMoveEvent(QGraphicsSceneDragDropEvent *event) + { + storeLastEvent(event); + event->setAccepted(!ignoresDragMove); + eventList << event->type(); + } + + void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) + { + storeLastEvent(event); + eventList << event->type(); + } + + void dropEvent(QGraphicsSceneDragDropEvent *event) + { + storeLastEvent(event); + eventList << event->type(); + } + +private: + void storeLastEvent(QGraphicsSceneDragDropEvent *event) + { + delete lastEvent; + lastEvent = new QGraphicsSceneDragDropEvent(event->type()); + lastEvent->setScenePos(event->scenePos()); + lastEvent->setScreenPos(event->screenPos()); + lastEvent->setButtons(event->buttons()); + lastEvent->setModifiers(event->modifiers()); + lastEvent->setPossibleActions(event->possibleActions()); + lastEvent->setProposedAction(event->proposedAction()); + lastEvent->setDropAction(event->dropAction()); + lastEvent->setMimeData(event->mimeData()); + lastEvent->setWidget(event->widget()); + lastEvent->setSource(event->source()); + } +}; + +#ifndef QT_NO_DRAGANDDROP +void tst_QGraphicsScene::dragAndDrop_simple() +{ + DndTester *item = new DndTester(QRectF(-10, -10, 20, 20)); + + QGraphicsScene scene; + scene.addItem(item); + + QGraphicsView view(&scene); + view.setFixedSize(100, 100); + + QMimeData mimeData; + + // Initial drag enter for the scene + QDragEnterEvent dragEnter(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragEnter); + QVERIFY(dragEnter.isAccepted()); + QCOMPARE(dragEnter.dropAction(), Qt::CopyAction); + + { + // Move outside the item + QDragMoveEvent dragMove(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::CopyAction); + } + { + // Move inside the item without setAcceptDrops + QDragMoveEvent dragMove(view.mapFromScene(item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::CopyAction); + QCOMPARE(item->eventList.size(), 0); + } + item->setAcceptDrops(true); + { + // Move inside the item with setAcceptDrops + QDragMoveEvent dragMove(view.mapFromScene(item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction); + QCOMPARE(item->eventList.size(), 2); + QCOMPARE(item->eventList.at(0), QEvent::GraphicsSceneDragEnter); + QCOMPARE(item->eventList.at(1), QEvent::GraphicsSceneDragMove); + QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(dragMove.pos())); + QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(dragMove.pos())); + QVERIFY(item->lastEvent->isAccepted()); + QCOMPARE(item->lastEvent->dropAction(), Qt::IgnoreAction); + } + { + // Another move inside the item + QDragMoveEvent dragMove(view.mapFromScene(item->mapToScene(5, 5)), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction); + QCOMPARE(item->eventList.size(), 3); + QCOMPARE(item->eventList.at(2), QEvent::GraphicsSceneDragMove); + QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(dragMove.pos())); + QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(dragMove.pos())); + QVERIFY(item->lastEvent->isAccepted()); + QCOMPARE(item->lastEvent->dropAction(), Qt::IgnoreAction); + } + { + // Move outside the item + QDragMoveEvent dragMove(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::CopyAction); + QCOMPARE(item->eventList.size(), 4); + QCOMPARE(item->eventList.at(3), QEvent::GraphicsSceneDragLeave); + QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(dragMove.pos())); + QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(dragMove.pos())); + QVERIFY(item->lastEvent->isAccepted()); + QCOMPARE(item->lastEvent->dropAction(), Qt::CopyAction); + } + { + // Move inside the item again + QDragMoveEvent dragMove(view.mapFromScene(item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction); + QCOMPARE(item->eventList.size(), 6); + QCOMPARE(item->eventList.at(4), QEvent::GraphicsSceneDragEnter); + QCOMPARE(item->eventList.at(5), QEvent::GraphicsSceneDragMove); + QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(dragMove.pos())); + QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(dragMove.pos())); + QVERIFY(item->lastEvent->isAccepted()); + QCOMPARE(item->lastEvent->dropAction(), Qt::IgnoreAction); + } + { + // Drop inside the item + QDropEvent drop(view.mapFromScene(item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &drop); + QVERIFY(drop.isAccepted()); + QCOMPARE(drop.dropAction(), Qt::CopyAction); + QCOMPARE(item->eventList.size(), 7); + QCOMPARE(item->eventList.at(6), QEvent::GraphicsSceneDrop); + QCOMPARE(item->lastEvent->screenPos(), view.mapToGlobal(drop.pos())); + QCOMPARE(item->lastEvent->scenePos(), view.mapToScene(drop.pos())); + QVERIFY(item->lastEvent->isAccepted()); + QCOMPARE(item->lastEvent->dropAction(), Qt::CopyAction); + } +} + +void tst_QGraphicsScene::dragAndDrop_disabledOrInvisible() +{ + DndTester *item = new DndTester(QRectF(-10, -10, 20, 20)); + item->setAcceptDrops(true); + + QGraphicsScene scene; + scene.addItem(item); + + QGraphicsView view(&scene); + view.setFixedSize(100, 100); + + QMimeData mimeData; + + // Initial drag enter for the scene + QDragEnterEvent dragEnter(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragEnter); + QVERIFY(dragEnter.isAccepted()); + QCOMPARE(dragEnter.dropAction(), Qt::CopyAction); + { + // Move inside the item + QDragMoveEvent dragMove(view.mapFromScene(item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction); + QCOMPARE(item->eventList.size(), 2); + QCOMPARE(item->eventList.at(0), QEvent::GraphicsSceneDragEnter); + QCOMPARE(item->eventList.at(1), QEvent::GraphicsSceneDragMove); + } + { + // Move outside the item + QDragMoveEvent dragMove(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::CopyAction); + QCOMPARE(item->eventList.size(), 3); + QCOMPARE(item->eventList.at(2), QEvent::GraphicsSceneDragLeave); + } + + // Now disable the item + item->setEnabled(false); + QVERIFY(!item->isEnabled()); + QVERIFY(item->isVisible()); + + { + // Move inside the item + QDragMoveEvent dragMove(view.mapFromScene(item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::CopyAction); + QCOMPARE(item->eventList.size(), 3); + QCOMPARE(item->eventList.at(2), QEvent::GraphicsSceneDragLeave); + } + + // Reenable it, and make it invisible + item->setEnabled(true); + item->setVisible(false); + QVERIFY(item->isEnabled()); + QVERIFY(!item->isVisible()); + + { + // Move inside the item + QDragMoveEvent dragMove(view.mapFromScene(item->scenePos()), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::CopyAction); + QCOMPARE(item->eventList.size(), 3); + QCOMPARE(item->eventList.at(2), QEvent::GraphicsSceneDragLeave); + } + + // Dummy drop event to keep the Mac from crashing. + QDropEvent dropEvent(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dropEvent); +} + +void tst_QGraphicsScene::dragAndDrop_propagate() +{ + DndTester *item1 = new DndTester(QRectF(-10, -10, 20, 20)); + DndTester *item2 = new DndTester(QRectF(0, 0, 20, 20)); + item1->setAcceptDrops(true); + item2->setAcceptDrops(true); + item2->ignoresDragMove = true; + item2->ignoresDragEnter = false; + item2->setZValue(1); + + item1->setData(0, "item1"); + item2->setData(0, "item2"); + + QGraphicsScene scene; + scene.addItem(item1); + scene.addItem(item2); + + QGraphicsView view(&scene); + view.setFixedSize(100, 100); + + QMimeData mimeData; + + // Initial drag enter for the scene + QDragEnterEvent dragEnter(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragEnter); + QVERIFY(dragEnter.isAccepted()); + QCOMPARE(dragEnter.dropAction(), Qt::CopyAction); + + { + // Move outside the items + QDragMoveEvent dragMove(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::CopyAction); + QVERIFY(item1->eventList.isEmpty()); + QVERIFY(item2->eventList.isEmpty()); + } + { + // Move inside item1 + QDragMoveEvent dragMove(view.mapFromScene(-5, -5), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction); + QCOMPARE(item1->eventList.size(), 2); + QCOMPARE(item1->eventList.at(0), QEvent::GraphicsSceneDragEnter); + QCOMPARE(item1->eventList.at(1), QEvent::GraphicsSceneDragMove); + } + + { + // Move into the intersection item1-item2 + QDragMoveEvent dragMove(view.mapFromScene(5, 5), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); // move does not propagate, (ignoresDragMove = true) + QCOMPARE(item1->eventList.size(), 3); + QCOMPARE(item1->eventList.at(2), QEvent::GraphicsSceneDragLeave); + QCOMPARE(item2->eventList.size(), 2); + QCOMPARE(item2->eventList.at(0), QEvent::GraphicsSceneDragEnter); + QCOMPARE(item2->eventList.at(1), QEvent::GraphicsSceneDragMove); + } + { + // Move into the item2 + QDragMoveEvent dragMove(view.mapFromScene(15, 15), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(!dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::CopyAction); + QCOMPARE(item1->eventList.size(), 3); + QCOMPARE(item2->eventList.size(), 3); + QCOMPARE(item2->eventList.at(2), QEvent::GraphicsSceneDragMove); + } + { + // Move inside item1 + QDragMoveEvent dragMove(view.mapFromScene(-5, -5), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(dragMove.isAccepted()); + QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction); + QCOMPARE(item1->eventList.size(), 5); + QCOMPARE(item1->eventList.at(3), QEvent::GraphicsSceneDragEnter); + QCOMPARE(item1->eventList.at(4), QEvent::GraphicsSceneDragMove); + QCOMPARE(item2->eventList.size(), 4); + QCOMPARE(item2->eventList.at(3), QEvent::GraphicsSceneDragLeave); + } + + { + item2->ignoresDragEnter = true; + // Move into the intersection item1-item2 + QDragMoveEvent dragMove(view.mapFromScene(5, 5), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dragMove); + QVERIFY(dragMove.isAccepted()); // dragEnter propagates down to item1, which then accepts the move event. + QCOMPARE(dragMove.dropAction(), Qt::IgnoreAction); + QCOMPARE(item1->eventList.size(), 6); + QCOMPARE(item1->eventList.at(5), QEvent::GraphicsSceneDragMove); + QCOMPARE(item2->eventList.size(), 5); + QCOMPARE(item2->eventList.at(4), QEvent::GraphicsSceneDragEnter); + } + + { + item2->ignoresDragEnter = false; + // Drop on the intersection item1-item2 + QDropEvent drop(view.mapFromScene(5, 5), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &drop); + QVERIFY(drop.isAccepted()); + QCOMPARE(drop.dropAction(), Qt::CopyAction); + + QCOMPARE(item1->eventList.size(), 7); + QCOMPARE(item1->eventList.at(6), QEvent::GraphicsSceneDrop); + QCOMPARE(item2->eventList.size(), 5); + } + + // Dummy drop event to keep the Mac from crashing. + QDropEvent dropEvent(QPoint(0, 0), Qt::CopyAction, &mimeData, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &dropEvent); +} +#endif + +void tst_QGraphicsScene::render_data() +{ + QTest::addColumn<QRectF>("targetRect"); + QTest::addColumn<QRectF>("sourceRect"); + QTest::addColumn<Qt::AspectRatioMode>("aspectRatioMode"); + QTest::addColumn<QMatrix>("matrix"); + QTest::addColumn<QPainterPath>("clip"); + + QPainterPath clip_rect; + clip_rect.addRect(50, 100, 200, 150); + + QPainterPath clip_ellipse; + clip_ellipse.addEllipse(100,50,150,200); + + QTest::newRow("all-all-untransformed") << QRectF() << QRectF() + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("all-topleft-untransformed") << QRectF(0, 0, 150, 150) + << QRectF() << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("all-topright-untransformed") << QRectF(150, 0, 150, 150) + << QRectF() << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("all-bottomleft-untransformed") << QRectF(0, 150, 150, 150) + << QRectF() << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("all-bottomright-untransformed") << QRectF(150, 150, 150, 150) + << QRectF() << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("topleft-all-untransformed") << QRectF() << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("topright-all-untransformed") << QRectF() << QRectF(0, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("bottomleft-all-untransformed") << QRectF() << QRectF(-10, 0, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("bottomright-all-untransformed") << QRectF() << QRectF(0, 0, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("topleft-topleft-untransformed") << QRectF(0, 0, 150, 150) << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("topright-topleft-untransformed") << QRectF(150, 0, 150, 150) << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("bottomleft-topleft-untransformed") << QRectF(0, 150, 150, 150) << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("bottomright-topleft-untransformed") << QRectF(150, 150, 150, 150) << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("top-topleft-untransformed") << QRectF(0, 0, 300, 150) << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("bottom-topleft-untransformed") << QRectF(0, 150, 300, 150) << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("left-topleft-untransformed") << QRectF(0, 0, 150, 300) << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("right-topleft-untransformed") << QRectF(150, 0, 150, 300) << QRectF(-10, -10, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("top-bottomright-untransformed") << QRectF(0, 0, 300, 150) << QRectF(0, 0, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("bottom-bottomright-untransformed") << QRectF(0, 150, 300, 150) << QRectF(0, 0, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("left-bottomright-untransformed") << QRectF(0, 0, 150, 300) << QRectF(0, 0, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("right-bottomright-untransformed") << QRectF(150, 0, 150, 300) << QRectF(0, 0, 10, 10) + << Qt::IgnoreAspectRatio << QMatrix() << QPainterPath(); + QTest::newRow("all-all-45-deg-right") << QRectF() << QRectF() + << Qt::IgnoreAspectRatio << QMatrix().rotate(-45) << QPainterPath(); + QTest::newRow("all-all-45-deg-left") << QRectF() << QRectF() + << Qt::IgnoreAspectRatio << QMatrix().rotate(45) << QPainterPath(); + QTest::newRow("all-all-scale-2x") << QRectF() << QRectF() + << Qt::IgnoreAspectRatio << QMatrix().scale(2, 2) << QPainterPath(); + QTest::newRow("all-all-translate-50-0") << QRectF() << QRectF() + << Qt::IgnoreAspectRatio << QMatrix().translate(50, 0) << QPainterPath(); + QTest::newRow("all-all-translate-0-50") << QRectF() << QRectF() + << Qt::IgnoreAspectRatio << QMatrix().translate(0, 50) << QPainterPath(); + QTest::newRow("all-all-untransformed-clip-rect") << QRectF() << QRectF() + << Qt::IgnoreAspectRatio << QMatrix() << clip_rect; + QTest::newRow("all-all-untransformed-clip-ellipse") << QRectF() << QRectF() + << Qt::IgnoreAspectRatio << QMatrix() << clip_ellipse; +} + +void tst_QGraphicsScene::render() +{ + QFETCH(QRectF, targetRect); + QFETCH(QRectF, sourceRect); + QFETCH(Qt::AspectRatioMode, aspectRatioMode); + QFETCH(QMatrix, matrix); + QFETCH(QPainterPath, clip); + + QPixmap pix(30, 30); + pix.fill(Qt::blue); + + QGraphicsView view; + QGraphicsScene scene(&view); + scene.addEllipse(QRectF(-10, -10, 20, 20), QPen(Qt::black), QBrush(Qt::white)); + scene.addEllipse(QRectF(-2, -7, 4, 4), QPen(Qt::black), QBrush(Qt::yellow))->setZValue(1); + QGraphicsPixmapItem *item = scene.addPixmap(pix); + item->setZValue(2); + item->setOffset(QPointF(3, 3)); + view.show(); + + scene.setSceneRect(scene.itemsBoundingRect()); + + QImage bigImage(300, 300, QImage::Format_RGB32); + bigImage.fill(0); + QPainter painter(&bigImage); + painter.setPen(Qt::lightGray); + for (int i = 0; i <= 300; i += 25) { + painter.drawLine(0, i, 300, i); + painter.drawLine(i, 0, i, 300); + } + painter.setPen(QPen(Qt::darkGray, 2)); + painter.drawLine(0, 150, 300, 150); + painter.drawLine(150, 0, 150, 300); + painter.setMatrix(matrix); + if (!clip.isEmpty()) painter.setClipPath(clip); + scene.render(&painter, targetRect, sourceRect, aspectRatioMode); + painter.end(); + + const QString renderPath = QLatin1String(SRCDIR) + "/testData/render"; + QString fileName = renderPath + QString("/%1.png").arg(QTest::currentDataTag()); + QImage original(fileName); + QVERIFY(!original.isNull()); + + // Compare + int wrongPixels = 0; + for (int y = 0; y < original.height(); ++y) { + for (int x = 0; x < original.width(); ++x) { + if (bigImage.pixel(x, y) != original.pixel(x, y)) + ++wrongPixels; + } + } + + // This is a pixmap compare test - because of rounding errors on diverse + // platforms, and especially because tests are compiled in release mode, + // we set a 95% acceptance threshold for comparing images. This number may + // have to be adjusted if this test fails. + qreal threshold = 0.95; + qreal similarity = (1 - (wrongPixels / qreal(original.width() * original.height()))); + if (similarity < threshold) { +#if 1 + // fail + QLabel *expectedLabel = new QLabel; + expectedLabel->setPixmap(QPixmap::fromImage(original)); + + QLabel *newLabel = new QLabel; + newLabel->setPixmap(QPixmap::fromImage(bigImage)); + + QGridLayout *gridLayout = new QGridLayout; + gridLayout->addWidget(new QLabel(tr("MISMATCH: %1").arg(QTest::currentDataTag())), 0, 0, 1, 2); + gridLayout->addWidget(new QLabel(tr("Current")), 1, 0); + gridLayout->addWidget(new QLabel(tr("Expected")), 1, 1); + gridLayout->addWidget(expectedLabel, 2, 1); + gridLayout->addWidget(newLabel, 2, 0); + + QWidget widget; + widget.setLayout(gridLayout); + widget.show(); + + QTestEventLoop::instance().enterLoop(1); + + QFAIL("Images are not identical."); +#else + // generate + qDebug() << "Updating" << QTest::currentDataTag() << ":" << bigImage.save(fileName, "png"); +#endif + } +} + +void tst_QGraphicsScene::renderItemsWithNegativeWidthOrHeight() +{ + QGraphicsScene scene(0, 0, 150, 150); + + // Add item with negative width. + QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, -150, 50); + item1->setBrush(Qt::red); + item1->setPos(150, 50); + scene.addItem(item1); + + // Add item with negative height. + QGraphicsRectItem *item2 = new QGraphicsRectItem(0, 0, 50, -150); + item2->setBrush(Qt::blue); + item2->setPos(50, 150); + scene.addItem(item2); + + QGraphicsView view(&scene); + view.setFrameStyle(QFrame::NoFrame); + view.resize(150, 150); + view.show(); + QCOMPARE(view.viewport()->size(), QSize(150, 150)); + + QImage expected(view.viewport()->size(), QImage::Format_RGB32); + view.viewport()->render(&expected); + + // Make sure the scene background is the same as the viewport background. + scene.setBackgroundBrush(view.viewport()->palette().brush(view.viewport()->backgroundRole())); + QImage actual(150, 150, QImage::Format_RGB32); + QPainter painter(&actual); + scene.render(&painter); + painter.end(); + + QCOMPARE(actual, expected); +} + +void tst_QGraphicsScene::contextMenuEvent() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + EventTester *item = new EventTester; + scene.addItem(item); + item->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFocus(); + + QVERIFY(item->hasFocus()); + QVERIFY(scene.hasFocus()); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + view.activateWindow(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + view.centerOn(item); + + { + QContextMenuEvent event(QContextMenuEvent::Keyboard, view.viewport()->rect().center(), + view.mapToGlobal(view.viewport()->rect().center())); + QApplication::sendEvent(view.viewport(), &event); + QCOMPARE(item->eventTypes.last(), QEvent::GraphicsSceneContextMenu); + } +} + +class ContextMenuItem : public QGraphicsRectItem +{ +public: + ContextMenuItem() : QGraphicsRectItem(0, 0, 100, 100) + { setBrush(Qt::red); } + +protected: + void contextMenuEvent(QGraphicsSceneContextMenuEvent *) + { /* just accept */ } +}; + +void tst_QGraphicsScene::contextMenuEvent_ItemIgnoresTransformations() +{ + QGraphicsScene scene(0, 0, 200, 200); + ContextMenuItem *item = new ContextMenuItem; + item->setFlag(QGraphicsItem::ItemIgnoresTransformations); + scene.addItem(item); + + QWidget topLevel; + QGraphicsView view(&scene, &topLevel); + view.resize(200, 200); + topLevel.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWaitForWindowShown(&topLevel); + + { + QPoint pos(50, 50); + QContextMenuEvent event(QContextMenuEvent::Keyboard, pos, view.viewport()->mapToGlobal(pos)); + event.ignore(); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + { + QPoint pos(150, 150); + QContextMenuEvent event(QContextMenuEvent::Keyboard, pos, view.viewport()->mapToGlobal(pos)); + event.ignore(); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(!event.isAccepted()); + } + view.scale(1.5, 1.5); + { + QPoint pos(25, 25); + QContextMenuEvent event(QContextMenuEvent::Keyboard, pos, view.viewport()->mapToGlobal(pos)); + event.ignore(); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + { + QPoint pos(55, 55); + QContextMenuEvent event(QContextMenuEvent::Keyboard, pos, view.viewport()->mapToGlobal(pos)); + event.ignore(); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(!event.isAccepted()); + } +} + +void tst_QGraphicsScene::update() +{ + QGraphicsScene scene; + + QGraphicsRectItem *rect = new QGraphicsRectItem(0, 0, 100, 100); + scene.addItem(rect); + qApp->processEvents(); + rect->setPos(-100, -100); + + // This function forces indexing + scene.itemAt(0, 0); + + qRegisterMetaType<QList<QRectF> >("QList<QRectF>"); + QSignalSpy spy(&scene, SIGNAL(changed(QList<QRectF>))); + + // We update the scene. + scene.update(); + + // This function forces a purge, which will post an update signal + scene.itemAt(0, 0); + + // This will process the pending update + QApplication::instance()->processEvents(); + + // Check that the update region is correct + QCOMPARE(spy.count(), 1); + QRectF region; + foreach (QRectF rectF, qVariantValue<QList<QRectF> >(spy.at(0).at(0))) + region |= rectF; + QCOMPARE(region, QRectF(-100, -100, 200, 200)); +} + +void tst_QGraphicsScene::update2() +{ + QGraphicsScene scene; + scene.setSceneRect(-200, -200, 200, 200); + CustomView view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.repaints >= 1); + view.repaints = 0; + + // Make sure QGraphicsScene::update only requires one event-loop iteration + // before the view is updated. + scene.update(); + qApp->processEvents(); + QTRY_COMPARE(view.repaints, 1); + view.repaints = 0; + + // The same for partial scene updates. + scene.update(QRectF(-100, -100, 100, 100)); + qApp->processEvents(); + QCOMPARE(view.repaints, 1); +} + +void tst_QGraphicsScene::views() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QCOMPARE(scene.views().size(), 1); + QCOMPARE(scene.views().at(0), &view); + + QGraphicsView view1(&scene); + QCOMPARE(scene.views().size(), 2); + QVERIFY(scene.views().contains(&view1)); + + view.setScene(0); + QCOMPARE(scene.views().size(), 1); + QCOMPARE(scene.views().at(0), &view1); + + QGraphicsView *view2 = new QGraphicsView(&scene); + QCOMPARE(scene.views().size(), 2); + QCOMPARE(scene.views().at(0), &view1); + QCOMPARE(scene.views().at(1), view2); + + delete view2; + + QCOMPARE(scene.views().size(), 1); + QCOMPARE(scene.views().at(0), &view1); +} + +class CustomScene : public QGraphicsScene +{ +public: + CustomScene() : gotTimerEvent(false) + { startTimer(10); } + + bool gotTimerEvent; +protected: + void timerEvent(QTimerEvent *) + { + gotTimerEvent = true; + } +}; + +void tst_QGraphicsScene::event() +{ + // Test that QGraphicsScene properly propagates events to QObject. + CustomScene scene; + QTestEventLoop::instance().enterLoop(1); + QVERIFY(scene.gotTimerEvent); +} + +class DisabledItemTester : public QGraphicsRectItem +{ +public: + DisabledItemTester(const QRectF &rect, QGraphicsItem *parent = 0) + : QGraphicsRectItem(rect, parent) + { } + + QList<QEvent::Type> receivedSceneEvents; + QList<QEvent::Type> receivedSceneEventFilters; + +protected: + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + receivedSceneEventFilters << event->type(); + return QGraphicsRectItem::sceneEventFilter(watched, event); + } + + bool sceneEvent(QEvent *event) + { + receivedSceneEvents << event->type(); + return QGraphicsRectItem::sceneEvent(event); + } +}; + +void tst_QGraphicsScene::eventsToDisabledItems() +{ + QGraphicsScene scene; + + DisabledItemTester *item1 = new DisabledItemTester(QRectF(-50, -50, 100, 100)); + DisabledItemTester *item2 = new DisabledItemTester(QRectF(-50, -50, 100, 100)); + item1->setZValue(1); // on top + + scene.addItem(item1); + scene.addItem(item2); + + item1->installSceneEventFilter(item2); + + QVERIFY(item1->receivedSceneEvents.isEmpty()); + QVERIFY(item2->receivedSceneEvents.isEmpty()); + QVERIFY(item1->receivedSceneEventFilters.isEmpty()); + QVERIFY(item2->receivedSceneEventFilters.isEmpty()); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setButton(Qt::LeftButton); + QApplication::sendEvent(&scene, &event); + + // First item2 receives a scene event filter. Then item1 receives the + // actual event. Finally the event propagates to item2. So both items + // should have received the event, and item1 also got the filter. + QCOMPARE(item1->receivedSceneEvents.size(), 3); + QCOMPARE(item2->receivedSceneEvents.size(), 3); + QCOMPARE(item1->receivedSceneEventFilters.size(), 0); + QCOMPARE(item2->receivedSceneEventFilters.size(), 3); + + item1->receivedSceneEvents.clear(); + item1->receivedSceneEventFilters.clear(); + item2->receivedSceneEvents.clear(); + item2->receivedSceneEventFilters.clear(); + + item1->setEnabled(false); // disable the topmost item, eat mouse events + + event.setButton(Qt::LeftButton); + event.setAccepted(false); + QApplication::sendEvent(&scene, &event); + + // Check that only item1 received anything - it only got the filter. + QCOMPARE(item1->receivedSceneEvents.size(), 0); + QCOMPARE(item2->receivedSceneEvents.size(), 0); + QCOMPARE(item1->receivedSceneEventFilters.size(), 0); + QCOMPARE(item2->receivedSceneEventFilters.size(), 3); +} + +class ExposedPixmapItem : public QGraphicsPixmapItem +{ +public: + ExposedPixmapItem(QGraphicsItem *item = 0) + : QGraphicsPixmapItem(item) + { } + + void paint(QPainter *, const QStyleOptionGraphicsItem *option, QWidget *) + { + exposed = option->exposedRect; + } + + QRectF exposed; +}; + +void tst_QGraphicsScene::exposedRect() +{ + ExposedPixmapItem *item = new ExposedPixmapItem; + item->setPixmap(QPixmap(":/Ash_European.jpg")); + QGraphicsScene scene; + scene.addItem(item); + + QCOMPARE(item->exposed, QRectF()); + + QImage image(100, 100, QImage::Format_ARGB32_Premultiplied); + QPainter painter(&image); + + scene.render(&painter); + QCOMPARE(item->exposed, item->boundingRect()); + + painter.rotate(180); + painter.translate(100, 100); + + scene.render(&painter); + QCOMPARE(item->exposed, item->boundingRect()); +} + +void tst_QGraphicsScene::tabFocus_emptyScene() +{ + QGraphicsScene scene; + QDial *dial1 = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + QDial *dial2 = new QDial; + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(dial1); + layout->addWidget(view); + layout->addWidget(dial2); + + QWidget widget; + widget.setLayout(layout); + widget.show(); + qApp->setActiveWindow(&widget); + widget.activateWindow(); + QTest::qWait(125); + + dial1->setFocus(); + QVERIFY(dial1->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QVERIFY(!dial1->hasFocus()); + QVERIFY(view->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QVERIFY(!view->hasFocus()); + QVERIFY(dial2->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QVERIFY(!dial2->hasFocus()); + QVERIFY(view->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QVERIFY(dial1->hasFocus()); + QVERIFY(!dial2->hasFocus()); +} + +void tst_QGraphicsScene::tabFocus_sceneWithFocusableItems() +{ + QGraphicsScene scene; + QGraphicsTextItem *item = scene.addText("Qt rocks!"); + item->setTabChangesFocus(true); + item->setTextInteractionFlags(Qt::TextEditorInteraction); + QVERIFY(item->flags() & QGraphicsItem::ItemIsFocusable); + item->setFocus(); + item->clearFocus(); + + QGraphicsTextItem *item2 = scene.addText("Trolltech rocks!"); + item2->setTabChangesFocus(true); + item2->setTextInteractionFlags(Qt::TextEditorInteraction); + item2->setPos(0, item->boundingRect().bottom()); + QVERIFY(item2->flags() & QGraphicsItem::ItemIsFocusable); + + QDial *dial1 = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + QDial *dial2 = new QDial; + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(dial1); + layout->addWidget(view); + layout->addWidget(dial2); + + QWidget widget; + widget.setLayout(layout); + widget.show(); + qApp->setActiveWindow(&widget); + widget.activateWindow(); + QTest::qWaitForWindowShown(&widget); + QApplication::processEvents(); + + dial1->setFocus(); + QTRY_VERIFY(dial1->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(item->hasFocus()); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(dial2->hasFocus()); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(view->hasFocus()); + QTRY_VERIFY(view->viewport()->hasFocus()); + QTRY_VERIFY(scene.hasFocus()); + QTRY_VERIFY(item->hasFocus()); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(dial1->hasFocus()); + + // If the item requests input focus, it can only ensure that the scene + // sets focus on itself, but the scene cannot request focus from any view. + item->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QTRY_VERIFY(scene.hasFocus()); + QVERIFY(item->hasFocus()); + + view->setFocus(); + QApplication::processEvents(); + QTRY_VERIFY(view->hasFocus()); + QTRY_VERIFY(view->viewport()->hasFocus()); + QTRY_VERIFY(scene.hasFocus()); + QTRY_VERIFY(item->hasFocus()); + + // Check that everyone loses focus when the widget is hidden. + widget.hide(); + QTest::qWait(15); + QTRY_VERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!item->hasFocus()); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item)); + + // Check that the correct item regains focus. + widget.show(); + qApp->setActiveWindow(&widget); + widget.activateWindow(); + QTest::qWaitForWindowShown(&widget); + QTRY_VERIFY(view->hasFocus()); + QTRY_VERIFY(scene.isActive()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item)); + QVERIFY(item->hasFocus()); +} + +class FocusWidget : public QGraphicsWidget +{ + Q_OBJECT +public: + FocusWidget(QGraphicsItem *parent = 0) + : QGraphicsWidget(parent), tabs(0), backTabs(0) + { + setFocusPolicy(Qt::StrongFocus); + resize(100, 100); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) + { + if (option->state & QStyle::State_HasFocus) { + painter->fillRect(rect(), Qt::blue); + } + painter->setBrush(Qt::green); + painter->drawEllipse(rect()); + if (option->state & QStyle::State_HasFocus) { + painter->setPen(QPen(Qt::black, 1, Qt::DashLine)); + painter->setBrush(Qt::NoBrush); + painter->drawEllipse(rect().adjusted(5, 5, -5, -5)); + } + } + + int tabs; + int backTabs; + +protected: + bool sceneEvent(QEvent *event) + { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast<QKeyEvent *>(event); + if (k->key() == Qt::Key_Tab) + ++tabs; + if (k->key() == Qt::Key_Backtab) + ++backTabs; + } + return QGraphicsWidget::sceneEvent(event); + } + + void focusInEvent(QFocusEvent *) + { update(); } + void focusOutEvent(QFocusEvent *) + { update(); } +}; + +void tst_QGraphicsScene::tabFocus_sceneWithFocusWidgets() +{ + QGraphicsScene scene; + + FocusWidget *widget1 = new FocusWidget; + FocusWidget *widget2 = new FocusWidget; + widget2->setPos(widget1->boundingRect().right(), 0); + scene.addItem(widget1); + scene.addItem(widget2); + + QDial *dial1 = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + view->setRenderHint(QPainter::Antialiasing); + QDial *dial2 = new QDial; + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(dial1); + layout->addWidget(view); + layout->addWidget(dial2); + + QWidget widget; + widget.setLayout(layout); + widget.show(); + qApp->setActiveWindow(&widget); + widget.activateWindow(); + QTest::qWaitForWindowShown(&widget); + QApplication::processEvents(); + + dial1->setFocus(); + QTRY_VERIFY(dial1->hasFocus()); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(view->hasFocus()); + QTRY_VERIFY(view->viewport()->hasFocus()); + QTRY_VERIFY(scene.hasFocus()); + QCOMPARE(widget1->tabs, 0); + QVERIFY(widget1->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_COMPARE(widget1->tabs, 1); + QTRY_VERIFY(widget2->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_COMPARE(widget2->tabs, 1); + QTRY_VERIFY(dial2->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(widget2->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_COMPARE(widget2->backTabs, 1); + QTRY_VERIFY(widget1->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_COMPARE(widget1->backTabs, 1); + QTRY_VERIFY(dial1->hasFocus()); + + widget1->setFocus(); + view->viewport()->setFocus(); + widget.hide(); + QTest::qWait(15); + widget.show(); + qApp->setActiveWindow(&widget); + widget.activateWindow(); + QTest::qWaitForWindowShown(&widget); + QTRY_VERIFY(widget1->hasFocus()); +} + +void tst_QGraphicsScene::tabFocus_sceneWithNestedFocusWidgets() +{ + QGraphicsScene scene; + + FocusWidget *widget1 = new FocusWidget; + FocusWidget *widget1_1 = new FocusWidget; + FocusWidget *widget1_2 = new FocusWidget; + widget1_1->setParentItem(widget1); + widget1_1->scale(0.5, 0.5); + widget1_1->setPos(0, widget1->boundingRect().height() / 2); + widget1_2->setParentItem(widget1); + widget1_2->scale(0.5, 0.5); + widget1_2->setPos(widget1->boundingRect().width() / 2, widget1->boundingRect().height() / 2); + + FocusWidget *widget2 = new FocusWidget; + widget2->setPos(widget1->boundingRect().right(), 0); + + widget1->setData(0, "widget1"); + widget1_1->setData(0, "widget1_1"); + widget1_2->setData(0, "widget1_2"); + widget2->setData(0, "widget2"); + + scene.addItem(widget1); + scene.addItem(widget2); + + QDial *dial1 = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + view->setRenderHint(QPainter::Antialiasing); + QDial *dial2 = new QDial; + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(dial1); + layout->addWidget(view); + layout->addWidget(dial2); + + QWidget widget; + widget.setLayout(layout); + widget.show(); + qApp->setActiveWindow(&widget); + widget.activateWindow(); + QTest::qWaitForWindowShown(&widget); + + dial1->setFocus(); + QTRY_VERIFY(dial1->hasFocus()); + + EventSpy focusInSpy_1(widget1, QEvent::FocusIn); + EventSpy focusOutSpy_1(widget1, QEvent::FocusOut); + EventSpy focusInSpy_1_1(widget1_1, QEvent::FocusIn); + EventSpy focusOutSpy_1_1(widget1_1, QEvent::FocusOut); + EventSpy focusInSpy_1_2(widget1_2, QEvent::FocusIn); + EventSpy focusOutSpy_1_2(widget1_2, QEvent::FocusOut); + EventSpy focusInSpy_2(widget2, QEvent::FocusIn); + EventSpy focusOutSpy_2(widget2, QEvent::FocusOut); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(widget1->hasFocus()); + QCOMPARE(focusInSpy_1.count(), 1); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(!widget1->hasFocus()); + QVERIFY(widget1_1->hasFocus()); + QCOMPARE(focusOutSpy_1.count(), 1); + QCOMPARE(focusInSpy_1_1.count(), 1); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(!widget1_1->hasFocus()); + QVERIFY(widget1_2->hasFocus()); + QCOMPARE(focusOutSpy_1_1.count(), 1); + QCOMPARE(focusInSpy_1_2.count(), 1); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(!widget1_2->hasFocus()); + QVERIFY(widget2->hasFocus()); + QCOMPARE(focusOutSpy_1_2.count(), 1); + QCOMPARE(focusInSpy_2.count(), 1); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QApplication::processEvents(); + QTRY_VERIFY(!widget2->hasFocus()); + QVERIFY(dial2->hasFocus()); + QCOMPARE(focusOutSpy_2.count(), 1); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(widget2->hasFocus()); + QCOMPARE(focusInSpy_2.count(), 2); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(!widget2->hasFocus()); + QTRY_VERIFY(widget1_2->hasFocus()); + QCOMPARE(focusOutSpy_2.count(), 2); + QCOMPARE(focusInSpy_1_2.count(), 2); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(!widget1_2->hasFocus()); + QTRY_VERIFY(widget1_1->hasFocus()); + QCOMPARE(focusOutSpy_1_2.count(), 2); + QCOMPARE(focusInSpy_1_1.count(), 2); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(!widget1_1->hasFocus()); + QTRY_VERIFY(widget1->hasFocus()); + QCOMPARE(focusOutSpy_1_1.count(), 2); + QCOMPARE(focusInSpy_1.count(), 2); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QApplication::processEvents(); + QTRY_VERIFY(!widget1->hasFocus()); + QTRY_VERIFY(dial1->hasFocus()); + QCOMPARE(focusOutSpy_1.count(), 2); + + widget1->setFocus(); + view->viewport()->setFocus(); + widget.hide(); + QTest::qWait(12); + widget.show(); + qApp->setActiveWindow(&widget); + widget.activateWindow(); + QTest::qWaitForWindowShown(&widget); + QApplication::processEvents(); + QTRY_VERIFY(widget1->hasFocus()); +} + +void tst_QGraphicsScene::style() +{ + QPointer<QWindowsStyle> windowsStyle = new QWindowsStyle; + + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget *proxy = scene.addWidget(edit); + + EventSpy sceneSpy(&scene, QEvent::StyleChange); + EventSpy proxySpy(proxy, QEvent::StyleChange); + EventSpy editSpy(edit, QEvent::StyleChange); + + QCOMPARE(scene.style(), QApplication::style()); + + scene.setStyle(windowsStyle); + QCOMPARE(sceneSpy.count(), 1); + QCOMPARE(proxySpy.count(), 1); + QCOMPARE(editSpy.count(), 1); + QCOMPARE(scene.style(), (QStyle *)windowsStyle); + QCOMPARE(proxy->style(), (QStyle *)windowsStyle); + QCOMPARE(edit->style(), (QStyle *)windowsStyle); + + scene.setStyle(0); + QCOMPARE(sceneSpy.count(), 2); + QCOMPARE(proxySpy.count(), 2); + QCOMPARE(editSpy.count(), 2); + QCOMPARE(scene.style(), QApplication::style()); + QCOMPARE(proxy->style(), QApplication::style()); + QCOMPARE(edit->style(), QApplication::style()); + QVERIFY(!windowsStyle); // deleted +} + +void tst_QGraphicsScene::task139710_bspTreeCrash() +{ + // create a scene with 2000 items + QGraphicsScene scene(0, 0, 1000, 1000); + + for (int i = 0; i < 2; ++i) { + // trigger delayed item indexing + qApp->processEvents(); + scene.setSceneRect(0, 0, 10000, 10000); + + // delete all items in the scene - pointers are now likely to be recycled + foreach (QGraphicsItem *item, scene.items()) { + scene.removeItem(item); + delete item; + } + + // add 1000 more items - the BSP tree is now resized + for (int i = 0; i < 1000; ++i) { + QGraphicsRectItem *item = scene.addRect(QRectF(0, 0, 200, 200)); + item->setPos(qrand() % 10000, qrand() % 10000); + } + + // trigger delayed item indexing for the first 1000 items + qApp->processEvents(); + + // add 1000 more items - the BSP tree is now resized + for (int i = 0; i < 1000; ++i) { + QGraphicsRectItem *item = scene.addRect(QRectF(0, 0, 200, 200)); + item->setPos(qrand() % 10000, qrand() % 10000); + } + + // get items from the BSP tree and use them. there was junk in the tree + // the second time this happened. + foreach (QGraphicsItem *item, scene.items(QRectF(0, 0, 1000, 1000))) + item->moveBy(0, 0); + } +} + +void tst_QGraphicsScene::task139782_containsItemBoundingRect() +{ + // The item in question has a scene bounding rect of (10, 10, 50, 50) + QGraphicsScene scene(0.0, 0.0, 200.0, 200.0); + QGraphicsRectItem *item = new QGraphicsRectItem(0.0, 0.0, 50.0, 50.0, 0, &scene); + item->setPos(10.0, 10.0); + + // The (0, 0, 50, 50) scene rect should not include the item's bounding rect + QVERIFY(!scene.items(QRectF(0.0, 0.0, 50.0, 50.0), Qt::ContainsItemBoundingRect).contains(item)); + + // The (9, 9, 500, 500) scene rect _should_ include the item's bounding rect + QVERIFY(scene.items(QRectF(9.0, 9.0, 500.0, 500.0), Qt::ContainsItemBoundingRect).contains(item)); + + // The (25, 25, 5, 5) scene rect should not include the item's bounding rect + QVERIFY(!scene.items(QRectF(25.0, 25.0, 5.0, 5.0), Qt::ContainsItemBoundingRect).contains(item)); +} + +void tst_QGraphicsScene::task176178_itemIndexMethodBreaksSceneRect() +{ + QGraphicsScene scene; + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + QGraphicsRectItem *rect = new QGraphicsRectItem; + rect->setRect(0,0,100,100); + scene.addItem(rect); + QCOMPARE(scene.sceneRect(), rect->rect()); +} + +void tst_QGraphicsScene::task160653_selectionChanged() +{ + QGraphicsScene scene(0, 0, 100, 100); + scene.addItem(new QGraphicsRectItem(0, 0, 20, 20)); + scene.addItem(new QGraphicsRectItem(30, 30, 20, 20)); + foreach (QGraphicsItem *item, scene.items()) { + item->setFlags( + item->flags() | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); + item->setSelected(true); + } + QVERIFY(scene.items().size() > 1); + QCOMPARE(scene.items().size(), scene.selectedItems().size()); + + QSignalSpy spy(&scene, SIGNAL(selectionChanged())); + QGraphicsView view(&scene); + QTest::mouseClick( + view.viewport(), Qt::LeftButton, 0, view.mapFromScene(scene.items().first()->scenePos())); + QCOMPARE(spy.count(), 1); +} + +void tst_QGraphicsScene::task250680_childClip() +{ + QGraphicsRectItem *clipper = new QGraphicsRectItem; + clipper->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + clipper->setPen(QPen(Qt::green)); + clipper->setRect(200, 200, 640, 480); + + QGraphicsRectItem *rect = new QGraphicsRectItem(clipper); + rect->setPen(QPen(Qt::red)); + rect->setBrush(QBrush(QColor(255, 0, 0, 75))); + rect->setPos(320, 240); + rect->setRect(-25, -25, 50, 50); + + QGraphicsScene scene; + scene.addItem(clipper); + + QPainterPath path; + path.addRect(-25, -25, 50, 50); + QVERIFY(QPathCompare::comparePaths(rect->clipPath().simplified(), path)); + + QCOMPARE(scene.items(QRectF(320, 240, 5, 5)).size(), 2); + rect->rotate(45); + QCOMPARE(scene.items(QRectF(320, 240, 5, 5)).size(), 2); +} + +void tst_QGraphicsScene::sorting_data() +{ + QTest::addColumn<bool>("cache"); + + QTest::newRow("Normal sorting") << false; + QTest::newRow("Cached sorting") << true; +} + +void tst_QGraphicsScene::sorting() +{ + QFETCH(bool, cache); + + QGraphicsScene scene; + scene.setSortCacheEnabled(cache); + + QGraphicsRectItem *t_1 = new QGraphicsRectItem(0, 0, 50, 50); + QGraphicsRectItem *c_1 = new QGraphicsRectItem(0, 0, 40, 40, t_1); + QGraphicsRectItem *c_1_1 = new QGraphicsRectItem(0, 0, 30, 30, c_1); + QGraphicsRectItem *c_1_1_1 = new QGraphicsRectItem(0, 0, 20, 20, c_1_1); + QGraphicsRectItem *c_1_2 = new QGraphicsRectItem(0, 0, 30, 30, c_1); + QGraphicsRectItem *c_2 = new QGraphicsRectItem(0, 0, 40, 40, t_1); + QGraphicsRectItem *c_2_1 = new QGraphicsRectItem(0, 0, 30, 30, c_2); + QGraphicsRectItem *c_2_1_1 = new QGraphicsRectItem(0, 0, 20, 20, c_2_1); + QGraphicsRectItem *c_2_2 = new QGraphicsRectItem(0, 0, 30, 30, c_2); + t_1->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + c_1->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + c_1_1->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + c_1_1_1->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + c_1_2->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + c_2->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + c_2_1->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + c_2_1_1->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + c_2_2->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + + c_1->setPos(23, 18); + c_1_1->setPos(24, 28); + c_1_1_1->setPos(-16, 16); + c_1_2->setPos(-16, 28); + c_1_2->setZValue(1); + c_2->setPos(-23, 18); + c_2->setZValue(1); + c_2_1->setPos(24, 28); + c_2_1_1->setPos(-16, 16); + c_2_2->setPos(-16, 28); + c_2_2->setZValue(1); + + c_1->setFlag(QGraphicsItem::ItemIsMovable); + c_1_1->setFlag(QGraphicsItem::ItemIsMovable); + c_1_1_1->setFlag(QGraphicsItem::ItemIsMovable); + c_1_2->setFlag(QGraphicsItem::ItemIsMovable); + c_2->setFlag(QGraphicsItem::ItemIsMovable); + c_2_1->setFlag(QGraphicsItem::ItemIsMovable); + c_2_1_1->setFlag(QGraphicsItem::ItemIsMovable); + c_2_2->setFlag(QGraphicsItem::ItemIsMovable); + + t_1->setData(0, "t_1"); + c_1->setData(0, "c_1"); + c_1_1->setData(0, "c_1_1"); + c_1_1_1->setData(0, "c_1_1_1"); + c_1_2->setData(0, "c_1_2"); + c_2->setData(0, "c_2"); + c_2_1->setData(0, "c_2_1"); + c_2_1_1->setData(0, "c_2_1_1"); + c_2_2->setData(0, "c_2_2"); + + scene.addItem(t_1); + + foreach (QGraphicsItem *item, scene.items()) + item->setFlag(QGraphicsItem::ItemIsSelectable); + + // QGraphicsView view(&scene); + // view.setDragMode(QGraphicsView::RubberBandDrag); + // view.show(); + + qDebug() << "items: {"; + foreach (QGraphicsItem *item, scene.items(32, 31, 4, 55)) + qDebug() << "\t" << item->data(0).toString(); + qDebug() << "}"; + + QCOMPARE(scene.items(32, 31, 4, 55), + QList<QGraphicsItem *>() + << c_1_2 << c_1_1_1 << c_1 << t_1); + QCOMPARE(scene.items(-53, 47, 136, 3), + QList<QGraphicsItem *>() + << c_2_2 << c_2_1 << c_2 << c_1_2 << c_1_1 << c_1 << t_1); + QCOMPARE(scene.items(-23, 79, 104, 3), + QList<QGraphicsItem *>() + << c_2_1_1 << c_1_1_1); + QCOMPARE(scene.items(-26, -3, 92, 79), + QList<QGraphicsItem *>() + << c_2_2 << c_2_1_1 << c_2_1 << c_2 + << c_1_2 << c_1_1_1 << c_1_1 << c_1 + << t_1); +} + +class ChangedListener : public QObject +{ + Q_OBJECT +public: + QList<QList<QRectF> > changes; + +public slots: + void changed(const QList<QRectF> &dirty) + { + changes << dirty; + } +}; + +void tst_QGraphicsScene::changedSignal_data() +{ + QTest::addColumn<bool>("withView"); + + QTest::newRow("without view") << false; + QTest::newRow("with view") << true; +} + +void tst_QGraphicsScene::changedSignal() +{ + QFETCH(bool, withView); + QGraphicsScene scene; + ChangedListener cl; + connect(&scene, SIGNAL(changed(const QList<QRectF> &)), &cl, SLOT(changed(const QList<QRectF> &))); + + QGraphicsView *view = 0; + if (withView) + view = new QGraphicsView(&scene); + + QGraphicsRectItem *rect = new QGraphicsRectItem(0, 0, 10, 10); + scene.addItem(rect); + + QCOMPARE(cl.changes.size(), 0); + QTRY_COMPARE(cl.changes.size(), 1); + QCOMPARE(cl.changes.at(0).size(), 1); + QCOMPARE(cl.changes.at(0).first(), QRectF(0, 0, 10, 10)); + + rect->setPos(20, 0); + + QCOMPARE(cl.changes.size(), 1); + qApp->processEvents(); + QCOMPARE(cl.changes.size(), 2); + QCOMPARE(cl.changes.at(1).size(), 2); + QCOMPARE(cl.changes.at(1).first(), QRectF(0, 0, 10, 10)); + QCOMPARE(cl.changes.at(1).last(), QRectF(20, 0, 10, 10)); + + QCOMPARE(scene.sceneRect(), QRectF(0, 0, 30, 10)); + + if (withView) + delete view; +} + +void tst_QGraphicsScene::stickyFocus_data() +{ + QTest::addColumn<bool>("sticky"); + QTest::newRow("sticky") << true; + QTest::newRow("not sticky") << false; +} + +void tst_QGraphicsScene::stickyFocus() +{ + QFETCH(bool, sticky); + + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsTextItem *text = scene.addText("Hei"); + text->setTextInteractionFlags(Qt::TextEditorInteraction); + text->setFocus(); + + scene.setStickyFocus(sticky); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(QPointF(-10, -10)); // outside item + event.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + + QCOMPARE(text->hasFocus(), sticky); +} + +void tst_QGraphicsScene::sendEvent() +{ + QGraphicsScene scene; + QGraphicsTextItem *item = scene.addText(QString()); + EventSpy *spy = new EventSpy(&scene, item, QEvent::User); + QCOMPARE(spy->count(), 0); + QEvent event(QEvent::User); + scene.sendEvent(item, &event); + QCOMPARE(spy->count(), 1); +} + +void tst_QGraphicsScene::inputMethod_data() +{ + QTest::addColumn<int>("flags"); + QTest::addColumn<bool>("callFocusItem"); + QTest::newRow("0") << 0 << false; + QTest::newRow("1") << (int)QGraphicsItem::ItemAcceptsInputMethod << false; + QTest::newRow("2") << (int)QGraphicsItem::ItemIsFocusable << false; + QTest::newRow("3") << + (int)(QGraphicsItem::ItemAcceptsInputMethod|QGraphicsItem::ItemIsFocusable) << true; +} + +class InputMethodTester : public QGraphicsRectItem +{ + void inputMethodEvent(QInputMethodEvent *) { ++eventCalls; } + QVariant inputMethodQuery(Qt::InputMethodQuery) const { ++queryCalls; return QVariant(); } +public: + int eventCalls; + mutable int queryCalls; +}; + +class TestInputContext : public QInputContext +{ +public: + TestInputContext() {} + + QString identifierName() { return QString(); } + QString language() { return QString(); } + + void reset() { + ++resetCalls; + sendEvent(QInputMethodEvent()); } + + bool isComposing() const { return false; } + + int resetCalls; +}; + +void tst_QGraphicsScene::inputMethod() +{ + QFETCH(int, flags); + QFETCH(bool, callFocusItem); + + InputMethodTester *item = new InputMethodTester; + item->setFlags((QGraphicsItem::GraphicsItemFlags)flags); + + QGraphicsScene scene; + QGraphicsView view(&scene); + TestInputContext *inputContext = new TestInputContext; + qApp->setInputContext(inputContext); + view.show(); + QApplication::setActiveWindow(&view); + view.setFocus(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + inputContext->resetCalls = 0; + scene.addItem(item); + QInputMethodEvent event; + + scene.setFocusItem(item); + QCOMPARE(!!(item->flags() & QGraphicsItem::ItemIsFocusable), scene.focusItem() == item); + QCOMPARE(inputContext->resetCalls, 0); + + item->eventCalls = 0; + qApp->sendEvent(&scene, &event); + QCOMPARE(item->eventCalls, callFocusItem ? 1 : 0); + + item->queryCalls = 0; + scene.inputMethodQuery((Qt::InputMethodQuery)0); + QCOMPARE(item->queryCalls, callFocusItem ? 1 : 0); + + scene.setFocusItem(0); + // the input context is reset twice, once because an item has lost focus and again because + // the Qt::WA_InputMethodEnabled flag is cleared because no item has focus. + QCOMPARE(inputContext->resetCalls, callFocusItem ? 2 : 0); + QCOMPARE(item->eventCalls, callFocusItem ? 2 : 0); // verify correct delivery of "reset" event + QCOMPARE(item->queryCalls, callFocusItem ? 1 : 0); // verify that value is unaffected + + item->eventCalls = 0; + qApp->sendEvent(&scene, &event); + QCOMPARE(item->eventCalls, 0); + + item->queryCalls = 0; + scene.inputMethodQuery((Qt::InputMethodQuery)0); + QCOMPARE(item->queryCalls, 0); +} + +void tst_QGraphicsScene::dispatchHoverOnPress() +{ + QGraphicsScene scene; + EventTester *tester1 = new EventTester; + tester1->setAcceptHoverEvents(true); + EventTester *tester2 = new EventTester; + tester2->setAcceptHoverEvents(true); + tester2->setPos(30, 30); + scene.addItem(tester1); + scene.addItem(tester2); + + tester1->eventTypes.clear(); + tester2->eventTypes.clear(); + + { + QGraphicsSceneMouseEvent me(QEvent::GraphicsSceneMousePress); + me.setButton(Qt::LeftButton); + me.setButtons(Qt::LeftButton); + QGraphicsSceneMouseEvent me2(QEvent::GraphicsSceneMouseRelease); + me2.setButton(Qt::LeftButton); + qApp->sendEvent(&scene, &me); + qApp->sendEvent(&scene, &me2); + QCOMPARE(tester1->eventTypes, QList<QEvent::Type>() + << QEvent::GraphicsSceneHoverEnter + << QEvent::GraphicsSceneHoverMove + << QEvent::GrabMouse + << QEvent::GraphicsSceneMousePress + << QEvent::UngrabMouse); + tester1->eventTypes.clear(); + qApp->sendEvent(&scene, &me); + qApp->sendEvent(&scene, &me2); + QCOMPARE(tester1->eventTypes, QList<QEvent::Type>() + << QEvent::GraphicsSceneHoverMove + << QEvent::GrabMouse + << QEvent::GraphicsSceneMousePress + << QEvent::UngrabMouse); + } + { + QGraphicsSceneMouseEvent me(QEvent::GraphicsSceneMousePress); + me.setScenePos(QPointF(30, 30)); + me.setButton(Qt::LeftButton); + me.setButtons(Qt::LeftButton); + QGraphicsSceneMouseEvent me2(QEvent::GraphicsSceneMouseRelease); + me2.setScenePos(QPointF(30, 30)); + me2.setButton(Qt::LeftButton); + tester1->eventTypes.clear(); + qApp->sendEvent(&scene, &me); + qApp->sendEvent(&scene, &me2); + qDebug() << tester1->eventTypes; + QCOMPARE(tester1->eventTypes, QList<QEvent::Type>() + << QEvent::GraphicsSceneHoverLeave); + QCOMPARE(tester2->eventTypes, QList<QEvent::Type>() + << QEvent::GraphicsSceneHoverEnter + << QEvent::GraphicsSceneHoverMove + << QEvent::GrabMouse + << QEvent::GraphicsSceneMousePress + << QEvent::UngrabMouse); + tester2->eventTypes.clear(); + qApp->sendEvent(&scene, &me); + qApp->sendEvent(&scene, &me2); + QCOMPARE(tester2->eventTypes, QList<QEvent::Type>() + << QEvent::GraphicsSceneHoverMove + << QEvent::GrabMouse + << QEvent::GraphicsSceneMousePress + << QEvent::UngrabMouse); + } +} + +void tst_QGraphicsScene::initialFocus_data() +{ + QTest::addColumn<bool>("activeScene"); + QTest::addColumn<bool>("explicitSetFocus"); + QTest::addColumn<bool>("isPanel"); + QTest::addColumn<bool>("shouldHaveFocus"); + + QTest::newRow("inactive scene, normal item") << false << false << false << false; + QTest::newRow("inactive scene, panel item") << false << false << true << false; + QTest::newRow("inactive scene, normal item, explicit focus") << false << true << false << true; + QTest::newRow("inactive scene, panel, explicit focus") << false << true << true << true; + QTest::newRow("active scene, normal item") << true << false << false << false; + QTest::newRow("active scene, panel item") << true << false << true << false; + QTest::newRow("active scene, normal item, explicit focus") << true << true << false << true; + QTest::newRow("active scene, panel, explicit focus") << true << true << true << true; +} + +void tst_QGraphicsScene::initialFocus() +{ + QFETCH(bool, activeScene); + QFETCH(bool, explicitSetFocus); + QFETCH(bool, isPanel); + QFETCH(bool, shouldHaveFocus); + + QGraphicsRectItem *rect = new QGraphicsRectItem; + rect->setFlag(QGraphicsItem::ItemIsFocusable); + QVERIFY(!rect->hasFocus()); + + if (isPanel) + rect->setFlag(QGraphicsItem::ItemIsPanel); + + // Setting focus on an item before adding to the scene will ensure + // it gets focus when the scene is activated. + if (explicitSetFocus) + rect->setFocus(); + + QGraphicsScene scene; + QVERIFY(!scene.isActive()); + + if (activeScene) { + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + scene.setFocus(); + } + + scene.addItem(rect); + + if (!activeScene) { + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + scene.setFocus(); + } + + QCOMPARE(rect->hasFocus(), shouldHaveFocus); +} + +class PolishItem : public QGraphicsTextItem +{ +public: + PolishItem(QGraphicsItem *parent = 0) + : QGraphicsTextItem(parent), polished(false), deleteChildrenInPolish(true), addChildrenInPolish(false) { } + + bool polished; + bool deleteChildrenInPolish; + bool addChildrenInPolish; +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant& value) + { + if (change == ItemVisibleChange) { + polished = true; + if (deleteChildrenInPolish) + qDeleteAll(childItems()); + if (addChildrenInPolish) { + for (int i = 0; i < 10; ++i) + new PolishItem(this); + } + } + return QGraphicsItem::itemChange(change, value); + } +}; + +void tst_QGraphicsScene::polishItems() +{ + QGraphicsScene scene; + PolishItem *parent = new PolishItem; + scene.addItem(parent); + PolishItem *child = new PolishItem(parent); + Q_UNUSED(child) + // test that QGraphicsScenePrivate::_q_polishItems() doesn't crash + QMetaObject::invokeMethod(&scene,"_q_polishItems"); +} + +void tst_QGraphicsScene::polishItems2() +{ + QGraphicsScene scene; + PolishItem *item = new PolishItem; + item->addChildrenInPolish = true; + item->deleteChildrenInPolish = true; + // These children should be deleted in the polish. + for (int i = 0; i < 20; ++i) + new PolishItem(item); + scene.addItem(item); + + // Wait for the polish event to be delivered. + QVERIFY(!item->polished); + QApplication::sendPostedEvents(&scene, QEvent::MetaCall); + QVERIFY(item->polished); + + // We deleted the children we added above, but we also + // added 10 new children. These should be polished in the next + // event loop iteration. + QList<QGraphicsItem *> children = item->childItems(); + QCOMPARE(children.count(), 10); + foreach (QGraphicsItem *child, children) + QVERIFY(!static_cast<PolishItem *>(child)->polished); + + QApplication::sendPostedEvents(&scene, QEvent::MetaCall); + foreach (QGraphicsItem *child, children) + QVERIFY(static_cast<PolishItem *>(child)->polished); +} + +void tst_QGraphicsScene::isActive() +{ + QGraphicsScene scene1; + QVERIFY(!scene1.isActive()); + QGraphicsScene scene2; + QVERIFY(!scene2.isActive()); + + { + QWidget toplevel1; + QHBoxLayout *layout = new QHBoxLayout; + toplevel1.setLayout(layout); + QGraphicsView *view1 = new QGraphicsView(&scene1); + QGraphicsView *view2 = new QGraphicsView(&scene2); + layout->addWidget(view1); + layout->addWidget(view2); + + QVERIFY(!scene1.isActive()); + QVERIFY(!scene2.isActive()); + + view1->setVisible(false); + + toplevel1.show(); + QApplication::setActiveWindow(&toplevel1); + QTest::qWaitForWindowShown(&toplevel1); + QTRY_COMPARE(QApplication::activeWindow(), &toplevel1); + + QVERIFY(!scene1.isActive()); //it is hidden; + QVERIFY(scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(scene2.hasFocus()); + + view1->show(); + QVERIFY(scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(scene2.hasFocus()); + + view2->hide(); + + QVERIFY(scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + toplevel1.hide(); + QTest::qWait(50); + QTRY_VERIFY(!scene1.isActive()); + QTRY_VERIFY(!scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + toplevel1.show(); + QApplication::setActiveWindow(&toplevel1); + QApplication::processEvents(); + QTRY_COMPARE(QApplication::activeWindow(), &toplevel1); + + QTRY_VERIFY(scene1.isActive()); + QTRY_VERIFY(!scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + view2->show(); + QVERIFY(scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + } + + QVERIFY(!scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + + { + QWidget toplevel2; + QHBoxLayout *layout = new QHBoxLayout; + toplevel2.setLayout(layout); + QGraphicsView *view1 = new QGraphicsView(&scene1); + QGraphicsView *view2 = new QGraphicsView(); + layout->addWidget(view1); + layout->addWidget(view2); + + QVERIFY(!scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + toplevel2.show(); + QApplication::setActiveWindow(&toplevel2); + QTest::qWaitForWindowShown(&toplevel2); + QTRY_COMPARE(QApplication::activeWindow(), &toplevel2); + + QVERIFY(scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + view2->setScene(&scene2); + + QVERIFY(scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + view1->setScene(&scene2); + QVERIFY(!scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(scene2.hasFocus()); + + view1->hide(); + QVERIFY(!scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(scene2.hasFocus()); + + view1->setScene(&scene1); + QVERIFY(!scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(scene2.hasFocus()); + + view1->show(); + QVERIFY(scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(scene2.hasFocus()); + + view2->hide(); + QVERIFY(scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + QGraphicsView topLevelView; + topLevelView.show(); + QApplication::setActiveWindow(&topLevelView); + topLevelView.setFocus(); + QTest::qWaitForWindowShown(&topLevelView); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&topLevelView)); + + QVERIFY(!scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + topLevelView.setScene(&scene1); + QVERIFY(scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + view2->show(); + QVERIFY(scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + view1->hide(); + QVERIFY(scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + QApplication::setActiveWindow(&toplevel2); + QTRY_COMPARE(QApplication::activeWindow(), &toplevel2); + + QVERIFY(!scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(scene2.hasFocus()); + } + + QVERIFY(!scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + { + QWidget toplevel3; + QHBoxLayout *layout = new QHBoxLayout; + toplevel3.setLayout(layout); + QGraphicsView *view1 = new QGraphicsView(&scene1); + QGraphicsView *view2 = new QGraphicsView(&scene2); + layout->addWidget(view1); + + QVERIFY(!scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + + toplevel3.show(); + QApplication::setActiveWindow(&toplevel3); + QTest::qWaitForWindowShown(&toplevel3); + QTRY_COMPARE(QApplication::activeWindow(), &toplevel3); + + QVERIFY(scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + layout->addWidget(view2); + QApplication::processEvents(); + QVERIFY(scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + + view1->setParent(0); + QVERIFY(!scene1.isActive()); + QVERIFY(scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(scene2.hasFocus()); + delete view1; + } + + QVERIFY(!scene1.isActive()); + QVERIFY(!scene2.isActive()); + QVERIFY(!scene1.hasFocus()); + QVERIFY(!scene2.hasFocus()); + +} + +void tst_QGraphicsScene::siblingIndexAlwaysValid() +{ + QGraphicsScene scene; + + QGraphicsWidget *parent = new QGraphicsWidget; + parent->setZValue(350); + parent->setGeometry(0, 0, 100, 100); + QGraphicsWidget *parent2 = new QGraphicsWidget; + parent2->setGeometry(10, 10, 50, 50); + QGraphicsWidget *child = new QGraphicsWidget(parent2); + child->setGeometry(15, 15, 25, 25); + child->setZValue(150); + //Both are top level + scene.addItem(parent); + scene.addItem(parent2); + + //Then we make the child a top level + child->setParentItem(0); + + //This is trigerred by a repaint... + QGraphicsScenePrivate::get(&scene)->index->estimateTopLevelItems(QRectF(), Qt::AscendingOrder); + + delete child; + + //If there are in the list that's bad, we crash... + QVERIFY(!QGraphicsScenePrivate::get(&scene)->topLevelItems.contains(static_cast<QGraphicsItem *>(child))); + + //Other case + QGraphicsScene scene2; + // works with bsp tree index + scene2.setItemIndexMethod(QGraphicsScene::NoIndex); + + QGraphicsView view2(&scene2); + + // first add the blue rect + QGraphicsRectItem* const item1 = new QGraphicsRectItem(QRect( 10, 10, 10, 10 )); + item1->setPen(QColor(Qt::blue)); + item1->setBrush(Qt::blue); + scene2.addItem(item1); + + // then add the red rect + QGraphicsRectItem* const item2 = new QGraphicsRectItem(5, 5, 10, 10); + item2->setPen(QColor(Qt::red)); + item2->setBrush(Qt::red); + scene2.addItem(item2); + + // now the blue one is visible on top of the red one -> swap them (important for the bug) + item1->setZValue(1.0); + item2->setZValue(0.0); + + view2.show(); + + // handle events as a real life app would do + QApplication::processEvents(); + + // now delete the red rect + delete item2; + + // handle events as a real life app would do + QApplication::processEvents(); + + //We should not crash + +} + +void tst_QGraphicsScene::removeFullyTransparentItem() +{ + QGraphicsScene scene; + + QGraphicsItem *parent = scene.addRect(0, 0, 100, 100); + parent->setFlag(QGraphicsItem::ItemHasNoContents); + + QGraphicsItem *child = scene.addRect(0, 0, 100, 100); + child->setParentItem(parent); + + CustomView view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + // NB! The parent has the ItemHasNoContents flag set, which means + // the parent itself doesn't generate any update requests, only the + // child can possibly trigger an update. Also note that the child + // is removed before processing events. + view.repaints = 0; + parent->setOpacity(0); + QVERIFY(qFuzzyIsNull(child->effectiveOpacity())); + scene.removeItem(child); + QVERIFY(!scene.items().contains(child)); + QTRY_VERIFY(view.repaints > 0); + + // Re-add child. There's nothing new to display (child is still + // effectively hidden), so it shouldn't trigger an update. + view.repaints = 0; + child->setParentItem(parent); + QVERIFY(scene.items().contains(child)); + QVERIFY(qFuzzyIsNull(child->effectiveOpacity())); + QApplication::processEvents(); + QCOMPARE(view.repaints, 0); + + // Nothing is visible on the screen, removing child item shouldn't trigger an update. + scene.removeItem(child); + QApplication::processEvents(); + QCOMPARE(view.repaints, 0); + delete child; +} + +void tst_QGraphicsScene::taskQTBUG_5904_crashWithDeviceCoordinateCache() +{ + QGraphicsScene scene; + QGraphicsRectItem *rectItem = scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green)); + + rectItem->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + + QPixmap pixmap(100,200); + QPainter painter(&pixmap); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + // No crash, then it passed! +} + +void tst_QGraphicsScene::taskQT657_paintIntoCacheWithTransparentParts() +{ + // Test using DeviceCoordinateCache and opaque item + QWidget *w = new QWidget(); + w->setPalette(QColor(0, 0, 255)); + w->setGeometry(0, 0, 50, 50); + + QGraphicsScene *scene = new QGraphicsScene(); + CustomView *view = new CustomView; + view->setScene(scene); + + QGraphicsProxyWidget *proxy = scene->addWidget(w); + proxy->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + proxy->rotate(15); + + view->show(); + QTest::qWaitForWindowShown(view); + view->repaints = 0; + proxy->update(10, 10, 10, 10); + QTest::qWait(50); + QTRY_VERIFY(view->repaints > 0); + + QPixmap pix; + QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(proxy); + QTRY_VERIFY(QPixmapCache::find(itemp->extraItemCache()->deviceData.value(view->viewport()).key, &pix)); + + QTransform t = proxy->sceneTransform(); + // Map from scene coordinates to pixmap coordinates. + // X origin in the pixmap is the most-left point + // of the item's boundingRect in the scene. + qreal adjust = t.mapRect(proxy->boundingRect().toRect()).left(); + QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(-adjust, 0, -adjust + 1, 1); + QPixmap subpix = pix.copy(rect); + + QImage im = subpix.toImage(); + for(int i = 0; i < im.width(); i++) { + for(int j = 0; j < im.height(); j++) + QCOMPARE(qAlpha(im.pixel(i, j)), 255); + } + + delete w; +} + +void tst_QGraphicsScene::taskQTBUG_7863_paintIntoCacheWithTransparentParts() +{ + // Test using DeviceCoordinateCache and semi-transparent item + { + QGraphicsRectItem *backItem = new QGraphicsRectItem(0, 0, 100, 100); + backItem->setBrush(QColor(255, 255, 0)); + QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 50, 50); + rectItem->setBrush(QColor(0, 0, 255, 125)); + rectItem->setParentItem(backItem); + + QGraphicsScene *scene = new QGraphicsScene(); + CustomView *view = new CustomView; + view->setScene(scene); + + scene->addItem(backItem); + rectItem->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + backItem->rotate(15); + + view->show(); + QTest::qWaitForWindowShown(view); + view->repaints = 0; + rectItem->update(10, 10, 10, 10); + QTest::qWait(50); + QTRY_VERIFY(view->repaints > 0); + + QPixmap pix; + QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(rectItem); + QTRY_VERIFY(QPixmapCache::find(itemp->extraItemCache()->deviceData.value(view->viewport()).key, &pix)); + + QTransform t = rectItem->sceneTransform(); + // Map from scene coordinates to pixmap coordinates. + // X origin in the pixmap is the most-left point + // of the item's boundingRect in the scene. + qreal adjust = t.mapRect(rectItem->boundingRect().toRect()).left(); + QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(-adjust, 0, -adjust + 1, 1); + QPixmap subpix = pix.copy(rect); + + QImage im = subpix.toImage(); + for(int i = 0; i < im.width(); i++) { + for(int j = 0; j < im.height(); j++) { + QCOMPARE(qAlpha(im.pixel(i, j)), 125); + } + } + + delete view; + } + + // Test using ItemCoordinateCache and opaque item + { + QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 50, 50); + rectItem->setBrush(QColor(0, 0, 255)); + + QGraphicsScene *scene = new QGraphicsScene(); + CustomView *view = new CustomView; + view->setScene(scene); + + scene->addItem(rectItem); + rectItem->setCacheMode(QGraphicsItem::ItemCoordinateCache); + rectItem->rotate(15); + + view->show(); + QTest::qWaitForWindowShown(view); + view->repaints = 0; + rectItem->update(10, 10, 10, 10); + QTest::qWait(50); + QTRY_VERIFY(view->repaints > 0); + + QPixmap pix; + QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(rectItem); + QTRY_VERIFY(QPixmapCache::find(itemp->extraItemCache()->key, &pix)); + + QTransform t = rectItem->sceneTransform(); + // Map from scene coordinates to pixmap coordinates. + // X origin in the pixmap is the most-left point + // of the item's boundingRect in the scene. + qreal adjust = t.mapRect(rectItem->boundingRect().toRect()).left(); + QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(-adjust, 0, -adjust + 1, 1); + QPixmap subpix = pix.copy(rect); + + QImage im = subpix.toImage(); + for(int i = 0; i < im.width(); i++) { + for(int j = 0; j < im.height(); j++) + QCOMPARE(qAlpha(im.pixel(i, j)), 255); + } + + delete view; + } + + // Test using ItemCoordinateCache and semi-transparent item + { + QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 50, 50); + rectItem->setBrush(QColor(0, 0, 255, 125)); + + QGraphicsScene *scene = new QGraphicsScene(); + CustomView *view = new CustomView; + view->setScene(scene); + + scene->addItem(rectItem); + rectItem->setCacheMode(QGraphicsItem::ItemCoordinateCache); + rectItem->rotate(15); + + view->show(); + QTest::qWaitForWindowShown(view); + view->repaints = 0; + rectItem->update(10, 10, 10, 10); + QTest::qWait(50); + QTRY_VERIFY(view->repaints > 0); + + QPixmap pix; + QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(rectItem); + QTRY_VERIFY(QPixmapCache::find(itemp->extraItemCache()->key, &pix)); + + QTransform t = rectItem->sceneTransform(); + // Map from scene coordinates to pixmap coordinates. + // X origin in the pixmap is the most-left point + // of the item's boundingRect in the scene. + qreal adjust = t.mapRect(rectItem->boundingRect().toRect()).left(); + QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(-adjust, 0, -adjust + 1, 1); + QPixmap subpix = pix.copy(rect); + + QImage im = subpix.toImage(); + for(int i = 0; i < im.width(); i++) { + for(int j = 0; j < im.height(); j++) + QCOMPARE(qAlpha(im.pixel(i, j)), 125); + } + + delete view; + } +} + +void tst_QGraphicsScene::taskQT_3674_doNotCrash() +{ + QGraphicsScene scene; + + QGraphicsView view(&scene); + view.resize(200, 200); + + QPixmap pixmap(view.size()); + QPainter painter(&pixmap); + view.render(&painter); + painter.end(); + + scene.addItem(new QGraphicsWidget); + scene.setBackgroundBrush(Qt::green); + + QApplication::processEvents(); + QApplication::processEvents(); +} + +void tst_QGraphicsScene::zeroScale() +{ + //should not crash + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 100, 100); + QGraphicsView view(&scene); + + ChangedListener cl; + connect(&scene, SIGNAL(changed(const QList<QRectF> &)), &cl, SLOT(changed(const QList<QRectF> &))); + + QGraphicsRectItem *rect1 = new QGraphicsRectItem(0, 0, 0.0000001, 0.00000001); + scene.addItem(rect1); + rect1->setRotation(82); + rect1->setScale(0.00000001); + + QApplication::processEvents(); + QTRY_COMPARE(cl.changes.count(), 1); + QGraphicsRectItem *rect2 = new QGraphicsRectItem(-0.0000001, -0.0000001, 0.0000001, 0.0000001); + rect2->setScale(0.00000001); + scene.addItem(rect2); + rect1->setPos(20,20); + QApplication::processEvents(); + QTRY_COMPARE(cl.changes.count(), 2); +} + +void tst_QGraphicsScene::taskQTBUG_15977_renderWithDeviceCoordinateCache() +{ + QGraphicsScene scene; + scene.setSceneRect(0, 0, 100, 100); + QGraphicsRectItem *rect = scene.addRect(0, 0, 100, 100); + rect->setPen(Qt::NoPen); + rect->setBrush(Qt::red); + rect->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + + QImage image(100, 100, QImage::Format_RGB32); + QPainter p(&image); + scene.render(&p); + p.end(); + + QImage expected(100, 100, QImage::Format_RGB32); + p.begin(&expected); + p.fillRect(expected.rect(), Qt::red); + p.end(); + + QCOMPARE(image, expected); +} + +void tst_QGraphicsScene::taskQTBUG_16401_focusItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsRectItem *rect = scene.addRect(0, 0, 100, 100); + rect->setFlag(QGraphicsItem::ItemIsFocusable); + + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::setActiveWindow(&view); + + QVERIFY(!scene.focusItem()); + + rect->setFocus(); + QCOMPARE(scene.focusItem(), rect); + QFocusEvent focusOut(QEvent::FocusOut); + QApplication::sendEvent(&view, &focusOut); + QVERIFY(!scene.focusItem()); + QFocusEvent focusIn(QEvent::FocusIn); + QApplication::sendEvent(&view, &focusIn); + QCOMPARE(scene.focusItem(), rect); + + rect->clearFocus(); + QVERIFY(!scene.focusItem()); + QApplication::sendEvent(&view, &focusOut); + QVERIFY(!scene.focusItem()); + QApplication::sendEvent(&view, &focusIn); + QVERIFY(!scene.focusItem()); +} + +QTEST_MAIN(tst_QGraphicsScene) +#include "tst_qgraphicsscene.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicssceneindex/qgraphicssceneindex.pro b/tests/auto/widgets/graphicsview/qgraphicssceneindex/qgraphicssceneindex.pro new file mode 100644 index 0000000000..5e61034d7c --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicssceneindex/qgraphicssceneindex.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +requires(contains(QT_CONFIG,private_tests)) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qgraphicssceneindex.cpp +CONFIG += parallel_test diff --git a/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp b/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp new file mode 100644 index 0000000000..7507701267 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtWidgets/qgraphicsscene.h> +#include <private/qgraphicsscenebsptreeindex_p.h> +#include <private/qgraphicssceneindex_p.h> +#include <private/qgraphicsscenelinearindex_p.h> + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QGraphicsSceneIndex : public QObject +{ + Q_OBJECT +public slots: + void initTestCase(); + +private slots: + void customIndex_data(); + void customIndex(); + void scatteredItems_data(); + void scatteredItems(); + void overlappedItems_data(); + void overlappedItems(); + void movingItems_data(); + void movingItems(); + void connectedToSceneRectChanged(); + void items(); + void removeItems(); + void clear(); + +private: + void common_data(); + QGraphicsSceneIndex *createIndex(const QString &name); +}; + +void tst_QGraphicsSceneIndex::initTestCase() +{ +} + +void tst_QGraphicsSceneIndex::common_data() +{ + QTest::addColumn<QString>("indexMethod"); + + QTest::newRow("BSP") << QString("bsp"); + QTest::newRow("Linear") << QString("linear"); +} + +QGraphicsSceneIndex *tst_QGraphicsSceneIndex::createIndex(const QString &indexMethod) +{ + QGraphicsSceneIndex *index = 0; + QGraphicsScene *scene = new QGraphicsScene(); + if (indexMethod == "bsp") + index = new QGraphicsSceneBspTreeIndex(scene); + + if (indexMethod == "linear") + index = new QGraphicsSceneLinearIndex(scene); + + return index; +} + +void tst_QGraphicsSceneIndex::customIndex_data() +{ + common_data(); +} + +void tst_QGraphicsSceneIndex::customIndex() +{ +#if 0 + QFETCH(QString, indexMethod); + QGraphicsSceneIndex *index = createIndex(indexMethod); + + QGraphicsScene scene; + scene.setSceneIndex(index); + + scene.addRect(0, 0, 30, 40); + QCOMPARE(scene.items(QRectF(0, 0, 10, 10)).count(), 1); +#endif +} + +void tst_QGraphicsSceneIndex::scatteredItems_data() +{ + common_data(); +} + +void tst_QGraphicsSceneIndex::scatteredItems() +{ + QFETCH(QString, indexMethod); + + QGraphicsScene scene; +#if 1 + scene.setItemIndexMethod(indexMethod == "linear" ? QGraphicsScene::NoIndex : QGraphicsScene::BspTreeIndex); +#else + QGraphicsSceneIndex *index = createIndex(indexMethod); + scene.setSceneIndex(index); +#endif + + for (int i = 0; i < 10; ++i) + scene.addRect(i*50, i*50, 40, 35); + + QCOMPARE(scene.items(QPointF(5, 5)).count(), 1); + QCOMPARE(scene.items(QPointF(55, 55)).count(), 1); + QCOMPARE(scene.items(QPointF(-100, -100)).count(), 0); + + QCOMPARE(scene.items(QRectF(0, 0, 10, 10)).count(), 1); + QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 10); + QCOMPARE(scene.items(QRectF(-100, -1000, 0, 0)).count(), 0); +} + +void tst_QGraphicsSceneIndex::overlappedItems_data() +{ + common_data(); +} + +void tst_QGraphicsSceneIndex::overlappedItems() +{ + QFETCH(QString, indexMethod); + + QGraphicsScene scene; +#if 1 + scene.setItemIndexMethod(indexMethod == "linear" ? QGraphicsScene::NoIndex : QGraphicsScene::BspTreeIndex); +#else + QGraphicsSceneIndex *index = createIndex(indexMethod); + scene.setSceneIndex(index); +#endif + + for (int i = 0; i < 10; ++i) + for (int j = 0; j < 10; ++j) + scene.addRect(i*50, j*50, 200, 200); + + QCOMPARE(scene.items(QPointF(5, 5)).count(), 1); + QCOMPARE(scene.items(QPointF(55, 55)).count(), 4); + QCOMPARE(scene.items(QPointF(105, 105)).count(), 9); + QCOMPARE(scene.items(QPointF(-100, -100)).count(), 0); + + QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 100); + QCOMPARE(scene.items(QRectF(-100, -1000, 0, 0)).count(), 0); + QCOMPARE(scene.items(QRectF(0, 0, 200, 200)).count(), 16); + QCOMPARE(scene.items(QRectF(0, 0, 100, 100)).count(), 4); + QCOMPARE(scene.items(QRectF(0, 0, 1, 100)).count(), 2); + QCOMPARE(scene.items(QRectF(0, 0, 1, 1000)).count(), 10); +} + +void tst_QGraphicsSceneIndex::movingItems_data() +{ + common_data(); +} + +void tst_QGraphicsSceneIndex::movingItems() +{ + QFETCH(QString, indexMethod); + + QGraphicsScene scene; +#if 1 + scene.setItemIndexMethod(indexMethod == "linear" ? QGraphicsScene::NoIndex : QGraphicsScene::BspTreeIndex); +#else + QGraphicsSceneIndex *index = createIndex(indexMethod); + scene.setSceneIndex(index); +#endif + + for (int i = 0; i < 10; ++i) + scene.addRect(i*50, i*50, 40, 35); + + QGraphicsRectItem *box = scene.addRect(0, 0, 10, 10); + QCOMPARE(scene.items(QPointF(5, 5)).count(), 2); + QCOMPARE(scene.items(QPointF(-1, -1)).count(), 0); + QCOMPARE(scene.items(QRectF(0, 0, 5, 5)).count(), 2); + + box->setPos(10, 10); + QCOMPARE(scene.items(QPointF(9, 9)).count(), 1); + QCOMPARE(scene.items(QPointF(15, 15)).count(), 2); + QCOMPARE(scene.items(QRectF(0, 0, 1, 1)).count(), 1); + + box->setPos(-5, -5); + QCOMPARE(scene.items(QPointF(-1, -1)).count(), 1); + QCOMPARE(scene.items(QRectF(0, 0, 1, 1)).count(), 2); + + QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 11); +} + +void tst_QGraphicsSceneIndex::connectedToSceneRectChanged() +{ + + class MyScene : public QGraphicsScene + { + public: + using QGraphicsScene::receivers; + }; + + MyScene scene; // Uses QGraphicsSceneBspTreeIndex by default. + QCOMPARE(scene.receivers(SIGNAL(sceneRectChanged(const QRectF&))), 1); + + scene.setItemIndexMethod(QGraphicsScene::NoIndex); // QGraphicsSceneLinearIndex + QCOMPARE(scene.receivers(SIGNAL(sceneRectChanged(const QRectF&))), 1); +} + +void tst_QGraphicsSceneIndex::items() +{ + QGraphicsScene scene; + QGraphicsItem *item1 = scene.addRect(0, 0, 10, 10); + QGraphicsItem *item2 = scene.addRect(10, 10, 10, 10); + QCOMPARE(scene.items().size(), 2); + + // Move from unindexed items into bsp tree. + QTest::qWait(50); + QCOMPARE(scene.items().size(), 2); + + // Add untransformable item. + QGraphicsItem *item3 = new QGraphicsRectItem(QRectF(20, 20, 10, 10)); + item3->setFlag(QGraphicsItem::ItemIgnoresTransformations); + scene.addItem(item3); + QCOMPARE(scene.items().size(), 3); + + // Move from unindexed items into untransformable items. + QTest::qWait(50); + QCOMPARE(scene.items().size(), 3); + + // Move from untransformable items into unindexed items. + item3->setFlag(QGraphicsItem::ItemIgnoresTransformations, false); + QCOMPARE(scene.items().size(), 3); + QTest::qWait(50); + QCOMPARE(scene.items().size(), 3); + + // Make all items untransformable. + item1->setFlag(QGraphicsItem::ItemIgnoresTransformations); + item2->setParentItem(item1); + item3->setParentItem(item2); + QCOMPARE(scene.items().size(), 3); + + // Move from unindexed items into untransformable items. + QTest::qWait(50); + QCOMPARE(scene.items().size(), 3); +} + +class RectWidget : public QGraphicsWidget +{ + Q_OBJECT +public: + RectWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent) + { + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem * /* option */, QWidget * /* widget */) + { + painter->setBrush(brush); + painter->drawRect(boundingRect()); + } +public: + QBrush brush; +}; + +void tst_QGraphicsSceneIndex::removeItems() +{ + QGraphicsScene scene; + + RectWidget *parent = new RectWidget; + parent->brush = QBrush(QColor(Qt::magenta)); + parent->setGeometry(250, 250, 400, 400); + + RectWidget *widget = new RectWidget(parent); + widget->brush = QBrush(QColor(Qt::blue)); + widget->setGeometry(10, 10, 200, 200); + + RectWidget *widgetChild1 = new RectWidget(widget); + widgetChild1->brush = QBrush(QColor(Qt::green)); + widgetChild1->setGeometry(20, 20, 100, 100); + + RectWidget *widgetChild2 = new RectWidget(widgetChild1); + widgetChild2->brush = QBrush(QColor(Qt::yellow)); + widgetChild2->setGeometry(25, 25, 50, 50); + + scene.addItem(parent); + + QGraphicsView view(&scene); + view.resize(600, 600); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + + QApplication::processEvents(); + + scene.removeItem(widgetChild1); + + delete widgetChild1; + + //We move the parent + scene.items(295, 295, 50, 50); + + //This should not crash +} + +void tst_QGraphicsSceneIndex::clear() +{ + class MyItem : public QGraphicsItem + { + public: + MyItem(QGraphicsItem *parent = 0) : QGraphicsItem(parent), numPaints(0) {} + int numPaints; + protected: + QRectF boundingRect() const { return QRectF(0, 0, 10, 10); } + void paint(QPainter * /* painter */, const QStyleOptionGraphicsItem *, QWidget *) + { ++numPaints; } + }; + + QGraphicsScene scene; + scene.setSceneRect(0, 0, 100, 100); + scene.addItem(new MyItem); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(250); + scene.clear(); + + // Make sure the index is re-generated after QGraphicsScene::clear(); + // otherwise no items will be painted. + MyItem *item = new MyItem; + scene.addItem(item); + qApp->processEvents(); + QTRY_COMPARE(item->numPaints, 1); +} + +QTEST_MAIN(tst_QGraphicsSceneIndex) +#include "tst_qgraphicssceneindex.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicstransform/qgraphicstransform.pro b/tests/auto/widgets/graphicsview/qgraphicstransform/qgraphicstransform.pro new file mode 100644 index 0000000000..de7f01f36f --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicstransform/qgraphicstransform.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qgraphicstransform.cpp +CONFIG += parallel_test + +linux-*:contains(QT_CONFIG,release):DEFINES+=MAY_HIT_QTBUG_20661 diff --git a/tests/auto/widgets/graphicsview/qgraphicstransform/tst_qgraphicstransform.cpp b/tests/auto/widgets/graphicsview/qgraphicstransform/tst_qgraphicstransform.cpp new file mode 100644 index 0000000000..7d5a9578ad --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicstransform/tst_qgraphicstransform.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qgraphicsitem.h> +#include <qgraphicstransform.h> + +class tst_QGraphicsTransform : public QObject { + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void scale(); + void rotation(); + void rotation3d_data(); + void rotation3d(); + void rotation3dArbitraryAxis_data(); + void rotation3dArbitraryAxis(); + +private: + QString toString(QTransform const&); +}; + + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsTransform::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsTransform::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsTransform::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsTransform::cleanup() +{ +} + +static QTransform transform2D(const QGraphicsTransform& t) +{ + QMatrix4x4 m; + t.applyTo(&m); + return m.toTransform(); +} + +void tst_QGraphicsTransform::scale() +{ + QGraphicsScale scale; + + // check initial conditions + QCOMPARE(scale.xScale(), qreal(1)); + QCOMPARE(scale.yScale(), qreal(1)); + QCOMPARE(scale.zScale(), qreal(1)); + QCOMPARE(scale.origin(), QVector3D(0, 0, 0)); + + scale.setOrigin(QVector3D(10, 10, 0)); + + QCOMPARE(scale.xScale(), qreal(1)); + QCOMPARE(scale.yScale(), qreal(1)); + QCOMPARE(scale.zScale(), qreal(1)); + QCOMPARE(scale.origin(), QVector3D(10, 10, 0)); + + QMatrix4x4 t; + scale.applyTo(&t); + + QCOMPARE(t, QMatrix4x4()); + QCOMPARE(transform2D(scale), QTransform()); + + scale.setXScale(10); + scale.setOrigin(QVector3D(0, 0, 0)); + + QCOMPARE(scale.xScale(), qreal(10)); + QCOMPARE(scale.yScale(), qreal(1)); + QCOMPARE(scale.zScale(), qreal(1)); + QCOMPARE(scale.origin(), QVector3D(0, 0, 0)); + + QTransform res; + res.scale(10, 1); + + QCOMPARE(transform2D(scale), res); + QCOMPARE(transform2D(scale).map(QPointF(10, 10)), QPointF(100, 10)); + + scale.setOrigin(QVector3D(10, 10, 0)); + QCOMPARE(transform2D(scale).map(QPointF(10, 10)), QPointF(10, 10)); + QCOMPARE(transform2D(scale).map(QPointF(11, 10)), QPointF(20, 10)); + + scale.setYScale(2); + scale.setZScale(4.5); + scale.setOrigin(QVector3D(1, 2, 3)); + + QCOMPARE(scale.xScale(), qreal(10)); + QCOMPARE(scale.yScale(), qreal(2)); + QCOMPARE(scale.zScale(), qreal(4.5)); + QCOMPARE(scale.origin(), QVector3D(1, 2, 3)); + + QMatrix4x4 t2; + scale.applyTo(&t2); + + QCOMPARE(t2.map(QVector3D(4, 5, 6)), QVector3D(31, 8, 16.5)); + + // Because the origin has a non-zero z, mapping (4, 5) in 2D + // will introduce a projective component into the result. + QTransform t3 = t2.toTransform(); + QCOMPARE(t3.map(QPointF(4, 5)), QPointF(31 / t3.m33(), 8 / t3.m33())); +} + +// QMatrix4x4 uses float internally, whereas QTransform uses qreal. +// This can lead to issues with qFuzzyCompare() where it uses double +// precision to compare values that have no more than float precision +// after conversion from QMatrix4x4 to QTransform. The following +// definitions correct for the difference. +static inline bool fuzzyCompare(qreal p1, qreal p2) +{ + // increase delta on small machines using float instead of double + if (sizeof(qreal) == sizeof(float)) + return (qAbs(p1 - p2) <= 0.00003f * qMin(qAbs(p1), qAbs(p2))); + else + return (qAbs(p1 - p2) <= 0.00001f * qMin(qAbs(p1), qAbs(p2))); +} + +static bool fuzzyCompare(const QTransform& t1, const QTransform& t2) +{ + return fuzzyCompare(t1.m11(), t2.m11()) && + fuzzyCompare(t1.m12(), t2.m12()) && + fuzzyCompare(t1.m13(), t2.m13()) && + fuzzyCompare(t1.m21(), t2.m21()) && + fuzzyCompare(t1.m22(), t2.m22()) && + fuzzyCompare(t1.m23(), t2.m23()) && + fuzzyCompare(t1.m31(), t2.m31()) && + fuzzyCompare(t1.m32(), t2.m32()) && + fuzzyCompare(t1.m33(), t2.m33()); +} + +static inline bool fuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + bool ok = true; + for (int y = 0; y < 4; ++y) + for (int x = 0; x < 4; ++x) + ok &= fuzzyCompare(m1(y, x), m2(y, x)); + return ok; +} + +void tst_QGraphicsTransform::rotation() +{ + QGraphicsRotation rotation; + QCOMPARE(rotation.axis(), QVector3D(0, 0, 1)); + QCOMPARE(rotation.origin(), QVector3D(0, 0, 0)); + QCOMPARE(rotation.angle(), (qreal)0); + + rotation.setOrigin(QVector3D(10, 10, 0)); + + QCOMPARE(rotation.axis(), QVector3D(0, 0, 1)); + QCOMPARE(rotation.origin(), QVector3D(10, 10, 0)); + QCOMPARE(rotation.angle(), (qreal)0); + + QMatrix4x4 t; + rotation.applyTo(&t); + + QCOMPARE(t, QMatrix4x4()); + QCOMPARE(transform2D(rotation), QTransform()); + + rotation.setAngle(40); + rotation.setOrigin(QVector3D(0, 0, 0)); + + QCOMPARE(rotation.axis(), QVector3D(0, 0, 1)); + QCOMPARE(rotation.origin(), QVector3D(0, 0, 0)); + QCOMPARE(rotation.angle(), (qreal)40); + + QTransform res; + res.rotate(40); + + QVERIFY(fuzzyCompare(transform2D(rotation), res)); + + rotation.setOrigin(QVector3D(10, 10, 0)); + rotation.setAngle(90); + QCOMPARE(transform2D(rotation).map(QPointF(10, 10)), QPointF(10, 10)); + QCOMPARE(transform2D(rotation).map(QPointF(20, 10)), QPointF(10, 20)); + + rotation.setOrigin(QVector3D(0, 0, 0)); + rotation.setAngle(qQNaN()); + QCOMPARE(transform2D(rotation).map(QPointF(20, 10)), QPointF(20, 10)); +} + +Q_DECLARE_METATYPE(Qt::Axis); +void tst_QGraphicsTransform::rotation3d_data() +{ + QTest::addColumn<Qt::Axis>("axis"); + QTest::addColumn<qreal>("angle"); + + for (int angle = 0; angle <= 360; angle++) { + QTest::newRow("test rotation on X") << Qt::XAxis << qreal(angle); + QTest::newRow("test rotation on Y") << Qt::YAxis << qreal(angle); + QTest::newRow("test rotation on Z") << Qt::ZAxis << qreal(angle); + } +} + +void tst_QGraphicsTransform::rotation3d() +{ + QFETCH(Qt::Axis, axis); + QFETCH(qreal, angle); + + QGraphicsRotation rotation; + rotation.setAxis(axis); + + QMatrix4x4 t; + rotation.applyTo(&t); + + QVERIFY(t.isIdentity()); + QVERIFY(transform2D(rotation).isIdentity()); + + rotation.setAngle(angle); + + // QGraphicsRotation uses a correct mathematical rotation in 3D. + // QTransform's Qt::YAxis rotation is inverted from the mathematical + // version of rotation. We correct for that here. + QTransform expected; + if (axis == Qt::YAxis && angle != 180.) + expected.rotate(-angle, axis); + else + expected.rotate(angle, axis); + + QVERIFY(fuzzyCompare(transform2D(rotation), expected)); + + // Check that "rotation" produces the 4x4 form of the 3x3 matrix. + // i.e. third row and column are 0 0 1 0. + t.setToIdentity(); + rotation.applyTo(&t); + QMatrix4x4 r(expected); + if (sizeof(qreal) == sizeof(float) && angle == 268) { + // This test fails, on only this angle, when qreal == float + // because the deg2rad value in QTransform is not accurate + // enough to match what QMatrix4x4 is doing. + } else { + QVERIFY(fuzzyCompare(t, r)); + } + + //now let's check that a null vector will not change the transform + rotation.setAxis(QVector3D(0, 0, 0)); + rotation.setOrigin(QVector3D(10, 10, 0)); + + t.setToIdentity(); + rotation.applyTo(&t); + + QVERIFY(t.isIdentity()); + QVERIFY(transform2D(rotation).isIdentity()); + + rotation.setAngle(angle); + + QVERIFY(t.isIdentity()); + QVERIFY(transform2D(rotation).isIdentity()); + + rotation.setOrigin(QVector3D(0, 0, 0)); + + QVERIFY(t.isIdentity()); + QVERIFY(transform2D(rotation).isIdentity()); +} + +QByteArray labelForTest(QVector3D const& axis, int angle) { + return QString("rotation of %1 on (%2, %3, %4)") + .arg(angle) + .arg(axis.x()) + .arg(axis.y()) + .arg(axis.z()) + .toLatin1(); +} + +void tst_QGraphicsTransform::rotation3dArbitraryAxis_data() +{ + QTest::addColumn<QVector3D>("axis"); + QTest::addColumn<qreal>("angle"); + + QVector3D axis1 = QVector3D(1.0f, 1.0f, 1.0f); + QVector3D axis2 = QVector3D(2.0f, -3.0f, 0.5f); + QVector3D axis3 = QVector3D(-2.0f, 0.0f, -0.5f); + QVector3D axis4 = QVector3D(0.0001f, 0.0001f, 0.0001f); + QVector3D axis5 = QVector3D(0.01f, 0.01f, 0.01f); + + for (int angle = 0; angle <= 360; angle++) { + QTest::newRow(labelForTest(axis1, angle).constData()) << axis1 << qreal(angle); + QTest::newRow(labelForTest(axis2, angle).constData()) << axis2 << qreal(angle); + QTest::newRow(labelForTest(axis3, angle).constData()) << axis3 << qreal(angle); + QTest::newRow(labelForTest(axis4, angle).constData()) << axis4 << qreal(angle); + QTest::newRow(labelForTest(axis5, angle).constData()) << axis5 << qreal(angle); + } +} + +void tst_QGraphicsTransform::rotation3dArbitraryAxis() +{ + QFETCH(QVector3D, axis); + QFETCH(qreal, angle); + + QGraphicsRotation rotation; + rotation.setAxis(axis); + + QMatrix4x4 t; + rotation.applyTo(&t); + + QVERIFY(t.isIdentity()); + QVERIFY(transform2D(rotation).isIdentity()); + + rotation.setAngle(angle); + + // Compute the expected answer using QMatrix4x4 and a projection. + // These two steps are performed in one hit by QGraphicsRotation. + QMatrix4x4 exp; + exp.rotate(angle, axis); + QTransform expected = exp.toTransform(1024.0f); + +#if defined(MAY_HIT_QTBUG_20661) + // These failures possibly relate to the float vs qreal issue mentioned + // in the comment above fuzzyCompare(). + if (sizeof(qreal) == sizeof(double)) { + QEXPECT_FAIL("rotation of 120 on (1, 1, 1)", "QTBUG-20661", Abort); + QEXPECT_FAIL("rotation of 240 on (1, 1, 1)", "QTBUG-20661", Abort); + QEXPECT_FAIL("rotation of 120 on (0.01, 0.01, 0.01)", "QTBUG-20661", Abort); + QEXPECT_FAIL("rotation of 240 on (0.01, 0.01, 0.01)", "QTBUG-20661", Abort); + QEXPECT_FAIL("rotation of 120 on (0.0001, 0.0001, 0.0001)", "QTBUG-20661", Abort); + QEXPECT_FAIL("rotation of 240 on (0.0001, 0.0001, 0.0001)", "QTBUG-20661", Abort); + } +#endif + + QTransform actual = transform2D(rotation); + QVERIFY2(fuzzyCompare(actual, expected), qPrintable( + QString("\nactual: %1\n" + "expected: %2") + .arg(toString(actual)) + .arg(toString(expected)) + )); + + // Check that "rotation" produces the 4x4 form of the 3x3 matrix. + // i.e. third row and column are 0 0 1 0. + t.setToIdentity(); + rotation.applyTo(&t); + QMatrix4x4 r(expected); + QVERIFY(qFuzzyCompare(t, r)); +} + +QString tst_QGraphicsTransform::toString(QTransform const& t) +{ + return QString("[ [ %1 %2 %3 ]; [ %4 %5 %6 ]; [ %7 %8 %9 ] ]") + .arg(t.m11()) + .arg(t.m12()) + .arg(t.m13()) + .arg(t.m21()) + .arg(t.m22()) + .arg(t.m23()) + .arg(t.m31()) + .arg(t.m32()) + .arg(t.m33()) + ; +} + + +QTEST_MAIN(tst_QGraphicsTransform) +#include "tst_qgraphicstransform.moc" + diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/.gitignore b/tests/auto/widgets/graphicsview/qgraphicsview/.gitignore new file mode 100644 index 0000000000..c8182c9e0d --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsview/.gitignore @@ -0,0 +1 @@ +tst_qgraphicsview diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/qgraphicsview.pro b/tests/auto/widgets/graphicsview/qgraphicsview/qgraphicsview.pro new file mode 100644 index 0000000000..9f32522546 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsview/qgraphicsview.pro @@ -0,0 +1,9 @@ +load(qttest_p4) + +QT += widgets widgets-private +QT += core-private gui-private + +SOURCES += tst_qgraphicsview.cpp tst_qgraphicsview_2.cpp +DEFINES += QT_NO_CAST_TO_ASCII + +contains(QT_CONFIG,xcb):qpa:CONFIG+=insignificant_test # QTBUG-20756 crashes on qpa, xcb diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp new file mode 100644 index 0000000000..0a404cab46 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -0,0 +1,4558 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qgraphicsitem.h> +#include <qgraphicsscene.h> +#include <qgraphicssceneevent.h> +#include <qgraphicsview.h> +#include <qgraphicswidget.h> +#include <qgraphicsproxywidget.h> + +#include <math.h> + +#include <QtWidgets/QLabel> +#if !defined(QT_NO_STYLE_MOTIF) +#include <QtWidgets/QMotifStyle> +#endif +#if !defined(QT_NO_STYLE_WINDOWS) +#include <QtWidgets/QWindowsStyle> +#endif +#if !defined(QT_NO_STYLE_PLASTIQUE) +#include <QtWidgets/QPlastiqueStyle> +#endif +#include <QtGui/QPainterPath> +#include <QtWidgets/QRubberBand> +#include <QtWidgets/QScrollBar> +#include <QtWidgets/QStyleOption> +#include <QtWidgets/QBoxLayout> +#include <QtWidgets/QStyle> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QInputContext> +#include <QtWidgets/QDesktopWidget> +#include <private/qgraphicsview_p.h> +#include "../../../platformquirks.h" + +//TESTED_CLASS= +//TESTED_FILES= + +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<QRectF>) +Q_DECLARE_METATYPE(QMatrix) +Q_DECLARE_METATYPE(QPainterPath) +Q_DECLARE_METATYPE(QPointF) +Q_DECLARE_METATYPE(QPolygonF) +Q_DECLARE_METATYPE(QRectF) +Q_DECLARE_METATYPE(Qt::ScrollBarPolicy) + +#ifdef Q_WS_MAC +//On mac we 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 QCOMPARE +#endif + +static void sendMousePress(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::LeftButton) +{ + QMouseEvent event(QEvent::MouseButtonPress, point, widget->mapToGlobal(point), button, 0, 0); + QApplication::sendEvent(widget, &event); +} + +static void sendMouseMove(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::NoButton, Qt::MouseButtons buttons = 0) +{ + QTest::mouseMove(widget, point); + QMouseEvent event(QEvent::MouseMove, point, button, buttons, 0); + QApplication::sendEvent(widget, &event); + QApplication::processEvents(); +} + +static void sendMouseRelease(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::LeftButton) +{ + QMouseEvent event(QEvent::MouseButtonRelease, point, widget->mapToGlobal(point), button, 0, 0); + QApplication::sendEvent(widget, &event); +} + +class EventSpy : public QObject +{ + Q_OBJECT +public: + EventSpy(QObject *watched, QEvent::Type type) + : _count(0), spied(type) + { + watched->installEventFilter(this); + } + + int count() const { return _count; } + void reset() { _count = 0; } + +protected: + bool eventFilter(QObject *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + int _count; + QEvent::Type spied; +}; + +class tst_QGraphicsView : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void construction(); + void renderHints(); + void alignment(); + void interactive(); + void scene(); + void setScene(); + void deleteScene(); + void sceneRect(); + void sceneRect_growing(); + void setSceneRect(); + void viewport(); + void dragMode_scrollHand(); + void dragMode_rubberBand(); + void rubberBandSelectionMode(); + void backgroundBrush(); + void foregroundBrush(); + void matrix(); + void matrix_convenience(); + void matrix_combine(); + void centerOnPoint(); + void centerOnItem(); + void ensureVisibleRect(); + void fitInView(); + void itemsAtPoint(); + void itemsInRect(); + void itemsInRect_cosmeticAdjust_data(); + void itemsInRect_cosmeticAdjust(); + void itemsInPoly(); + void itemsInPath(); + void itemAt(); + void itemAt2(); + void mapToScene(); + void mapToScenePoint(); + void mapToSceneRect_data(); + void mapToSceneRect(); + void mapToScenePoly(); + void mapToScenePath(); + void mapFromScenePoint(); + void mapFromSceneRect(); + void mapFromScenePoly(); + void mapFromScenePath(); + void sendEvent(); + void wheelEvent(); +#if !defined(QT_NO_CURSOR) && !defined(Q_OS_WINCE) + void cursor(); + void cursor2(); +#endif + void transformationAnchor(); + void resizeAnchor(); + void viewportUpdateMode(); + void viewportUpdateMode2(); +#ifndef QT_NO_DRAGANDDROP + void acceptDrops(); +#endif + void optimizationFlags(); + void optimizationFlags_dontSavePainterState(); + void optimizationFlags_dontSavePainterState2_data(); + void optimizationFlags_dontSavePainterState2(); + void levelOfDetail_data(); + void levelOfDetail(); + void scrollBarRanges_data(); + void scrollBarRanges(); + void acceptMousePressEvent(); + void replayMouseMove(); + void itemsUnderMouse(); + void embeddedViews(); + void scrollAfterResize_data(); + void scrollAfterResize(); + void moveItemWhileScrolling_data(); + void moveItemWhileScrolling(); + void centerOnDirtyItem(); + void mouseTracking(); + void mouseTracking2(); + void mouseTracking3(); + void render(); + void exposeRegion(); + void update_data(); + void update(); + void update2_data(); + void update2(); + void update_ancestorClipsChildrenToShape(); + void update_ancestorClipsChildrenToShape2(); + void inputMethodSensitivity(); + void inputContextReset(); + void indirectPainting(); + void compositionModeInDrawBackground(); + + // task specific tests below me + void task172231_untransformableItems(); + void task180429_mouseReleaseDragMode(); + void task187791_setSceneCausesUpdate(); + void task186827_deleteReplayedItem(); + void task207546_focusCrash(); + void task210599_unsetDragWhileDragging(); + void task236394_sendShortcutOverrideEvent(); + void task239729_noViewUpdate_data(); + void task239729_noViewUpdate(); + void task239047_fitInViewSmallViewport(); + void task245469_itemsAtPointWithClip(); + void task253415_reconnectUpdateSceneOnSceneChanged(); +#ifndef Q_OS_WINCE + void task255529_transformationAnchorMouseAndViewportMargins(); +#endif + void task259503_scrollingArtifacts(); + void QTBUG_4151_clipAndIgnore_data(); + void QTBUG_4151_clipAndIgnore(); + void QTBUG_5859_exposedRect(); +#if !defined(QT_NO_CURSOR) && !defined(Q_OS_WINCE) + void QTBUG_7438_cursor(); +#endif + void hoverLeave(); + void QTBUG_16063_microFocusRect(); + +public slots: + void dummySlot() {} +}; + +void tst_QGraphicsView::initTestCase() +{ +#ifdef Q_OS_WINCE_WM + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +void tst_QGraphicsView::construction() +{ + QGraphicsView view; + QCOMPARE(view.renderHints(), QPainter::TextAntialiasing); + QCOMPARE(view.dragMode(), QGraphicsView::NoDrag); + QVERIFY(view.isInteractive()); + QVERIFY(!view.scene()); + QCOMPARE(view.sceneRect(), QRectF()); + QVERIFY(view.viewport()); + QCOMPARE(view.viewport()->metaObject()->className(), "QWidget"); + QCOMPARE(view.matrix(), QMatrix()); + QVERIFY(view.items().isEmpty()); + QVERIFY(view.items(QPoint()).isEmpty()); + QVERIFY(view.items(QRect()).isEmpty()); + QVERIFY(view.items(QPolygon()).isEmpty()); + QVERIFY(view.items(QPainterPath()).isEmpty()); + QVERIFY(!view.itemAt(QPoint())); + QCOMPARE(view.mapToScene(QPoint()), QPointF()); + QCOMPARE(view.mapToScene(QRect()), QPolygonF()); + QCOMPARE(view.mapToScene(QPolygon()), QPolygonF()); + QCOMPARE(view.mapFromScene(QPointF()), QPoint()); + QPolygon poly; + poly << QPoint() << QPoint() << QPoint() << QPoint(); + QCOMPARE(view.mapFromScene(QRectF()), poly); + QCOMPARE(view.mapFromScene(QPolygonF()), QPolygon()); + QCOMPARE(view.transformationAnchor(), QGraphicsView::AnchorViewCenter); + QCOMPARE(view.resizeAnchor(), QGraphicsView::NoAnchor); + view.show(); + QTest::qWaitForWindowShown(&view); +} + +class TestItem : public QGraphicsItem +{ +public: + QRectF boundingRect() const + { return QRectF(-10, -10, 20, 20); } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { hints = painter->renderHints(); painter->drawRect(boundingRect()); } + + bool sceneEvent(QEvent *event) + { + events << event->type(); + return QGraphicsItem::sceneEvent(event); + } + + QList<QEvent::Type> events; + QPainter::RenderHints hints; +}; + +void tst_QGraphicsView::renderHints() +{ + QGraphicsView view; + QCOMPARE(view.renderHints(), QPainter::TextAntialiasing); + view.setRenderHint(QPainter::TextAntialiasing, false); + QCOMPARE(view.renderHints(), 0); + view.setRenderHint(QPainter::Antialiasing, false); + QCOMPARE(view.renderHints(), 0); + view.setRenderHint(QPainter::TextAntialiasing, true); + QCOMPARE(view.renderHints(), QPainter::TextAntialiasing); + view.setRenderHint(QPainter::Antialiasing); + QCOMPARE(view.renderHints(), QPainter::TextAntialiasing | QPainter::Antialiasing); + view.setRenderHints(0); + QCOMPARE(view.renderHints(), 0); + + TestItem *item = new TestItem; + QGraphicsScene scene; + scene.addItem(item); + + view.setScene(&scene); + + view.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing | QPainter::NonCosmeticDefaultPen); + QCOMPARE(view.renderHints(), QPainter::TextAntialiasing | QPainter::Antialiasing | QPainter::NonCosmeticDefaultPen); + + QCOMPARE(item->hints, 0); + view.show(); + QTest::qWaitForWindowShown(&view); + view.repaint(); + QTRY_COMPARE(item->hints, view.renderHints()); + + view.setRenderHints(QPainter::Antialiasing | QPainter::NonCosmeticDefaultPen); + QCOMPARE(view.renderHints(), QPainter::Antialiasing | QPainter::NonCosmeticDefaultPen); + + view.repaint(); + QTRY_COMPARE(item->hints, view.renderHints()); +} + +void tst_QGraphicsView::alignment() +{ + QGraphicsScene scene; + scene.addRect(QRectF(-10, -10, 20, 20)); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + Qt::Alignment alignment = 0; + switch (i) { + case 0: + alignment |= Qt::AlignLeft; + break; + case 1: + alignment |= Qt::AlignHCenter; + break; + case 2: + default: + alignment |= Qt::AlignRight; + break; + } + switch (j) { + case 0: + alignment |= Qt::AlignTop; + break; + case 1: + alignment |= Qt::AlignVCenter; + break; + case 2: + default: + alignment |= Qt::AlignBottom; + break; + } + view.setAlignment(alignment); + QCOMPARE(view.alignment(), alignment); + + for (int k = 0; k < 3; ++k) { + view.resize(100 + k * 25, 100 + k * 25); + QApplication::processEvents(); + } + } + } +} + +void tst_QGraphicsView::interactive() +{ + TestItem *item = new TestItem; + item->setFlags(QGraphicsItem::ItemIsMovable); + QCOMPARE(item->events.size(), 0); + + QGraphicsScene scene(-200, -200, 400, 400); + scene.addItem(item); + + QGraphicsView view(&scene); + if (PlatformQuirks::isAutoMaximizing()) + view.setWindowFlags(view.windowFlags()|Qt::X11BypassWindowManagerHint); + view.setFixedSize(300, 300); + QCOMPARE(item->events.size(), 0); + view.show(); + QTest::qWaitForWindowShown(&view); + view.activateWindow(); + + QApplication::processEvents(); + QTRY_COMPARE(item->events.size(), 1); // activate + + QPoint itemPoint = view.mapFromScene(item->scenePos()); + + QVERIFY(view.itemAt(itemPoint)); + + for (int i = 0; i < 100; ++i) { + sendMousePress(view.viewport(), itemPoint); + QCOMPARE(item->events.size(), i * 5 + 3); + QCOMPARE(item->events.at(item->events.size() - 2), QEvent::GrabMouse); + QCOMPARE(item->events.at(item->events.size() - 1), QEvent::GraphicsSceneMousePress); + sendMouseRelease(view.viewport(), itemPoint); + QCOMPARE(item->events.size(), i * 5 + 5); + QCOMPARE(item->events.at(item->events.size() - 2), QEvent::GraphicsSceneMouseRelease); + QCOMPARE(item->events.at(item->events.size() - 1), QEvent::UngrabMouse); + QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, itemPoint, view.mapToGlobal(itemPoint)); + QApplication::sendEvent(view.viewport(), &contextEvent); + QCOMPARE(item->events.size(), i * 5 + 6); + QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu); + } + + view.setInteractive(false); + + for (int i = 0; i < 100; ++i) { + sendMousePress(view.viewport(), itemPoint); + QCOMPARE(item->events.size(), 501); + QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu); + sendMouseRelease(view.viewport(), itemPoint); + QCOMPARE(item->events.size(), 501); + QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu); + QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, itemPoint, view.mapToGlobal(itemPoint)); + QApplication::sendEvent(view.viewport(), &contextEvent); + QCOMPARE(item->events.size(), 501); + QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu); + } +} + +void tst_QGraphicsView::scene() +{ + QGraphicsView view; + QVERIFY(!view.scene()); + view.setScene(0); + QVERIFY(!view.scene()); + + { + QGraphicsScene scene; + view.setScene(&scene); + QCOMPARE(view.scene(), &scene); + } + + QCOMPARE(view.scene(), (QGraphicsScene *)0); +} + +void tst_QGraphicsView::setScene() +{ + QGraphicsScene scene(-1000, -1000, 2000, 2000); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(view.sceneRect(), scene.sceneRect()); + + QVERIFY(view.horizontalScrollBar()->isVisible()); + QVERIFY(view.verticalScrollBar()->isVisible()); + QVERIFY(!view.horizontalScrollBar()->isHidden()); + QVERIFY(!view.verticalScrollBar()->isHidden()); + + view.setScene(0); + + QTest::qWait(25); + + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); + QVERIFY(!view.horizontalScrollBar()->isHidden()); + QVERIFY(!view.verticalScrollBar()->isHidden()); + + QCOMPARE(view.sceneRect(), QRectF()); +} + +void tst_QGraphicsView::deleteScene() +{ + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsView view1(scene); + view1.show(); + QGraphicsView view2(scene); + view2.show(); + QGraphicsView view3(scene); + view3.show(); + delete scene; + QCOMPARE(view1.scene(), (QGraphicsScene *)0); + QCOMPARE(view2.scene(), (QGraphicsScene *)0); + QCOMPARE(view3.scene(), (QGraphicsScene *)0); +} + +void tst_QGraphicsView::sceneRect() +{ + QGraphicsView view; + QCOMPARE(view.sceneRect(), QRectF()); + + view.setSceneRect(QRectF(-100, -100, 200, 200)); + QCOMPARE(view.sceneRect(), QRectF(-100, -100, 200, 200)); + view.setSceneRect(-100, -100, 200, 200); + QCOMPARE(view.sceneRect(), QRectF(-100, -100, 200, 200)); + + view.setSceneRect(QRectF()); + QCOMPARE(view.sceneRect(), QRectF()); + QGraphicsScene scene; + QGraphicsRectItem *item = scene.addRect(QRectF(-100, -100, 100, 100)); + + view.setScene(&scene); + + QCOMPARE(view.sceneRect(), QRectF(-100, -100, 100, 100)); + item->moveBy(-100, -100); + QCOMPARE(view.sceneRect(), QRectF(-200, -200, 200, 200)); + item->moveBy(100, 100); + QCOMPARE(view.sceneRect(), QRectF(-200, -200, 200, 200)); + + view.setScene(0); + view.setSceneRect(QRectF()); + QCOMPARE(view.sceneRect(), QRectF()); +} + +void tst_QGraphicsView::sceneRect_growing() +{ + QWidget toplevel; + + QGraphicsScene scene; + for (int i = 0; i < 100; ++i) + scene.addText(QString("(0, %1)").arg((i - 50) * 20))->setPos(0, (i - 50) * 20); + + QGraphicsView view(&scene, &toplevel); + view.setFixedSize(200, 200); + toplevel.show(); + + int size = 200; + scene.setSceneRect(-size, -size, size * 2, size * 2); + QCOMPARE(view.sceneRect(), scene.sceneRect()); + + QTest::qWait(25); + + QPointF topLeft = view.mapToScene(0, 0); + + for (int i = 0; i < 5; ++i) { + size *= 2; + scene.setSceneRect(-size, -size, size * 2, size * 2); + + QApplication::processEvents(); + + QCOMPARE(view.sceneRect(), scene.sceneRect()); + QCOMPARE(view.mapToScene(0, 0), topLeft); + view.setSceneRect(-size, -size, size * 2, size * 2); + QCOMPARE(view.mapToScene(0, 0), topLeft); + view.setSceneRect(QRectF()); + } +} + +void tst_QGraphicsView::setSceneRect() +{ + QRectF rect1(-100, -100, 200, 200); + QRectF rect2(-300, -300, 150, 150); + + QGraphicsScene scene; + QGraphicsView view(&scene); + + scene.setSceneRect(rect1); + QCOMPARE(scene.sceneRect(), rect1); + QCOMPARE(view.sceneRect(), rect1); + + scene.setSceneRect(rect2); + QCOMPARE(scene.sceneRect(), rect2); + QCOMPARE(view.sceneRect(), rect2); + + view.setSceneRect(rect1); + QCOMPARE(scene.sceneRect(), rect2); + QCOMPARE(view.sceneRect(), rect1); + + view.setSceneRect(rect2); + QCOMPARE(scene.sceneRect(), rect2); + QCOMPARE(view.sceneRect(), rect2); + + scene.setSceneRect(rect1); + QCOMPARE(scene.sceneRect(), rect1); + QCOMPARE(view.sceneRect(), rect2); + + // extreme transformations will max out the scrollbars' ranges. + view.setSceneRect(-2000000, -2000000, 4000000, 4000000); + view.scale(9000, 9000); + QCOMPARE(view.horizontalScrollBar()->minimum(), INT_MIN); + QCOMPARE(view.horizontalScrollBar()->maximum(), INT_MAX); + QCOMPARE(view.verticalScrollBar()->minimum(), INT_MIN); + QCOMPARE(view.verticalScrollBar()->maximum(), INT_MAX); +} + +void tst_QGraphicsView::viewport() +{ + QGraphicsScene scene; + scene.addText("GraphicsView"); + + QGraphicsView view(&scene); + QVERIFY(view.viewport() != 0); + + view.show(); + QTest::qWait(25); + + QPointer<QWidget> widget = new QWidget; + view.setViewport(widget); + QCOMPARE(view.viewport(), (QWidget *)widget); + + view.show(); + QTest::qWait(25); + + view.setViewport(0); + QVERIFY(widget.isNull()); + QVERIFY(view.viewport() != 0); + QVERIFY(view.viewport() != widget); + + view.show(); + QTest::qWait(25); +} + +void tst_QGraphicsView::dragMode_scrollHand() +{ + for (int j = 0; j < 2; ++j) { + QGraphicsView view; + QCOMPARE(view.dragMode(), QGraphicsView::NoDrag); + + view.setSceneRect(-1000, -1000, 2000, 2000); + view.setFixedSize(100, 100); + view.show(); + + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + view.setInteractive(j ? false : true); + + QGraphicsScene scene; + scene.addRect(QRectF(-100, -100, 5, 5)); + scene.addRect(QRectF(95, -100, 5, 5)); + scene.addRect(QRectF(95, 95, 5, 5)); + QGraphicsItem *item = scene.addRect(QRectF(-100, 95, 5, 5)); + item->setFlag(QGraphicsItem::ItemIsSelectable); + item->setSelected(true); + QVERIFY(item->isSelected()); + QVERIFY(!view.scene()); + + view.setDragMode(QGraphicsView::ScrollHandDrag); + + for (int i = 0; i < 2; ++i) { + // ScrollHandDrag +#ifndef QT_NO_CURSOR + Qt::CursorShape cursorShape = view.viewport()->cursor().shape(); +#endif + int horizontalScrollBarValue = view.horizontalScrollBar()->value(); + int verticalScrollBarValue = view.verticalScrollBar()->value(); + { + // Press + QMouseEvent event(QEvent::MouseButtonPress, + view.viewport()->rect().center(), + Qt::LeftButton, Qt::LeftButton, 0); + event.setAccepted(true); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + QApplication::processEvents(); + + QTRY_VERIFY(item->isSelected()); + + for (int k = 0; k < 4; ++k) { +#ifndef QT_NO_CURSOR + QCOMPARE(view.viewport()->cursor().shape(), Qt::ClosedHandCursor); +#endif + { + // Move + QMouseEvent event(QEvent::MouseMove, + view.viewport()->rect().center() + QPoint(10, 0), + Qt::LeftButton, Qt::LeftButton, 0); + event.setAccepted(true); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + QVERIFY(item->isSelected()); + QCOMPARE(view.horizontalScrollBar()->value(), horizontalScrollBarValue - 10); + QCOMPARE(view.verticalScrollBar()->value(), verticalScrollBarValue); + { + // Move + QMouseEvent event(QEvent::MouseMove, + view.viewport()->rect().center() + QPoint(10, 10), + Qt::LeftButton, Qt::LeftButton, 0); + event.setAccepted(true); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + QVERIFY(item->isSelected()); + QCOMPARE(view.horizontalScrollBar()->value(), horizontalScrollBarValue - 10); + QCOMPARE(view.verticalScrollBar()->value(), verticalScrollBarValue - 10); + } + + { + // Release + QMouseEvent event(QEvent::MouseButtonRelease, + view.viewport()->rect().center() + QPoint(10, 10), + Qt::LeftButton, Qt::LeftButton, 0); + event.setAccepted(true); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + QApplication::processEvents(); + + QTRY_VERIFY(item->isSelected()); + QCOMPARE(view.horizontalScrollBar()->value(), horizontalScrollBarValue - 10); + QCOMPARE(view.verticalScrollBar()->value(), verticalScrollBarValue - 10); +#ifndef QT_NO_CURSOR + QCOMPARE(view.viewport()->cursor().shape(), cursorShape); +#endif + + // Check that items are not unselected because of a scroll hand drag. + QVERIFY(item->isSelected()); + + // Check that a click will still unselect the item. + { + // Press + QMouseEvent event(QEvent::MouseButtonPress, + view.viewport()->rect().center() + QPoint(10, 10), + Qt::LeftButton, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &event); + } + { + // Release + QMouseEvent event(QEvent::MouseButtonRelease, + view.viewport()->rect().center() + QPoint(10, 10), + Qt::LeftButton, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + if (view.isInteractive()) { + if (view.scene()) { + QVERIFY(!item->isSelected()); + item->setSelected(true); + } else { + QVERIFY(item->isSelected()); + } + } else { + QVERIFY(item->isSelected()); + } + + view.setScene(&scene); + } + } +} + +void tst_QGraphicsView::dragMode_rubberBand() +{ + QGraphicsView view; + QCOMPARE(view.dragMode(), QGraphicsView::NoDrag); + + view.setSceneRect(-1000, -1000, 2000, 2000); + view.show(); + + QGraphicsScene scene; + scene.addRect(QRectF(-100, -100, 25, 25))->setFlag(QGraphicsItem::ItemIsSelectable); + scene.addRect(QRectF(75, -100, 25, 25))->setFlag(QGraphicsItem::ItemIsSelectable); + scene.addRect(QRectF(75, 75, 25, 25))->setFlag(QGraphicsItem::ItemIsSelectable); + scene.addRect(QRectF(-100, 75, 25, 25))->setFlag(QGraphicsItem::ItemIsSelectable); + + view.setDragMode(QGraphicsView::RubberBandDrag); + + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + for (int i = 0; i < 2; ++i) { + // RubberBandDrag +#ifndef QT_NO_CURSOR + Qt::CursorShape cursorShape = view.viewport()->cursor().shape(); +#endif + int horizontalScrollBarValue = view.horizontalScrollBar()->value(); + int verticalScrollBarValue = view.verticalScrollBar()->value(); + { + // Press + QMouseEvent event(QEvent::MouseButtonPress, + view.viewport()->rect().center(), + Qt::LeftButton, Qt::LeftButton, 0); + event.setAccepted(true); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } +#ifndef QT_NO_CURSOR + QCOMPARE(view.viewport()->cursor().shape(), cursorShape); +#endif + + QApplication::processEvents(); + + { + // Move + QMouseEvent event(QEvent::MouseMove, + view.viewport()->rect().center() + QPoint(100, 0), + Qt::LeftButton, Qt::LeftButton, 0); + event.setAccepted(true); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + QCOMPARE(view.horizontalScrollBar()->value(), horizontalScrollBarValue); + QCOMPARE(view.verticalScrollBar()->value(), verticalScrollBarValue); + + // We don't use QRubberBand as of 4.3; the band is drawn internally. + QVERIFY(!qFindChild<QRubberBand *>(&view)); + + QTest::qWait(25); + + { + // Move + QMouseEvent event(QEvent::MouseMove, + view.viewport()->rect().center() + QPoint(100, 100), + Qt::LeftButton, Qt::LeftButton, 0); + event.setAccepted(true); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + QCOMPARE(view.horizontalScrollBar()->value(), horizontalScrollBarValue); + QCOMPARE(view.verticalScrollBar()->value(), verticalScrollBarValue); + + QTest::qWait(25); + + { + // Release + QMouseEvent event(QEvent::MouseButtonRelease, + view.viewport()->rect().center() + QPoint(100, 100), + Qt::LeftButton, Qt::LeftButton, 0); + event.setAccepted(true); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + } + QCOMPARE(view.horizontalScrollBar()->value(), horizontalScrollBarValue); + QCOMPARE(view.verticalScrollBar()->value(), verticalScrollBarValue); +#ifndef QT_NO_CURSOR + QCOMPARE(view.viewport()->cursor().shape(), cursorShape); +#endif + + QTest::qWait(25); + + if (view.scene()) + QCOMPARE(scene.selectedItems().size(), 1); + + view.setScene(&scene); + view.centerOn(0, 0); + } +} + +void tst_QGraphicsView::rubberBandSelectionMode() +{ + QWidget toplevel; + + QGraphicsScene scene; + QGraphicsRectItem *rect = scene.addRect(QRectF(10, 10, 80, 80)); + rect->setFlag(QGraphicsItem::ItemIsSelectable); + + QGraphicsView view(&scene, &toplevel); + QCOMPARE(view.rubberBandSelectionMode(), Qt::IntersectsItemShape); + view.setDragMode(QGraphicsView::RubberBandDrag); + view.resize(120, 120); + toplevel.show(); + + // Disable mouse tracking to prevent the window system from sending mouse + // move events to the viewport while we are synthesizing events. If + // QGraphicsView gets a mouse move event with no buttons down, it'll + // terminate the rubber band. + view.viewport()->setMouseTracking(false); + + QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>()); + sendMousePress(view.viewport(), QPoint(), Qt::LeftButton); + sendMouseMove(view.viewport(), view.viewport()->rect().center(), + Qt::LeftButton, Qt::LeftButton); + QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>() << rect); + sendMouseRelease(view.viewport(), QPoint(), Qt::LeftButton); + + view.setRubberBandSelectionMode(Qt::ContainsItemShape); + QCOMPARE(view.rubberBandSelectionMode(), Qt::ContainsItemShape); + sendMousePress(view.viewport(), QPoint(), Qt::LeftButton); + QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>()); + sendMouseMove(view.viewport(), view.viewport()->rect().center(), + Qt::LeftButton, Qt::LeftButton); + QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>()); + sendMouseMove(view.viewport(), view.viewport()->rect().bottomRight(), + Qt::LeftButton, Qt::LeftButton); + QCOMPARE(scene.selectedItems(), QList<QGraphicsItem *>() << rect); +} + +void tst_QGraphicsView::backgroundBrush() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + scene.setBackgroundBrush(Qt::blue); + QCOMPARE(scene.backgroundBrush(), QBrush(Qt::blue)); + + view.show(); + QTest::qWait(25); + + scene.setBackgroundBrush(QBrush()); + QCOMPARE(scene.backgroundBrush(), QBrush()); + QTest::qWait(25); + + QRadialGradient gradient(0, 0, 10); + gradient.setSpread(QGradient::RepeatSpread); + scene.setBackgroundBrush(gradient); + + QCOMPARE(scene.backgroundBrush(), QBrush(gradient)); + QTest::qWait(25); +} + +void tst_QGraphicsView::foregroundBrush() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + scene.setForegroundBrush(Qt::blue); + QCOMPARE(scene.foregroundBrush(), QBrush(Qt::blue)); + + view.show(); + QTest::qWait(25); + + scene.setForegroundBrush(QBrush()); + QCOMPARE(scene.foregroundBrush(), QBrush()); + QTest::qWait(25); + + QRadialGradient gradient(0, 0, 10); + gradient.setSpread(QGradient::RepeatSpread); + scene.setForegroundBrush(gradient); + + QCOMPARE(scene.foregroundBrush(), QBrush(gradient)); + QTest::qWait(25); + + for (int i = 0; i < 50; ++i) { + QRadialGradient gradient(view.rect().center() + QPoint(int(sin(i / 2.0) * 10), int(cos(i / 2.0) * 10)), 10); + gradient.setColorAt(0, Qt::transparent); + gradient.setColorAt(0.5, Qt::black); + gradient.setColorAt(1, Qt::transparent); + gradient.setSpread(QGradient::RepeatSpread); + scene.setForegroundBrush(gradient); + + QRadialGradient gradient2(view.rect().center() + QPoint(int(sin(i / 1.7) * 10), int(cos(i / 1.7) * 10)), 10); + gradient2.setColorAt(0, Qt::transparent); + gradient2.setColorAt(0.5, Qt::black); + gradient2.setColorAt(1, Qt::transparent); + gradient2.setSpread(QGradient::RepeatSpread); + scene.setBackgroundBrush(gradient2); + + QRadialGradient gradient3(view.rect().center() + QPoint(int(sin(i / 1.85) * 10), int(cos(i / 1.85) * 10)), 10); + gradient3.setColorAt(0, Qt::transparent); + gradient3.setColorAt(0.5, Qt::black); + gradient3.setColorAt(1, Qt::transparent); + gradient3.setSpread(QGradient::RepeatSpread); + scene.setBackgroundBrush(gradient3); + + QApplication::processEvents(); + } + + view.setSceneRect(-1000, -1000, 2000, 2000); + for (int i = -500; i < 500; i += 10) { + view.centerOn(i, 0); + QApplication::processEvents(); + QApplication::processEvents(); + } + for (int i = -500; i < 500; i += 10) { + view.centerOn(0, i); + QApplication::processEvents(); + QApplication::processEvents(); + } +} + +void tst_QGraphicsView::matrix() +{ + { + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + // Show rendering of background with no scene + for (int i = 0; i < 50; ++i) { + view.rotate(5); + QRadialGradient gradient(view.rect().center() + QPoint(int(sin(i / 2.0) * 10), int(cos(i / 2.0) * 10)), 10); + gradient.setColorAt(0, Qt::transparent); + gradient.setColorAt(0.5, Qt::black); + gradient.setColorAt(1, Qt::transparent); + gradient.setSpread(QGradient::RepeatSpread); + scene.setForegroundBrush(gradient); + QRadialGradient gradient2(view.rect().center() + QPoint(int(sin(i / 1.7) * 10), int(cos(i / 1.7) * 10)), 10); + gradient2.setColorAt(0, Qt::transparent); + gradient2.setColorAt(0.5, Qt::black); + gradient2.setColorAt(1, Qt::transparent); + gradient2.setSpread(QGradient::RepeatSpread); + scene.setBackgroundBrush(gradient2); + QApplication::processEvents(); + QApplication::processEvents(); + } + } + + // Test transformation extremes, see if they cause crashes + { + QGraphicsScene scene; + scene.addText("GraphicsView rotated clockwise"); + + QGraphicsView view(&scene); + view.show(); + for (int i = 0; i < 160; ++i) { + view.rotate(18); + QApplication::processEvents(); + QApplication::processEvents(); + } + /* + // These cause a crash + for (int i = 0; i < 40; ++i) { + view.shear(1.2, 1.2); + QTest::qWait(20); + } + for (int i = 0; i < 40; ++i) { + view.shear(-1.2, -1.2); + QTest::qWait(20); + } + */ + for (int i = 0; i < 20; ++i) { + view.scale(1.2, 1.2); + QApplication::processEvents(); + QApplication::processEvents(); + } + for (int i = 0; i < 20; ++i) { + view.scale(0.6, 0.6); + QApplication::processEvents(); + QApplication::processEvents(); + } + } +} + +void tst_QGraphicsView::matrix_convenience() +{ + QGraphicsView view; + QCOMPARE(view.matrix(), QMatrix()); + + // Check the convenience functions + view.rotate(90); + QCOMPARE(view.matrix(), QMatrix().rotate(90)); + view.scale(2, 2); + QCOMPARE(view.matrix(), QMatrix().scale(2, 2) * QMatrix().rotate(90)); + view.shear(1.2, 1.2); + QCOMPARE(view.matrix(), QMatrix().shear(1.2, 1.2) * QMatrix().scale(2, 2) * QMatrix().rotate(90)); + view.translate(1, 1); + QCOMPARE(view.matrix(), QMatrix().translate(1, 1) * QMatrix().shear(1.2, 1.2) * QMatrix().scale(2, 2) * QMatrix().rotate(90)); +} + +void tst_QGraphicsView::matrix_combine() +{ + // Check matrix combining + QGraphicsView view; + QCOMPARE(view.matrix(), QMatrix()); + view.setMatrix(QMatrix().rotate(90), true); + view.setMatrix(QMatrix().rotate(90), true); + view.setMatrix(QMatrix().rotate(90), true); + view.setMatrix(QMatrix().rotate(90), true); + QCOMPARE(view.matrix(), QMatrix()); + + view.resetMatrix(); + QCOMPARE(view.matrix(), QMatrix()); + view.setMatrix(QMatrix().rotate(90), false); + view.setMatrix(QMatrix().rotate(90), false); + view.setMatrix(QMatrix().rotate(90), false); + view.setMatrix(QMatrix().rotate(90), false); + QCOMPARE(view.matrix(), QMatrix().rotate(90)); +} + +void tst_QGraphicsView::centerOnPoint() +{ + QWidget toplevel; + + QGraphicsScene scene; + scene.addEllipse(QRectF(-100, -100, 50, 50)); + scene.addEllipse(QRectF(50, -100, 50, 50)); + scene.addEllipse(QRectF(-100, 50, 50, 50)); + scene.addEllipse(QRectF(50, 50, 50, 50)); + + QGraphicsView view(&scene, &toplevel); + view.setSceneRect(-400, -400, 800, 800); + view.setFixedSize(100, 100); + toplevel.show(); + + int tolerance = 5; + + for (int i = 0; i < 3; ++i) { + for (int y = -100; y < 100; y += 23) { + for (int x = -100; x < 100; x += 23) { + view.centerOn(x, y); + QPoint viewCenter = view.mapToScene(view.viewport()->rect().center()).toPoint(); + + // Fuzzy compare + if (viewCenter.x() < x - tolerance || viewCenter.x() > x + tolerance + || viewCenter.y() < y - tolerance || viewCenter.y() > y + tolerance) { + QString error = QString("Compared values are not the same\n\tActual: (%1, %2)\n\tExpected: (%3, %4)") + .arg(viewCenter.x()).arg(viewCenter.y()).arg(x).arg(y); + QFAIL(qPrintable(error)); + } + + QApplication::processEvents(); + } + } + + view.rotate(13); + view.scale(1.5, 1.5); + view.shear(1.25, 1.25); + } +} + +void tst_QGraphicsView::centerOnItem() +{ + QGraphicsScene scene; + QGraphicsItem *items[4]; + items[0] = scene.addEllipse(QRectF(-25, -25, 50, 50)); + items[1] = scene.addEllipse(QRectF(-25, -25, 50, 50)); + items[2] = scene.addEllipse(QRectF(-25, -25, 50, 50)); + items[3] = scene.addEllipse(QRectF(-25, -25, 50, 50)); + items[0]->setPos(-100, -100); + items[1]->setPos(100, -100); + items[2]->setPos(-100, 100); + items[3]->setPos(100, 100); + + QGraphicsView view(&scene); + view.setSceneRect(-1000, -1000, 2000, 2000); + view.show(); + QTest::qWaitForWindowShown(&view); + int tolerance = 7; + + for (int x = 0; x < 3; ++x) { + for (int i = 0; i < 4; ++i) { + QApplication::processEvents(); + view.centerOn(items[i]); + + QPoint viewCenter = view.mapToScene(view.viewport()->rect().center()).toPoint(); + qreal x = items[i]->pos().x(); + qreal y = items[i]->pos().y(); + + // Fuzzy compare + if (viewCenter.x() < x - tolerance || viewCenter.x() > x + tolerance + || viewCenter.y() < y - tolerance || viewCenter.y() > y + tolerance) { + QString error = QString("Compared values are not the same\n\tActual: (%1, %2)\n\tExpected: (%3, %4)") + .arg(viewCenter.x()).arg(viewCenter.y()).arg(x).arg(y); + QFAIL(qPrintable(error)); + } + + QApplication::processEvents(); + } + + view.rotate(13); + view.scale(1.5, 1.5); + view.shear(1.25, 1.25); + } +} + +void tst_QGraphicsView::ensureVisibleRect() +{ + QWidget toplevel; + + QGraphicsScene scene; + QGraphicsItem *items[4]; + items[0] = scene.addEllipse(QRectF(-25, -25, 50, 50), QPen(Qt::black), QBrush(Qt::green)); + items[1] = scene.addEllipse(QRectF(-25, -25, 50, 50), QPen(Qt::black), QBrush(Qt::red)); + items[2] = scene.addEllipse(QRectF(-25, -25, 50, 50), QPen(Qt::black), QBrush(Qt::blue)); + items[3] = scene.addEllipse(QRectF(-25, -25, 50, 50), QPen(Qt::black), QBrush(Qt::yellow)); + scene.addLine(QLineF(0, -100, 0, 100), QPen(Qt::blue, 2)); + scene.addLine(QLineF(-100, 0, 100, 0), QPen(Qt::blue, 2)); + items[0]->setPos(-100, -100); + items[1]->setPos(100, -100); + items[2]->setPos(-100, 100); + items[3]->setPos(100, 100); + + QGraphicsItem *icon = scene.addEllipse(QRectF(-10, -10, 20, 20), QPen(Qt::black), QBrush(Qt::gray)); + + QGraphicsView view(&scene, &toplevel); + view.setSceneRect(-500, -500, 1000, 1000); + view.setFixedSize(250, 250); + toplevel.show(); + QTest::qWaitForWindowShown(&toplevel); + + for (int y = -100; y < 100; y += 25) { + for (int x = -100; x < 100; x += 13) { + + icon->setPos(x, y); + + switch (x & 3) { + case 0: + view.centerOn(-500, -500); + break; + case 1: + view.centerOn(500, -500); + break; + case 2: + view.centerOn(-500, 500); + break; + case 3: + default: + view.centerOn(500, 500); + break; + } + + QVERIFY(!view.viewport()->rect().contains(view.mapFromScene(x, y))); + + for (int margin = 10; margin < 60; margin += 15) { + view.ensureVisible(x, y, 0, 0, margin, margin); + + QRect viewRect = view.viewport()->rect(); + QPoint viewPoint = view.mapFromScene(x, y); + + QVERIFY(viewRect.contains(viewPoint)); + QVERIFY(qAbs(viewPoint.x() - viewRect.left()) >= margin -1); + QVERIFY(qAbs(viewPoint.x() - viewRect.right()) >= margin -1); + QVERIFY(qAbs(viewPoint.y() - viewRect.top()) >= margin -1); + QVERIFY(qAbs(viewPoint.y() - viewRect.bottom()) >= margin -1); + + QApplication::processEvents(); + } + } + view.rotate(5); + view.scale(1.05, 1.05); + view.translate(30, -30); + } +} + +void tst_QGraphicsView::fitInView() +{ + QGraphicsScene scene; + QGraphicsItem *items[4]; + items[0] = scene.addEllipse(QRectF(-25, -25, 100, 20), QPen(Qt::black), QBrush(Qt::green)); + items[1] = scene.addEllipse(QRectF(-25, -25, 20, 100), QPen(Qt::black), QBrush(Qt::red)); + items[2] = scene.addEllipse(QRectF(-25, -25, 50, 50), QPen(Qt::black), QBrush(Qt::blue)); + items[3] = scene.addEllipse(QRectF(-25, -25, 50, 50), QPen(Qt::black), QBrush(Qt::yellow)); + scene.addLine(QLineF(0, -100, 0, 100), QPen(Qt::blue, 2)); + scene.addLine(QLineF(-100, 0, 100, 0), QPen(Qt::blue, 2)); + items[0]->setPos(-100, -100); + items[1]->setPos(100, -100); + items[2]->setPos(-100, 100); + items[3]->setPos(100, 100); + + items[0]->rotate(30); + items[1]->rotate(-30); + +#if defined(Q_OS_WINCE) + //Is the standard scrollbar size + int scrollbarSize = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent) - 13; +#endif + + QGraphicsView view(&scene); + view.setSceneRect(-400, -400, 800, 800); + +#if defined(Q_OS_WINCE) + //We need to take in account the scrollbar size for the WindowsMobilStyle + view.setFixedSize(400 + scrollbarSize, 200 + scrollbarSize); +#else + view.setFixedSize(400, 200); +#endif + + if (PlatformQuirks::isAutoMaximizing()) + view.setWindowFlags(view.windowFlags()|Qt::X11BypassWindowManagerHint); + + view.show(); + view.fitInView(scene.itemsBoundingRect(), Qt::IgnoreAspectRatio); + qApp->processEvents(); + + // Sampled coordinates. + QVERIFY(!view.itemAt(45, 41)); + QVERIFY(!view.itemAt(297, 44)); + QVERIFY(!view.itemAt(359, 143)); + QCOMPARE(view.itemAt(79, 22), items[0]); + QCOMPARE(view.itemAt(329, 41), items[1]); + QCOMPARE(view.itemAt(38, 158), items[2]); + QCOMPARE(view.itemAt(332, 160), items[3]); + + view.fitInView(items[0], Qt::IgnoreAspectRatio); + qApp->processEvents(); + + QCOMPARE(view.itemAt(19, 13), items[0]); + QCOMPARE(view.itemAt(91, 47), items[0]); + QCOMPARE(view.itemAt(202, 94), items[0]); + QCOMPARE(view.itemAt(344, 161), items[0]); + QVERIFY(!view.itemAt(236, 54)); + QVERIFY(!view.itemAt(144, 11)); + QVERIFY(!view.itemAt(29, 69)); + QVERIFY(!view.itemAt(251, 167)); + + view.fitInView(items[0], Qt::KeepAspectRatio); + qApp->processEvents(); + + QCOMPARE(view.itemAt(325, 170), items[0]); + QCOMPARE(view.itemAt(206, 74), items[0]); + QCOMPARE(view.itemAt(190, 115), items[0]); + QCOMPARE(view.itemAt(55, 14), items[0]); + QVERIFY(!view.itemAt(109, 4)); + QVERIFY(!view.itemAt(244, 68)); + QVERIFY(!view.itemAt(310, 125)); + QVERIFY(!view.itemAt(261, 168)); + + view.fitInView(items[0], Qt::KeepAspectRatioByExpanding); + qApp->processEvents(); + + QCOMPARE(view.itemAt(18, 10), items[0]); + QCOMPARE(view.itemAt(95, 4), items[0]); + QCOMPARE(view.itemAt(279, 175), items[0]); + QCOMPARE(view.itemAt(359, 170), items[0]); + QVERIFY(!view.itemAt(370, 166)); + QVERIFY(!view.itemAt(136, 7)); + QVERIFY(!view.itemAt(31, 44)); + QVERIFY(!view.itemAt(203, 153)); +} + +void tst_QGraphicsView::itemsAtPoint() +{ + QGraphicsScene scene; + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(1); + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(0); + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(2); + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(-1); + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(3); + + QGraphicsView view; + QVERIFY(view.items(0, 0).isEmpty()); + + view.setScene(&scene); + view.setSceneRect(-10000, -10000, 20000, 20000); + view.show(); + + QList<QGraphicsItem *> items = view.items(view.viewport()->rect().center()); + QCOMPARE(items.size(), 5); + QCOMPARE(items.takeFirst()->zValue(), qreal(3)); + QCOMPARE(items.takeFirst()->zValue(), qreal(2)); + QCOMPARE(items.takeFirst()->zValue(), qreal(1)); + QCOMPARE(items.takeFirst()->zValue(), qreal(0)); + QCOMPARE(items.takeFirst()->zValue(), qreal(-1)); +} + +void tst_QGraphicsView::itemsInRect() +{ + QGraphicsScene scene; + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(1); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(0); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(2); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(-1); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(3); + + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(5); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(4); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(6); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(3); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(7); + + QGraphicsView view; + QVERIFY(view.items(QRect(-100, -100, 200, 200)).isEmpty()); + view.setScene(&scene); + view.setSceneRect(-10000, -10000, 20000, 20000); + view.show(); + + QPoint centerPoint = view.viewport()->rect().center(); + QRect leftRect = view.mapFromScene(-30, -10, 20, 20).boundingRect(); + QRect rightRect = view.mapFromScene(30, -10, 20, 20).boundingRect(); + + QList<QGraphicsItem *> items = view.items(leftRect); + QCOMPARE(items.size(), 5); + QCOMPARE(items.takeFirst()->zValue(), qreal(3)); + QCOMPARE(items.takeFirst()->zValue(), qreal(2)); + QCOMPARE(items.takeFirst()->zValue(), qreal(1)); + QCOMPARE(items.takeFirst()->zValue(), qreal(0)); + QCOMPARE(items.takeFirst()->zValue(), qreal(-1)); + + items = view.items(rightRect); + QCOMPARE(items.size(), 5); + QCOMPARE(items.takeFirst()->zValue(), qreal(7)); + QCOMPARE(items.takeFirst()->zValue(), qreal(6)); + QCOMPARE(items.takeFirst()->zValue(), qreal(5)); + QCOMPARE(items.takeFirst()->zValue(), qreal(4)); + QCOMPARE(items.takeFirst()->zValue(), qreal(3)); +} + +class CountPaintItem : public QGraphicsRectItem +{ +public: + int numPaints; + + CountPaintItem(const QRectF &rect) + : QGraphicsRectItem(rect), numPaints(0) + { } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) + { + ++numPaints; + QGraphicsRectItem::paint(painter, option, widget); + } +}; + +void tst_QGraphicsView::itemsInRect_cosmeticAdjust_data() +{ + QTest::addColumn<QRect>("updateRect"); + QTest::addColumn<int>("numPaints"); + QTest::addColumn<bool>("adjustForAntialiasing"); + + // Aliased. + QTest::newRow("nil") << QRect() << 1 << false; + QTest::newRow("0, 0, 300, 100") << QRect(0, 0, 300, 100) << 1 << false; + QTest::newRow("0, 0, 100, 300") << QRect(0, 0, 100, 300) << 1 << false; + QTest::newRow("200, 0, 100, 300") << QRect(200, 0, 100, 300) << 1 << false; + QTest::newRow("0, 200, 300, 100") << QRect(0, 200, 300, 100) << 1 << false; + QTest::newRow("0, 0, 300, 99") << QRect(0, 0, 300, 99) << 0 << false; + QTest::newRow("0, 0, 99, 300") << QRect(0, 0, 99, 300) << 0 << false; + QTest::newRow("201, 0, 99, 300") << QRect(201, 0, 99, 300) << 0 << false; + QTest::newRow("0, 201, 300, 99") << QRect(0, 201, 300, 99) << 0 << false; + + // Anti-aliased. + QTest::newRow("nil") << QRect() << 1 << true; + QTest::newRow("0, 0, 300, 100") << QRect(0, 0, 300, 100) << 1 << true; + QTest::newRow("0, 0, 100, 300") << QRect(0, 0, 100, 300) << 1 << true; + QTest::newRow("200, 0, 100, 300") << QRect(200, 0, 100, 300) << 1 << true; + QTest::newRow("0, 200, 300, 100") << QRect(0, 200, 300, 100) << 1 << true; + QTest::newRow("0, 0, 300, 99") << QRect(0, 0, 300, 99) << 1 << true; + QTest::newRow("0, 0, 99, 300") << QRect(0, 0, 99, 300) << 1 << true; + QTest::newRow("201, 0, 99, 300") << QRect(201, 0, 99, 300) << 1 << true; + QTest::newRow("0, 201, 300, 99") << QRect(0, 201, 300, 99) << 1 << true; + QTest::newRow("0, 0, 300, 98") << QRect(0, 0, 300, 98) << 0 << false; + QTest::newRow("0, 0, 98, 300") << QRect(0, 0, 98, 300) << 0 << false; + QTest::newRow("202, 0, 98, 300") << QRect(202, 0, 98, 300) << 0 << false; + QTest::newRow("0, 202, 300, 98") << QRect(0, 202, 300, 98) << 0 << false; +} + +void tst_QGraphicsView::itemsInRect_cosmeticAdjust() +{ + QFETCH(QRect, updateRect); + QFETCH(int, numPaints); + QFETCH(bool, adjustForAntialiasing); + + QGraphicsScene scene(-100, -100, 200, 200); + CountPaintItem *rect = new CountPaintItem(QRectF(-50, -50, 100, 100)); + scene.addItem(rect); + + QGraphicsView view(&scene); + view.setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, !adjustForAntialiasing); + view.setRenderHint(QPainter::Antialiasing, adjustForAntialiasing); + if (PlatformQuirks::isAutoMaximizing()) + view.setWindowFlags(view.windowFlags()|Qt::X11BypassWindowManagerHint); + view.setFrameStyle(0); + view.resize(300, 300); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(rect->numPaints > 0); + + rect->numPaints = 0; + if (updateRect.isNull()) + view.viewport()->update(); + else + view.viewport()->update(updateRect); + qApp->processEvents(); + QTRY_COMPARE(rect->numPaints, numPaints); +} + +void tst_QGraphicsView::itemsInPoly() +{ + QGraphicsScene scene; + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(1); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(0); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(2); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(-1); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(3); + + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(5); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(4); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(6); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(3); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(7); + + QGraphicsView view; + QVERIFY(view.items(QPolygon()).isEmpty()); + view.setScene(&scene); + view.setSceneRect(-10000, -10000, 20000, 20000); + view.show(); + + QPoint centerPoint = view.viewport()->rect().center(); + QPolygon leftPoly = view.mapFromScene(QRectF(-30, -10, 20, 20)); + QPolygon rightPoly = view.mapFromScene(QRectF(30, -10, 20, 20)); + + QList<QGraphicsItem *> items = view.items(leftPoly); + QCOMPARE(items.size(), 5); + QCOMPARE(items.takeFirst()->zValue(), qreal(3)); + QCOMPARE(items.takeFirst()->zValue(), qreal(2)); + QCOMPARE(items.takeFirst()->zValue(), qreal(1)); + QCOMPARE(items.takeFirst()->zValue(), qreal(0)); + QCOMPARE(items.takeFirst()->zValue(), qreal(-1)); + + items = view.items(rightPoly); + QCOMPARE(items.size(), 5); + QCOMPARE(items.takeFirst()->zValue(), qreal(7)); + QCOMPARE(items.takeFirst()->zValue(), qreal(6)); + QCOMPARE(items.takeFirst()->zValue(), qreal(5)); + QCOMPARE(items.takeFirst()->zValue(), qreal(4)); + QCOMPARE(items.takeFirst()->zValue(), qreal(3)); +} + +void tst_QGraphicsView::itemsInPath() +{ + QGraphicsScene scene; + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(1); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(0); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(2); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(-1); + scene.addRect(QRectF(-30, -10, 20, 20))->setZValue(3); + + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(5); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(4); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(6); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(3); + scene.addRect(QRectF(30, -10, 20, 20))->setZValue(7); + + QGraphicsView view; + QVERIFY(view.items(QPainterPath()).isEmpty()); + view.setScene(&scene); + view.translate(100, 400); + view.rotate(22.3); + view.setSceneRect(-10000, -10000, 20000, 20000); + view.show(); + + QPoint centerPoint = view.viewport()->rect().center(); + QPainterPath leftPath; + leftPath.addEllipse(QRect(view.mapFromScene(-30, -10), QSize(20, 20))); + + QPainterPath rightPath; + rightPath.addEllipse(QRect(view.mapFromScene(30, -10), QSize(20, 20))); + + QList<QGraphicsItem *> items = view.items(leftPath); + + QCOMPARE(items.size(), 5); + QCOMPARE(items.takeFirst()->zValue(), qreal(3)); + QCOMPARE(items.takeFirst()->zValue(), qreal(2)); + QCOMPARE(items.takeFirst()->zValue(), qreal(1)); + QCOMPARE(items.takeFirst()->zValue(), qreal(0)); + QCOMPARE(items.takeFirst()->zValue(), qreal(-1)); + + items = view.items(rightPath); + QCOMPARE(items.size(), 5); + QCOMPARE(items.takeFirst()->zValue(), qreal(7)); + QCOMPARE(items.takeFirst()->zValue(), qreal(6)); + QCOMPARE(items.takeFirst()->zValue(), qreal(5)); + QCOMPARE(items.takeFirst()->zValue(), qreal(4)); + QCOMPARE(items.takeFirst()->zValue(), qreal(3)); +} + +void tst_QGraphicsView::itemAt() +{ + QGraphicsScene scene; + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(1); + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(0); + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(2); + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(-1); + scene.addRect(QRectF(-10, -10, 20, 20))->setZValue(3); + + QGraphicsView view; + QCOMPARE(view.itemAt(0, 0), (QGraphicsItem *)0); + + view.setScene(&scene); + view.setSceneRect(-10000, -10000, 20000, 20000); + view.show(); + + QCOMPARE(view.itemAt(0, 0), (QGraphicsItem *)0); + QGraphicsItem* item = view.itemAt(view.viewport()->rect().center()); + QVERIFY(item); + QCOMPARE(item->zValue(), qreal(3)); +} + +void tst_QGraphicsView::itemAt2() +{ + // test precision of the itemAt() function with items that are smaller + // than 1 pixel. + QGraphicsScene scene(0, 0, 100, 100); + + // Add a 0.5x0.5 item at position 0 on the scene, top-left corner at -0.25, -0.25. + QGraphicsItem *item = scene.addRect(QRectF(-0.25, -0.25, 0.5, 0.5), QPen(Qt::black, 0.1)); + + QGraphicsView view(&scene); + view.setFixedSize(200, 200); + view.setTransformationAnchor(QGraphicsView::NoAnchor); + view.setRenderHint(QPainter::Antialiasing); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + QPoint itemViewPoint = view.mapFromScene(item->scenePos()); + + for (int i = 0; i < 3; ++i) { + QVERIFY(view.itemAt(itemViewPoint)); + QVERIFY(!view.items(itemViewPoint).isEmpty()); + QVERIFY(view.itemAt(itemViewPoint + QPoint(-1, 0))); + QVERIFY(!view.items(itemViewPoint + QPoint(-1, 0)).isEmpty()); + QVERIFY(view.itemAt(itemViewPoint + QPoint(-1, -1))); + QVERIFY(!view.items(itemViewPoint + QPoint(-1, -1)).isEmpty()); + QVERIFY(view.itemAt(itemViewPoint + QPoint(0, -1))); + QVERIFY(!view.items(itemViewPoint + QPoint(0, -1)).isEmpty()); + item->moveBy(0.1, 0); + } + + // Here + QVERIFY(view.itemAt(itemViewPoint)); + QVERIFY(!view.items(itemViewPoint).isEmpty()); + QVERIFY(view.itemAt(itemViewPoint + QPoint(0, -1))); + QVERIFY(!view.items(itemViewPoint + QPoint(0, -1)).isEmpty()); + + if (sizeof(qreal) != sizeof(double)) { + QSKIP("Skipped due to rounding errors", SkipAll); + } + // Not here + QVERIFY(!view.itemAt(itemViewPoint + QPoint(-1, 0))); + QVERIFY(view.items(itemViewPoint + QPoint(-1, 0)).isEmpty()); + QVERIFY(!view.itemAt(itemViewPoint + QPoint(-1, -1))); + QVERIFY(view.items(itemViewPoint + QPoint(-1, -1)).isEmpty()); +} + +void tst_QGraphicsView::mapToScene() +{ + // Uncomment the commented-out code to see what's going on. It doesn't + // affect the test; it just slows it down. + + QGraphicsScene scene; + scene.addPixmap(QPixmap("3D-Qt-1-2.png")); + + QWidget topLevel; + QGraphicsView view(&topLevel); + view.setScene(&scene); + view.setSceneRect(-500, -500, 1000, 1000); +#if defined(Q_OS_WINCE) + QSize viewSize(200,200); +#else + QSize viewSize(300,300); +#endif + + view.setFixedSize(viewSize); + topLevel.show(); + QApplication::processEvents(); + QVERIFY(view.isVisible()); + QCOMPARE(view.size(), viewSize); + + // First once without setting the scene rect +#ifdef QT_ARCH_ARM + const int step = 20; +#else + const int step = 1; +#endif + + for (int x = 0; x < view.width(); x += step) { + for (int y = 0; y < view.height(); y += step) { + QCOMPARE(view.mapToScene(QPoint(x, y)), + QPointF(view.horizontalScrollBar()->value() + x, + view.verticalScrollBar()->value() + y)); + } + } + + for (int sceneRectHeight = 250; sceneRectHeight < 1000; sceneRectHeight += 250) { + for (int sceneRectWidth = 250; sceneRectWidth < 1000; sceneRectWidth += 250) { + view.setSceneRect(QRectF(-int(sceneRectWidth / 2), -int(sceneRectHeight / 2), + sceneRectWidth, sceneRectHeight)); + QApplication::processEvents(); + + int hmin = view.horizontalScrollBar()->minimum(); + int hmax = view.horizontalScrollBar()->maximum(); + int hstep = (hmax - hmin) / 3; + int vmin = view.verticalScrollBar()->minimum(); + int vmax = view.verticalScrollBar()->maximum(); + int vstep = (vmax - vmin) / 3; + + for (int hscrollValue = hmin; hscrollValue < hmax; hscrollValue += hstep) { + for (int vscrollValue = vmin; vscrollValue < vmax; vscrollValue += vstep) { + + view.horizontalScrollBar()->setValue(hscrollValue); + view.verticalScrollBar()->setValue(vscrollValue); + QApplication::processEvents(); + + int h = view.horizontalScrollBar()->value(); + int v = view.verticalScrollBar()->value(); + + for (int x = 0; x < view.width(); x += step) { + for (int y = 0; y < view.height(); y += step) { + QCOMPARE(view.mapToScene(QPoint(x, y)), QPointF(h + x, v + y)); + QCOMPARE(view.mapFromScene(QPointF(h + x, v + y)), QPoint(x, y)); + } + } + } + } + } + } +} + +void tst_QGraphicsView::mapToScenePoint() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.rotate(90); + view.setFixedSize(117, 117); + view.show(); + QPoint center = view.viewport()->rect().center(); + QCOMPARE(view.mapToScene(center + QPoint(10, 0)), + view.mapToScene(center) + QPointF(0, -10)); +} + +void tst_QGraphicsView::mapToSceneRect_data() +{ + QTest::addColumn<QRect>("viewRect"); + QTest::addColumn<QPolygonF>("scenePoly"); + QTest::addColumn<qreal>("rotation"); + + QTest::newRow("nil") << QRect() << QPolygonF() << qreal(0); + QTest::newRow("0, 0, 1, 1") << QRect(0, 0, 1, 1) << QPolygonF(QRectF(0, 0, 1, 1)) << qreal(0); + QTest::newRow("0, 0, 10, 10") << QRect(0, 0, 10, 10) << QPolygonF(QRectF(0, 0, 10, 10)) << qreal(0); + QTest::newRow("nil") << QRect() << QPolygonF() << qreal(90); + QPolygonF p; + p << QPointF(0, 0) << QPointF(0, -1) << QPointF(1, -1) << QPointF(1, 0) << QPointF(0, 0); + QTest::newRow("0, 0, 1, 1") << QRect(0, 0, 1, 1) + << p + << qreal(90); + p.clear(); + p << QPointF(0, 0) << QPointF(0, -10) << QPointF(10, -10) << QPointF(10, 0) << QPointF(0, 0); + QTest::newRow("0, 0, 10, 10") << QRect(0, 0, 10, 10) + << p + << qreal(90); +} + +void tst_QGraphicsView::mapToSceneRect() +{ + QFETCH(QRect, viewRect); + QFETCH(QPolygonF, scenePoly); + QFETCH(qreal, rotation); + + QGraphicsScene scene(-1000, -1000, 2000, 2000); + scene.addRect(25, -25, 50, 50); + QGraphicsView view(&scene); + view.setFrameStyle(0); + view.setAlignment(Qt::AlignTop | Qt::AlignLeft); + view.setFixedSize(200, 200); + view.setTransformationAnchor(QGraphicsView::NoAnchor); + view.setResizeAnchor(QGraphicsView::NoAnchor); + view.show(); + + view.rotate(rotation); + + QPolygonF poly = view.mapToScene(viewRect); + if (!poly.isEmpty()) + poly << poly[0]; + + QCOMPARE(poly, scenePoly); +} + +void tst_QGraphicsView::mapToScenePoly() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.translate(100, 100); + view.setFixedSize(117, 117); + view.show(); + QPoint center = view.viewport()->rect().center(); + QRect rect(center + QPoint(10, 0), QSize(10, 10)); + + QPolygon poly; + poly << rect.topLeft(); + poly << rect.topRight(); + poly << rect.bottomRight(); + poly << rect.bottomLeft(); + + QPolygonF poly2; + poly2 << view.mapToScene(rect.topLeft()); + poly2 << view.mapToScene(rect.topRight()); + poly2 << view.mapToScene(rect.bottomRight()); + poly2 << view.mapToScene(rect.bottomLeft()); + + QCOMPARE(view.mapToScene(poly), poly2); +} + +void tst_QGraphicsView::mapToScenePath() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setSceneRect(-300, -300, 600, 600); + view.translate(10, 10); + view.setFixedSize(300, 300); + view.show(); + QPoint center = view.viewport()->rect().center(); + QRect rect(QPoint(10, 0), QSize(10, 10)); + + QPainterPath path; + path.addRect(rect); + + QPainterPath path2; + path2.addRect(rect.translated(view.horizontalScrollBar()->value() - 10, + view.verticalScrollBar()->value() - 10)); + QCOMPARE(view.mapToScene(path), path2); +} + +void tst_QGraphicsView::mapFromScenePoint() +{ + { + QGraphicsScene scene; + QGraphicsView view(&scene); + view.rotate(90); + view.scale(10, 10); + view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.show(); + + QPoint mapped = view.mapFromScene(0, 0); + QPoint center = view.viewport()->rect().center(); + if (qAbs(mapped.x() - center.x()) >= 2 + || qAbs(mapped.y() - center.y()) >= 2) { + QString error = QString("Compared values are not the same\n\tActual: (%1, %2)\n\tExpected: (%3, %4)") + .arg(mapped.x()).arg(mapped.y()).arg(center.x()).arg(center.y()); + QFAIL(qPrintable(error)); + } + } + { + QWidget toplevel; + + QGraphicsScene scene(0, 0, 200, 200); + scene.addRect(QRectF(0, 0, 200, 200), QPen(Qt::black, 1)); + QGraphicsView view(&scene, &toplevel); + view.ensurePolished(); + view.resize(view.sizeHint()); + toplevel.show(); + + QCOMPARE(view.mapFromScene(0, 0), QPoint(0, 0)); + QCOMPARE(view.mapFromScene(0.4, 0.4), QPoint(0, 0)); + QCOMPARE(view.mapFromScene(0.5, 0.5), QPoint(1, 1)); + QCOMPARE(view.mapFromScene(0.9, 0.9), QPoint(1, 1)); + QCOMPARE(view.mapFromScene(1.0, 1.0), QPoint(1, 1)); + QCOMPARE(view.mapFromScene(100, 100), QPoint(100, 100)); + QCOMPARE(view.mapFromScene(100.5, 100.5), QPoint(101, 101)); + QCOMPARE(view.mapToScene(0, 0), QPointF(0, 0)); + QCOMPARE(view.mapToScene(1, 1), QPointF(1, 1)); + QCOMPARE(view.mapToScene(100, 100), QPointF(100, 100)); + } +} + +void tst_QGraphicsView::mapFromSceneRect() +{ + QGraphicsScene scene; + QWidget topLevel; + QGraphicsView view(&scene,&topLevel); + view.rotate(90); + view.setFixedSize(200, 200); + view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + topLevel.show(); + QTest::qWait(25); + + QPolygon polygon; + polygon << QPoint(98, 98); + polygon << QPoint(98, 108); + polygon << QPoint(88, 108); + polygon << QPoint(88, 98); + + + QPolygon viewPolygon = view.mapFromScene(0, 0, 10, 10); + for (int i = 0; i < 4; ++i) { + QVERIFY(qAbs(viewPolygon[i].x() - polygon[i].x()) < 3); + QVERIFY(qAbs(viewPolygon[i].y() - polygon[i].y()) < 3); + } + + QPoint pt = view.mapFromScene(QPointF()); + QPolygon p; + p << pt << pt << pt << pt; + QCOMPARE(view.mapFromScene(QRectF()), p); +} + +void tst_QGraphicsView::mapFromScenePoly() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.rotate(90); + view.setFixedSize(200, 200); + view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.show(); + + QPolygonF polygon; + polygon << QPoint(0, 0); + polygon << QPoint(10, 0); + polygon << QPoint(10, 10); + polygon << QPoint(0, 10); + + QPolygon polygon2; + polygon2 << QPoint(98, 98); + polygon2 << QPoint(98, 108); + polygon2 << QPoint(88, 108); + polygon2 << QPoint(88, 98); + + QPolygon viewPolygon = view.mapFromScene(polygon); + for (int i = 0; i < 4; ++i) { + QVERIFY(qAbs(viewPolygon[i].x() - polygon2[i].x()) < 3); + QVERIFY(qAbs(viewPolygon[i].y() - polygon2[i].y()) < 3); + } +} + +void tst_QGraphicsView::mapFromScenePath() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.rotate(90); + view.setFixedSize(200, 200); + view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.show(); + + QPolygonF polygon; + polygon << QPoint(0, 0); + polygon << QPoint(10, 0); + polygon << QPoint(10, 10); + polygon << QPoint(0, 10); + QPainterPath path; + path.addPolygon(polygon); + + QPolygon polygon2; + polygon2 << QPoint(98, 98); + polygon2 << QPoint(98, 108); + polygon2 << QPoint(88, 108); + polygon2 << QPoint(88, 98); + QPainterPath path2; + path2.addPolygon(polygon2); + + QPolygonF pathPoly = view.mapFromScene(path).toFillPolygon(); + QPolygonF path2Poly = path2.toFillPolygon(); + + for (int i = 0; i < pathPoly.size(); ++i) { + QVERIFY(qAbs(pathPoly[i].x() - path2Poly[i].x()) < 3); + QVERIFY(qAbs(pathPoly[i].y() - path2Poly[i].y()) < 3); + } +} + +void tst_QGraphicsView::sendEvent() +{ + QGraphicsScene scene; + + TestItem *item = new TestItem; + scene.addItem(item); + item->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFlag(QGraphicsItem::ItemIsMovable); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::setActiveWindow(&view); + QTest::qWait(20); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + item->setFocus(); + + QCOMPARE(scene.focusItem(), (QGraphicsItem *)item); + QCOMPARE(item->events.size(), 2); + QCOMPARE(item->events.last(), QEvent::FocusIn); + + QPoint itemPoint = view.mapFromScene(item->scenePos()); + sendMousePress(view.viewport(), itemPoint); + QCOMPARE(item->events.size(), 4); + QCOMPARE(item->events.at(item->events.size() - 2), QEvent::GrabMouse); + QCOMPARE(item->events.at(item->events.size() - 1), QEvent::GraphicsSceneMousePress); + + QMouseEvent mouseMoveEvent(QEvent::MouseMove, itemPoint, view.viewport()->mapToGlobal(itemPoint), + Qt::LeftButton, Qt::LeftButton, 0); + QApplication::sendEvent(view.viewport(), &mouseMoveEvent); + QCOMPARE(item->events.size(), 5); + QCOMPARE(item->events.last(), QEvent::GraphicsSceneMouseMove); + + QMouseEvent mouseReleaseEvent(QEvent::MouseButtonRelease, itemPoint, + view.viewport()->mapToGlobal(itemPoint), + Qt::LeftButton, 0, 0); + QApplication::sendEvent(view.viewport(), &mouseReleaseEvent); + QCOMPARE(item->events.size(), 7); + QCOMPARE(item->events.at(item->events.size() - 2), QEvent::GraphicsSceneMouseRelease); + QCOMPARE(item->events.at(item->events.size() - 1), QEvent::UngrabMouse); + + QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Space, 0); + QApplication::sendEvent(view.viewport(), &keyPress); + QCOMPARE(item->events.size(), 9); + QCOMPARE(item->events.at(item->events.size() - 2), QEvent::ShortcutOverride); + QCOMPARE(item->events.last(), QEvent::KeyPress); +} + +class MouseWheelScene : public QGraphicsScene +{ +public: + Qt::Orientation orientation; + + void wheelEvent(QGraphicsSceneWheelEvent *event) + { + orientation = event->orientation(); + QGraphicsScene::wheelEvent(event); + } +}; + +void tst_QGraphicsView::wheelEvent() +{ + // Create a scene with an invalid orientation. + MouseWheelScene scene; + scene.orientation = Qt::Orientation(-1); + + QGraphicsWidget *widget = new QGraphicsWidget; + widget->setGeometry(0, 0, 400, 400); + widget->setFocusPolicy(Qt::WheelFocus); + + EventSpy spy(widget, QEvent::GraphicsSceneWheel); + QCOMPARE(spy.count(), 0); + + scene.addItem(widget); + + // Assign a view. + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::setActiveWindow(&view); + QTest::qWait(20); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + + // Send a wheel event with horizontal orientation. + { + QWheelEvent event(view.mapFromScene(widget->boundingRect().center()), + view.mapToGlobal(view.mapFromScene(widget->boundingRect().center())), + 120, 0, 0, Qt::Horizontal); + QApplication::sendEvent(view.viewport(), &event); + QCOMPARE(scene.orientation, Qt::Horizontal); + } + + // Send a wheel event with vertical orientation. + { + QWheelEvent event(view.mapFromScene(widget->boundingRect().center()), + view.mapToGlobal(view.mapFromScene(widget->boundingRect().center())), + 120, 0, 0, Qt::Vertical); + QApplication::sendEvent(view.viewport(), &event); + QCOMPARE(scene.orientation, Qt::Vertical); + } + + QCOMPARE(spy.count(), 2); + QVERIFY(widget->hasFocus()); +} + +// Qt/CE does not have regular cursor support. +#if !defined(QT_NO_CURSOR) && !defined(Q_OS_WINCE) +void tst_QGraphicsView::cursor() +{ + if (PlatformQuirks::haveMouseCursor()) + QSKIP("The Platform does not have regular cursor support", SkipAll); + + QGraphicsScene scene; + QGraphicsItem *item = scene.addRect(QRectF(-10, -10, 20, 20)); + item->setCursor(Qt::IBeamCursor); + + QGraphicsView view(&scene); + view.setFixedSize(400, 400); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(view.viewport()->cursor().shape(), QCursor().shape()); + view.viewport()->setCursor(Qt::PointingHandCursor); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + + sendMouseMove(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + + sendMouseMove(view.viewport(), QPoint(5, 5)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); +} +#endif + +// Qt/CE does not have regular cursor support. +#if !defined(QT_NO_CURSOR) && !defined(Q_OS_WINCE) +void tst_QGraphicsView::cursor2() +{ + if (PlatformQuirks::haveMouseCursor()) + QSKIP("The Platform does not have regular cursor support", SkipAll); + + QGraphicsScene scene; + QGraphicsItem *item = scene.addRect(QRectF(-10, -10, 20, 20)); + item->setCursor(Qt::IBeamCursor); + item->setZValue(1); + + QGraphicsItem *item2 = scene.addRect(QRectF(-20, -20, 40, 40)); + item2->setZValue(0); + + QGraphicsView view(&scene); + view.viewport()->setCursor(Qt::PointingHandCursor); + view.setFixedSize(400, 400); + view.show(); + QTest::qWaitForWindowShown(&view); + + sendMouseMove(view.viewport(), view.mapFromScene(-30, -30)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + sendMouseMove(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + sendMouseMove(view.viewport(), view.mapFromScene(-30, -30)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + sendMouseMove(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + sendMouseMove(view.viewport(), view.mapFromScene(-15, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + + view.setDragMode(QGraphicsView::ScrollHandDrag); + + sendMouseMove(view.viewport(), view.mapFromScene(-30, -30)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::OpenHandCursor); + sendMouseMove(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + sendMouseMove(view.viewport(), view.mapFromScene(-15, -15)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::OpenHandCursor); + + view.setDragMode(QGraphicsView::NoDrag); + QCOMPARE(view.viewport()->cursor().shape(), Qt::ArrowCursor); + view.viewport()->setCursor(Qt::PointingHandCursor); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + + item2->setCursor(Qt::SizeAllCursor); + + sendMouseMove(view.viewport(), view.mapFromScene(-30, -30)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + sendMouseMove(view.viewport(), view.mapFromScene(-15, -15)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::SizeAllCursor); + sendMouseMove(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + sendMouseMove(view.viewport(), view.mapFromScene(-15, -15)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::SizeAllCursor); + sendMouseMove(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + sendMouseMove(view.viewport(), view.mapFromScene(-30, -30)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + + view.setDragMode(QGraphicsView::ScrollHandDrag); + + sendMouseMove(view.viewport(), view.mapFromScene(-30, -30)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::OpenHandCursor); + sendMouseMove(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + sendMouseMove(view.viewport(), view.mapFromScene(-15, -15)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::SizeAllCursor); +} +#endif + +void tst_QGraphicsView::transformationAnchor() +{ + QGraphicsScene scene(-1000, -1000, 2000, 2000); + scene.addRect(QRectF(-50, -50, 100, 100), QPen(Qt::black), QBrush(Qt::blue)); + + QGraphicsView view(&scene); + + for (int i = 0; i < 2; ++i) { + view.resize(100, 100); + view.show(); + + if (i == 0) { + QCOMPARE(view.transformationAnchor(), QGraphicsView::AnchorViewCenter); + } else { + view.setTransformationAnchor(QGraphicsView::NoAnchor); + } + view.centerOn(0, 0); + view.horizontalScrollBar()->setValue(100); + QApplication::processEvents(); + + QPointF center = view.mapToScene(view.viewport()->rect().center()); + + view.scale(10, 10); + + QPointF newCenter = view.mapToScene(view.viewport()->rect().center()); + + if (i == 0) { + qreal slack = 3; + QVERIFY(qAbs(newCenter.x() - center.x()) < slack); + QVERIFY(qAbs(newCenter.y() - center.y()) < slack); + } else { + qreal slack = qreal(0.3); + QVERIFY(qAbs(newCenter.x() - center.x() / 10) < slack); + QVERIFY(qAbs(newCenter.y() - center.y() / 10) < slack); + } + } +} + +void tst_QGraphicsView::resizeAnchor() +{ + QGraphicsScene scene(-1000, -1000, 2000, 2000); + scene.addRect(QRectF(-50, -50, 100, 100), QPen(Qt::black), QBrush(Qt::blue)); + + QGraphicsView view(&scene); + + for (int i = 0; i < 2; ++i) { + view.resize(100, 100); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + if (i == 0) { + QCOMPARE(view.resizeAnchor(), QGraphicsView::NoAnchor); + } else { + view.setResizeAnchor(QGraphicsView::AnchorViewCenter); + } + view.centerOn(0, 0); + QTest::qWait(25); + + QPointF f = view.mapToScene(50, 50); + QPointF center = view.mapToScene(view.viewport()->rect().center()); + + QApplication::processEvents(); + + for (int size = 200; size <= 400; size += 25) { + view.resize(size, size); + if (i == 0) { + QTRY_COMPARE(view.mapToScene(50, 50), f); + QTRY_VERIFY(view.mapToScene(view.viewport()->rect().center()) != center); + } else { + QTRY_VERIFY(view.mapToScene(50, 50) != f); + + QPointF newCenter = view.mapToScene(view.viewport()->rect().center()); + int slack = 3; + QVERIFY(qAbs(newCenter.x() - center.x()) < slack); + QVERIFY(qAbs(newCenter.y() - center.y()) < slack); + } + QApplication::processEvents(); + } + } +} + +class CustomView : public QGraphicsView +{ + Q_OBJECT +public: + CustomView(QGraphicsScene *s = 0) : QGraphicsView(s) {} + CustomView(QGraphicsScene *s, QWidget *parent) + : QGraphicsView(s, parent) {} + QList<QRegion> lastUpdateRegions; + bool painted; + +protected: + void paintEvent(QPaintEvent *event) + { + lastUpdateRegions << event->region(); + painted = true; + QGraphicsView::paintEvent(event); + } +}; + +void tst_QGraphicsView::viewportUpdateMode() +{ + QGraphicsScene scene(0, 0, 100, 100); + scene.setBackgroundBrush(Qt::red); + + CustomView view; + QDesktopWidget desktop; + view.setFixedSize(QSize(500, 500).boundedTo(desktop.availableGeometry().size())); // 500 is too big for all common smartphones + view.setScene(&scene); + if(PlatformQuirks::isAutoMaximizing()) + view.setWindowFlags(view.windowFlags()|Qt::X11BypassWindowManagerHint); + QCOMPARE(view.viewportUpdateMode(), QGraphicsView::MinimalViewportUpdate); + + // Show the view, and initialize our test. + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(!view.lastUpdateRegions.isEmpty()); + view.lastUpdateRegions.clear(); + + // Issue two scene updates. + scene.update(QRectF(0, 0, 10, 10)); + scene.update(QRectF(20, 0, 10, 10)); + QTest::qWait(50); + + // The view gets two updates for the update scene updates. + QTRY_VERIFY(!view.lastUpdateRegions.isEmpty()); +#ifndef Q_OS_MAC //cocoa doesn't support drawing regions + QCOMPARE(view.lastUpdateRegions.last().rects().size(), 2); + QCOMPARE(view.lastUpdateRegions.last().rects().at(0).size(), QSize(14, 14)); + QCOMPARE(view.lastUpdateRegions.last().rects().at(1).size(), QSize(14, 14)); +#endif + + // Set full update mode. + view.setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + QCOMPARE(view.viewportUpdateMode(), QGraphicsView::FullViewportUpdate); + view.lastUpdateRegions.clear(); + + // Issue two scene updates. + scene.update(QRectF(0, 0, 10, 10)); + scene.update(QRectF(20, 0, 10, 10)); + qApp->processEvents(); + qApp->processEvents(); + + // The view gets one full viewport update for the update scene updates. + QCOMPARE(view.lastUpdateRegions.last().rects().size(), 1); + QCOMPARE(view.lastUpdateRegions.last().rects().at(0).size(), view.viewport()->size()); + view.lastUpdateRegions.clear(); + + // Set smart update mode + view.setViewportUpdateMode(QGraphicsView::SmartViewportUpdate); + QCOMPARE(view.viewportUpdateMode(), QGraphicsView::SmartViewportUpdate); + + // Issue 100 mini-updates + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + scene.update(QRectF(i * 3, j * 3, 1, 1)); + } + } + qApp->processEvents(); + qApp->processEvents(); + + // The view gets one bounding rect update. + QCOMPARE(view.lastUpdateRegions.last().rects().size(), 1); + QCOMPARE(view.lastUpdateRegions.last().rects().at(0).size(), QSize(32, 32)); + + // Set no update mode + view.setViewportUpdateMode(QGraphicsView::NoViewportUpdate); + QCOMPARE(view.viewportUpdateMode(), QGraphicsView::NoViewportUpdate); + + // Issue two scene updates. + view.lastUpdateRegions.clear(); + TestItem item; + scene.addItem(&item); + item.moveBy(10, 10); + scene.update(QRectF(0, 0, 10, 10)); + scene.update(QRectF(20, 0, 10, 10)); + qApp->processEvents(); + qApp->processEvents(); + + // The view should not get any painting calls from the scene updates + QCOMPARE(view.lastUpdateRegions.size(), 0); +} + +void tst_QGraphicsView::viewportUpdateMode2() +{ + QWidget toplevel; + + // Create a view with viewport rect equal to QRect(0, 0, 200, 200). + QGraphicsScene dummyScene; + CustomView view(0, &toplevel); + view.painted = false; + view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + view.setScene(&dummyScene); + view.ensurePolished(); // make sure we get the right content margins + int left, top, right, bottom; + view.getContentsMargins(&left, &top, &right, &bottom); + view.resize(200 + left + right, 200 + top + bottom); + toplevel.show(); + QTest::qWaitForWindowShown(&toplevel); + QTest::qWait(50); + QTRY_VERIFY(view.painted); + const QRect viewportRect = view.viewport()->rect(); + QCOMPARE(viewportRect, QRect(0, 0, 200, 200)); + +#if defined QT_BUILD_INTERNAL + QGraphicsViewPrivate *viewPrivate = static_cast<QGraphicsViewPrivate *>(qt_widget_private(&view)); + + QRect boundingRect; + const QRect rect1(0, 0, 10, 10); + QVERIFY(viewPrivate->updateRect(rect1)); + QVERIFY(!viewPrivate->fullUpdatePending); + boundingRect |= rect1; + QCOMPARE(viewPrivate->dirtyBoundingRect, boundingRect); + + const QRect rect2(50, 50, 10, 10); + QVERIFY(viewPrivate->updateRect(rect2)); + QVERIFY(!viewPrivate->fullUpdatePending); + boundingRect |= rect2; + QCOMPARE(viewPrivate->dirtyBoundingRect, boundingRect); + + const QRect rect3(190, 190, 10, 10); + QVERIFY(viewPrivate->updateRect(rect3)); + QVERIFY(viewPrivate->fullUpdatePending); + boundingRect |= rect3; + QCOMPARE(viewPrivate->dirtyBoundingRect, boundingRect); + + view.lastUpdateRegions.clear(); + viewPrivate->processPendingUpdates(); + QTest::qWait(50); + QCOMPARE(view.lastUpdateRegions.size(), 1); + // Note that we adjust by 2 for antialiasing. + QCOMPARE(view.lastUpdateRegions.at(0), QRegion(boundingRect.adjusted(-2, -2, 2, 2) & viewportRect)); +#endif +} + +#ifndef QT_NO_DRAGANDDROP +void tst_QGraphicsView::acceptDrops() +{ + QGraphicsView view; + + // Excepted default behavior. + QVERIFY(view.acceptDrops()); + QVERIFY(view.viewport()->acceptDrops()); + + // Excepted behavior with no drops. + view.setAcceptDrops(false); + QVERIFY(!view.acceptDrops()); + QVERIFY(!view.viewport()->acceptDrops()); + + // Setting a widget with drops on a QGraphicsView without drops. + QWidget *widget = new QWidget; + widget->setAcceptDrops(true); + view.setViewport(widget); + QVERIFY(!view.acceptDrops()); + QVERIFY(!view.viewport()->acceptDrops()); + + // Switching the view to accept drops. + view.setAcceptDrops(true); + QVERIFY(view.acceptDrops()); + QVERIFY(view.viewport()->acceptDrops()); + + // Setting a widget with no drops on a QGraphicsView with drops. + widget = new QWidget; + widget->setAcceptDrops(false); + view.setViewport(widget); + QVERIFY(view.viewport()->acceptDrops()); + QVERIFY(view.acceptDrops()); + + // Switching the view to not accept drops. + view.setAcceptDrops(false); + QVERIFY(!view.viewport()->acceptDrops()); +} +#endif + +void tst_QGraphicsView::optimizationFlags() +{ + QGraphicsView view; + QVERIFY(!view.optimizationFlags()); + + view.setOptimizationFlag(QGraphicsView::DontClipPainter); + QVERIFY(view.optimizationFlags() & QGraphicsView::DontClipPainter); + view.setOptimizationFlag(QGraphicsView::DontClipPainter, false); + QVERIFY(!view.optimizationFlags()); + + view.setOptimizationFlag(QGraphicsView::DontSavePainterState); + QVERIFY(view.optimizationFlags() & QGraphicsView::DontSavePainterState); + view.setOptimizationFlag(QGraphicsView::DontSavePainterState, false); + QVERIFY(!view.optimizationFlags()); + + view.setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing); + QVERIFY(view.optimizationFlags() & QGraphicsView::DontAdjustForAntialiasing); + view.setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, false); + QVERIFY(!view.optimizationFlags()); + + view.setOptimizationFlags(QGraphicsView::DontAdjustForAntialiasing + | QGraphicsView::DontClipPainter); + QCOMPARE(view.optimizationFlags(), QGraphicsView::OptimizationFlags(QGraphicsView::DontAdjustForAntialiasing + | QGraphicsView::DontClipPainter)); +} + +class MessUpPainterItem : public QGraphicsRectItem +{ +public: + MessUpPainterItem(const QRectF &rect) : QGraphicsRectItem(rect), dirtyPainter(false) + { } + + bool dirtyPainter; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + dirtyPainter = (painter->pen().width() != 0); + painter->setPen(QPen(Qt::black, 1.0)); + } +}; + +class MyGraphicsView : public QGraphicsView +{ +public: + MyGraphicsView(QGraphicsScene * scene) : QGraphicsView(scene) + { } + + void drawBackground(QPainter * painter, const QRectF & rect) { + painter->setCompositionMode(QPainter::CompositionMode_Source); + painter->drawRect(rect); + } + + void drawItems (QPainter * painter, int numItems, QGraphicsItem *items[], const QStyleOptionGraphicsItem options[]) { + if (!(optimizationFlags() & QGraphicsView::DontSavePainterState)) + QCOMPARE(painter->compositionMode(),QPainter::CompositionMode_SourceOver); + else + QCOMPARE(painter->compositionMode(),QPainter::CompositionMode_Source); + QGraphicsView::drawItems(painter,numItems,items,options); + } +}; + +void tst_QGraphicsView::optimizationFlags_dontSavePainterState() +{ + MessUpPainterItem *parent = new MessUpPainterItem(QRectF(0, 0, 100, 100)); + MessUpPainterItem *child = new MessUpPainterItem(QRectF(0, 0, 100, 100)); + child->setParentItem(parent); + + QGraphicsScene scene; + scene.addItem(parent); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + view.viewport()->repaint(); + + QVERIFY(!parent->dirtyPainter); + QVERIFY(!child->dirtyPainter); + + view.setOptimizationFlags(QGraphicsView::DontSavePainterState); + view.viewport()->repaint(); + +#ifdef Q_WS_MAC + // Repaint on Mac OS X actually does require spinning the event loop. + QTest::qWait(100); +#endif + QVERIFY(!parent->dirtyPainter); + QVERIFY(child->dirtyPainter); + + MyGraphicsView painter(&scene); + painter.show(); + QTest::qWaitForWindowShown(&painter); + + MyGraphicsView painter2(&scene); + painter2.setOptimizationFlag(QGraphicsView::DontSavePainterState,true); + painter2.show(); + QTest::qWaitForWindowShown(&painter2); +} + +void tst_QGraphicsView::optimizationFlags_dontSavePainterState2_data() +{ + QTest::addColumn<bool>("savePainter"); + QTest::addColumn<bool>("indirectPainting"); + QTest::newRow("With painter state protection, without indirect painting") << true << false; + QTest::newRow("Without painter state protection, without indirect painting") << false << false; + QTest::newRow("With painter state protectionm, with indirect painting") << true << true; + QTest::newRow("Without painter state protection, with indirect painting") << false << true; +} + +void tst_QGraphicsView::optimizationFlags_dontSavePainterState2() +{ + QFETCH(bool, savePainter); + QFETCH(bool, indirectPainting); + + class MyScene : public QGraphicsScene + { + public: + void drawBackground(QPainter *p, const QRectF &) + { transformInDrawBackground = p->worldTransform(); opacityInDrawBackground = p->opacity(); } + + void drawForeground(QPainter *p, const QRectF &) + { transformInDrawForeground = p->worldTransform(); opacityInDrawForeground = p->opacity(); } + + QTransform transformInDrawBackground; + QTransform transformInDrawForeground; + qreal opacityInDrawBackground; + qreal opacityInDrawForeground; + }; + + MyScene scene; + // Add transformed dummy items to make sure the painter's worldTransform() is changed in drawItems. + scene.addRect(0, 0, 20, 20)->setTransform(QTransform::fromScale(2, 2)); + scene.addRect(50, 50, 20, 20)->setTransform(QTransform::fromTranslate(200, 200)); + + foreach (QGraphicsItem *item, scene.items()) + item->setOpacity(0.6); + + CustomView view(&scene); + if (!savePainter) + view.setOptimizationFlag(QGraphicsView::DontSavePainterState); + view.setOptimizationFlag(QGraphicsView::IndirectPainting, indirectPainting); + view.rotate(45); + view.scale(1.5, 1.5); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + // Make sure the view is repainted; otherwise the tests below will fail. + view.viewport()->repaint(); + QTest::qWait(200); + QVERIFY(view.painted); + + // Make sure the painter's world transform is preserved after drawItems. + QTransform expectedTransform = view.viewportTransform(); + QVERIFY(!expectedTransform.isIdentity()); + QCOMPARE(scene.transformInDrawForeground, expectedTransform); + QCOMPARE(scene.transformInDrawBackground, expectedTransform); + + qreal expectedOpacity = 1.0; + QCOMPARE(scene.opacityInDrawBackground, expectedOpacity); + QCOMPARE(scene.opacityInDrawForeground, expectedOpacity); + + // Trigger more painting, this time from QGraphicsScene::render. + QImage image(scene.sceneRect().size().toSize(), QImage::Format_RGB32); + QPainter painter(&image); + scene.render(&painter); + painter.end(); + + expectedTransform = QTransform(); + QCOMPARE(scene.transformInDrawForeground, expectedTransform); + QCOMPARE(scene.transformInDrawBackground, expectedTransform); + QCOMPARE(scene.opacityInDrawBackground, expectedOpacity); + QCOMPARE(scene.opacityInDrawForeground, expectedOpacity); + + // Trigger more painting with another opacity on the painter. + painter.begin(&image); + painter.setOpacity(0.4); + expectedOpacity = 0.4; + scene.render(&painter); + painter.end(); + + QCOMPARE(scene.transformInDrawForeground, expectedTransform); + QCOMPARE(scene.transformInDrawBackground, expectedTransform); + QCOMPARE(scene.opacityInDrawBackground, expectedOpacity); + QCOMPARE(scene.opacityInDrawForeground, expectedOpacity); +} + +class LodItem : public QGraphicsRectItem +{ +public: + LodItem(const QRectF &rect) : QGraphicsRectItem(rect), lastLod(-42) + { } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *viewport) + { + lastLod = option->levelOfDetailFromTransform(painter->worldTransform()); + QGraphicsRectItem::paint(painter, option, viewport); + } + + qreal lastLod; +}; + +void tst_QGraphicsView::levelOfDetail_data() +{ + QTest::addColumn<QTransform>("transform"); + QTest::addColumn<qreal>("lod"); + + QTest::newRow("1:4, 1:4") << QTransform().scale(0.25, 0.25) << qreal(0.25); + QTest::newRow("1:2, 1:4") << QTransform().scale(0.5, 0.25) << qreal(::sqrt(0.125)); + QTest::newRow("4:1, 1:2") << QTransform().scale(0.25, 0.5) << qreal(::sqrt(0.125)); + + QTest::newRow("1:2, 1:2") << QTransform().scale(0.5, 0.5) << qreal(0.5); + QTest::newRow("1:1, 1:2") << QTransform().scale(1, 0.5) << qreal(::sqrt(0.5)); + QTest::newRow("2:1, 1:1") << QTransform().scale(0.5, 1) << qreal(::sqrt(0.5)); + + QTest::newRow("1:1, 1:1") << QTransform().scale(1, 1) << qreal(1.0); + QTest::newRow("2:1, 1:1") << QTransform().scale(2, 1) << qreal(::sqrt(2.0)); + QTest::newRow("1:1, 2:1") << QTransform().scale(2, 1) << qreal(::sqrt(2.0)); + QTest::newRow("2:1, 2:1") << QTransform().scale(2, 2) << qreal(2.0); + QTest::newRow("2:1, 4:1") << QTransform().scale(2, 4) << qreal(::sqrt(8.0)); + QTest::newRow("4:1, 2:1") << QTransform().scale(4, 2) << qreal(::sqrt(8.0)); + QTest::newRow("4:1, 4:1") << QTransform().scale(4, 4) << qreal(4.0); +} + +void tst_QGraphicsView::levelOfDetail() +{ + QFETCH(QTransform, transform); + QFETCH(qreal, lod); + + LodItem *item = new LodItem(QRectF(0, 0, 100, 100)); + + QGraphicsScene scene; + scene.addItem(item); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QTRY_COMPARE(item->lastLod, qreal(1)); + + view.setTransform(transform); + + QTRY_COMPARE(item->lastLod, lod); +} + +// Moved to tst_qgraphicsview_2.cpp +extern void _scrollBarRanges_data(); + +void tst_QGraphicsView::scrollBarRanges_data() +{ + _scrollBarRanges_data(); +} + +void tst_QGraphicsView::scrollBarRanges() +{ + QFETCH(QSize, viewportSize); + QFETCH(QRectF, sceneRect); + QFETCH(QTransform, transform); + QFETCH(Qt::ScrollBarPolicy, hbarpolicy); + QFETCH(Qt::ScrollBarPolicy, vbarpolicy); + QFETCH(int, hmin); + QFETCH(int, hmax); + QFETCH(int, vmin); + QFETCH(int, vmax); + QFETCH(bool, useMotif); + QFETCH(bool, useStyledPanel); + + QGraphicsScene scene(sceneRect); + scene.addRect(sceneRect, QPen(Qt::blue), QBrush(QColor(Qt::green))); + QGraphicsView view(&scene); + view.setRenderHint(QPainter::Antialiasing); + view.setTransform(transform); + view.setFrameStyle(useStyledPanel ? QFrame::StyledPanel : QFrame::NoFrame); + + if (useMotif) { +#if !defined(QT_NO_STYLE_MOTIF) + view.setStyle(new QMotifStyle); +#else + QSKIP("No Motif style compiled.", SkipSingle); +#endif + } else { +#if defined(Q_OS_WINCE) + view.setStyle(new QWindowsStyle); +#elif !defined(QT_NO_STYLE_PLASTIQUE) + view.setStyle(new QPlastiqueStyle); +#endif + } + view.setStyleSheet(" "); // enables style propagation ;-) + + int adjust = 0; + if (useStyledPanel) + adjust = view.style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; + view.resize(viewportSize + QSize(adjust, adjust)); + + view.setHorizontalScrollBarPolicy(hbarpolicy); + view.setVerticalScrollBarPolicy(vbarpolicy); + + view.show(); + + QCOMPARE(view.horizontalScrollBar()->minimum(), hmin); + QCOMPARE(view.verticalScrollBar()->minimum(), vmin); + QCOMPARE(view.horizontalScrollBar()->maximum(), hmax); + QCOMPARE(view.verticalScrollBar()->maximum(), vmax); +} + +class TestView : public QGraphicsView +{ +public: + TestView(QGraphicsScene *scene) + : QGraphicsView(scene), accepted(false) + { } + + bool accepted; + +protected: + void mousePressEvent(QMouseEvent *event) + { + QGraphicsView::mousePressEvent(event); + accepted = event->isAccepted(); + } +}; + +void tst_QGraphicsView::acceptMousePressEvent() +{ + QGraphicsScene scene; + + TestView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QMouseEvent event(QEvent::MouseButtonPress, + view.viewport()->rect().center(), + view.viewport()->mapToGlobal(view.viewport()->rect().center()), + Qt::LeftButton, 0, 0); + event.setAccepted(false); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(!view.accepted); + + scene.addRect(0, 0, 2000, 2000)->setFlag(QGraphicsItem::ItemIsMovable); + + qApp->processEvents(); // ensure scene rect is updated + + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(view.accepted); +} + +void tst_QGraphicsView::replayMouseMove() +{ + // An empty scene in a view. The view will send the events to the scene in + // any case. Note that the view doesn't have to be shown - the mouse event + // sending functions below send the events directly to the viewport. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + + EventSpy sceneSpy(&scene, QEvent::GraphicsSceneMouseMove); + EventSpy viewSpy(view.viewport(), QEvent::MouseMove); + + sendMousePress(view.viewport(), view.viewport()->rect().center()); + + // One mouse event should be translated into one scene event. + for (int i = 0; i < 3; ++i) { + sendMouseMove(view.viewport(), view.viewport()->rect().center(), + Qt::LeftButton, Qt::MouseButtons(Qt::LeftButton)); + QCOMPARE(viewSpy.count(), i + 1); + QCOMPARE(sceneSpy.count(), i + 1); + } + + // When the view is transformed, the view should get no more events. But + // the scene should get replays. + for (int i = 0; i < 3; ++i) { + view.rotate(10); + QCOMPARE(viewSpy.count(), 3); + QCOMPARE(sceneSpy.count(), 3 + i + 1); + } + + // When the view is scrolled, the view should get no more events. But the + // scene should get replays. + for (int i = 0; i < 3; ++i) { + view.horizontalScrollBar()->setValue((i + 1) * 10); + QCOMPARE(viewSpy.count(), 3); + QCOMPARE(sceneSpy.count(), 6 + i + 1); + } +} + +void tst_QGraphicsView::itemsUnderMouse() +{ + QGraphicsScene scene; + QGraphicsProxyWidget w; + w.setWidget(new QPushButton("W")); + w.resize(50,50); + QGraphicsProxyWidget w2(&w); + w2.setWidget(new QPushButton("W2")); + w2.resize(50,50); + QGraphicsProxyWidget w3(&w2); + w3.setWidget(new QPushButton("W3")); + w3.resize(50,50); + w.setZValue(150); + w2.setZValue(50); + w3.setZValue(0); + scene.addItem(&w); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(view.items(view.mapFromScene(w3.boundingRect().center())).first(), + static_cast<QGraphicsItem *>(&w3)); + w2.setFlag(QGraphicsItem::ItemIgnoresTransformations, true); + QCOMPARE(view.items(view.mapFromScene(w3.boundingRect().center())).first(), + static_cast<QGraphicsItem *>(&w3)); +} + +class QGraphicsTextItem_task172231 : public QGraphicsTextItem +{ +public: + QGraphicsTextItem_task172231(const QString & text, QGraphicsItem * parent = 0) + : QGraphicsTextItem(text, parent) {} + QRectF exposedRect; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + exposedRect = option->exposedRect; + QGraphicsTextItem::paint(painter, option, widget); + } +}; + +void tst_QGraphicsView::task172231_untransformableItems() +{ + // check fix in QGraphicsView::paintEvent() + + QGraphicsScene scene; + + QGraphicsTextItem_task172231 *text = + new QGraphicsTextItem_task172231("abcdefghijklmnopqrstuvwxyz"); + text->setFlag(QGraphicsItem::ItemIgnoresTransformations); + scene.addItem(text); + + QGraphicsView view(&scene); + + view.scale(2, 1); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + QRectF origExposedRect = text->exposedRect; + + view.resize(int(0.75 * view.width()), view.height()); + qApp->processEvents(); + + QCOMPARE(text->exposedRect, origExposedRect); + + // notice that the fix also goes into QGraphicsView::render() + // and QGraphicsScene::render(), but in duplicated code that + // is pending a refactoring, so for now we omit autotesting + // these functions separately +} + +class MousePressReleaseScene : public QGraphicsScene +{ +public: + MousePressReleaseScene() + : presses(0), releases(0) + { } + int presses; + int releases; + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event) + { ++presses; QGraphicsScene::mousePressEvent(event); } + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) + { ++releases; QGraphicsScene::mouseReleaseEvent(event); } +}; + +void tst_QGraphicsView::task180429_mouseReleaseDragMode() +{ + MousePressReleaseScene scene; + + QGraphicsView view(&scene); + view.show(); + + sendMousePress(view.viewport(), view.viewport()->rect().center()); + QCOMPARE(scene.presses, 1); + QCOMPARE(scene.releases, 0); + sendMouseRelease(view.viewport(), view.viewport()->rect().center()); + QCOMPARE(scene.presses, 1); + QCOMPARE(scene.releases, 1); + + view.setDragMode(QGraphicsView::RubberBandDrag); + sendMousePress(view.viewport(), view.viewport()->rect().center()); + QCOMPARE(scene.presses, 2); + QCOMPARE(scene.releases, 1); + sendMouseRelease(view.viewport(), view.viewport()->rect().center()); + QCOMPARE(scene.presses, 2); + QCOMPARE(scene.releases, 2); +} + +void tst_QGraphicsView::task187791_setSceneCausesUpdate() +{ + QGraphicsScene scene(0, 0, 200, 200); + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + EventSpy updateSpy(view.viewport(), QEvent::Paint); + QCOMPARE(updateSpy.count(), 0); + + view.setScene(0); + QApplication::processEvents(); + QTRY_COMPARE(updateSpy.count(), 1); + view.setScene(&scene); + QApplication::processEvents(); + QTRY_COMPARE(updateSpy.count(), 2); +} + +class MouseMoveCounter : public QGraphicsView +{ +public: + MouseMoveCounter() : mouseMoves(0) + { } + int mouseMoves; +protected: + void mouseMoveEvent(QMouseEvent *event) + { + ++mouseMoves; + QGraphicsView::mouseMoveEvent(event); + foreach (QGraphicsItem *item, scene()->items()) { + scene()->removeItem(item); + delete item; + } + scene()->addRect(0, 0, 50, 50); + scene()->addRect(0, 0, 100, 100); + } +}; + +void tst_QGraphicsView::task186827_deleteReplayedItem() +{ + // make sure the mouse is not over the window, causing spontaneous mouse moves + QCursor::setPos(1, 1); + + QGraphicsScene scene; + scene.addRect(0, 0, 50, 50); + scene.addRect(0, 0, 100, 100); + + MouseMoveCounter view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + view.viewport()->setMouseTracking(true); + + QCOMPARE(view.mouseMoves, 0); + { + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(25, 25), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + QCOMPARE(view.mouseMoves, 1); + QTest::qWait(25); + QTRY_COMPARE(view.mouseMoves, 1); + QTest::qWait(25); + { + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(25, 25), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + QCOMPARE(view.mouseMoves, 2); + QTest::qWait(15); +} + +void tst_QGraphicsView::task207546_focusCrash() +{ + class _Widget : public QWidget + { + public: + bool focusNextPrevChild(bool next) { return QWidget::focusNextPrevChild(next); } + } widget; + + widget.setLayout(new QVBoxLayout()); + QGraphicsView *gr1 = new QGraphicsView(&widget); + QGraphicsView *gr2 = new QGraphicsView(&widget); + widget.layout()->addWidget(gr1); + widget.layout()->addWidget(gr2); + widget.show(); + QTest::qWaitForWindowShown(&widget); + widget.activateWindow(); + QApplication::setActiveWindow(&widget); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&widget)); + widget.focusNextPrevChild(true); + QCOMPARE(static_cast<QWidget *>(gr2), widget.focusWidget()); +} + +void tst_QGraphicsView::task210599_unsetDragWhileDragging() +{ + QGraphicsScene scene(0, 0, 400, 400); + QGraphicsView view(&scene); + view.setGeometry(0, 0, 200, 200); + view.show(); + + QPoint origPos = QPoint(100, 100); + QPoint step1Pos = QPoint(100, 110); + QPoint step2Pos = QPoint(100, 120); + + // Enable and do a drag + { + view.setDragMode(QGraphicsView::ScrollHandDrag); + QMouseEvent press(QEvent::MouseButtonPress, origPos, Qt::LeftButton, 0, 0); + QMouseEvent move(QEvent::MouseMove, step1Pos, Qt::LeftButton, 0, 0); + QApplication::sendEvent(view.viewport(), &press); + QApplication::sendEvent(view.viewport(), &move); + } + + // unset drag and release mouse, inverse order + { + view.setDragMode(QGraphicsView::NoDrag); + QMouseEvent release(QEvent::MouseButtonRelease, step1Pos, Qt::LeftButton, 0, 0); + QApplication::sendEvent(view.viewport(), &release); + } + + QPoint basePos = view.mapFromScene(0, 0); + + // reset drag, and move mouse without holding button down. + { + view.setDragMode(QGraphicsView::ScrollHandDrag); + QMouseEvent move(QEvent::MouseMove, step2Pos, Qt::LeftButton, 0, 0); + QApplication::sendEvent(view.viewport(), &move); + } + + // Check that no draggin has occurred... + QCOMPARE(basePos, view.mapFromScene(0, 0)); +} + +void tst_QGraphicsView::task236394_sendShortcutOverrideEvent() +{ + QGraphicsView view; + view.show(); + QKeyEvent event(QEvent::ShortcutOverride, Qt::Key_A, 0, QString("A")); + QApplication::sendEvent(&view, &event); +} + +class ChangedListener : public QObject +{ + Q_OBJECT +public: + QList<QList<QRectF> > changes; + +public slots: + void changed(const QList<QRectF> &dirty) + { + changes << dirty; + } +}; + +void tst_QGraphicsView::task239729_noViewUpdate_data() +{ + QTest::addColumn<bool>("a"); + + QTest::newRow("a") << false; + QTest::newRow("b") << true; +} + +void tst_QGraphicsView::task239729_noViewUpdate() +{ + QFETCH(bool, a); + // The scene's changed signal is connected to something that isn't a view. + QGraphicsScene scene; + ChangedListener cl; + QGraphicsView *view = 0; + + if (a) { + view = new QGraphicsView(&scene); + connect(&scene, SIGNAL(changed(const QList<QRectF> &)), &cl, SLOT(changed(const QList<QRectF> &))); + } else { + connect(&scene, SIGNAL(changed(const QList<QRectF> &)), &cl, SLOT(changed(const QList<QRectF> &))); + view = new QGraphicsView(&scene); + } + + EventSpy spy(view->viewport(), QEvent::Paint); + QCOMPARE(spy.count(), 0); + + view->show(); + QTest::qWaitForWindowShown(view); + + QTRY_VERIFY(spy.count() >= 1); + spy.reset(); + scene.update(); + QApplication::processEvents(); + QTRY_COMPARE(spy.count(), 1); + + delete view; +} + +void tst_QGraphicsView::task239047_fitInViewSmallViewport() +{ + // Ensure that with a small viewport, fitInView doesn't mirror the + // scene. + QWidget widget; + QGraphicsScene scene; + QGraphicsView *view = new QGraphicsView(&scene, &widget); + view->resize(3, 3); + QCOMPARE(view->size(), QSize(3, 3)); + widget.show(); + view->fitInView(0, 0, 100, 100); + QPointF topLeft = view->mapToScene(0, 0); + QPointF bottomRight = view->mapToScene(100, 100); + QVERIFY(bottomRight.x() > topLeft.x()); + QVERIFY(bottomRight.y() > topLeft.y()); + + view->fitInView(0, 0, 0, 100); + + // Don't crash + view->scale(0, 0); + view->fitInView(0, 0, 100, 100); +} + +void tst_QGraphicsView::task245469_itemsAtPointWithClip() +{ + QGraphicsScene scene; + QGraphicsItem *parent = scene.addRect(0, 0, 100, 100); + QGraphicsItem *child = new QGraphicsRectItem(40, 40, 20, 20, parent); + parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + QGraphicsView view(&scene); + view.resize(150,150); + view.rotate(90); + view.show(); + QTest::qWaitForWindowShown(&view); + + QList<QGraphicsItem *> itemsAtCenter = view.items(view.viewport()->rect().center()); + QCOMPARE(itemsAtCenter, (QList<QGraphicsItem *>() << child << parent)); + + QPolygonF p = view.mapToScene(QRect(view.viewport()->rect().center(), QSize(1, 1))); + QList<QGraphicsItem *> itemsAtCenter2 = scene.items(p); + QCOMPARE(itemsAtCenter2, itemsAtCenter); +} + +static QGraphicsView *createSimpleViewAndScene() +{ + QGraphicsView *view = new QGraphicsView; + QGraphicsScene *scene = new QGraphicsScene; + view->setScene(scene); + + view->setBackgroundBrush(Qt::blue); + + QGraphicsRectItem *rect = scene->addRect(0, 0, 10, 10); + rect->setBrush(Qt::red); + rect->setPen(Qt::NoPen); + return view; +} + +class SpyItem : public QGraphicsRectItem +{ +public: + SpyItem() + : QGraphicsRectItem(QRectF(0, 0, 100, 100)) + { + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + transform = painter->transform(); + } + + QTransform transform; +}; + +void tst_QGraphicsView::embeddedViews() +{ + QGraphicsView *v1 = createSimpleViewAndScene(); + QGraphicsView *v2 = createSimpleViewAndScene(); + + QGraphicsProxyWidget *proxy = v1->scene()->addWidget(v2); + + SpyItem *item = new SpyItem; + v2->scene()->addItem(item); + + proxy->translate(5, 5); + + QImage actual(64, 64, QImage::Format_ARGB32_Premultiplied); + actual.fill(0); + v1->QWidget::render(&actual); + QTransform a = item->transform; + + v2->QWidget::render(&actual); + QTransform b = item->transform; + + QVERIFY(a == b); + delete v1; +} + +void tst_QGraphicsView::scrollAfterResize_data() +{ + QTest::addColumn<bool>("reverse"); + QTest::addColumn<QTransform>("x1"); + QTest::addColumn<QTransform>("x2"); + QTest::addColumn<QTransform>("x3"); + +#if !defined(QT_NO_STYLE_PLASTIQUE) + QPlastiqueStyle style; +#elif !defined(QT_NO_STYLE_WINDOWS) + QWindowsStyle style; +#else + QCommonStyle style; +#endif + + int frameWidth = style.pixelMetric(QStyle::PM_DefaultFrameWidth); + int extent = style.pixelMetric(QStyle::PM_ScrollBarExtent); + int inside = style.styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents); + int viewportWidth = 300; + int scrollBarIndent = viewportWidth - extent - (inside ? 4 : 2)*frameWidth; + + QTest::newRow("normal") << false + << QTransform() + << QTransform() + << QTransform().translate(-10, 0); + QTest::newRow("reverse") << true + << QTransform().translate(scrollBarIndent, 0) + << QTransform().translate(scrollBarIndent + 100, 0) + << QTransform().translate(scrollBarIndent + 110, 0); +} + +void tst_QGraphicsView::scrollAfterResize() +{ + QFETCH(bool, reverse); + QFETCH(QTransform, x1); + QFETCH(QTransform, x2); + QFETCH(QTransform, x3); + +#if !defined(QT_NO_STYLE_PLASTIQUE) + QPlastiqueStyle style; +#elif !defined(QT_NO_STYLE_WINDOWS) + QWindowsStyle style; +#else + QCommonStyle style; +#endif + QWidget toplevel; + + QGraphicsView view(&toplevel); + view.setStyle(&style); + if (reverse) + view.setLayoutDirection(Qt::RightToLeft); + + view.setSceneRect(-1000, -1000, 2000, 2000); + view.resize(300, 300); + toplevel.show(); + QTest::qWaitForWindowShown(&toplevel); + view.horizontalScrollBar()->setValue(0); + view.verticalScrollBar()->setValue(0); + QCOMPARE(view.viewportTransform(), x1); + view.resize(400, 300); + QCOMPARE(view.viewportTransform(), x2); + view.horizontalScrollBar()->setValue(10); + QCOMPARE(view.viewportTransform(), x3); +} + +void tst_QGraphicsView::moveItemWhileScrolling_data() +{ + QTest::addColumn<bool>("adjustForAntialiasing"); + QTest::addColumn<bool>("changedConnected"); + + QTest::newRow("no adjust") << false << false; + QTest::newRow("adjust") << true << false; + QTest::newRow("no adjust changedConnected") << false << true; + QTest::newRow("adjust changedConnected") << true << true; +} + +void tst_QGraphicsView::moveItemWhileScrolling() +{ + QFETCH(bool, adjustForAntialiasing); + QFETCH(bool, changedConnected); + + class MoveItemScrollView : public QGraphicsView + { + public: + MoveItemScrollView() + { + setWindowFlags(Qt::X11BypassWindowManagerHint); + setScene(new QGraphicsScene(0, 0, 1000, 1000)); + rect = scene()->addRect(0, 0, 10, 10); + rect->setPos(50, 50); + painted = false; + } + QRegion lastPaintedRegion; + QGraphicsItem *rect; + bool painted; + void waitForPaintEvent() + { + QTimer::singleShot(2000, &eventLoop, SLOT(quit())); + eventLoop.exec(); + } + protected: + QEventLoop eventLoop; + void paintEvent(QPaintEvent *event) + { + painted = true; + lastPaintedRegion = event->region(); + QGraphicsView::paintEvent(event); + if (eventLoop.isRunning()) + eventLoop.quit(); + } + }; + + MoveItemScrollView view; + view.setFrameStyle(0); + view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setResizeAnchor(QGraphicsView::NoAnchor); + view.setTransformationAnchor(QGraphicsView::NoAnchor); + if (!adjustForAntialiasing) + view.setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing); + view.resize(200, 200); + view.painted = false; + view.show(); + if (changedConnected) + QObject::connect(view.scene(), SIGNAL(changed(QList<QRectF>)), this, SLOT(dummySlot())); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + QTRY_VERIFY(view.painted); + view.painted = false; + view.lastPaintedRegion = QRegion(); + view.horizontalScrollBar()->setValue(view.horizontalScrollBar()->value() + 10); + view.rect->moveBy(0, 10); + view.waitForPaintEvent(); + QTRY_VERIFY(view.painted); + + QRegion expectedRegion; + expectedRegion += QRect(0, 0, 200, 200); + expectedRegion -= QRect(0, 0, 190, 200); + int a = adjustForAntialiasing ? 2 : 1; + expectedRegion += QRect(40, 50, 10, 10).adjusted(-a, -a, a, a); + expectedRegion += QRect(40, 60, 10, 10).adjusted(-a, -a, a, a); +#ifdef Q_OS_MAC + QEXPECT_FAIL("", "This will fail with Cocoa because paint events are not send in the order expected by graphicsview", Continue); +#endif + COMPARE_REGIONS(view.lastPaintedRegion, expectedRegion); +} + +void tst_QGraphicsView::centerOnDirtyItem() +{ + QWidget toplevel; + + QGraphicsView view(&toplevel); + toplevel.setWindowFlags(view.windowFlags() | Qt::WindowStaysOnTopHint); + view.resize(200, 200); + + QGraphicsScene *scene = new QGraphicsScene; + view.setScene(scene); + view.setSceneRect(-1000, -1000, 2000, 2000); + + QGraphicsRectItem *item = new QGraphicsRectItem(0, 0, 10, 10); + item->setBrush(Qt::red); + scene->addItem(item); + view.centerOn(item); + + toplevel.show(); + QTest::qWaitForWindowShown(&toplevel); + QTest::qWait(50); + + QImage before(view.viewport()->size(), QImage::Format_ARGB32); + view.viewport()->render(&before); + + item->setPos(20, 0); + view.centerOn(item); + + QTest::qWait(50); + + QImage after(view.viewport()->size(), QImage::Format_ARGB32); + view.viewport()->render(&after); + + QCOMPARE(before, after); +} + +void tst_QGraphicsView::mouseTracking() +{ + // Mouse tracking should only be automatically enabled if items either accept hover events + // or have a cursor set. We never disable mouse tracking if it is already enabled. + + { // Make sure mouse tracking is disabled by default. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + QVERIFY(!view.viewport()->hasMouseTracking()); + } + + { // Make sure we don't disable mouse tracking in setupViewport/setScene. + QGraphicsView view; + QWidget *viewport = new QWidget; + viewport->setMouseTracking(true); + view.setViewport(viewport); + QVERIFY(viewport->hasMouseTracking()); + + QGraphicsScene scene(-10000, -10000, 20000, 20000); + view.setScene(&scene); + QVERIFY(viewport->hasMouseTracking()); + } + + // Make sure we enable mouse tracking when having items that accept hover events. + { + // Adding an item to the scene after the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + item->setAcceptHoverEvents(true); + scene.addItem(item); + QVERIFY(view.viewport()->hasMouseTracking()); + } + { + // Adding an item to the scene before the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + item->setAcceptHoverEvents(true); + scene.addItem(item); + + QGraphicsView view(&scene); + QVERIFY(view.viewport()->hasMouseTracking()); + } + { + // QGraphicsWidget implicitly accepts hover if it has window decoration. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + + QGraphicsWidget *widget = new QGraphicsWidget; + scene.addItem(widget); + QVERIFY(!view.viewport()->hasMouseTracking()); + // Enable window decoraton. + widget->setWindowFlags(Qt::Window | Qt::WindowTitleHint); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + // Make sure we enable mouse tracking when having items with a cursor set. + { + // Adding an item to the scene after the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); +#ifndef QT_NO_CURSOR + item->setCursor(Qt::CrossCursor); +#endif + scene.addItem(item); + QVERIFY(view.viewport()->hasMouseTracking()); + } + { + // Adding an item to the scene before the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); +#ifndef QT_NO_CURSOR + item->setCursor(Qt::CrossCursor); +#endif + scene.addItem(item); + + QGraphicsView view(&scene); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + // Make sure we propagate mouse tracking to all views. + { + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view1(&scene); + QGraphicsView view2(&scene); + QGraphicsView view3(&scene); + + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); +#ifndef QT_NO_CURSOR + item->setCursor(Qt::CrossCursor); +#endif + scene.addItem(item); + + QVERIFY(view1.viewport()->hasMouseTracking()); + QVERIFY(view2.viewport()->hasMouseTracking()); + QVERIFY(view3.viewport()->hasMouseTracking()); + } +} + +void tst_QGraphicsView::mouseTracking2() +{ + // Make sure mouse move events propagates to the scene when + // mouse tracking is explicitly enabled on the view, + // even when all items ignore hover events / use default cursor. + + QGraphicsScene scene; + scene.addRect(0, 0, 100, 100); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QVERIFY(!view.viewport()->hasMouseTracking()); + view.viewport()->setMouseTracking(true); // Explicitly enable mouse tracking. + QVERIFY(view.viewport()->hasMouseTracking()); + + EventSpy spy(&scene, QEvent::GraphicsSceneMouseMove); + QCOMPARE(spy.count(), 0); + QMouseEvent event(QEvent::MouseMove,view.viewport()->rect().center(), Qt::NoButton, + Qt::MouseButtons(Qt::NoButton), 0); + QApplication::sendEvent(view.viewport(), &event); + QCOMPARE(spy.count(), 1); +} + +void tst_QGraphicsView::mouseTracking3() +{ + // Mouse tracking should be automatically enabled if AnchorUnderMouse is used for + // view transform or resize. We never disable mouse tracking if it is already enabled. + + { // Make sure we enable mouse tracking when using AnchorUnderMouse for view transformation. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + QVERIFY(!view.viewport()->hasMouseTracking()); + + view.setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + { // Make sure we enable mouse tracking when using AnchorUnderMouse for view resizing. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + QVERIFY(!view.viewport()->hasMouseTracking()); + + view.setResizeAnchor(QGraphicsView::AnchorUnderMouse); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + { // Make sure we don't disable mouse tracking in setViewport/setScene (transformation anchor). + QGraphicsView view; + view.setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + QVERIFY(view.viewport()->hasMouseTracking()); + + QWidget *viewport = new QWidget; + view.setViewport(viewport); + QVERIFY(viewport->hasMouseTracking()); + + QGraphicsScene scene(-10000, -10000, 20000, 20000); + view.setScene(&scene); + QVERIFY(viewport->hasMouseTracking()); + } + + { // Make sure we don't disable mouse tracking in setViewport/setScene (resize anchor). + QGraphicsView view; + view.setResizeAnchor(QGraphicsView::AnchorUnderMouse); + QVERIFY(view.viewport()->hasMouseTracking()); + + QWidget *viewport = new QWidget; + view.setViewport(viewport); + QVERIFY(viewport->hasMouseTracking()); + + QGraphicsScene scene(-10000, -10000, 20000, 20000); + view.setScene(&scene); + QVERIFY(viewport->hasMouseTracking()); + } + + // Make sure we don't disable mouse tracking when adding an item (transformation anchor). + { // Adding an item to the scene before the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + scene.addItem(item); + + QGraphicsView view; + view.setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + view.setScene(&scene); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + { // Adding an item to the scene after the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + view.setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + scene.addItem(item); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + // Make sure we don't disable mouse tracking when adding an item (resize anchor). + { // Adding an item to the scene before the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + scene.addItem(item); + + QGraphicsView view; + view.setResizeAnchor(QGraphicsView::AnchorUnderMouse); + view.setScene(&scene); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + { // Adding an item to the scene after the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + view.setResizeAnchor(QGraphicsView::AnchorUnderMouse); + + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + scene.addItem(item); + QVERIFY(view.viewport()->hasMouseTracking()); + } +} + +class RenderTester : public QGraphicsRectItem +{ +public: + RenderTester(const QRectF &rect) + : QGraphicsRectItem(rect), paints(0) + { } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) + { + QGraphicsRectItem::paint(painter, option, widget); + ++paints; + } + + int paints; +}; + +void tst_QGraphicsView::render() +{ + // ### This test can be much more thorough - see QGraphicsScene::render. + QGraphicsScene scene; + CustomView view(&scene); + view.setFrameStyle(0); + view.resize(200, 200); + view.painted = false; + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + QTRY_VERIFY(view.painted > 0); + + RenderTester *r1 = new RenderTester(QRectF(0, 0, 50, 50)); + RenderTester *r2 = new RenderTester(QRectF(50, 50, 50, 50)); + RenderTester *r3 = new RenderTester(QRectF(0, 50, 50, 50)); + RenderTester *r4 = new RenderTester(QRectF(50, 0, 50, 50)); + scene.addItem(r1); + scene.addItem(r2); + scene.addItem(r3); + scene.addItem(r4); + + qApp->processEvents(); + + QTRY_COMPARE(r1->paints, 1); + QCOMPARE(r2->paints, 1); + QCOMPARE(r3->paints, 1); + QCOMPARE(r4->paints, 1); + + QPixmap pix(200, 200); + pix.fill(Qt::transparent); + QPainter painter(&pix); + view.render(&painter); + painter.end(); + + QCOMPARE(r1->paints, 2); + QCOMPARE(r2->paints, 2); + QCOMPARE(r3->paints, 2); + QCOMPARE(r4->paints, 2); +} + +void tst_QGraphicsView::exposeRegion() +{ + RenderTester *item = new RenderTester(QRectF(0, 0, 20, 20)); + QGraphicsScene scene; + scene.addItem(item); + + item->paints = 0; + CustomView view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(item->paints > 0); + + item->paints = 0; + view.lastUpdateRegions.clear(); + + // Update a small area in the viewport's topLeft() and bottomRight(). + // (the boundingRect() of this area covers the entire viewport). + QWidget *viewport = view.viewport(); + QRegion expectedExposeRegion = QRect(0, 0, 5, 5); + expectedExposeRegion += QRect(viewport->rect().bottomRight() - QPoint(5, 5), QSize(5, 5)); + viewport->update(expectedExposeRegion); + QApplication::processEvents(); + + // Make sure it triggers correct repaint on the view. + QTRY_COMPARE(view.lastUpdateRegions.size(), 1); + COMPARE_REGIONS(view.lastUpdateRegions.at(0), expectedExposeRegion); + + // Make sure the item didn't get any repaints. +#ifndef Q_OS_MAC + QCOMPARE(item->paints, 0); +#endif +} + +void tst_QGraphicsView::update_data() +{ + // In view.viewport() coordinates. (viewport rect: QRect(0, 0, 200, 200)) + QTest::addColumn<QRect>("updateRect"); + QTest::newRow("empty") << QRect(); + QTest::newRow("outside left") << QRect(-200, 0, 100, 100); + QTest::newRow("outside right") << QRect(400, 0 ,100, 100); + QTest::newRow("outside top") << QRect(0, -200, 100, 100); + QTest::newRow("outside bottom") << QRect(0, 400, 100, 100); + QTest::newRow("partially inside left") << QRect(-50, 0, 100, 100); + QTest::newRow("partially inside right") << QRect(-150, 0, 100, 100); + QTest::newRow("partially inside top") << QRect(0, -150, 100, 100); + QTest::newRow("partially inside bottom") << QRect(0, 150, 100, 100); + QTest::newRow("on topLeft edge") << QRect(-100, -100, 100, 100); + QTest::newRow("on topRight edge") << QRect(200, -100, 100, 100); + QTest::newRow("on bottomRight edge") << QRect(200, 200, 100, 100); + QTest::newRow("on bottomLeft edge") << QRect(-200, 200, 100, 100); + QTest::newRow("inside topLeft") << QRect(-99, -99, 100, 100); + QTest::newRow("inside topRight") << QRect(199, -99, 100, 100); + QTest::newRow("inside bottomRight") << QRect(199, 199, 100, 100); + QTest::newRow("inside bottomLeft") << QRect(-199, 199, 100, 100); + QTest::newRow("large1") << QRect(50, -100, 100, 400); + QTest::newRow("large2") << QRect(-100, 50, 400, 100); + QTest::newRow("large3") << QRect(-100, -100, 400, 400); + QTest::newRow("viewport rect") << QRect(0, 0, 200, 200); +} + +void tst_QGraphicsView::update() +{ + QFETCH(QRect, updateRect); + + // some window manager resize the toplevel to max screen size + // so we must make our view a child (no layout!) of a dummy toplevel + // to ensure that it's really 200x200 pixels + QWidget toplevel; + + // Create a view with viewport rect equal to QRect(0, 0, 200, 200). + QGraphicsScene dummyScene; + CustomView view(0, &toplevel); + view.setScene(&dummyScene); + view.ensurePolished(); // must ensure polished to get content margins right + int left, top, right, bottom; + view.getContentsMargins(&left, &top, &right, &bottom); + view.resize(200 + left + right, 200 + top + bottom); + toplevel.show(); + QTest::qWaitForWindowShown(&toplevel); + + + QApplication::setActiveWindow(&toplevel); + QApplication::processEvents(); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&toplevel)); + + const QRect viewportRect = view.viewport()->rect(); + QCOMPARE(viewportRect, QRect(0, 0, 200, 200)); + +#if defined QT_BUILD_INTERNAL + const bool intersects = updateRect.intersects(viewportRect); + QGraphicsViewPrivate *viewPrivate = static_cast<QGraphicsViewPrivate *>(qt_widget_private(&view)); + QTRY_COMPARE(viewPrivate->updateRect(updateRect), intersects); + QApplication::processEvents(); + + view.lastUpdateRegions.clear(); + viewPrivate->processPendingUpdates(); + QVERIFY(viewPrivate->dirtyRegion.isEmpty()); + QVERIFY(viewPrivate->dirtyBoundingRect.isEmpty()); + QApplication::processEvents(); + if (!intersects) { + QTRY_VERIFY(view.lastUpdateRegions.isEmpty()); + } else { + QTRY_COMPARE(view.lastUpdateRegions.size(), 1); + QTRY_COMPARE(view.lastUpdateRegions.at(0), QRegion(updateRect) & viewportRect); + } + QTRY_VERIFY(!viewPrivate->fullUpdatePending); +#endif +} + +void tst_QGraphicsView::update2_data() +{ + QTest::addColumn<qreal>("penWidth"); + QTest::addColumn<bool>("antialiasing"); + QTest::addColumn<bool>("changedConnected"); + + // Anti-aliased. + QTest::newRow("pen width: 0.0, antialiasing: true") << qreal(0.0) << true << false; + QTest::newRow("pen width: 1.5, antialiasing: true") << qreal(1.5) << true << false; + QTest::newRow("pen width: 2.0, antialiasing: true") << qreal(2.0) << true << false; + QTest::newRow("pen width: 3.0, antialiasing: true") << qreal(3.0) << true << false; + + // Aliased. + QTest::newRow("pen width: 0.0, antialiasing: false") << qreal(0.0) << false << false; + QTest::newRow("pen width: 1.5, antialiasing: false") << qreal(1.5) << false << false; + QTest::newRow("pen width: 2.0, antialiasing: false") << qreal(2.0) << false << false; + QTest::newRow("pen width: 3.0, antialiasing: false") << qreal(3.0) << false << false; + + // changed() connected + QTest::newRow("pen width: 0.0, antialiasing: false, changed") << qreal(0.0) << false << true; + QTest::newRow("pen width: 1.5, antialiasing: true, changed") << qreal(1.5) << true << true; + QTest::newRow("pen width: 2.0, antialiasing: false, changed") << qreal(2.0) << false << true; + QTest::newRow("pen width: 3.0, antialiasing: true, changed") << qreal(3.0) << true << true; +} + +void tst_QGraphicsView::update2() +{ + QFETCH(qreal, penWidth); + QFETCH(bool, antialiasing); + QFETCH(bool, changedConnected); + + // Create a rect item. + const QRectF rawItemRect(-50.4, -50.3, 100.2, 100.1); + CountPaintItem *rect = new CountPaintItem(rawItemRect); + QPen pen; + pen.setWidthF(penWidth); + rect->setPen(pen); + + // Add item to a scene. + QGraphicsScene scene(-100, -100, 200, 200); + if (changedConnected) + QObject::connect(&scene, SIGNAL(changed(QList<QRectF>)), this, SLOT(dummySlot())); + + scene.addItem(rect); + + // Create a view on the scene. + CustomView view(&scene); + view.setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, !antialiasing); + view.setRenderHint(QPainter::Antialiasing, antialiasing); + view.setFrameStyle(0); + view.resize(200, 200); + view.show(); + QTest::qWaitForWindowShown(&view) ; + QTRY_VERIFY(rect->numPaints > 0); + + // Calculate expected update region for the rect. + QRectF expectedItemBoundingRect = rawItemRect; + const qreal halfPenWidth = penWidth / qreal(2.0); + expectedItemBoundingRect.adjust(-halfPenWidth, -halfPenWidth, halfPenWidth, halfPenWidth); + QCOMPARE(rect->boundingRect(), expectedItemBoundingRect); + + QRect expectedItemDeviceBoundingRect = rect->deviceTransform(view.viewportTransform()) + .mapRect(expectedItemBoundingRect).toAlignedRect(); + if (antialiasing) + expectedItemDeviceBoundingRect.adjust(-2, -2, 2, 2); + else + expectedItemDeviceBoundingRect.adjust(-1, -1, 1, 1); + const QRegion expectedUpdateRegion(expectedItemDeviceBoundingRect); + + // Reset. + rect->numPaints = 0; + view.lastUpdateRegions.clear(); + view.painted = false; + + rect->update(); + QTRY_VERIFY(view.painted); + +#ifndef Q_OS_MAC //cocoa doesn't support drawing regions + QTRY_VERIFY(view.painted); + QCOMPARE(view.lastUpdateRegions.size(), 1); + QCOMPARE(view.lastUpdateRegions.at(0), expectedUpdateRegion); +#endif +} + +void tst_QGraphicsView::update_ancestorClipsChildrenToShape() +{ + QGraphicsScene scene(-150, -150, 300, 300); + + /* + Add three rects: + + +------------------+ + | child | + | +--------------+ | + | | parent | | + | | +-----------+ | + | | |grandParent| | + | | +-----------+ | + | +--------------+ | + +------------------+ + + ... where both the parent and the grand parent clips children to shape. + */ + QApplication::processEvents(); // Get rid of pending update. + + QGraphicsRectItem *grandParent = static_cast<QGraphicsRectItem *>(scene.addRect(0, 0, 50, 50)); + grandParent->setBrush(Qt::black); + grandParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + QGraphicsRectItem *parent = static_cast<QGraphicsRectItem *>(scene.addRect(-50, -50, 100, 100)); + parent->setBrush(QColor(0, 0, 255, 125)); + parent->setParentItem(grandParent); + parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + QGraphicsRectItem *child = static_cast<QGraphicsRectItem *>(scene.addRect(-100, -100, 200, 200)); + child->setBrush(QColor(255, 0, 0, 125)); + child->setParentItem(parent); + + CustomView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.painted); + + view.lastUpdateRegions.clear(); + view.painted = false; + + // Call child->update() and make sure the updated area is within the ancestors' clip. + QRectF expected = child->deviceTransform(view.viewportTransform()).mapRect(child->boundingRect()); + expected &= grandParent->deviceTransform(view.viewportTransform()).mapRect(grandParent->boundingRect()); + + child->update(); + QTRY_VERIFY(view.painted); + +#ifndef Q_OS_MAC //cocoa doesn't support drawing regions + QTRY_VERIFY(view.painted); + QCOMPARE(view.lastUpdateRegions.size(), 1); + QCOMPARE(view.lastUpdateRegions.at(0), QRegion(expected.toAlignedRect())); +#endif +} + +void tst_QGraphicsView::update_ancestorClipsChildrenToShape2() +{ + QGraphicsScene scene(-150, -150, 300, 300); + + /* + Add two rects: + + +------------------+ + | child | + | +--------------+ | + | | parent | | + | | | | + | | | | + | | | | + | +--------------+ | + +------------------+ + + ... where the parent has no contents and clips the child to shape. + */ + QApplication::processEvents(); // Get rid of pending update. + + QGraphicsRectItem *parent = static_cast<QGraphicsRectItem *>(scene.addRect(-50, -50, 100, 100)); + parent->setBrush(QColor(0, 0, 255, 125)); + parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + parent->setFlag(QGraphicsItem::ItemHasNoContents); + + QGraphicsRectItem *child = static_cast<QGraphicsRectItem *>(scene.addRect(-100, -100, 200, 200)); + child->setBrush(QColor(255, 0, 0, 125)); + child->setParentItem(parent); + + CustomView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.painted); + + view.lastUpdateRegions.clear(); + view.painted = false; + + // Call child->update() and make sure the updated area is within its parent's clip. + QRectF expected = child->deviceTransform(view.viewportTransform()).mapRect(child->boundingRect()); + expected &= parent->deviceTransform(view.viewportTransform()).mapRect(parent->boundingRect()); + + child->update(); + QTRY_VERIFY(view.painted); + +#ifndef Q_OS_MAC //cocoa doesn't support drawing regions + QTRY_VERIFY(view.painted); + QCOMPARE(view.lastUpdateRegions.size(), 1); + QCOMPARE(view.lastUpdateRegions.at(0), QRegion(expected.toAlignedRect())); +#endif + + QTest::qWait(50); + + view.lastUpdateRegions.clear(); + view.painted = false; + + // Invalidate the parent's geometry and trigger an update. + // The update area should be clipped to the parent's bounding rect for 'normal' items, + // but in this case the item has no contents (ItemHasNoContents) and its geometry + // is invalidated, which means we cannot clip the child update. So, the expected + // area is exactly the same as the child's bounding rect (adjusted for antialiasing). + parent->setRect(parent->rect().adjusted(-10, -10, -10, -10)); + expected = child->deviceTransform(view.viewportTransform()).mapRect(child->boundingRect()); + expected.adjust(-2, -2, 2, 2); // Antialiasing + +#ifndef Q_OS_MAC //cocoa doesn't support drawing regions + QTRY_VERIFY(view.painted); + QCOMPARE(view.lastUpdateRegions.size(), 1); + QCOMPARE(view.lastUpdateRegions.at(0), QRegion(expected.toAlignedRect())); +#endif +} + +class FocusItem : public QGraphicsRectItem +{ +public: + FocusItem() : QGraphicsRectItem(0, 0, 20, 20) { + m_viewHasIMEnabledInFocusInEvent = false; + } + + void focusInEvent(QFocusEvent * /* event */) + { + QGraphicsView *view = scene()->views().first(); + m_viewHasIMEnabledInFocusInEvent = view->testAttribute(Qt::WA_InputMethodEnabled); + } + + bool m_viewHasIMEnabledInFocusInEvent; +}; + +void tst_QGraphicsView::inputMethodSensitivity() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + FocusItem *item = new FocusItem; + + view.setAttribute(Qt::WA_InputMethodEnabled, true); + + scene.addItem(item); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + + scene.removeItem(item); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + + item->setFlag(QGraphicsItem::ItemAcceptsInputMethod); + scene.addItem(item); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + + scene.removeItem(item); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + + scene.addItem(item); + scene.setFocusItem(item); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + + scene.removeItem(item); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + + item->setFlag(QGraphicsItem::ItemIsFocusable); + scene.addItem(item); + scene.setFocusItem(item); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item)); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), true); + QCOMPARE(item->m_viewHasIMEnabledInFocusInEvent, true); + + item->setFlag(QGraphicsItem::ItemAcceptsInputMethod, false); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + + item->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), true); + + // introduce another item that is focusable but does not accept input methods + FocusItem *item2 = new FocusItem; + item2->setFlag(QGraphicsItem::ItemIsFocusable); + scene.addItem(item2); + scene.setFocusItem(item2); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + QCOMPARE(item2->m_viewHasIMEnabledInFocusInEvent, false); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item2)); + + scene.setFocusItem(item); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), true); + QCOMPARE(item->m_viewHasIMEnabledInFocusInEvent, true); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item)); + + view.setScene(0); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item)); + + view.setScene(&scene); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), true); + QCOMPARE(item->m_viewHasIMEnabledInFocusInEvent, true); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item)); + + scene.setFocusItem(item2); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + QCOMPARE(item2->m_viewHasIMEnabledInFocusInEvent, false); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item2)); + + view.setScene(0); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item2)); + + scene.setFocusItem(item); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), false); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item)); + + view.setScene(&scene); + QCOMPARE(view.testAttribute(Qt::WA_InputMethodEnabled), true); + QCOMPARE(item->m_viewHasIMEnabledInFocusInEvent, true); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(item)); +} + +class InputContextTester : public QInputContext +{ + Q_OBJECT +public: + QString identifierName() { return QString(); } + bool isComposing() const { return false; } + QString language() { return QString(); } + void reset() { ++resets; } + int resets; +}; + +void tst_QGraphicsView::inputContextReset() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled)); + + InputContextTester *inputContext = new InputContextTester; + qApp->setInputContext(inputContext); + + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + QGraphicsItem *item1 = new QGraphicsRectItem; + item1->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemAcceptsInputMethod); + + inputContext->resets = 0; + scene.addItem(item1); + QCOMPARE(inputContext->resets, 0); + + inputContext->resets = 0; + scene.setFocusItem(item1); + QCOMPARE(scene.focusItem(), (QGraphicsItem *)item1); + QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled)); + QCOMPARE(inputContext->resets, 0); + + inputContext->resets = 0; + scene.setFocusItem(0); + // the input context is reset twice, once because an item has lost focus and again because + // the Qt::WA_InputMethodEnabled flag is cleared because no item has focus. + QCOMPARE(inputContext->resets, 2); + + // introduce another item that is focusable but does not accept input methods + QGraphicsItem *item2 = new QGraphicsRectItem; + item2->setFlags(QGraphicsItem::ItemIsFocusable); + scene.addItem(item2); + + inputContext->resets = 0; + scene.setFocusItem(item2); + QCOMPARE(inputContext->resets, 0); + + inputContext->resets = 0; + scene.setFocusItem(item1); + QCOMPARE(inputContext->resets, 0); + + // test changing between between items that accept input methods. + item2->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemAcceptsInputMethod); + scene.setFocusItem(item2); + QCOMPARE(inputContext->resets, 1); +} + +void tst_QGraphicsView::indirectPainting() +{ + class MyScene : public QGraphicsScene + { public: + MyScene() : QGraphicsScene(), drawCount(0) {} + void drawItems(QPainter *, int, QGraphicsItem **, const QStyleOptionGraphicsItem *, QWidget *) + { ++drawCount; } + int drawCount; + }; + + MyScene scene; + QGraphicsItem *item = scene.addRect(0, 0, 50, 50); + + QGraphicsView view(&scene); + view.setOptimizationFlag(QGraphicsView::IndirectPainting); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(100); + + scene.drawCount = 0; + item->setPos(20, 20); + QApplication::processEvents(); + QTRY_VERIFY(scene.drawCount > 0); +} + +void tst_QGraphicsView::compositionModeInDrawBackground() +{ + class MyView : public QGraphicsView + { public: + MyView(QGraphicsScene *scene) : QGraphicsView(scene), + painted(false), compositionMode(QPainter::CompositionMode_SourceOver) {} + bool painted; + QPainter::CompositionMode compositionMode; + void drawBackground(QPainter *painter, const QRectF &) + { + compositionMode = painter->compositionMode(); + painted = true; + } + }; + + QGraphicsScene dummy; + MyView view(&dummy); + view.show(); + QTest::qWaitForWindowShown(&view); + + // Make sure the painter's composition mode is SourceOver in drawBackground. + QTRY_VERIFY(view.painted); + QCOMPARE(view.compositionMode, QPainter::CompositionMode_SourceOver); + + view.painted = false; + view.setCacheMode(QGraphicsView::CacheBackground); + view.viewport()->update(); + + // Make sure the painter's composition mode is SourceOver in drawBackground + // with background cache enabled. + QTRY_VERIFY(view.painted); + QCOMPARE(view.compositionMode, QPainter::CompositionMode_SourceOver); +} +void tst_QGraphicsView::task253415_reconnectUpdateSceneOnSceneChanged() +{ + QGraphicsView view; + QGraphicsView dummyView; + view.setWindowFlags(view.windowFlags() | Qt::WindowStaysOnTopHint); + view.resize(200, 200); + + QGraphicsScene scene1; + QObject::connect(&scene1, SIGNAL(changed(QList<QRectF>)), &dummyView, SLOT(updateScene(QList<QRectF>))); + view.setScene(&scene1); + + QTest::qWait(12); + + QGraphicsScene scene2; + QObject::connect(&scene2, SIGNAL(changed(QList<QRectF>)), &dummyView, SLOT(updateScene(QList<QRectF>))); + view.setScene(&scene2); + + QTest::qWait(12); + + bool wasConnected2 = QObject::disconnect(&scene2, SIGNAL(changed(QList<QRectF>)), &view, 0); + QVERIFY(wasConnected2); +} + +// Qt/CE does not implement mouse tracking at this point. +#ifndef Q_OS_WINCE +void tst_QGraphicsView::task255529_transformationAnchorMouseAndViewportMargins() +{ + QGraphicsScene scene(-100, -100, 200, 200); + scene.addRect(QRectF(-50, -50, 100, 100), QPen(Qt::black), QBrush(Qt::blue)); + + class VpGraphicsView: public QGraphicsView + { + public: + VpGraphicsView(QGraphicsScene *scene, QWidget *parent=0) + : QGraphicsView(scene, parent) + { + setViewportMargins(8, 16, 12, 20); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + setMouseTracking(true); + } + }; + + VpGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + view.show(); + + QTest::qWaitForWindowShown(&view); + QTest::qWait(50); + QPoint mouseViewPos(20, 20); + sendMouseMove(view.viewport(), mouseViewPos); + + QPointF mouseScenePos = view.mapToScene(mouseViewPos); + view.setTransform(QTransform().scale(5, 5).rotate(5, Qt::ZAxis), true); + + QPointF newMouseScenePos = view.mapToScene(mouseViewPos); + + qreal slack = 1; + QVERIFY(qAbs(newMouseScenePos.x() - mouseScenePos.x()) < slack); + QVERIFY(qAbs(newMouseScenePos.y() - mouseScenePos.y()) < slack); +} +#endif + +void tst_QGraphicsView::task259503_scrollingArtifacts() +{ + QGraphicsScene scene(0, 0, 800, 600); + + QGraphicsRectItem card; + card.setRect(0, 0, 50, 50); + card.setPen(QPen(Qt::darkRed)); + card.setBrush(QBrush(Qt::cyan)); + card.setZValue(2.0); + card.setPos(300, 300); + scene.addItem(&card); + + class SAGraphicsView: public QGraphicsView + { + public: + SAGraphicsView(QGraphicsScene *scene) + : QGraphicsView(scene) + , itSTimeToTest(false) + { + setViewportUpdateMode( QGraphicsView::MinimalViewportUpdate ); + resize(QSize(640, 480)); + } + + QRegion updateRegion; + bool itSTimeToTest; + + void paintEvent(QPaintEvent *event) + { + QGraphicsView::paintEvent(event); + + if (itSTimeToTest) + { +// qDebug() << event->region(); +// qDebug() << updateRegion; + QEXPECT_FAIL("", "The event region doesn't include the original item position region. See QTBUG-4416", Continue); + QCOMPARE(event->region(), updateRegion); + } + } + }; + + SAGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + int hsbValue = view.horizontalScrollBar()->value(); + view.horizontalScrollBar()->setValue(hsbValue / 2); + QTest::qWait(10); + view.horizontalScrollBar()->setValue(0); + QTest::qWait(10); + + QRect itemDeviceBoundingRect = card.deviceTransform(view.viewportTransform()).mapRect(card.boundingRect()).toRect(); + itemDeviceBoundingRect.adjust(-2, -2, 2, 2); + view.updateRegion = itemDeviceBoundingRect; + view.updateRegion += itemDeviceBoundingRect.translated(-100, 0); + view.itSTimeToTest = true; + card.setPos(200, 300); + QTest::qWait(10); +} + +void tst_QGraphicsView::QTBUG_4151_clipAndIgnore_data() +{ + QTest::addColumn<bool>("clip"); + QTest::addColumn<bool>("ignoreTransformations"); + QTest::addColumn<int>("numItems"); + + QTest::newRow("none") << false << false << 3; + QTest::newRow("clip") << true << false << 3; + QTest::newRow("ignore") << false << true << 3; + QTest::newRow("clip+ignore") << true << true << 3; +} + +void tst_QGraphicsView::QTBUG_4151_clipAndIgnore() +{ + QFETCH(bool, clip); + QFETCH(bool, ignoreTransformations); + QFETCH(int, numItems); + + QGraphicsScene scene; + + QGraphicsRectItem *parent = new QGraphicsRectItem(QRectF(0, 0, 50, 50), 0); + QGraphicsRectItem *child = new QGraphicsRectItem(QRectF(-10, -10, 40, 40), parent); + QGraphicsRectItem *ignore = new QGraphicsRectItem(QRectF(60, 60, 50, 50), 0); + + if (clip) + parent->setFlags(QGraphicsItem::ItemClipsChildrenToShape); + if (ignoreTransformations) + ignore->setFlag(QGraphicsItem::ItemIgnoresTransformations); + + parent->setBrush(Qt::red); + child->setBrush(QColor(0, 0, 255, 128)); + ignore->setBrush(Qt::green); + + scene.addItem(parent); + scene.addItem(ignore); + + QGraphicsView view(&scene); + view.setFrameStyle(0); + view.resize(75, 75); + view.show(); + QTest::qWaitForWindowShown(&view); + view.activateWindow(); + + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&view); + + QCOMPARE(view.items(view.rect()).size(), numItems); +} + +void tst_QGraphicsView::QTBUG_5859_exposedRect() +{ + class CustomScene : public QGraphicsScene + { + public: + CustomScene(const QRectF &rect) : QGraphicsScene(rect) { } + void drawBackground(QPainter * /* painter */, const QRectF &rect) + { lastBackgroundExposedRect = rect; } + QRectF lastBackgroundExposedRect; + }; + + class CustomRectItem : public QGraphicsRectItem + { + public: + CustomRectItem(const QRectF &rect) : QGraphicsRectItem(rect) + { setFlag(QGraphicsItem::ItemUsesExtendedStyleOption); } + void paint(QPainter * /* painter */, const QStyleOptionGraphicsItem *option, QWidget * /* widget */ = 0) + { lastExposedRect = option->exposedRect; } + QRectF lastExposedRect; + }; + + CustomScene scene(QRectF(0,0,50,50)); + + CustomRectItem item(scene.sceneRect()); + + scene.addItem(&item); + + QGraphicsView view(&scene); + if (PlatformQuirks::isAutoMaximizing()) + view.setWindowFlags(view.windowFlags()|Qt::X11BypassWindowManagerHint); + view.scale(4.15, 4.15); + view.show(); + QTest::qWaitForWindowShown(&view); + + view.viewport()->repaint(10,10,20,20); + QApplication::processEvents(); + + QCOMPARE(item.lastExposedRect, scene.lastBackgroundExposedRect); +} + +// Qt/CE does not have regular cursor support. +#if !defined(QT_NO_CURSOR) && !defined(Q_OS_WINCE) +void tst_QGraphicsView::QTBUG_7438_cursor() +{ + QGraphicsScene scene; + QGraphicsItem *item = scene.addRect(QRectF(-10, -10, 20, 20)); + item->setFlag(QGraphicsItem::ItemIsMovable); + + QGraphicsView view(&scene); + view.setFixedSize(400, 400); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(view.viewport()->cursor().shape(), QCursor().shape()); + view.viewport()->setCursor(Qt::PointingHandCursor); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + sendMouseMove(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + sendMousePress(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); + sendMouseRelease(view.viewport(), view.mapFromScene(0, 0)); + QCOMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor); +} +#endif + +class GraphicsItemWithHover : public QGraphicsRectItem +{ +public: + GraphicsItemWithHover() + : receivedEnterEvent(false), receivedLeaveEvent(false), + enterWidget(0), leaveWidget(0) + { + setRect(0, 0, 100, 100); + setAcceptHoverEvents(true); + } + + bool sceneEvent(QEvent *event) + { + if (event->type() == QEvent::GraphicsSceneHoverEnter) { + receivedEnterEvent = true; + enterWidget = static_cast<QGraphicsSceneHoverEvent *>(event)->widget(); + } else if (event->type() == QEvent::GraphicsSceneHoverLeave) { + receivedLeaveEvent = true; + leaveWidget = static_cast<QGraphicsSceneHoverEvent *>(event)->widget(); + } + return QGraphicsRectItem::sceneEvent(event); + } + + bool receivedEnterEvent; + bool receivedLeaveEvent; + QWidget *enterWidget; + QWidget *leaveWidget; +}; + +void tst_QGraphicsView::hoverLeave() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + GraphicsItemWithHover *item = new GraphicsItemWithHover; + scene.addItem(item); + + // move the cursor out of the way + QCursor::setPos(1,1); + + view.show(); + QTest::qWaitForWindowShown(&view); + + QPoint pos = view.viewport()->mapToGlobal(view.mapFromScene(item->mapToScene(10, 10))); + QCursor::setPos(pos); + QTest::qWait(200); + QVERIFY(item->receivedEnterEvent); + QCOMPARE(item->enterWidget, view.viewport()); + + QCursor::setPos(1,1); + QTest::qWait(200); + QVERIFY(item->receivedLeaveEvent); + QCOMPARE(item->leaveWidget, view.viewport()); +} + +class IMItem : public QGraphicsRectItem +{ +public: + IMItem(QGraphicsItem *parent = 0): + QGraphicsRectItem(QRectF(0, 0, 20, 20), parent) + { + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); + } + + QVariant inputMethodQuery(Qt::InputMethodQuery) const + { + return mf; + } + + static QRectF mf; +}; + +QRectF IMItem::mf(1.5, 1.6, 10, 10); + +void tst_QGraphicsView::QTBUG_16063_microFocusRect() +{ + QGraphicsScene scene; + IMItem *item = new IMItem(); + scene.addItem(item); + + QGraphicsView view(&scene); + + view.setFixedSize(40, 40); + view.show(); + QTest::qWaitForWindowShown(&view); + + scene.setFocusItem(item); + view.setFocus(); + QRectF mfv = view.inputMethodQuery(Qt::ImMicroFocus).toRectF(); + QCOMPARE(mfv, IMItem::mf.translated(-view.mapToScene(view.sceneRect().toRect()).boundingRect().topLeft())); +} + +QTEST_MAIN(tst_QGraphicsView) +#include "tst_qgraphicsview.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp new file mode 100644 index 0000000000..122ffbc875 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp @@ -0,0 +1,976 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest/QtTest> +#include <QSize> +#include <QRectF> +#include <QTransform> + +#ifdef Q_OS_WINCE +#include <qguifunctions_wince.h> + +bool qt_wince_is_high_dpi() { + HDC deviceContext = GetDC(0); + int dpi = GetDeviceCaps(deviceContext, LOGPIXELSX); + ReleaseDC(0, deviceContext); + if ((dpi < 1000) && (dpi > 0)) + return dpi > 96; + else + return false; +} +#endif + +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<QRectF>) +Q_DECLARE_METATYPE(QMatrix) +Q_DECLARE_METATYPE(QPainterPath) +Q_DECLARE_METATYPE(QPointF) +Q_DECLARE_METATYPE(QRectF) +Q_DECLARE_METATYPE(Qt::ScrollBarPolicy) + +static void _scrollBarRanges_data_1(int offset) +{ + // No motif, flat frame + QTest::newRow("1") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << 0 << 0 << 0 << false << false; + QTest::newRow("2") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset) << 0 << offset << false << false; + QTest::newRow("3") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset) << 0 << (100 + offset) << false << false; + QTest::newRow("4") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << 0 << 0 << 0 << false << false; + QTest::newRow("5") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -100 << (offset -50) << -100 << (-100 + offset) << false << false; + QTest::newRow("6") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -100 << (offset -50) << -100 << offset << false << false; + QTest::newRow("7") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (offset + 1) << 0 << offset + 1 << false << false; + QTest::newRow("8") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 1) << 0 << offset + 1 << false << false; + QTest::newRow("9") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 1) << 0 << (100 + offset + 1) << false << false; + QTest::newRow("10") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -101 << (-100 + offset) << -101 << (-100 + offset) << false << false; + QTest::newRow("11") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-101) << (offset + -50) << -101 << (-100 + offset) << false << false; + QTest::newRow("12") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-101) << (offset -50) << (-101) << offset << false << false; + QTest::newRow("13") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (offset + 16) << 0 << (offset + 16) << false << false; + QTest::newRow("14") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 16) << 0 << (offset + 16) << false << false; + QTest::newRow("15") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 16) << 0 << (100 + offset + 16) << false << false; + QTest::newRow("16") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (-100 + offset) << (-100 - 16 ) << (-100 + offset) << false << false; + QTest::newRow("17") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (offset -50) << (-100 - 16) << (-100 + offset) << false << false; + QTest::newRow("18") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (offset -50) << (-100 - 16) << offset << false << false; + QTest::newRow("1 x2") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (150 + offset) << 0 << (100 + offset) << false << false; + QTest::newRow("2 x2") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (250 + offset) << 0 << (100 + offset) << false << false; + QTest::newRow("3 x2") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (250 + offset) << 0 << (300 + offset) << false << false; + QTest::newRow("4 x2") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (-50 + offset) << -200 << (-100 + offset) << false << false; + QTest::newRow("5 x2") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (50 + offset) << -200 << (-100 + offset) << false << false; + QTest::newRow("6 x2") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (50 + offset) << -200 << (100 + offset) << false << false; + QTest::newRow("1 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 0 << 0 << 0 << false << false; + QTest::newRow("2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 50 << 0 << 0 << false << false; + QTest::newRow("3 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 50 << 0 << 100 << false << false; + QTest::newRow("4 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 0 << 0 << 0 << false << false; + QTest::newRow("5 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -100 << -50 << 0 << 0 << false << false; + QTest::newRow("6 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -100 << -50 << -100 << 0 << false << false; + QTest::newRow("7 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 1 << 0 << 1 << false << false; + QTest::newRow("8 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 51 << 0 << 1 << false << false; + QTest::newRow("9 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 51 << 0 << 101 << false << false; + QTest::newRow("10 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -100 << -101 << -100 << false << false; + QTest::newRow("11 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -50 << -101 << -100 << false << false; + QTest::newRow("12 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -50 << -101 << 0 << false << false; + QTest::newRow("13 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 16 << 0 << 16 << false << false; + QTest::newRow("14 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << (50 + 16) << 0 << 16 << false << false; + QTest::newRow("15 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << (50 + 16) << 0 << (100 + 16) << false << false; + QTest::newRow("16 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -100 << (-100 - 16) << -100 << false << false; + QTest::newRow("17 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -50 << (-100 - 16) << -100 << false << false; + QTest::newRow("18 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -50 << (-100 - 16) << 0 << false << false; + QTest::newRow("1 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 150 << 0 << 100 << false << false; + QTest::newRow("2 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 250 << 0 << 100 << false << false; + QTest::newRow("3 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 250 << 0 << 300 << false << false; + QTest::newRow("4 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << -50 << -200 << -100 << false << false; + QTest::newRow("5 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << 50 << -200 << -100 << false << false; + QTest::newRow("6 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << 50 << -200 << 100 << false << false; + QTest::newRow("1 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 16 << 0 << 16 << false << false; + QTest::newRow("2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 16) << 0 << 16 << false << false; + QTest::newRow("3 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 16) << 0 << (100 + 16) << false << false; + QTest::newRow("4 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (-100 + 16) << -100 << (-100 + 16) << false << false; + QTest::newRow("5 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (16-50) << -100 << (-100 + 16) << false << false; + QTest::newRow("6 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (16-50) << -100 << 16 << false << false; + QTest::newRow("7 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 17 << 0 << 17 << false << false; + QTest::newRow("8 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (17+50) << 0 << 17 << false << false; + QTest::newRow("9 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 67 << 0 << 117 << false << false; + QTest::newRow("10 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (-100 + 16) << -101 << (-100 + 16) << false << false; + QTest::newRow("11 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (16-50) << -101 << (-100 + 16) << false << false; + QTest::newRow("12 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (16-50) << -101 << 16 << false << false; + QTest::newRow("13 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 32 << 0 << 32 << false << false; + QTest::newRow("14 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 32) << 0 << 32 << false << false; + QTest::newRow("15 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 32) << 0 << (100 + 32) << false << false; + QTest::newRow("16 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (-100 + 16) << (-100 - 16) << (-100 + 16) << false << false; + QTest::newRow("17 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (16-50) << (-100 - 16) << (-100 + 16) << false << false; + QTest::newRow("18 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (16-50) << (-100 - 16) << 16 << false << false; + QTest::newRow("1 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (150 + 16) << 0 << (100 + 16) << false << false; + QTest::newRow("2 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (250 + 16) << 0 << (100 + 16) << false << false; + QTest::newRow("3 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (250 + 16) << 0 << (300 + 16) << false << false; + QTest::newRow("4 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (-50 + 16) << -200 << (-100 + 16) << false << false; + QTest::newRow("5 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (50 + 16) << -200 << (-100 + 16) << false << false; + QTest::newRow("6 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (50 + 16) << -200 << (100 + 16) << false << false; +} + +static void _scrollBarRanges_data_2(int offset) +{ + // Motif, flat frame + QTest::newRow("Motif, 1") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << 0 << 0 << 0 << true << false; + QTest::newRow("Motif, 2") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset) << 0 << offset << true << false; + QTest::newRow("Motif, 3") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset) << 0 << (100 + offset) << true << false; + QTest::newRow("Motif, 4") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << 0 << 0 << 0 << true << false; + QTest::newRow("Motif, 5") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -100 << (offset-50) << -100 << (-100 + offset) << true << false; + QTest::newRow("Motif, 6") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -100 << (offset-50) << -100 << offset << true << false; + QTest::newRow("Motif, 7") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << offset + 1 << 0 << offset + 1 << true << false; + QTest::newRow("Motif, 8") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 1) << 0 << offset + 1 << true << false; + QTest::newRow("Motif, 9") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 1) << 0 << (100 + offset + 1) << true << false; + QTest::newRow("Motif, 10") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -101 << (-100 + offset) << -101 << (-100 + offset) << true << false; + QTest::newRow("Motif, 11") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-101) << (offset-50) << -101 << (-100 + offset) << true << false; + QTest::newRow("Motif, 12") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-101) << (offset-50) << (-101) << offset << true << false; + QTest::newRow("Motif, 13") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (offset + 16) << 0 << (offset + 16) << true << false; + QTest::newRow("Motif, 14") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 16) << 0 << (offset + 16) << true << false; + QTest::newRow("Motif, 15") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 16) << 0 << (100 + offset + 16) << true << false; + QTest::newRow("Motif, 16") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (-100 + offset) << (-100 - 16) << (-100 + offset) << true << false; + QTest::newRow("Motif, 17") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (offset-50) << (-100 - 16) << (-100 + offset) << true << false; + QTest::newRow("Motif, 18") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (offset-50) << (-100 - 16) << offset << true << false; + QTest::newRow("Motif, 1 x2") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (150 + offset) << 0 << (100 + offset) << true << false; + QTest::newRow("Motif, 2 x2") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (250 + offset) << 0 << (100 + offset) << true << false; + QTest::newRow("Motif, 3 x2") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (250 + offset) << 0 << (300 + offset) << true << false; + QTest::newRow("Motif, 4 x2") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (-50 + offset) << -200 << (-100 + offset) << true << false; + QTest::newRow("Motif, 5 x2") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (50 + offset) << -200 << (-100 + offset) << true << false; + QTest::newRow("Motif, 6 x2") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (50 + offset) << -200 << (100 + offset) << true << false; + QTest::newRow("Motif, 1 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 0 << 0 << 0 << true << false; + QTest::newRow("Motif, 2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 50 << 0 << 0 << true << false; + QTest::newRow("Motif, 3 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 50 << 0 << 100 << true << false; + QTest::newRow("Motif, 4 No ScrollBars") << QSize(100, 100) << QRectF(-100, -100, 100, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 0 << 0 << 0 << true << false; + QTest::newRow("Motif, 5 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -100 << -50 << 0 << 0 << true << false; + QTest::newRow("Motif, 6 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -100 << -50 << -100 << 0 << true << false; + QTest::newRow("Motif, 7 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 1 << 0 << 1 << true << false; + QTest::newRow("Motif, 8 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 51 << 0 << 1 << true << false; + QTest::newRow("Motif, 9 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 51 << 0 << 101 << true << false; + QTest::newRow("Motif, 10 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -100 << -101 << -100 << true << false; + QTest::newRow("Motif, 11 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -50 << -101 << -100 << true << false; + QTest::newRow("Motif, 12 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -50 << -101 << 0 << true << false; + QTest::newRow("Motif, 13 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 16 << 0 << 16 << true << false; + QTest::newRow("Motif, 14 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << (50 + 16) << 0 << 16 << true << false; + QTest::newRow("Motif, 15 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << (50 + 16) << 0 << (100 + 16) << true << false; + QTest::newRow("Motif, 16 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -100 << (-100 - 16) << -100 << true << false; + QTest::newRow("Motif, 17 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -50 << (-100 - 16) << -100 << true << false; + QTest::newRow("Motif, 18 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -50 << (-100 - 16) << 0 << true << false; + QTest::newRow("Motif, 1 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 150 << 0 << 100 << true << false; + QTest::newRow("Motif, 2 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 250 << 0 << 100 << true << false; + QTest::newRow("Motif, 3 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 250 << 0 << 300 << true << false; + QTest::newRow("Motif, 4 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << -50 << -200 << -100 << true << false; + QTest::newRow("Motif, 5 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << 50 << -200 << -100 << true << false; + QTest::newRow("Motif, 6 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << 50 << -200 << 100 << true << false; + QTest::newRow("Motif, 1 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 16 << 0 << 16 << true << false; + QTest::newRow("Motif, 2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 16) << 0 << 16 << true << false; + QTest::newRow("Motif, 3 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 16) << 0 << (100 + 16) << true << false; + QTest::newRow("Motif, 4 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (-100 + 16) << -100 << (-100 + 16) << true << false; + QTest::newRow("Motif, 5 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (16-50) << -100 << (-100 + 16) << true << false; + QTest::newRow("Motif, 6 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (16-50) << -100 << 16 << true << false; + QTest::newRow("Motif, 7 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 17 << 0 << 17 << true << false; + QTest::newRow("Motif, 8 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (117-50) << 0 << 17 << true << false; + QTest::newRow("Motif, 9 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (117-50) << 0 << 117 << true << false; + QTest::newRow("Motif, 10 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (-100 + 16) << -101 << (-100 + 16) << true << false; + QTest::newRow("Motif, 11 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (16-50) << -101 << (-100 + 16) << true << false; + QTest::newRow("Motif, 12 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (16-50) << -101 << 16 << true << false; + QTest::newRow("Motif, 13 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 32 << 0 << 32 << true << false; + QTest::newRow("Motif, 14 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 32) << 0 << 32 << true << false; + QTest::newRow("Motif, 15 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 32) << 0 << (100 + 32) << true << false; + QTest::newRow("Motif, 16 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (-100 + 16) << (-100 - 16) << (-100 + 16) << true << false; + QTest::newRow("Motif, 17 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (16-50) << (-100 - 16) << (-100 + 16) << true << false; + QTest::newRow("Motif, 18 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (16-50) << (-100 - 16) << 16 << true << false; + QTest::newRow("Motif, 1 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (150 + 16) << 0 << (100 + 16) << true << false; + QTest::newRow("Motif, 2 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (250 + 16) << 0 << (100 + 16) << true << false; + QTest::newRow("Motif, 3 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (250 + 16) << 0 << (300 + 16) << true << false; + QTest::newRow("Motif, 4 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (-50 + 16) << -200 << (-100 + 16) << true << false; + QTest::newRow("Motif, 5 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (50 + 16) << -200 << (-100 + 16) << true << false; + QTest::newRow("Motif, 6 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (50 + 16) << -200 << (100 + 16) << true << false; +} + +static void _scrollBarRanges_data_3(int offset) +{ + // No motif, styled panel + QTest::newRow("Styled, 1") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << 0 << 0 << 0 << false << true; + QTest::newRow("Styled, 2") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset) << 0 << offset << false << true; + QTest::newRow("Styled, 3") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset) << 0 << (100 + offset) << false << true; + QTest::newRow("Styled, 4") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << 0 << 0 << 0 << false << true; + QTest::newRow("Styled, 5") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -100 << (offset-50) << -100 << (-100 + offset) << false << true; + QTest::newRow("Styled, 6") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -100 << (offset-50) << -100 << offset << false << true; + QTest::newRow("Styled, 7") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << offset + 1 << 0 << offset + 1 << false << true; + QTest::newRow("Styled, 8") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 1) << 0 << offset + 1 << false << true; + QTest::newRow("Styled, 9") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 1) << 0 << (100 + offset + 1) << false << true; + QTest::newRow("Styled, 10") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -101 << (-100 + offset) << -101 << (-100 + offset) << false << true; + QTest::newRow("Styled, 11") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-101) << (offset-50) << -101 << (-100 + offset) << false << true; + QTest::newRow("Styled, 12") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-101) << (offset-50) << (-101) << offset << false << true; + QTest::newRow("Styled, 13") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (offset + 16) << 0 << (offset + 16) << false << true; + QTest::newRow("Styled, 14") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 16) << 0 << (offset + 16) << false << true; + QTest::newRow("Styled, 15") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 16) << 0 << (100 + offset + 16) << false << true; + QTest::newRow("Styled, 16") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (-100 + offset) << (-100 - 16) << (-100 + offset) << false << true; + QTest::newRow("Styled, 17") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (offset-50) << (-100 - 16) << (-100 + offset) << false << true; + QTest::newRow("Styled, 18") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (offset-50) << (-100 - 16) << offset << false << true; + QTest::newRow("Styled, 1 x2") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (150 + offset) << 0 << (100 + offset) << false << true; + QTest::newRow("Styled, 2 x2") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (250 + offset) << 0 << (100 + offset) << false << true; + QTest::newRow("Styled, 3 x2") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (250 + offset) << 0 << (300 + offset) << false << true; + QTest::newRow("Styled, 4 x2") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (-50 + offset) << -200 << (-100 + offset) << false << true; + QTest::newRow("Styled, 5 x2") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (50 + offset) << -200 << (-100 + offset) << false << true; + QTest::newRow("Styled, 6 x2") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (50 + offset) << -200 << (100 + offset) << false << true; + QTest::newRow("Styled, 1 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 0 << 0 << 0 << false << true; + QTest::newRow("Styled, 2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 50 << 0 << 0 << false << true; + QTest::newRow("Styled, 3 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 50 << 0 << 100 << false << true; + QTest::newRow("Styled, 4 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 0 << 0 << 0 << false << true; + QTest::newRow("Styled, 5 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -100 << -50 << 0 << 0 << false << true; + QTest::newRow("Styled, 6 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -100 << -50 << -100 << 0 << false << true; + QTest::newRow("Styled, 7 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 1 << 0 << 1 << false << true; + QTest::newRow("Styled, 8 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 51 << 0 << 1 << false << true; + QTest::newRow("Styled, 9 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 51 << 0 << 101 << false << true; + QTest::newRow("Styled, 10 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -100 << -101 << -100 << false << true; + QTest::newRow("Styled, 11 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -50 << -101 << -100 << false << true; + QTest::newRow("Styled, 12 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -50 << -101 << 0 << false << true; + QTest::newRow("Styled, 13 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 16 << 0 << 16 << false << true; + QTest::newRow("Styled, 14 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << (50 + 16) << 0 << 16 << false << true; + QTest::newRow("Styled, 15 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << (50 + 16) << 0 << (100 + 16) << false << true; + QTest::newRow("Styled, 16 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -100 << (-100 - 16) << -100 << false << true; + QTest::newRow("Styled, 17 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -50 << (-100 - 16) << -100 << false << true; + QTest::newRow("Styled, 18 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -50 << (-100 - 16) << 0 << false << true; + QTest::newRow("Styled, 1 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 150 << 0 << 100 << false << true; + QTest::newRow("Styled, 2 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 250 << 0 << 100 << false << true; + QTest::newRow("Styled, 3 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 250 << 0 << 300 << false << true; + QTest::newRow("Styled, 4 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << -50 << -200 << -100 << false << true; + QTest::newRow("Styled, 5 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << 50 << -200 << -100 << false << true; + QTest::newRow("Styled, 6 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << 50 << -200 << 100 << false << true; + QTest::newRow("Styled, 1 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 16 << 0 << 16 << false << true; + QTest::newRow("Styled, 2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 16) << 0 << 16 << false << true; + QTest::newRow("Styled, 3 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 16) << 0 << (100 + 16) << false << true; + QTest::newRow("Styled, 4 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (-100 + 16) << -100 << (-100 + 16) << false << true; + QTest::newRow("Styled, 5 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (16-50) << -100 << (-100 + 16) << false << true; + QTest::newRow("Styled, 6 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (16-50) << -100 << 16 << false << true; + QTest::newRow("Styled, 7 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 17 << 0 << 17 << false << true; + QTest::newRow("Styled, 8 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (117-50) << 0 << 17 << false << true; + QTest::newRow("Styled, 9 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (117-50) << 0 << 117 << false << true; + QTest::newRow("Styled, 10 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (-100 + 16) << -101 << (-100 + 16) << false << true; + QTest::newRow("Styled, 11 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (16-50) << -101 << (-100 + 16) << false << true; + QTest::newRow("Styled, 12 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (16-50) << -101 << 16 << false << true; + QTest::newRow("Styled, 13 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << 32 << 0 << 32 << false << true; + QTest::newRow("Styled, 14 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 32) << 0 << 32 << false << true; + QTest::newRow("Styled, 15 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 32) << 0 << (100 + 32) << false << true; + QTest::newRow("Styled, 16 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (-100 + 16) << (-100 - 16) << (-100 + 16) << false << true; + QTest::newRow("Styled, 17 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (16-50) << (-100 - 16) << (-100 + 16) << false << true; + QTest::newRow("Styled, 18 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (16-50) << (-100 - 16) << 16 << false << true; + QTest::newRow("Styled, 1 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (150 + 16) << 0 << (100 + 16) << false << true; + QTest::newRow("Styled, 2 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (250 + 16) << 0 << (100 + 16) << false << true; + QTest::newRow("Styled, 3 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (250 + 16) << 0 << (300 + 16) << false << true; + QTest::newRow("Styled, 4 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (-50 + 16) << -200 << (-100 + 16) << false << true; + QTest::newRow("Styled, 5 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (50 + 16) << -200 << (-100 + 16) << false << true; + QTest::newRow("Styled, 6 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (50 + 16) << -200 << (100 + 16) << false << true; +} + +static void _scrollBarRanges_data_4(int offset) +{ + // Motif, styled panel + QTest::newRow("Motif, Styled, 1") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << 0 << 0 << 0 << true << true; + QTest::newRow("Motif, Styled, 2") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 4) << 0 << (offset + 4) << true << true; + QTest::newRow("Motif, Styled, 3") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 4) << 0 << (100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 4") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << 0 << 0 << 0 << true << true; + QTest::newRow("Motif, Styled, 5") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -100 << (offset + 4 - 50) << -100 << (-100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 6") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -100 << (offset + 4 - 50) << -100 << (offset + 4) << true << true; + QTest::newRow("Motif, Styled, 7") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (offset + 1 + 4) << 0 << (offset + 1 + 4) << true << true; + QTest::newRow("Motif, Styled, 8") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 1 + 4) << 0 << (offset + 1 + 4) << true << true; + QTest::newRow("Motif, Styled, 9") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 1 + 4) << 0 << (100 + offset + 1 + 4) << true << true; + QTest::newRow("Motif, Styled, 10") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -101 << (-100 + offset + 4) << -101 << (-100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 11") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-101) << (offset + 4 - 50) << -101 << (-100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 12") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-101) << (offset + 4 - 50) << (-101) << (offset + 4) << true << true; + QTest::newRow("Motif, Styled, 13") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (offset + 16 + 4) << 0 << (offset + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 14") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 16 + 4) << 0 << (offset + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 15") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (50 + offset + 16 + 4) << 0 << (100 + offset + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 16") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (-100 + offset + 4) << (-100 - 16) << (-100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 17") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (offset + 4 - 50) << (-100 - 16) << (-100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 18") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << (-100 - 16) << (offset + 4 - 50) << (-100 - 16) << (offset + 4) << true << true; + QTest::newRow("Motif, Styled, 1 x2") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (150 + offset + 4) << 0 << (100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 2 x2") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (250 + offset + 4) << 0 << (100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 3 x2") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << 0 << (250 + offset + 4) << 0 << (300 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 4 x2") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (-50 + offset + 4) << -200 << (-100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 5 x2") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (50 + offset + 4) << -200 << (-100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 6 x2") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAsNeeded << Qt::ScrollBarAsNeeded + << -200 << (50 + offset + 4) << -200 << (100 + offset + 4) << true << true; + QTest::newRow("Motif, Styled, 1 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 0 << 0 << 0 << true << true; + QTest::newRow("Motif, Styled, 2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 50 << 0 << 0 << true << true; + QTest::newRow("Motif, Styled, 3 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 50 << 0 << 100 << true << true; + QTest::newRow("Motif, Styled, 4 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 0 << 0 << 0 << true << true; + QTest::newRow("Motif, Styled, 5 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -100 << -50 << 0 << 0 << true << true; + QTest::newRow("Motif, Styled, 6 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -100 << -50 << -100 << 0 << true << true; + QTest::newRow("Motif, Styled, 7 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 1 << 0 << 1 << true << true; + QTest::newRow("Motif, Styled, 8 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 51 << 0 << 1 << true << true; + QTest::newRow("Motif, Styled, 9 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 51 << 0 << 101 << true << true; + QTest::newRow("Motif, Styled, 10 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -100 << -101 << -100 << true << true; + QTest::newRow("Motif, Styled, 11 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -50 << -101 << -100 << true << true; + QTest::newRow("Motif, Styled, 12 No ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -101 << -50 << -101 << 0 << true << true; + QTest::newRow("Motif, Styled, 13 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 16 << 0 << 16 << true << true; + QTest::newRow("Motif, Styled, 14 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << (50 + 16) << 0 << 16 << true << true; + QTest::newRow("Motif, Styled, 15 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << (50 + 16) << 0 << (100 + 16) << true << true; + QTest::newRow("Motif, Styled, 16 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -100 << (-100 - 16) << -100 << true << true; + QTest::newRow("Motif, Styled, 17 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -50 << (-100 - 16) << -100 << true << true; + QTest::newRow("Motif, Styled, 18 No ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << (-100 - 16) << -50 << (-100 - 16) << 0 << true << true; + QTest::newRow("Motif, Styled, 1 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 150 << 0 << 100 << true << true; + QTest::newRow("Motif, Styled, 2 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 250 << 0 << 100 << true << true; + QTest::newRow("Motif, Styled, 3 x2 No ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << 0 << 250 << 0 << 300 << true << true; + QTest::newRow("Motif, Styled, 4 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << -50 << -200 << -100 << true << true; + QTest::newRow("Motif, Styled, 5 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << 50 << -200 << -100 << true << true; + QTest::newRow("Motif, Styled, 6 x2 No ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOff << Qt::ScrollBarAlwaysOff + << -200 << 50 << -200 << 100 << true << true; + QTest::newRow("Motif, Styled, 1 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (16 + 4) << 0 << (16 + 4) << true << true; + QTest::newRow("Motif, Styled, 2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 16 + 4) << 0 << (16 + 4) << true << true; + QTest::newRow("Motif, Styled, 3 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 16 + 4) << 0 << (100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 4 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (-100 + 16 + 4) << -100 << (-100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 5 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (16 + 4 - 50) << -100 << (-100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 6 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -100 << (16 + 4 - 50) << -100 << (16 + 4) << true << true; + QTest::newRow("Motif, Styled, 7 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (17 + 4) << 0 << (17 + 4) << true << true; + QTest::newRow("Motif, Styled, 8 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (117 + 4 - 50) << 0 << (17 + 4) << true << true; + QTest::newRow("Motif, Styled, 9 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (117 + 4 - 50) << 0 << (117 + 4) << true << true; + QTest::newRow("Motif, Styled, 10 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 151, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (-100 + 16 + 4) << -101 << (-100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 11 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 101) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (16 + 4 - 50) << -101 << (-100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 12 Always ScrollBars") << QSize(150, 100) << QRectF(-101, -101, 201, 201) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -101 << (16 + 4 - 50) << -101 << (16 + 4) << true << true; + QTest::newRow("Motif, Styled, 13 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (32 + 4) << 0 << (32 + 4) << true << true; + QTest::newRow("Motif, Styled, 14 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 32 + 4) << 0 << (32 + 4) << true << true; + QTest::newRow("Motif, Styled, 15 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (50 + 32 + 4) << 0 << (100 + 32 + 4) << true << true; + QTest::newRow("Motif, Styled, 16 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 166, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (-100 + 16 + 4) << (-100 - 16) << (-100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 17 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 116) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (16 + 4 - 50) << (-100 - 16) << (-100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 18 Always ScrollBars") << QSize(150, 100) << QRectF(-116, -116, 216, 216) << QTransform() + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << (-100 - 16) << (16 + 4 - 50) << (-100 - 16) << (16 + 4) << true << true; + QTest::newRow("Motif, Styled, 1 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (150 + 16 + 4) << 0 << (100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 2 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (250 + 16 + 4) << 0 << (100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 3 x2 Always ScrollBars") << QSize(150, 100) << QRectF(0, 0, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << 0 << (250 + 16 + 4) << 0 << (300 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 4 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 150, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (-50 + 16 + 4) << -200 << (-100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 5 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 100) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (50 + 16 + 4) << -200 << (-100 + 16 + 4) << true << true; + QTest::newRow("Motif, Styled, 6 x2 Always ScrollBars") << QSize(150, 100) << QRectF(-100, -100, 200, 200) << QTransform().scale(2, 2) + << Qt::ScrollBarAlwaysOn << Qt::ScrollBarAlwaysOn + << -200 << (50 + 16 + 4) << -200 << (100 + 16 + 4) << true << true; +} + +void _scrollBarRanges_data() +{ + QTest::addColumn<QSize>("viewportSize"); + QTest::addColumn<QRectF>("sceneRect"); + QTest::addColumn<QTransform>("transform"); + QTest::addColumn<Qt::ScrollBarPolicy>("hbarpolicy"); + QTest::addColumn<Qt::ScrollBarPolicy>("vbarpolicy"); + QTest::addColumn<int>("hmin"); + QTest::addColumn<int>("hmax"); + QTest::addColumn<int>("vmin"); + QTest::addColumn<int>("vmax"); + QTest::addColumn<bool>("useMotif"); + QTest::addColumn<bool>("useStyledPanel"); + + int offset = 16; +#ifdef Q_OS_WINCE + if (qt_wince_is_high_dpi()) + offset *= 2; +#endif + + _scrollBarRanges_data_1(offset); + _scrollBarRanges_data_2(offset); + _scrollBarRanges_data_3(offset); + _scrollBarRanges_data_4(offset); +} diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/.gitignore b/tests/auto/widgets/graphicsview/qgraphicswidget/.gitignore new file mode 100644 index 0000000000..ae39b803e5 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/.gitignore @@ -0,0 +1 @@ +tst_qgraphicswidget diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/qgraphicswidget.pro b/tests/auto/widgets/graphicsview/qgraphicswidget/qgraphicswidget.pro new file mode 100644 index 0000000000..330076eafc --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/qgraphicswidget.pro @@ -0,0 +1,10 @@ +load(qttest_p4) + +QT += widgets widgets-private +QT += core-private gui-private + +SOURCES += tst_qgraphicswidget.cpp + + +mac*:CONFIG+=insignificant_test +qpa:contains(QT_CONFIG,xcb):CONFIG+=insignificant_test # QTBUG-20778 unstable on qpa, xcb diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp new file mode 100644 index 0000000000..c983016a20 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp @@ -0,0 +1,3361 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qgraphicswidget.h> +#include <qgraphicsscene.h> +#include <qgraphicssceneevent.h> +#include <qgraphicsview.h> +#include <qstyleoption.h> +#include <qgraphicslinearlayout.h> +#include <qcleanlooksstyle.h> +#include <qlineedit.h> +#include <qboxlayout.h> +#include <qaction.h> +#include <qwidgetaction.h> +#include "../../../platformquirks.h" + + +class EventSpy : public QObject +{ + Q_OBJECT +public: + EventSpy(QObject *watched, QEvent::Type type) + : _count(0), spied(type) + { + watched->installEventFilter(this); + } + + int count() const { return _count; } + +protected: + bool eventFilter(QObject *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + int _count; + QEvent::Type spied; +}; + +class tst_QGraphicsWidget : public QObject { +Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qgraphicswidget(); + + void activation(); + void boundingRect_data(); + void boundingRect(); + void dumpFocusChain_data(); + void dumpFocusChain(); + void focusWidget_data(); + void focusWidget(); + void focusWidget2(); + void focusWidget3(); + void focusPolicy_data(); + void focusPolicy(); + void font_data(); + void font(); + void fontPropagation(); + void fontChangedEvent(); + void fontPropagationWidgetItemWidget(); + void fontPropagationSceneChange(); + void geometry_data(); + void geometry(); + void geometryChanged(); + void width(); + void height(); + void getContentsMargins_data(); + void getContentsMargins(); + void initStyleOption_data(); + void initStyleOption(); + void layout_data(); + void layout(); + void layoutDirection_data(); + void layoutDirection(); + void paint_data(); + void paint(); + void palettePropagation(); + void parentWidget_data(); + void parentWidget(); + void resize_data(); + void resize(); + void setAttribute_data(); + void setAttribute(); + void setStyle_data(); + void setStyle(); + void setTabOrder_data(); + void setTabOrder(); + void setTabOrderAndReparent(); + void topLevelWidget_data(); + void topLevelWidget(); + void unsetLayoutDirection_data(); + void unsetLayoutDirection(); + void focusNextPrevChild_data(); + void focusNextPrevChild(); + void verifyFocusChain(); + void updateFocusChainWhenChildDie(); + void sizeHint_data(); + void sizeHint(); + void consistentPosSizeGeometry_data(); + void consistentPosSizeGeometry(); + void setSizes_data(); + void setSizes(); + void closePopupOnOutsideClick(); + void defaultSize(); + void explicitMouseGrabber(); + void implicitMouseGrabber(); + void doubleClickAfterExplicitMouseGrab(); + void popupMouseGrabber(); + void windowFlags_data(); + void windowFlags(); + void shortcutsDeletion(); + void painterStateProtectionOnWindowFrame(); + void ensureClipping(); + void widgetSendsGeometryChanges(); + void respectHFW(); + void addChildInpolishEvent(); + void polishEvent(); + void polishEvent2(); + void autoFillBackground(); + void initialShow(); + void initialShow2(); + void itemChangeEvents(); + void itemSendGeometryPosChangesDeactivated(); + + void fontPropagatesResolveToChildren(); + void fontPropagatesResolveToGrandChildren(); + void fontPropagatesResolveInParentChange(); + void fontPropagatesResolveViaNonWidget(); + void fontPropagatesResolveFromScene(); + + // Task fixes + void task236127_bspTreeIndexFails(); + void task243004_setStyleCrash(); + void task250119_shortcutContext(); + void QT_BUG_6544_tabFocusFirstUnsetWhenRemovingItems(); + void QT_BUG_12056_tabFocusFirstUnsetWhenRemovingItems(); +}; + + +static void sendMouseMove(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::NoButton, Qt::MouseButtons buttons = 0) +{ + QTest::mouseMove(widget, point); + QMouseEvent event(QEvent::MouseMove, point, button, buttons, 0); + QApplication::sendEvent(widget, &event); +} + +// Subclass that exposes the protected functions. +class SubQGraphicsWidget : public QGraphicsWidget { +public: + SubQGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags windowFlags = 0) + : QGraphicsWidget(parent, windowFlags), eventCount(0) + { } + + void initStyleOption(QStyleOption *option) + { QGraphicsWidget::initStyleOption(option); } + + void call_changeEvent(QEvent* event) + { return QGraphicsWidget::changeEvent(event); } + + bool call_event(QEvent *e) + { return event(e); } + + void call_focusInEvent(QFocusEvent* event) + { return QGraphicsWidget::focusInEvent(event); } + + bool call_focusNextPrevChild(bool next) + { return QGraphicsWidget::focusNextPrevChild(next); } + + void call_focusOutEvent(QFocusEvent* event) + { return QGraphicsWidget::focusOutEvent(event); } + + void call_hideEvent(QHideEvent* event) + { return QGraphicsWidget::hideEvent(event); } + + QVariant call_itemChange(QGraphicsItem::GraphicsItemChange change, QVariant const& value) + { return QGraphicsWidget::itemChange(change, value); } + + void call_moveEvent(QGraphicsSceneMoveEvent* event) + { return QGraphicsWidget::moveEvent(event); } + + void call_polishEvent() + { return QGraphicsWidget::polishEvent(); } + + QVariant call_propertyChange(QString const& propertyName, QVariant const& value) + { return QGraphicsWidget::propertyChange(propertyName, value); } + + void call_resizeEvent(QGraphicsSceneResizeEvent* event) + { return QGraphicsWidget::resizeEvent(event); } + + bool call_sceneEvent(QEvent* event) + { return QGraphicsWidget::sceneEvent(event); } + + void call_showEvent(QShowEvent* event) + { return QGraphicsWidget::showEvent(event); } + + QSizeF call_sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const + { return QGraphicsWidget::sizeHint(which, constraint); } + + void call_updateGeometry() + { return QGraphicsWidget::updateGeometry(); } + + int eventCount; + Qt::LayoutDirection m_painterLayoutDirection; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + m_painterLayoutDirection = painter->layoutDirection(); + QGraphicsWidget::paint(painter, option, widget); + if (hasFocus()) { + painter->setPen(Qt::DotLine); + painter->drawRect(rect()); + } + //painter->drawText(QPointF(0,15), data(0).toString()); + } + +protected: + bool event(QEvent *event) + { + eventCount++; + return QGraphicsWidget::event(event); + } +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsWidget::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsWidget::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsWidget::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsWidget::cleanup() +{ +} + +class SizeHinter : public QGraphicsWidget +{ +public: + SizeHinter(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0, + const QSizeF &min = QSizeF(5,5), + const QSizeF &pref = QSizeF(50, 50), + const QSizeF &max = QSizeF(500, 500)) + : QGraphicsWidget(parent, wFlags) + { + m_sizes[Qt::MinimumSize] = min; + m_sizes[Qt::PreferredSize] = pref; + m_sizes[Qt::MaximumSize] = max; + + } + void setSizeHint(Qt::SizeHint which, const QSizeF &newSizeHint) + { + m_sizes[which] = newSizeHint; + } + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const + { + Q_UNUSED(constraint); + return m_sizes[which]; + } +private: + QSizeF m_sizes[4]; +}; + +void tst_QGraphicsWidget::qgraphicswidget() +{ + SubQGraphicsWidget widget; + QVERIFY(widget.isVisible()); + + QVERIFY(!widget.isWindow()); + QCOMPARE(widget.boundingRect(), QRectF(0, 0, 0, 0)); + QCOMPARE(widget.focusWidget(), (QGraphicsWidget*)0); + QCOMPARE(widget.focusPolicy(), Qt::NoFocus); + QCOMPARE(widget.font(), QFont()); + QCOMPARE(widget.geometry(), QRectF(widget.pos(), widget.size())); + QCOMPARE(widget.layout(), (QGraphicsLayout*)0); + QCOMPARE(widget.layoutDirection(), Qt::LeftToRight); + QCOMPARE(widget.palette(), QPalette()); + QCOMPARE(widget.parentWidget(), (QGraphicsWidget*)0); + QCOMPARE(widget.rect(), QRectF(QPointF(), widget.size())); + QCOMPARE(widget.size(), QSizeF(0, 0)); + QVERIFY(widget.style() != (QStyle*)0); + QCOMPARE(widget.testAttribute(Qt::WA_AcceptDrops), false); + QCOMPARE(widget.topLevelWidget(), (QGraphicsWidget*)&widget); + QCOMPARE(widget.type(), (int)QGraphicsWidget::Type); + QCOMPARE(widget.call_propertyChange(QString(), QVariant()), QVariant()); + widget.call_sizeHint(Qt::PreferredSize, QSizeF()); + + QGraphicsScene scene; + QGraphicsWidget *parent = new QGraphicsWidget; + SizeHinter *child = new SizeHinter(parent); + + QCOMPARE(child->minimumSize(), QSizeF(5, 5)); +} + +void tst_QGraphicsWidget::activation() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsWidget *window1 = new QGraphicsWidget(0, Qt::Window); + QGraphicsWidget *window2 = new QGraphicsWidget(0, Qt::Window); + QVERIFY(!widget->isActiveWindow()); + QVERIFY(!window1->isActiveWindow()); + QVERIFY(!window2->isActiveWindow()); + + QGraphicsScene scene; + scene.addItem(widget); + scene.addItem(window1); + scene.addItem(window2); + + QVERIFY(!widget->isActiveWindow()); + QVERIFY(!window1->isActiveWindow()); + QVERIFY(!window2->isActiveWindow()); + + QEvent activateEvent(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activateEvent); + + QVERIFY(!widget->isActiveWindow()); + QVERIFY(window1->isActiveWindow()); + QVERIFY(!window2->isActiveWindow()); + + scene.setActiveWindow(window1); + QVERIFY(!widget->isActiveWindow()); + QVERIFY(window1->isActiveWindow()); + QVERIFY(!window2->isActiveWindow()); + + QEvent deactivateEvent(QEvent::WindowDeactivate); + QApplication::sendEvent(&scene, &deactivateEvent); + + QVERIFY(!widget->isActiveWindow()); + QVERIFY(!window1->isActiveWindow()); + QVERIFY(!window2->isActiveWindow()); +} + +void tst_QGraphicsWidget::boundingRect_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::newRow("null") << QSizeF(0, 0); + QTest::newRow("avg") << QSizeF(10, 10); +} + +// QRectF boundingRect() const public +void tst_QGraphicsWidget::boundingRect() +{ + QFETCH(QSizeF, size); + SubQGraphicsWidget widget; + widget.resize(size); + QCOMPARE(widget.rect(), QRectF(QPointF(), size)); + QCOMPARE(widget.boundingRect(), QRectF(QPointF(0, 0), size)); +} + +void tst_QGraphicsWidget::dumpFocusChain_data() +{ + QTest::addColumn<bool>("scene"); + QTest::addColumn<int>("children"); + QTest::addColumn<bool>("setFocus"); + QTest::newRow("empty world") << false << 0 << false; + QTest::newRow("one world") << true << 2 << false; + QTest::newRow("one world w/focus") << true << 2 << true; +} + +// void dumpFocusChain(QGraphicsScene* scene) public (static) +void tst_QGraphicsWidget::dumpFocusChain() +{ + // ### this test is very strange... + QFETCH(bool, scene); + SubQGraphicsWidget *parent = new SubQGraphicsWidget; + QGraphicsScene *theScene = 0; + if (scene) { + theScene = new QGraphicsScene(this); + theScene->addItem(parent); + } + QFETCH(int, children); + QFETCH(bool, setFocus); + for (int i = 0; i < children; ++i) { + SubQGraphicsWidget *widget = new SubQGraphicsWidget(parent); + if (setFocus) { + widget->setFlag(QGraphicsItem::ItemIsFocusable, true); + if (scene) + theScene->setFocusItem(widget); + } + } + + if (!scene) + delete parent; +} + +void tst_QGraphicsWidget::focusWidget_data() +{ + QTest::addColumn<int>("childCount"); + QTest::addColumn<int>("childWithFocus"); + QTest::newRow("none") << 0 << 0; + QTest::newRow("first") << 3 << 0; + QTest::newRow("last") << 3 << 2; +} + +// QGraphicsWidget* focusWidget() const public +void tst_QGraphicsWidget::focusWidget() +{ + SubQGraphicsWidget *parent = new SubQGraphicsWidget; + QCOMPARE(parent->focusWidget(), (QGraphicsWidget *)0); + QGraphicsScene scene; + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + scene.addItem(parent); + + QFETCH(int, childCount); + QList<SubQGraphicsWidget *> children; + for (int i = 0; i < childCount; ++i) { + SubQGraphicsWidget *widget = new SubQGraphicsWidget(parent); + widget->setFlag(QGraphicsItem::ItemIsFocusable, true); + children.append(widget); + } + if (childCount > 0) { + QFETCH(int, childWithFocus); + SubQGraphicsWidget *widget = children[childWithFocus]; + widget->setFocus(); + QTRY_VERIFY(widget->hasFocus()); + QCOMPARE(parent->focusWidget(), static_cast<QGraphicsWidget*>(widget)); + } +} + +void tst_QGraphicsWidget::focusWidget2() +{ + QGraphicsScene scene; + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + + QGraphicsWidget *widget = new QGraphicsWidget; + EventSpy focusInSpy(widget, QEvent::FocusIn); + EventSpy focusOutSpy(widget, QEvent::FocusOut); + + scene.addItem(widget); + + QTRY_VERIFY(!widget->hasFocus()); + widget->setFocusPolicy(Qt::StrongFocus); + QTRY_VERIFY(!widget->hasFocus()); + + QGraphicsWidget *subWidget = new QGraphicsWidget(widget); + QTRY_VERIFY(!subWidget->hasFocus()); + + scene.setFocus(); + + QTRY_VERIFY(!widget->hasFocus()); + QTRY_VERIFY(!subWidget->hasFocus()); + + widget->setFocus(); + + QTRY_VERIFY(widget->hasFocus()); + QTRY_COMPARE(focusInSpy.count(), 1); + QTRY_VERIFY(!subWidget->hasFocus()); + + QGraphicsWidget *otherSubWidget = new QGraphicsWidget; + EventSpy otherFocusInSpy(otherSubWidget, QEvent::FocusIn); + EventSpy otherFocusOutSpy(otherSubWidget, QEvent::FocusOut); + + otherSubWidget->setFocusPolicy(Qt::StrongFocus); + otherSubWidget->setParentItem(widget); + + QTRY_VERIFY(widget->hasFocus()); + QCOMPARE(scene.focusItem(), (QGraphicsItem *)widget); + QTRY_VERIFY(!subWidget->hasFocus()); + QTRY_VERIFY(!otherSubWidget->hasFocus()); + + widget->hide(); + QTRY_VERIFY(!widget->hasFocus()); // lose but still has subfocus + QCOMPARE(focusInSpy.count(), 1); + QCOMPARE(focusOutSpy.count(), 1); + + widget->show(); + QTRY_VERIFY(!widget->hasFocus()); // no regain + QCOMPARE(focusInSpy.count(), 1); + QCOMPARE(focusOutSpy.count(), 1); + + widget->hide(); + + // try to setup subFocus on item that can't take focus + subWidget->setFocus(); + QTRY_VERIFY(!subWidget->hasFocus()); + QVERIFY(!scene.focusItem()); // but isn't the scene's focus item + + // try to setup subFocus on item that can take focus + otherSubWidget->setFocus(); + QTRY_VERIFY(!otherSubWidget->hasFocus()); + QCOMPARE(widget->focusWidget(), otherSubWidget); + QVERIFY(!scene.focusItem()); // but isn't the scene's focus item + + widget->show(); + + QTRY_COMPARE(scene.focusItem(), (QGraphicsItem *)otherSubWidget); // but isn't the scene's focus item + QCOMPARE(otherFocusInSpy.count(), 1); + QCOMPARE(otherFocusOutSpy.count(), 0); + + delete otherSubWidget; + + QTRY_COMPARE(otherFocusOutSpy.count(), 1); + QVERIFY(!scene.focusItem()); + QVERIFY(!widget->focusWidget()); +} + +class FocusWatchWidget : public QGraphicsWidget +{ +public: + FocusWatchWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent) { gotFocusInCount = 0; gotFocusOutCount = 0; } + int gotFocusInCount, gotFocusOutCount; +protected: + void focusInEvent(QFocusEvent *fe) { gotFocusInCount++; QGraphicsWidget::focusInEvent(fe); } + void focusOutEvent(QFocusEvent *fe) { gotFocusOutCount++; QGraphicsWidget::focusOutEvent(fe); } +}; + +void tst_QGraphicsWidget::focusWidget3() +{ + QGraphicsScene scene; + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + + QGraphicsWidget *widget = new QGraphicsWidget; + FocusWatchWidget *subWidget = new FocusWatchWidget(widget); + subWidget->setFocusPolicy(Qt::StrongFocus); + + scene.addItem(widget); + widget->show(); + + QTRY_VERIFY(!widget->hasFocus()); + QTRY_VERIFY(!subWidget->hasFocus()); + + subWidget->setFocus(); + QCOMPARE(subWidget->gotFocusInCount, 1); + QCOMPARE(subWidget->gotFocusOutCount, 0); + widget->hide(); + QCOMPARE(subWidget->gotFocusOutCount, 1); +} + +Q_DECLARE_METATYPE(Qt::FocusPolicy) +void tst_QGraphicsWidget::focusPolicy_data() +{ + QTest::addColumn<Qt::FocusPolicy>("focusPolicy1"); + QTest::addColumn<Qt::FocusPolicy>("focusPolicy2"); + + for (int i = 0; i < 25; ++i) { + QTestData &data = QTest::newRow(QString("%1").arg(i).toLatin1()); + switch(i % 5) { + case 0: data << Qt::TabFocus; break; + case 1: data << Qt::ClickFocus; break; + case 2: data << Qt::StrongFocus; break; + case 3: data << Qt::WheelFocus; break; + case 4: data << Qt::NoFocus; break; + } + switch(i / 5) { + case 0: data << Qt::TabFocus; break; + case 1: data << Qt::ClickFocus; break; + case 2: data << Qt::StrongFocus; break; + case 3: data << Qt::WheelFocus; break; + case 4: data << Qt::NoFocus; break; + } + } +} + +// Qt::FocusPolicy focusPolicy() const public +void tst_QGraphicsWidget::focusPolicy() +{ + QGraphicsScene scene; + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + + SubQGraphicsWidget *widget = new SubQGraphicsWidget; + scene.addItem(widget); + QTRY_COMPARE(Qt::NoFocus, widget->focusPolicy()); + + QFETCH(Qt::FocusPolicy, focusPolicy1); + widget->setFocusPolicy(focusPolicy1); + QTRY_COMPARE(widget->focusPolicy(), focusPolicy1); + bool isFocusable = widget->flags() & QGraphicsItem::ItemIsFocusable; + bool wasFocusable = isFocusable; + QTRY_VERIFY(isFocusable == (focusPolicy1 != Qt::NoFocus)); + widget->setFocus(); + QTRY_COMPARE(widget->hasFocus(), isFocusable); + + QFETCH(Qt::FocusPolicy, focusPolicy2); + widget->setFocusPolicy(focusPolicy2); + QCOMPARE(widget->focusPolicy(), focusPolicy2); + isFocusable = widget->flags() & QGraphicsItem::ItemIsFocusable; + QVERIFY(isFocusable == (focusPolicy2 != Qt::NoFocus)); + QCOMPARE(widget->hasFocus(), wasFocusable && isFocusable); +} + +void tst_QGraphicsWidget::font_data() +{ + QTest::addColumn<QString>("fontName"); + QTest::newRow("Helvetica") << "Helvetica"; +} + +// QFont font() const public +void tst_QGraphicsWidget::font() +{ + QFETCH(QString, fontName); + SubQGraphicsWidget widget; + QCOMPARE(widget.font(), QFont()); + + QFont font(fontName); + widget.setFont(font); + QCOMPARE(widget.font().family(), font.family()); +} + +void tst_QGraphicsWidget::fontPropagatesResolveToChildren() +{ + QGraphicsWidget *root = new QGraphicsWidget(); + QGraphicsWidget *child1 = new QGraphicsWidget(root); + + QGraphicsScene scene; + scene.addItem(root); + + QFont font; + font.setItalic(true); + root->setFont(font); + + QGraphicsWidget *child2 = new QGraphicsWidget(root); + QGraphicsWidget *child3 = new QGraphicsWidget(); + child3->setParentItem(root); + + QGraphicsView view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(font.resolve(), uint(QFont::StyleResolved)); + QCOMPARE(root->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(child1->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(child2->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(child3->font().resolve(), uint(QFont::StyleResolved)); +} + +void tst_QGraphicsWidget::fontPropagatesResolveToGrandChildren() +{ + QGraphicsWidget *root = new QGraphicsWidget(); + QGraphicsWidget *child1 = new QGraphicsWidget(root); + QGraphicsWidget *grandChild1 = new QGraphicsWidget(child1); + + QGraphicsScene scene; + scene.addItem(root); + + QFont font; + font.setItalic(true); + root->setFont(font); + + QGraphicsWidget *child2 = new QGraphicsWidget(root); + QGraphicsWidget *grandChild2 = new QGraphicsWidget(child2); + QGraphicsWidget *grandChild3 = new QGraphicsWidget(child2); + + QGraphicsWidget *child3 = new QGraphicsWidget(); + QGraphicsWidget *grandChild4 = new QGraphicsWidget(child3); + QGraphicsWidget *grandChild5 = new QGraphicsWidget(child3); + child3->setParentItem(root); + grandChild5->setParentItem(child3); + + QGraphicsView view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(font.resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild1->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild2->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild3->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild4->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild5->font().resolve(), uint(QFont::StyleResolved)); +} + +void tst_QGraphicsWidget::fontPropagatesResolveViaNonWidget() +{ + QGraphicsWidget *root = new QGraphicsWidget(); + QGraphicsPixmapItem *child1 = new QGraphicsPixmapItem(root); + QGraphicsWidget *grandChild1 = new QGraphicsWidget(child1); + + QGraphicsScene scene; + scene.addItem(root); + + QFont font; + font.setItalic(true); + root->setFont(font); + + QGraphicsPixmapItem *child2 = new QGraphicsPixmapItem(root); + QGraphicsWidget *grandChild2 = new QGraphicsWidget(child2); + QGraphicsWidget *grandChild3 = new QGraphicsWidget(child2); + + QGraphicsPixmapItem *child3 = new QGraphicsPixmapItem(); + QGraphicsWidget *grandChild4 = new QGraphicsWidget(child3); + QGraphicsWidget *grandChild5 = new QGraphicsWidget(child3); + child3->setParentItem(root); + grandChild5->setParentItem(child3); + + QGraphicsView view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(font.resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild1->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild2->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild3->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild4->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild5->font().resolve(), uint(QFont::StyleResolved)); +} + +void tst_QGraphicsWidget::fontPropagatesResolveFromScene() +{ + QGraphicsWidget *root = new QGraphicsWidget(); + QGraphicsWidget *child1 = new QGraphicsWidget(root); + QGraphicsWidget *grandChild1 = new QGraphicsWidget(child1); + + QGraphicsScene scene; + scene.addItem(root); + + QFont font; + font.setItalic(true); + scene.setFont(font); + + QGraphicsWidget *child2 = new QGraphicsWidget(root); + QGraphicsWidget *grandChild2 = new QGraphicsWidget(child2); + QGraphicsWidget *grandChild3 = new QGraphicsWidget(child2); + + QGraphicsWidget *child3 = new QGraphicsWidget(); + QGraphicsWidget *grandChild4 = new QGraphicsWidget(child3); + QGraphicsWidget *grandChild5 = new QGraphicsWidget(child3); + child3->setParentItem(root); + grandChild5->setParentItem(child3); + + QGraphicsView view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(font.resolve(), uint(QFont::StyleResolved)); + QCOMPARE(root->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(child1->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(child2->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(child3->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild1->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild2->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild3->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild4->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild5->font().resolve(), uint(QFont::StyleResolved)); +} + +void tst_QGraphicsWidget::fontPropagatesResolveInParentChange() +{ + QGraphicsWidget *root = new QGraphicsWidget(); + + QGraphicsWidget *child1 = new QGraphicsWidget(root); + QGraphicsWidget *grandChild1 = new QGraphicsWidget(child1); + + QGraphicsWidget *child2 = new QGraphicsWidget(root); + QGraphicsWidget *grandChild2 = new QGraphicsWidget(child2); + + QGraphicsScene scene; + scene.addItem(root); + + QFont italicFont; + italicFont.setItalic(true); + child1->setFont(italicFont); + + QFont boldFont; + boldFont.setBold(true); + child2->setFont(boldFont); + + QVERIFY(grandChild1->font().italic()); + QVERIFY(!grandChild1->font().bold()); + QVERIFY(!grandChild2->font().italic()); + QVERIFY(grandChild2->font().bold()); + + QCOMPARE(grandChild1->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild2->font().resolve(), uint(QFont::WeightResolved)); + + grandChild2->setParentItem(child1); + + QGraphicsView view; + view.setScene(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QVERIFY(grandChild1->font().italic()); + QVERIFY(!grandChild1->font().bold()); + QVERIFY(grandChild2->font().italic()); + QVERIFY(!grandChild2->font().bold()); + + QCOMPARE(grandChild1->font().resolve(), uint(QFont::StyleResolved)); + QCOMPARE(grandChild2->font().resolve(), uint(QFont::StyleResolved)); + +} + +void tst_QGraphicsWidget::fontPropagation() +{ + QGraphicsWidget *root = new QGraphicsWidget; + QGraphicsWidget *child0 = new QGraphicsWidget(root); + QGraphicsWidget *child1 = new QGraphicsWidget(child0); + QGraphicsWidget *child2 = new QGraphicsWidget(child1); + QGraphicsScene scene; + scene.addItem(root); + + // Check that only the application fonts apply. + QFont appFont = QApplication::font(); + QCOMPARE(scene.font(), appFont); + QCOMPARE(root->font(), appFont); + QCOMPARE(child0->font(), appFont); + QCOMPARE(child1->font(), appFont); + + // Set child0's Text, and set ToolTipBase on child1. + QFont boldFont; + boldFont.setBold(true); + child0->setFont(boldFont); + QFont italicFont; + italicFont.setItalic(true); + child1->setFont(italicFont); + + // Check that the above settings propagate correctly. + QCOMPARE(root->font(), appFont); + QCOMPARE(scene.font(), appFont); + QVERIFY(child0->font().bold()); + QVERIFY(!child0->font().italic()); + QVERIFY(child1->font().bold()); + QVERIFY(child1->font().italic()); + QVERIFY(child2->font().bold()); + QVERIFY(child2->font().italic()); + + QGraphicsWidget *child3 = new QGraphicsWidget(child2); + QVERIFY(child3->font().bold()); + QVERIFY(child3->font().italic()); + + QGraphicsWidget *child4 = new QGraphicsWidget; + child4->setParentItem(child3); + QVERIFY(child4->font().bold()); + QVERIFY(child4->font().italic()); + + // Replace the app font for child2. Button should propagate but Text + // should still be ignored. The previous ToolTipBase setting is gone. + QFont sizeFont; + sizeFont.setPointSize(43); + child1->setFont(sizeFont); + + // Check that the above settings propagate correctly. + QCOMPARE(root->font(), appFont); + QCOMPARE(scene.font(), appFont); + QVERIFY(child0->font().bold()); + QVERIFY(!child0->font().italic()); + QVERIFY(child1->font().bold()); + QVERIFY(!child1->font().italic()); + QCOMPARE(child1->font().pointSize(), 43); + QVERIFY(child2->font().bold()); + QVERIFY(!child2->font().italic()); + QCOMPARE(child2->font().pointSize(), 43); +} + +void tst_QGraphicsWidget::fontChangedEvent() +{ + QGraphicsWidget *root = new QGraphicsWidget; + QGraphicsScene scene; + scene.addItem(root); + + // Check that only the application fonts apply. + QFont appFont = QApplication::font(); + QCOMPARE(scene.font(), appFont); + QCOMPARE(root->font(), appFont); + + EventSpy rootSpyFont(root, QEvent::FontChange); + EventSpy rootSpyPolish(root, QEvent::Polish); + QTRY_COMPARE(rootSpyFont.count(), 0); + QTRY_COMPARE(rootSpyPolish.count(), 1); + //The font is still the same so no fontChangeEvent + QTRY_COMPARE(rootSpyFont.count(), 0); + + QFont font; + font.setPointSize(43); + root->setFont(font); + //The font changed + QTRY_COMPARE(rootSpyFont.count(), 1); + + //then roll back to the default one. + root->setFont(appFont); + //The font changed + QTRY_COMPARE(rootSpyFont.count(), 2); +} + +void tst_QGraphicsWidget::fontPropagationWidgetItemWidget() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsRectItem *rect = new QGraphicsRectItem(widget); + QGraphicsWidget *widget2 = new QGraphicsWidget(rect); + + QGraphicsScene scene; + scene.addItem(widget); + + QFont font; + font.setPointSize(43); + widget->setFont(font); + + QCOMPARE(widget2->font().pointSize(), 43); + QCOMPARE(widget2->font().resolve(), uint(QFont::SizeResolved)); + + widget->setFont(QFont()); + + QCOMPARE(widget2->font().pointSize(), qApp->font().pointSize()); + QCOMPARE(widget2->font().resolve(), QFont().resolve()); +} + +void tst_QGraphicsWidget::fontPropagationSceneChange() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsRectItem *rect = new QGraphicsRectItem(widget); + QGraphicsWidget *widget2 = new QGraphicsWidget(rect); + + QGraphicsScene scene; + QGraphicsScene scene2; + + QFont font; + font.setPointSize(47); + scene.setFont(font); + + QFont font2; + font2.setPointSize(74); + scene2.setFont(font2); + + scene.addItem(widget); + QCOMPARE(widget2->font().pointSize(), 47); + scene2.addItem(widget); + QCOMPARE(widget2->font().pointSize(), 74); +} + +void tst_QGraphicsWidget::geometry_data() +{ + QTest::addColumn<QPointF>("pos"); + QTest::addColumn<QSizeF>("size"); + QTest::newRow("null, null") << QPointF() << QSizeF(0, 0); + QTest::newRow("null, normal") << QPointF() << QSizeF(10, 10); + QTest::newRow("neg, normal") << QPointF(-5, -5) << QSizeF(10, 10); +} + +// QRectF geometry() const public +void tst_QGraphicsWidget::geometry() +{ + SubQGraphicsWidget widget; + QCOMPARE(widget.geometry(), QRectF(widget.pos(), widget.size())); + QSignalSpy spy(&widget, SIGNAL(geometryChanged())); + QFETCH(QPointF, pos); + QFETCH(QSizeF, size); + widget.setPos(pos); + widget.resize(size); + if (!size.isNull() && !pos.isNull()) + QCOMPARE(spy.count(), 2); + if (!size.isNull() && pos.isNull()) + QCOMPARE(spy.count(), 1); + QCOMPARE(widget.geometry(), QRectF(pos, size)); +} + +void tst_QGraphicsWidget::geometryChanged() +{ + QGraphicsWidget w; + w.setGeometry(0, 0, 200, 200); + QCOMPARE(w.geometry(), QRectF(0, 0, 200, 200)); + QSignalSpy spy(&w, SIGNAL(geometryChanged())); + w.setGeometry(0, 0, 100, 100); + QCOMPARE(spy.count(), 1); + QCOMPARE(w.geometry(), QRectF(0, 0, 100, 100)); + w.setPos(10, 10); + QCOMPARE(spy.count(), 2); + QCOMPARE(w.geometry(), QRectF(10, 10, 100, 100)); + +} + +void tst_QGraphicsWidget::width() +{ + QGraphicsWidget w; + QCOMPARE(w.property("width").toReal(), qreal(0)); + QSignalSpy spy(&w, SIGNAL(widthChanged())); + w.setProperty("width", qreal(50)); + QCOMPARE(w.property("width").toReal(), qreal(50)); + QCOMPARE(spy.count(), 1); + //calling old school setGeometry should work too + w.setGeometry(0, 0, 200, 200); + QCOMPARE(spy.count(), 2); +} + +void tst_QGraphicsWidget::height() +{ + QGraphicsWidget w; + QCOMPARE(w.property("height").toReal(), qreal(0)); + QSignalSpy spy(&w, SIGNAL(heightChanged())); + w.setProperty("height", qreal(50)); + QCOMPARE(w.property("height").toReal(), qreal(50)); + QCOMPARE(spy.count(), 1); + //calling old school setGeometry should work too + w.setGeometry(0, 0, 200, 200); + QCOMPARE(spy.count(), 2); +} + +void tst_QGraphicsWidget::getContentsMargins_data() +{ + QTest::addColumn<qreal>("left"); + QTest::addColumn<qreal>("top"); + QTest::addColumn<qreal>("right"); + QTest::addColumn<qreal>("bottom"); + QTest::newRow("null") << (qreal)0 << (qreal)0 << (qreal)0 << (qreal)0; + QTest::newRow("something") << (qreal)10 << (qreal)5 << (qreal)3 << (qreal)7; + QTest::newRow("real") << (qreal)1.7 << (qreal)5.9 << (qreal)3.2 << (qreal)9.7; +} + +// void getContentsMargins(qreal* left, qreal* top, qreal* right, qreal* bottom) const public +void tst_QGraphicsWidget::getContentsMargins() +{ + qreal gleft; + qreal gtop; + qreal gright; + qreal gbottom; + + SubQGraphicsWidget widget; + widget.getContentsMargins(&gleft, >op, &gright, &gbottom); + QCOMPARE(gleft, (qreal)0); + QCOMPARE(gtop, (qreal)0); + QCOMPARE(gright, (qreal)0); + QCOMPARE(gbottom, (qreal)0); + + QFETCH(qreal, left); + QFETCH(qreal, top); + QFETCH(qreal, right); + QFETCH(qreal, bottom); + int oldEventCounts = widget.eventCount; + widget.setContentsMargins(left, top, right, bottom); + QVERIFY(left == 0 || oldEventCounts != widget.eventCount); + widget.getContentsMargins(&gleft, >op, &gright, &gbottom); + QCOMPARE(gleft, left); + QCOMPARE(gtop, top); + QCOMPARE(gright, right); + QCOMPARE(gbottom, bottom); +} + +Q_DECLARE_METATYPE(Qt::LayoutDirection) +void tst_QGraphicsWidget::initStyleOption_data() +{ + QTest::addColumn<bool>("enabled"); + QTest::addColumn<bool>("focus"); + QTest::addColumn<bool>("underMouse"); + QTest::addColumn<Qt::LayoutDirection>("layoutDirection"); + QTest::addColumn<QSizeF>("size"); + QTest::addColumn<QPalette>("palette"); + QTest::addColumn<QString>("fontName"); + QTest::newRow("none") << false << false << false << Qt::LeftToRight << QSizeF(0, 0) << QPalette() << QString(); + QTest::newRow("all") << true << true << true << Qt::RightToLeft << QSizeF(300, 300) << QPalette(Qt::magenta) << "Helvetica"; + QTest::newRow("rand") << true << false << false << Qt::RightToLeft << QSizeF(1, 0) << QPalette(Qt::darkCyan) << "Times"; +} + +// void initStyleOption(QStyleOption* option) const public +void tst_QGraphicsWidget::initStyleOption() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + view.setAlignment(Qt::AlignTop | Qt::AlignLeft); + SubQGraphicsWidget *widget = new SubQGraphicsWidget; + widget->setAcceptsHoverEvents(true); + QStyleOption option; + scene.addItem(widget); + + QFETCH(QSizeF, size); + widget->resize(size); + + QFETCH(bool, enabled); + widget->setEnabled(enabled); + QFETCH(bool, focus); + if (focus) { + widget->setFlag(QGraphicsItem::ItemIsFocusable, true); + widget->setFocus(); + QVERIFY(widget->hasFocus()); + } + QFETCH(bool, underMouse); + if (underMouse) { + view.resize(300, 300); + view.show(); + QTest::qWaitForWindowShown(&view); + sendMouseMove(view.viewport(), view.mapFromScene(widget->mapToScene(widget->boundingRect().center()))); + } + + QFETCH(QPalette, palette); + widget->setPalette(palette); + + QFETCH(QString, fontName); + widget->setFont(QFont(fontName)); + + // The test + widget->initStyleOption(&option); + + bool isEnabled = option.state & QStyle::State_Enabled; + QCOMPARE(isEnabled, enabled); + bool hasFocus = option.state & QStyle::State_HasFocus; + QCOMPARE(hasFocus, focus); + bool isUnderMouse = option.state & QStyle::State_MouseOver; +#ifndef Q_OS_WINCE + QCOMPARE(isUnderMouse, underMouse); +#endif + // if (layoutDirection != Qt::LeftToRight) + //QEXPECT_FAIL("", "QApplicaiton::layoutDirection doesn't propagate to QGraphicsWidget", Continue); + //QCOMPARE(option.direction, layoutDirection); + QCOMPARE(option.rect, QRectF(QPointF(), size).toRect()); + QCOMPARE(option.palette, palette.resolve(QApplication::palette())); + QCOMPARE(option.fontMetrics, QFontMetrics(widget->font())); +} + +void tst_QGraphicsWidget::layout_data() +{ + QTest::addColumn<int>("childCount"); + QTest::newRow("empty") << 0; + QTest::newRow("10") << 10; +} + +// QGraphicsLayout* layout() const public +void tst_QGraphicsWidget::layout() +{ + SubQGraphicsWidget widget; + widget.setContentsMargins(10, 5, 50, 100); + QCOMPARE(widget.layout(), (QGraphicsLayout *)0); + QFETCH(int, childCount); + + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout; + QList<SubQGraphicsWidget*> children; + for (int i = 0; i < childCount; ++i) { + SubQGraphicsWidget *item = new SubQGraphicsWidget; + layout->addItem(item); + children.append(item); + } + QSignalSpy spy(&widget, SIGNAL(layoutChanged())); + widget.setLayout(layout); + + QTRY_COMPARE(widget.layout(), static_cast<QGraphicsLayout*>(layout)); + for (int i = 0; i < children.count(); ++i) { + SubQGraphicsWidget *item = children[i]; + QCOMPARE(item->parentWidget(), (QGraphicsWidget *)&widget); + QVERIFY(item->geometry() != QRectF(0, 0, -1, -1)); + } + QCOMPARE(spy.count(), 1); + // don't crash + widget.setLayout(0); +} + +void tst_QGraphicsWidget::layoutDirection_data() +{ + QTest::addColumn<Qt::LayoutDirection>("layoutDirection"); + QTest::newRow("rtl") << Qt::RightToLeft; + QTest::newRow("ltr") << Qt::LeftToRight; +} + +// Qt::LayoutDirection layoutDirection() const public +void tst_QGraphicsWidget::layoutDirection() +{ + QFETCH(Qt::LayoutDirection, layoutDirection); + QGraphicsScene scene; + QGraphicsView *view = new QGraphicsView(&scene); + SubQGraphicsWidget widget; + scene.addItem(&widget); + QCOMPARE(widget.layoutDirection(), Qt::LeftToRight); + QCOMPARE(widget.testAttribute(Qt::WA_SetLayoutDirection), false); + + QList<SubQGraphicsWidget*> children; + for (int i = 0; i < 10; ++i) { + SubQGraphicsWidget *item = new SubQGraphicsWidget(&widget); + children.append(item); + QCOMPARE(item->testAttribute(Qt::WA_SetLayoutDirection), false); + } + widget.setLayoutDirection(layoutDirection); + QCOMPARE(widget.testAttribute(Qt::WA_SetLayoutDirection), true); + view->show(); + QTest::qWaitForWindowShown(view); + for (int i = 0; i < children.count(); ++i) { + QTRY_COMPARE(children[i]->layoutDirection(), layoutDirection); + QTRY_COMPARE(children[i]->testAttribute(Qt::WA_SetLayoutDirection), false); + view->repaint(); + QTRY_COMPARE(children[i]->m_painterLayoutDirection, layoutDirection); + } + delete view; +} + +void tst_QGraphicsWidget::paint_data() +{ + // currently QGraphicsWidget doesn't paint or do anything ... +} + +// void paint(QPainter* painter, QStyleOptionGraphicsItem const* option, QWidget* widget) public +void tst_QGraphicsWidget::paint() +{ + SubQGraphicsWidget widget; + QPainter painter; + QStyleOptionGraphicsItem option; + widget.paint(&painter, &option, 0); // check that widget = 0 works. +} + +void tst_QGraphicsWidget::palettePropagation() +{ + QGraphicsWidget *root = new QGraphicsWidget; + QGraphicsWidget *child0 = new QGraphicsWidget(root); + QGraphicsWidget *child1 = new QGraphicsWidget(child0); + QGraphicsWidget *child2 = new QGraphicsWidget(child1); + QGraphicsScene scene; + scene.addItem(root); + + // These colors are unlikely to be imposed on the default palette of + // QWidget ;-). + QColor sysPalText(21, 22, 23); + QColor sysPalToolTipBase(12, 13, 14); + QColor overridePalText(42, 43, 44); + QColor overridePalToolTipBase(45, 46, 47); + QColor sysPalButton(99, 98, 97); + + // Check that only the application fonts apply. + QPalette appPal = QApplication::palette(); + QCOMPARE(scene.palette(), appPal); + QCOMPARE(root->palette(), appPal); + QCOMPARE(child0->palette(), appPal); + QCOMPARE(child1->palette(), appPal); + + // Set child0's Text, and set ToolTipBase on child1. + QPalette textPalette; + textPalette.setColor(QPalette::Text, overridePalText); + child0->setPalette(textPalette); + QPalette toolTipPalette; + toolTipPalette.setColor(QPalette::ToolTipBase, overridePalToolTipBase); + child1->setPalette(toolTipPalette); + + // Check that the above settings propagate correctly. + QCOMPARE(root->palette(), appPal); + QCOMPARE(scene.palette(), appPal); + QCOMPARE(child0->palette().color(QPalette::Text), overridePalText); + QCOMPARE(child0->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); + QCOMPARE(child1->palette().color(QPalette::Text), overridePalText); + QCOMPARE(child1->palette().color(QPalette::ToolTipBase), overridePalToolTipBase); + QCOMPARE(child2->palette().color(QPalette::Text), overridePalText); + QCOMPARE(child2->palette().color(QPalette::ToolTipBase), overridePalToolTipBase); + + QGraphicsWidget *child3 = new QGraphicsWidget(child2); + QCOMPARE(child3->palette().color(QPalette::Text), overridePalText); + QCOMPARE(child3->palette().color(QPalette::ToolTipBase), overridePalToolTipBase); + + QGraphicsWidget *child4 = new QGraphicsWidget; + child4->setParentItem(child3); + QCOMPARE(child4->palette().color(QPalette::Text), overridePalText); + QCOMPARE(child4->palette().color(QPalette::ToolTipBase), overridePalToolTipBase); + + // Replace the app palette for child2. Button should propagate but Text + // should still be ignored. The previous ToolTipBase setting is gone. + QPalette buttonPalette; + buttonPalette.setColor(QPalette::Button, sysPalButton); + child1->setPalette(buttonPalette); + + QCOMPARE(root->palette(), appPal); + QCOMPARE(scene.palette(), appPal); + QCOMPARE(child0->palette().color(QPalette::Text), overridePalText); + QCOMPARE(child0->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); + QCOMPARE(child1->palette().color(QPalette::Text), overridePalText); + QCOMPARE(child1->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); + QCOMPARE(child1->palette().color(QPalette::Button), sysPalButton); + QCOMPARE(child2->palette().color(QPalette::Text), overridePalText); + QCOMPARE(child2->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); + QCOMPARE(child2->palette().color(QPalette::Button), sysPalButton); +} + +void tst_QGraphicsWidget::parentWidget_data() +{ + QTest::addColumn<int>("childrenCount"); + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; + QTest::newRow("10") << 10; +} + +// QGraphicsWidget* parentWidget() const public +void tst_QGraphicsWidget::parentWidget() +{ + QFETCH(int, childrenCount); + SubQGraphicsWidget standAlongWidget; + QGraphicsLineItem standAlongItem; + + SubQGraphicsWidget widgetChild(&standAlongWidget); + SubQGraphicsWidget itemChild(&standAlongItem); + + QCOMPARE(standAlongWidget.parentWidget(), (QGraphicsWidget*)0); + QCOMPARE(widgetChild.parentWidget(), static_cast<QGraphicsWidget*>(&standAlongWidget)); + QCOMPARE(itemChild.parentWidget(), (QGraphicsWidget*)0); + + for (int i = 0; i < childrenCount; ++i) { + SubQGraphicsWidget *item = new SubQGraphicsWidget(&standAlongWidget); + QCOMPARE(item->parentWidget(), static_cast<QGraphicsWidget*>(&standAlongWidget)); + } +} + +void tst_QGraphicsWidget::resize_data() +{ + QTest::addColumn<QSizeF>("size"); + QTest::newRow("null") << QSizeF(); + QTest::newRow("10x10") << QSizeF(10, 10); + QTest::newRow("10x-1") << QSizeF(10, -1); +} + +// void resize(qreal w, qreal h) public +void tst_QGraphicsWidget::resize() +{ + QFETCH(QSizeF, size); + SubQGraphicsWidget widget; + + int oldEventCounts = widget.eventCount; + QSizeF oldSize = widget.size(); + widget.resize(size); + + QSizeF boundedSize = size.expandedTo(widget.minimumSize()).boundedTo(widget.maximumSize()); + QCOMPARE(widget.eventCount, oldEventCounts + ((oldSize == boundedSize) ? 0 : 1)); + QCOMPARE(widget.size(), boundedSize); +} + +Q_DECLARE_METATYPE(Qt::WidgetAttribute) +void tst_QGraphicsWidget::setAttribute_data() +{ + QTest::addColumn<Qt::WidgetAttribute>("attribute"); + QTest::addColumn<bool>("supported"); + QTest::newRow("WA_SetLayoutDirection") << Qt::WA_SetLayoutDirection << true; + QTest::newRow("WA_RightToLeft") << Qt::WA_RightToLeft << true; + QTest::newRow("WA_SetStyle") << Qt::WA_SetStyle << true; + QTest::newRow("WA_Resized") << Qt::WA_Resized << true; + QTest::newRow("unsupported") << Qt::WA_PaintOutsidePaintEvent << false; +} + +// void setAttribute(Qt::WidgetAttribute attribute, bool on = true) public +void tst_QGraphicsWidget::setAttribute() +{ + QFETCH(Qt::WidgetAttribute, attribute); + QFETCH(bool, supported); + SubQGraphicsWidget widget; + if (attribute == Qt::WA_PaintOutsidePaintEvent) + QTest::ignoreMessage(QtWarningMsg, "QGraphicsWidget::setAttribute: unsupported attribute 13"); + widget.setAttribute(attribute); + QCOMPARE(widget.testAttribute(attribute), supported); +} + +void tst_QGraphicsWidget::setStyle_data() +{ + QTest::addColumn<QString>("style"); + QTest::newRow("null") << ""; + QTest::newRow("cleanlooks") << "QCleanlooksStyle"; +} + +// void setStyle(QStyle* style) public +void tst_QGraphicsWidget::setStyle() +{ + SubQGraphicsWidget widget; + QCleanlooksStyle cleanlooksStyle; + + int oldEventCounts = widget.eventCount; + + QFETCH(QString, style); + if (style == "QCleanlooksStyle") { + widget.setStyle(&cleanlooksStyle); + QCOMPARE(widget.style(), static_cast<QStyle*>(&cleanlooksStyle)); + } else { + widget.setStyle(0); + QVERIFY(widget.style() != (QStyle *)0); + } + QCOMPARE(widget.eventCount, oldEventCounts + 1); + QCOMPARE(widget.testAttribute(Qt::WA_SetStyle), !style.isEmpty()); + + // cleanup + widget.setStyle(0); +} + +void tst_QGraphicsWidget::setTabOrder_data() +{ + QTest::addColumn<int>("childrenCount"); + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; + QTest::newRow("10") << 10; +} + +// void setTabOrder(QGraphicsWidget* first, QGraphicsWidget* second) public +void tst_QGraphicsWidget::setTabOrder() +{ + QFETCH(int, childrenCount); + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + QGraphicsWidget *lastItem = 0; + QTest::ignoreMessage(QtWarningMsg, "QGraphicsWidget::setTabOrder(0, 0) is undefined"); + QGraphicsWidget::setTabOrder(0, 0); + + QList<SubQGraphicsWidget*> children; + for (int i = 0; i < childrenCount; ++i) { + SubQGraphicsWidget *item = new SubQGraphicsWidget(); + item->setFocusPolicy(Qt::TabFocus); + children.append(item); + scene.addItem(item); + if (lastItem) + QGraphicsWidget::setTabOrder(lastItem, item); + lastItem = item; + } + + if (!children.isEmpty()) { + QGraphicsWidget *first = children.first(); + view.viewport()->setFocus(); + QTRY_VERIFY(view.viewport()->hasFocus()); + first->setFocus(); + QVERIFY(first->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(view.viewport()->hasFocus()); + + int currentItem = 0; + while (currentItem < children.count() - 1) { + QTest::keyPress(view.viewport(), Qt::Key_Tab); + ++currentItem; + QVERIFY(children[currentItem % children.size()]->hasFocus()); + } + } +} + +static bool compareFocusChain(QGraphicsView *view, const QList<QGraphicsItem*> &order) +{ + QGraphicsScene *scene = view->scene(); + QStringList actual; + QGraphicsItem *oldFocusItem = scene->focusItem(); + for (int i = 0; i < order.count(); ++i) { + QGraphicsItem *focusItem = scene->focusItem(); + actual << focusItem->data(0).toString(); + //qDebug() << "i:" << i << "expected:" << QString::number(uint(order.at(i)), 16) << QString::number(uint(focusItem), 16); + if (focusItem != order.at(i)) { + qDebug() << "actual:" << actual; + scene->setFocusItem(oldFocusItem); + return false; + } + if (i < order.count() - 1) + QTest::keyPress(view, Qt::Key_Tab); + } + scene->setFocusItem(oldFocusItem); + return true; +} + +void tst_QGraphicsWidget::setTabOrderAndReparent() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + int i; + QGraphicsWidget *w1, *w2, *w3, *w4; + for (i = 1; i < 4; ++i) { + QGraphicsWidget *wid = new QGraphicsWidget; + wid->setFocusPolicy(Qt::StrongFocus); + wid->setData(0, QString::fromAscii("w%1").arg(i)); + scene.addItem(wid); + if (i == 1) + w1 = wid; + else if (i == 2) + w2 = wid; + else if (i == 3) + w3 = wid; + } + + w1->setFocus(); + QTRY_VERIFY(w1->hasFocus()); + QVERIFY(compareFocusChain(&view, QList<QGraphicsItem*>() << w1 << w2 << w3)); + + QGraphicsWidget *p = new QGraphicsWidget; + p->setData(0, QLatin1String("parent")); + p->setFocusPolicy(Qt::StrongFocus); + + w1->setFocus(); + QVERIFY(compareFocusChain(&view, QList<QGraphicsItem*>() << w1 << w2 << w3)); + + w1->setParentItem(p); + w2->setFocus(); + QVERIFY(compareFocusChain(&view, QList<QGraphicsItem*>() << w2 << w3)); + + w2->setParentItem(p); + w3->setFocus(); + QVERIFY(compareFocusChain(&view, QList<QGraphicsItem*>() << w3)); + w3->setParentItem(p); + QCOMPARE(scene.focusItem(), static_cast<QGraphicsItem*>(0)); + + scene.addItem(p); + p->setFocus(); + + QVERIFY(compareFocusChain(&view, QList<QGraphicsItem*>() << p << w1 << w2 << w3)); + delete p; + + for (i = 1; i < 5; ++i) { + QGraphicsWidget *wid = new QGraphicsWidget; + wid->setFocusPolicy(Qt::StrongFocus); + wid->setData(0, QString::fromAscii("w%1").arg(i)); + scene.addItem(wid); + if (i == 1) + w1 = wid; + else if (i == 2) + w2 = wid; + else if (i == 3) + w3 = wid; + else if (i == 4) + w4 = wid; + } + w4->setParentItem(w1); + QGraphicsWidget::setTabOrder(w1, w4); + w1->setFocus(); + QVERIFY(compareFocusChain(&view, QList<QGraphicsItem*>() << w1 << w4 << w2 << w3)); + + p = new QGraphicsWidget; + p->setData(0, QLatin1String("parent")); + p->setFocusPolicy(Qt::StrongFocus); + + w1->setParentItem(p); + w2->setFocus(); + QVERIFY(compareFocusChain(&view, QList<QGraphicsItem*>() << w2 << w3)); + + scene.addItem(p); + w2->setFocus(); + QVERIFY(compareFocusChain(&view, QList<QGraphicsItem*>() << w2 << w3 << p << w1 << w4)); +} + +void tst_QGraphicsWidget::topLevelWidget_data() +{ + QTest::addColumn<QString>("str"); + QTest::newRow("test one") << "foo"; +} + +// QGraphicsWidget* topLevelWidget() const public +void tst_QGraphicsWidget::topLevelWidget() +{ + QFETCH(QString, str); + SubQGraphicsWidget widget; + QCOMPARE(widget.topLevelWidget(), (QGraphicsWidget *)&widget); +} + +void tst_QGraphicsWidget::unsetLayoutDirection_data() +{ + QTest::addColumn<Qt::LayoutDirection>("layoutDirection"); + QTest::newRow("rtl") << Qt::RightToLeft; + QTest::newRow("ltr") << Qt::LeftToRight; +} + +// void unsetLayoutDirection() public +void tst_QGraphicsWidget::unsetLayoutDirection() +{ + QApplication::setLayoutDirection(Qt::LeftToRight); + QFETCH(Qt::LayoutDirection, layoutDirection); + SubQGraphicsWidget widget; + QCOMPARE(Qt::LeftToRight, widget.layoutDirection()); + + QList<SubQGraphicsWidget*> children; + for (int i = 0; i < 10; ++i) { + SubQGraphicsWidget *item = new SubQGraphicsWidget(&widget); + children.append(item); + } + widget.setLayoutDirection(layoutDirection); + widget.unsetLayoutDirection(); + QCOMPARE(widget.testAttribute(Qt::WA_SetLayoutDirection), false); + for (int i = 0; i < children.count(); ++i) { + QCOMPARE(children[i]->layoutDirection(), Qt::LeftToRight); + } +} + +void tst_QGraphicsWidget::focusNextPrevChild_data() +{ + QTest::addColumn<QString>("str"); + QTest::newRow("test one") << "foo"; +} + +// bool focusNextPrevChild(bool next) protected +void tst_QGraphicsWidget::focusNextPrevChild() +{ + QFETCH(QString, str); + SubQGraphicsWidget widget; + // ### write test after just calling it stops crashing :) + widget.call_focusNextPrevChild(true); +} + +void tst_QGraphicsWidget::verifyFocusChain() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + { + // parent/child focus + SubQGraphicsWidget *w = new SubQGraphicsWidget(0, Qt::Window); + w->setFocusPolicy(Qt::StrongFocus); + SubQGraphicsWidget *w1_1 = new SubQGraphicsWidget(w); + w1_1->setFocusPolicy(Qt::StrongFocus); + scene.addItem(w); + w->setFocus(); + QVERIFY(w->hasFocus()); + w->call_focusNextPrevChild(true); + QVERIFY(w1_1->hasFocus()); + delete w; + } + + { + // delete item in focus chain and verify chain + SubQGraphicsWidget *w = new SubQGraphicsWidget(0, Qt::Window); + SubQGraphicsWidget *w1_1 = new SubQGraphicsWidget(w); + SubQGraphicsWidget *w1_2 = new SubQGraphicsWidget(w); + SubQGraphicsWidget *w1_3 = new SubQGraphicsWidget(w); + w1_1->setFocusPolicy(Qt::StrongFocus); + w1_2->setFocusPolicy(Qt::StrongFocus); + w1_3->setFocusPolicy(Qt::StrongFocus); + scene.addItem(w); + w1_1->setFocus(); + QVERIFY(w1_1->hasFocus()); + QCOMPARE(w->call_focusNextPrevChild(true), true); + QVERIFY(w1_2->hasFocus()); + QCOMPARE(w->call_focusNextPrevChild(true), true); + QVERIFY(w1_3->hasFocus()); + w1_1->setFocus(); + delete w1_2; + w->call_focusNextPrevChild(true); + QVERIFY(w1_3->hasFocus()); + delete w; + } + { + // parent/child focus + SubQGraphicsWidget *w = new SubQGraphicsWidget(0, Qt::Window); + w->setFocusPolicy(Qt::StrongFocus); + SubQGraphicsWidget *w1_1 = new SubQGraphicsWidget(w); + w1_1->setFocusPolicy(Qt::StrongFocus); + scene.addItem(w); + w->setFocus(); + QVERIFY(w->hasFocus()); + w->call_focusNextPrevChild(true); + QVERIFY(w1_1->hasFocus()); + delete w; + } + { + // remove the tabFocusFirst widget from the scene. + QWidget *window = new QWidget; + QVBoxLayout *layout = new QVBoxLayout; + window->setLayout(layout); + QLineEdit *lineEdit = new QLineEdit; + layout->addWidget(lineEdit); + QGraphicsView *view = new QGraphicsView(&scene); + scene.setSceneRect(-20, -20, 200, 50); + layout->addWidget(view); + view->setMinimumSize(150, 50); + SubQGraphicsWidget *w1_1 = new SubQGraphicsWidget; + w1_1->setData(0, "w1_1"); + w1_1->setGeometry(0,0,25, 25); + w1_1->setFocusPolicy(Qt::StrongFocus); + scene.addItem(w1_1); + SubQGraphicsWidget *w1_2 = new SubQGraphicsWidget; + w1_2->setData(0, "w1_2"); + w1_2->setGeometry(25,0,25, 25); + w1_2->setFocusPolicy(Qt::StrongFocus); + scene.addItem(w1_2); + window->show(); + QApplication::setActiveWindow(window); + QTest::qWaitForWindowShown(window); + + lineEdit->setFocus(); + QTRY_VERIFY(lineEdit->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTRY_VERIFY(w1_1->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTRY_VERIFY(w1_2->hasFocus()); + + // remove the tabFocusFirst and insert new item + delete w1_1; // calls _q_removeItemLater + SubQGraphicsWidget *w1_3 = new SubQGraphicsWidget; + w1_3->setFocusPolicy(Qt::StrongFocus); + w1_3->setData(0, "w1_3"); + w1_3->setGeometry(50,0,25, 25); + scene.addItem(w1_3); + QTRY_VERIFY(w1_2->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTRY_VERIFY(lineEdit->hasFocus()); + // tabFocusFirst should now point to w1_2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTRY_VERIFY(w1_2->hasFocus()); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTRY_VERIFY(w1_3->hasFocus()); + scene.removeItem(w1_2); // does not call _q_removeItemLater + delete w1_2; // calls _q_removeItemLater + + SubQGraphicsWidget *w1_4 = new SubQGraphicsWidget; + w1_4->setFocusPolicy(Qt::StrongFocus); + w1_4->setData(0, "w1_4"); + w1_4->setGeometry(75,0,25, 25); + scene.addItem(w1_4); + QTRY_VERIFY(w1_3->hasFocus()); + QTRY_VERIFY(compareFocusChain(view, QList<QGraphicsItem*>() << w1_3 << w1_4)); + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTRY_VERIFY(lineEdit->hasFocus()); + // tabFocusFirst should now point to w1_3 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTRY_VERIFY(w1_3->hasFocus()); + QTRY_VERIFY(compareFocusChain(view, QList<QGraphicsItem*>() << w1_3 << w1_4)); + delete window; + } +} + +void tst_QGraphicsWidget::updateFocusChainWhenChildDie() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + // delete item in focus chain with no focus and verify chain + SubQGraphicsWidget *parent = new SubQGraphicsWidget(0, Qt::Window); + SubQGraphicsWidget *w = new SubQGraphicsWidget(0, Qt::Window); + w->resize(50,50); + w->resize(100,100); + SubQGraphicsWidget *w1_1 = new SubQGraphicsWidget(w); + w1_1->setFocusPolicy(Qt::StrongFocus); + w->setFocusPolicy(Qt::StrongFocus); + scene.addItem(w); + scene.addItem(parent); + w1_1->setFocus(); + + QVERIFY(w1_1->hasFocus()); + QWidget myWidget(0); + QLineEdit edit(&myWidget); + myWidget.show(); + edit.setFocus(); + QTRY_VERIFY(edit.hasFocus()); + delete w1_1; + myWidget.hide(); + w->setParentItem(parent); + //We don't crash perfect + QVERIFY(w); + QTest::mouseMove(view.viewport()); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0); + QTRY_COMPARE(qApp->activeWindow(), static_cast<QWidget *>(&view)); + QTRY_COMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(w)); +} + +void tst_QGraphicsWidget::sizeHint_data() +{ + QTest::addColumn<bool>("layout"); + QTest::newRow("no layout") << false; + QTest::newRow("layout") << true; +} + +// QSizeF sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const protected +void tst_QGraphicsWidget::sizeHint() +{ + QFETCH(bool, layout); + SubQGraphicsWidget widget; + + if (layout) { + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout; + widget.setLayout(layout); + } + widget.call_sizeHint(Qt::MinimumSize, QSizeF()); +} + +void tst_QGraphicsWidget::consistentPosSizeGeometry_data() +{ + QTest::addColumn<QSizeF>("minSize"); + QTest::addColumn<QSizeF>("maxSize"); + QTest::addColumn<QRectF>("geometry"); + QTest::addColumn<QRectF>("expectedGeometry"); + + QTest::newRow("size is valid") << QSizeF(0, 0) << QSizeF(200, 200) << QRectF(0, 0, 100, 100) << QRectF(0, 0, 100, 100); + QTest::newRow("size is larger than max") << QSizeF(0, 0) << QSizeF(50, 50) << QRectF(0, 0, 100, 100) << QRectF(0, 0, 50, 50); + QTest::newRow("size is smaller than min") << QSizeF(50, 50) << QSizeF(150, 150) << QRectF(0, 0, 10, 10) << QRectF(0, 0, 50, 50); +} + +void tst_QGraphicsWidget::consistentPosSizeGeometry() +{ + QFETCH(QSizeF, minSize); + QFETCH(QSizeF, maxSize); + QFETCH(QRectF, geometry); + QFETCH(QRectF, expectedGeometry); + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *w = new QGraphicsWidget; + scene.addItem(w); + w->setMinimumSize(minSize); + w->setMaximumSize(maxSize); + w->setGeometry(geometry); + QCOMPARE(w->geometry(), expectedGeometry); + QCOMPARE(w->pos(), expectedGeometry.topLeft()); + QCOMPARE(w->size(), expectedGeometry.size()); + + QRectF otherGeom = QRectF(QPointF(12.34,12.34), minSize); + w->setGeometry(otherGeom); + QCOMPARE(w->geometry(), otherGeom); + QCOMPARE(w->pos(), otherGeom.topLeft()); + QCOMPARE(w->size(), otherGeom.size()); + + w->setPos(geometry.topLeft()); + QCOMPARE(w->geometry().topLeft(), expectedGeometry.topLeft()); + QCOMPARE(w->pos(), expectedGeometry.topLeft()); + + w->resize(geometry.size()); + QCOMPARE(w->geometry().size(), expectedGeometry.size()); + QCOMPARE(w->geometry(), expectedGeometry); + + view.show(); + +} + + +enum WhichSize { + MinimumWidth, + PreferredWidth, + MaximumWidth, + MinimumHeight, + PreferredHeight, + MaximumHeight, + MinimumSize, + PreferredSize, + MaximumSize, + MinimumSizeHint, + PreferredSizeHint, + MaximumSizeHint, + Size, + None, +}; + +typedef QPair<int, QVariant> Inst; + +Q_DECLARE_METATYPE(Inst) +Q_DECLARE_METATYPE(QVector<Inst>) + +void tst_QGraphicsWidget::setSizes_data() +{ + + QTest::addColumn<QVector<Inst> >("inputInstructions"); + QTest::addColumn<QVector<Inst> >("compareInstructions"); + + QTest::newRow("minSize1") << (QVector<Inst>() << Inst(Size, QSize(25, 25)) << Inst(MinimumSize, QSize(10, 10))) + << (QVector<Inst>() << Inst(Size, QSize(25,25))); + QTest::newRow("minSize2") << (QVector<Inst>() << Inst(Size, QSizeF(20, 20)) << Inst(MinimumSize, QSizeF(25, 25))) + << (QVector<Inst>() << Inst(Size, QSizeF(25, 25))); + QTest::newRow("minWidth1") << (QVector<Inst>() << Inst(Size, QSizeF(20, 20)) << Inst(MinimumWidth, 5.0)) + << (QVector<Inst>() << Inst(Size, QSizeF(20, 20))); + QTest::newRow("minWidth2") << (QVector<Inst>() << Inst(Size, QSizeF(20, 20)) << Inst(MinimumWidth, 25.0)) + << (QVector<Inst>() << Inst(Size, QSizeF(25, 20))); + QTest::newRow("minHeight1") << (QVector<Inst>() << Inst(Size, QSizeF(20, 20)) << Inst(MinimumHeight, 5.0)) + << (QVector<Inst>() << Inst(Size, QSizeF(20, 20))); + QTest::newRow("minHeight2") << (QVector<Inst>() << Inst(Size, QSizeF(20, 20)) << Inst(MinimumHeight, 25.0)) + << (QVector<Inst>() << Inst(Size, QSizeF(20, 25))); + QTest::newRow("maxSize1") << (QVector<Inst>() << Inst(Size, QSizeF(40, 40)) << Inst(MaximumSize, QSizeF(30, 30))) + << (QVector<Inst>() << Inst(Size, QSizeF(30, 30))); + QTest::newRow("maxSize2") << (QVector<Inst>() << Inst(Size, QSizeF(40, 40)) << Inst(MaximumSize, QSizeF(30, -1))) + << (QVector<Inst>() << Inst(Size, QSizeF(30, 40))); + QTest::newRow("maxSize3") << (QVector<Inst>() << Inst(Size, QSizeF(40, 40)) << Inst(MaximumSize, QSizeF(-1, 30))) + << (QVector<Inst>() << Inst(Size, QSizeF(40, 30))); + QTest::newRow("maxWidth1")<< (QVector<Inst>() << Inst(Size, QSizeF(40, 40)) << Inst(MaximumWidth, 30)) + << (QVector<Inst>() << Inst(Size, QSizeF(30, 40))); + QTest::newRow("maxHeight")<< (QVector<Inst>() << Inst(Size, QSizeF(40, 40)) << Inst(MaximumHeight, 20)) + << (QVector<Inst>() << Inst(Size, QSizeF(40, 20))); + QTest::newRow("unsetMinSize")<< (QVector<Inst>() << Inst(Size, QSizeF(40, 40)) << Inst(MinimumSize, QSizeF(-1, -1))) + << (QVector<Inst>() << Inst(MinimumSize, QSizeF(5, 5))); + QTest::newRow("unsetMaxSize")<< (QVector<Inst>() << Inst(Size, QSizeF(40, 40)) << Inst(MaximumSize, QSizeF(-1, -1))) + << (QVector<Inst>() << Inst(MaximumSize, QSizeF(500, 500))); + QTest::newRow("unsetMinSize, expand size to minimumSizeHint") << (QVector<Inst>() + << Inst(MinimumSize, QSize(0, 0)) + << Inst(Size, QSize(1,1)) + << Inst(MinimumSize, QSize(-1.0, -1.0)) + ) + << (QVector<Inst>() + << Inst(Size, QSize(5,5)) + << Inst(MinimumSize, QSize(5,5)) + ); + +} + +void tst_QGraphicsWidget::setSizes() +{ + QFETCH(QVector<Inst>, inputInstructions); + QFETCH(QVector<Inst>, compareInstructions); + + QGraphicsScene scene; + QGraphicsView view(&scene); + SizeHinter *widget = new SizeHinter(0, Qt::Window); + QSizeF min = QSizeF(10, 10); + QSizeF pref = QSizeF(25, 25); + QSizeF max = QSizeF(50, 50); + + int i; + for (i = 0; i < inputInstructions.count(); ++i) { + Inst input = inputInstructions.at(i); + + // defaults + switch (input.first) { + case MinimumSize: + min = input.second.toSizeF(); + break; + case PreferredSize: + pref = input.second.toSizeF(); + break; + case MaximumSize: + max = input.second.toSizeF(); + break; + case Size: + widget->resize(input.second.toSizeF()); + break; + case MinimumWidth: + widget->setMinimumWidth(qreal(input.second.toDouble())); + break; + case PreferredWidth: + widget->setPreferredWidth(qreal(input.second.toDouble())); + break; + case MaximumWidth: + widget->setMaximumWidth(qreal(input.second.toDouble())); + break; + case MinimumHeight: + widget->setMinimumHeight(qreal(input.second.toDouble())); + break; + case PreferredHeight: + widget->setPreferredHeight(qreal(input.second.toDouble())); + break; + case MaximumHeight: + widget->setMaximumHeight(qreal(input.second.toDouble())); + break; + case MinimumSizeHint: + widget->setSizeHint(Qt::MinimumSize, input.second.toSizeF()); + break; + case PreferredSizeHint: + widget->setSizeHint(Qt::PreferredSize, input.second.toSizeF()); + break; + case MaximumSizeHint: + widget->setSizeHint(Qt::MaximumSize, input.second.toSizeF()); + break; + default: + qWarning("instruction not implemented"); + break; + } + } + + widget->setMinimumSize(min); + widget->setPreferredSize(pref); + widget->setMaximumSize(max); + + for (i = 0; i < compareInstructions.count(); ++i) { + Inst input = compareInstructions.at(i); + switch (input.first) { + case MinimumSize: + QTRY_COMPARE(widget->minimumSize(), input.second.toSizeF()); + break; + case PreferredSize: + QTRY_COMPARE(widget->preferredSize(), input.second.toSizeF()); + break; + case MaximumSize: + QTRY_COMPARE(widget->maximumSize(), input.second.toSizeF()); + break; + case Size: + QTRY_COMPARE(widget->size(), input.second.toSizeF()); + break; + case MinimumWidth: + QTRY_COMPARE(widget->minimumWidth(), qreal(input.second.toDouble())); + break; + case PreferredWidth: + QTRY_COMPARE(widget->preferredWidth(), qreal(input.second.toDouble())); + break; + case MaximumWidth: + QTRY_COMPARE(widget->maximumWidth(), qreal(input.second.toDouble())); + break; + default: + qWarning("instruction not implemented"); + break; + } + } + delete widget; +} + +void tst_QGraphicsWidget::closePopupOnOutsideClick() +{ + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Popup); + widget->resize(100, 100); + + QGraphicsScene scene; + scene.addItem(widget); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + + QVERIFY(widget->isVisible()); + QVERIFY(event.isAccepted()); + + event.ignore(); + event.setScenePos(QPointF(150, 150)); + qApp->sendEvent(&scene, &event); + + QVERIFY(!widget->isVisible()); + QVERIFY(event.isAccepted()); +} + +void tst_QGraphicsWidget::task236127_bspTreeIndexFails() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsWidget *widget2 = new QGraphicsWidget; + widget->resize(10, 10); + widget2->resize(10, 10); + widget2->setZValue(1); + QCOMPARE(widget2->zValue(), qreal(1)); + QCOMPARE(widget->zValue(), qreal(0)); + widget->setData(0, "widget"); + widget2->setData(0, "widget2"); + + QGraphicsScene scene; + scene.addItem(widget); + scene.addItem(widget2); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + QTRY_VERIFY(!scene.itemAt(25, 25)); + widget->setGeometry(0, 112, 360, 528); + QTRY_COMPARE(scene.itemAt(15, 120), (QGraphicsItem *)widget); + widget2->setGeometry(0, 573, 360, 67); + QTRY_COMPARE(scene.itemAt(15, 120), (QGraphicsItem *)widget); + QTRY_COMPARE(scene.itemAt(50, 585), (QGraphicsItem *)widget2); +} + +void tst_QGraphicsWidget::defaultSize() +{ + SubQGraphicsWidget *widget = new SubQGraphicsWidget; + widget->setMinimumSize(40, 40); + QGraphicsScene scene; + scene.addItem(widget); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QSizeF initialSize = widget->size(); + + widget->resize(initialSize); + QCOMPARE(widget->geometry().size(), initialSize); + widget->setVisible(false); + widget->setMinimumSize(10, 10); + widget->setPreferredSize(60, 60); + widget->setMaximumSize(110, 110); + widget->setVisible(true); + // should still have its size set to initialsize + QTRY_COMPARE(widget->geometry().size(), initialSize); + +} + +void tst_QGraphicsWidget::explicitMouseGrabber() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + EventSpy widgetGrabEventSpy(widget, QEvent::GrabMouse); + EventSpy widgetUngrabEventSpy(widget, QEvent::UngrabMouse); + + // Grab without scene + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::grabMouse: cannot grab mouse without scene"); + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::ungrabMouse: cannot ungrab mouse without scene"); + widget->ungrabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 0); + + // Add to scene + QGraphicsScene scene; + scene.addItem(widget); + + // Ungrab while not grabber + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::ungrabMouse: not a mouse grabber"); + widget->ungrabMouse(); + + // Simple grab with scene + QVERIFY(!scene.mouseGrabberItem()); + widget->grabMouse(); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 1); + widget->ungrabMouse(); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + + // Grab while grabbing + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 2); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::grabMouse: already a mouse grabber"); + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 2); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + widget->ungrabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + + // Add two more widgets to the scene + QGraphicsWidget *widget2 = new QGraphicsWidget; + scene.addItem(widget2); + EventSpy widget2GrabEventSpy(widget2, QEvent::GrabMouse); + EventSpy widget2UngrabEventSpy(widget2, QEvent::UngrabMouse); + QGraphicsWidget *widget3 = new QGraphicsWidget; + scene.addItem(widget3); + EventSpy widget3GrabEventSpy(widget3, QEvent::GrabMouse); + EventSpy widget3UngrabEventSpy(widget3, QEvent::UngrabMouse); + + widget->setData(0, "widget"); + widget2->setData(0, "widget2"); + widget3->setData(0, "widget3"); + + // Simple nested grabbing + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 3); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + widget2->grabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 3); + QCOMPARE(widget2GrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + widget3->grabMouse(); + QCOMPARE(widget2UngrabEventSpy.count(), 1); + QCOMPARE(widget3GrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); + widget3->ungrabMouse(); + QCOMPARE(widget3UngrabEventSpy.count(), 1); + QCOMPARE(widget2GrabEventSpy.count(), 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + widget2->ungrabMouse(); + QCOMPARE(widget2UngrabEventSpy.count(), 2); + QCOMPARE(widgetGrabEventSpy.count(), 4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + widget->ungrabMouse(); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + // Out of order ungrab + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 5); + widget2->grabMouse(); + QCOMPARE(widget2GrabEventSpy.count(), 3); + widget3->grabMouse(); + QCOMPARE(widget3GrabEventSpy.count(), 2); + widget2->ungrabMouse(); + QCOMPARE(widget3UngrabEventSpy.count(), 2); + QCOMPARE(widget2UngrabEventSpy.count(), 4); + QCOMPARE(widgetGrabEventSpy.count(), 6); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); +} + +void tst_QGraphicsWidget::implicitMouseGrabber() +{ + QGraphicsScene scene; + QGraphicsWidget *widget = new QGraphicsWidget; + widget->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget->resize(200, 200); + EventSpy widgetGrabEventSpy(widget, QEvent::GrabMouse); + EventSpy widgetUngrabEventSpy(widget, QEvent::UngrabMouse); + scene.addItem(widget); + + QVERIFY(!scene.mouseGrabberItem()); + + // Click on an item, see if gain and lose implicit mouse grab. + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 1); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QCOMPARE(widgetGrabEventSpy.count(), 1); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + + // Click on an item that already grabs the mouse. Shouldn't have any effect. + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 2); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + widget->ungrabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + // Implicit mouse grabber tries to explicitly grab the mouse + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 3); + widget->grabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 3); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + widget->ungrabMouse(); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QCOMPARE(widgetGrabEventSpy.count(), 3); + QCOMPARE(widgetUngrabEventSpy.count(), 3); + + // Arrival of a new widget + QGraphicsWidget *widget2 = new QGraphicsWidget; + widget2->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget2->resize(200, 200); + widget2->setPos(205, 0); + EventSpy widget2GrabEventSpy(widget2, QEvent::GrabMouse); + EventSpy widget2UngrabEventSpy(widget2, QEvent::UngrabMouse); + scene.addItem(widget2); + + // Implicit grab while there's an explicit grab is not possible. + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 4); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(250, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 4); + QCOMPARE(widget2GrabEventSpy.count(), 0); + QCOMPARE(widget2UngrabEventSpy.count(), 0); + + scene.removeItem(widget); + QCOMPARE(widgetUngrabEventSpy.count(), 4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); +} + +class GrabOnPressItem : public QGraphicsRectItem +{ +public: + GrabOnPressItem(const QRectF &rect) + : QGraphicsRectItem(rect), + npress(0), nrelease(0), ndoubleClick(0), + ngrab(0), nungrab(0) + { + } + int npress; + int nrelease; + int ndoubleClick; + int ngrab; + int nungrab; +protected: + bool sceneEvent(QEvent *event) + { + switch (event->type()) { + case QEvent::GrabMouse: + ++ngrab; + break; + case QEvent::UngrabMouse: + ++nungrab; + break; + default: + break; + } + return QGraphicsRectItem::sceneEvent(event); + } + + void mousePressEvent(QGraphicsSceneMouseEvent *) + { + grabMouse(); + ++npress; + } + void mouseReleaseEvent(QGraphicsSceneMouseEvent *) + { + ungrabMouse(); + ++nrelease; + } + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) + { + ++ndoubleClick; + } +}; + +void tst_QGraphicsWidget::doubleClickAfterExplicitMouseGrab() +{ + QGraphicsScene scene; + GrabOnPressItem *item = new GrabOnPressItem(QRectF(0, 0, 100, 100)); + scene.addItem(item); + + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + event.ignore(); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + QCOMPARE(item->npress, 1); + QCOMPARE(item->ngrab, 1); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setButton(Qt::LeftButton); + event.setButtons(0); + event.ignore(); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QCOMPARE(item->nrelease, 1); + QCOMPARE(item->nungrab, 1); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseDoubleClick); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + event.ignore(); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + QCOMPARE(item->ndoubleClick, 1); + QCOMPARE(item->ngrab, 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setButton(Qt::LeftButton); + event.setButtons(0); + event.ignore(); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QCOMPARE(item->nrelease, 2); + QCOMPARE(item->nungrab, 2); +} + +void tst_QGraphicsWidget::popupMouseGrabber() +{ + QGraphicsScene scene; + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Popup); + widget->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget->resize(200, 200); + EventSpy widgetGrabEventSpy(widget, QEvent::GrabMouse); + EventSpy widgetUngrabEventSpy(widget, QEvent::UngrabMouse); + + // Simply adding a visible popup to the scene immediately grabs the mouse. + scene.addItem(widget); + QCOMPARE(widgetGrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + + // Hiding it loses the grab again. + widget->hide(); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + // Showing it grabs the mouse again + widget->show(); + QCOMPARE(widgetGrabEventSpy.count(), 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + + // Add two popups + QGraphicsWidget *widget2 = new QGraphicsWidget(0, Qt::Popup); + widget2->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget2->resize(200, 200); + EventSpy widget2GrabEventSpy(widget2, QEvent::GrabMouse); + EventSpy widget2UngrabEventSpy(widget2, QEvent::UngrabMouse); + QGraphicsWidget *widget3 = new QGraphicsWidget(0, Qt::Popup); + widget3->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget3->resize(200, 200); + EventSpy widget3GrabEventSpy(widget3, QEvent::GrabMouse); + EventSpy widget3UngrabEventSpy(widget3, QEvent::UngrabMouse); + + // Adding to the scene grabs + scene.addItem(widget2); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + QCOMPARE(widget2GrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + + // Adding to the scene grabs again + scene.addItem(widget3); + QCOMPARE(widget2UngrabEventSpy.count(), 1); + QCOMPARE(widget3GrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); + + // Hiding the topmost widget causes widget 2 to regain grab. + widget3->hide(); + QCOMPARE(widget2GrabEventSpy.count(), 2); + QCOMPARE(widget3UngrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + widget3->show(); + QCOMPARE(widget2UngrabEventSpy.count(), 2); + QCOMPARE(widget3GrabEventSpy.count(), 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); + + // Clicking outside the popup still causes it to close (despite that it's + // an explicit mouse grabber). + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(500, 500)); // outside + qApp->sendEvent(&scene, &event); + } + QVERIFY(!widget3->isVisible()); + QCOMPARE(widget3UngrabEventSpy.count(), 2); + QCOMPARE(widget2GrabEventSpy.count(), 3); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + QVERIFY(widget2->isVisible()); + QVERIFY(widget->isVisible()); + widget3->show(); + QCOMPARE(widget3GrabEventSpy.count(), 3); + QCOMPARE(widget2UngrabEventSpy.count(), 3); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); + + // This is something of a curiosity. What happens if you call + // ungrabMouse() on a popup? The answer is - it loses the grab. If you + // hide and show the popup again, it will regain the grab. + widget3->ungrabMouse(); + QCOMPARE(widget3UngrabEventSpy.count(), 3); + QCOMPARE(widget2GrabEventSpy.count(), 4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + widget3->hide(); + widget3->show(); + QCOMPARE(widget3GrabEventSpy.count(), 4); + QCOMPARE(widget2UngrabEventSpy.count(), 4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); +} + +void tst_QGraphicsWidget::windowFlags_data() +{ + QTest::addColumn<int>("inputFlags"); + QTest::addColumn<int>("outputFlags"); + + QTest::newRow("nil") << 0 << 0; + + // Window types + QTest::newRow("Qt::Window") << int(Qt::Window) + << int(Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); + QTest::newRow("Qt::SubWindow") << int(Qt::SubWindow) + << int(Qt::SubWindow | Qt::WindowTitleHint | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); + QTest::newRow("Qt::Dialog") << int(Qt::Dialog) + << int(Qt::Dialog | Qt::WindowTitleHint | Qt::WindowSystemMenuHint + | Qt::WindowContextHelpButtonHint); + QTest::newRow("Qt::Sheet") << int(Qt::Sheet) + << int(Qt::Sheet | Qt::WindowTitleHint | Qt::WindowSystemMenuHint + | Qt::WindowContextHelpButtonHint); + QTest::newRow("Qt::Tool") << int(Qt::Tool) + << int(Qt::Tool | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + + // Custom window flags + QTest::newRow("Qt::FramelessWindowHint") << int(Qt::FramelessWindowHint) + << int(Qt::FramelessWindowHint); + QTest::newRow("Qt::CustomizeWindowHint") << int(Qt::CustomizeWindowHint) + << int(Qt::CustomizeWindowHint); +} + +void tst_QGraphicsWidget::windowFlags() +{ + QFETCH(int, inputFlags); + QFETCH(int, outputFlags); + + // Construct with flags set already + QGraphicsWidget widget(0, Qt::WindowFlags(inputFlags)); + QCOMPARE(widget.windowFlags(), Qt::WindowFlags(outputFlags)); + + // Set flags after construction + QGraphicsWidget widget2; + widget2.setWindowFlags(Qt::WindowFlags(inputFlags)); + QCOMPARE(widget2.windowFlags(), Qt::WindowFlags(outputFlags)); + + // Reset flags + widget2.setWindowFlags(0); + QVERIFY(!widget2.windowFlags()); + + // Set flags back again + widget2.setWindowFlags(Qt::WindowFlags(inputFlags)); + QCOMPARE(widget2.windowFlags(), Qt::WindowFlags(outputFlags)); + + // Construct with custom flags set already + QGraphicsWidget widget3(0, Qt::WindowFlags(inputFlags | Qt::FramelessWindowHint)); + QCOMPARE(widget3.windowFlags(), Qt::WindowFlags(inputFlags | Qt::FramelessWindowHint)); + + // Set custom flags after construction + QGraphicsWidget widget4; + widget4.setWindowFlags(Qt::WindowFlags(inputFlags | Qt::FramelessWindowHint)); + QCOMPARE(widget4.windowFlags(), Qt::WindowFlags(inputFlags | Qt::FramelessWindowHint)); + + // Reset flags + widget4.setWindowFlags(0); + QVERIFY(!widget4.windowFlags()); + + // Set custom flags back again + widget4.setWindowFlags(Qt::WindowFlags(inputFlags | Qt::FramelessWindowHint)); + QCOMPARE(widget4.windowFlags(), Qt::WindowFlags(inputFlags | Qt::FramelessWindowHint)); + + QGraphicsWidget *widget5 = new QGraphicsWidget; + widget5->setWindowFlags(Qt::WindowFlags(inputFlags)); + QCOMPARE(widget5->windowFlags(), Qt::WindowFlags(outputFlags)); + QGraphicsWidget window(0, Qt::Window); + widget5->setParentItem(&window); + QCOMPARE(widget5->windowFlags(), Qt::WindowFlags(outputFlags)); +} + +void tst_QGraphicsWidget::shortcutsDeletion() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsWidget *widget2 = new QGraphicsWidget; + widget->setMinimumSize(40, 40); + QWidgetAction *del = new QWidgetAction(widget); + del->setIcon(QIcon("edit-delete")); + del->setShortcut(Qt::Key_Delete); + del->setShortcutContext(Qt::WidgetShortcut); + widget2->addAction(del); + widget2->addAction(del); + delete widget; +} + +class MessUpPainterWidget : public QGraphicsWidget +{ +public: + MessUpPainterWidget(QGraphicsItem * parent = 0, Qt::WindowFlags wFlags = 0) + : QGraphicsWidget(parent, wFlags) + {} + + void paintWindowFrame(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QCOMPARE(painter->opacity(), 1.0); + painter->setOpacity(0.0); + QGraphicsWidget::paintWindowFrame(painter, option, widget); + } + void paint(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QCOMPARE(painter->opacity(), 1.0); + painter->drawRect(0, 0, 100, 100); + QGraphicsWidget::paint(painter, option, widget); + } + +}; + +void tst_QGraphicsWidget::painterStateProtectionOnWindowFrame() +{ + MessUpPainterWidget *widget = new MessUpPainterWidget(0, Qt::Window); + QGraphicsScene scene(0, 0, 300, 300); + QGraphicsView view(&scene); + scene.addItem(widget); + view.show(); + QTest::qWaitForWindowShown(&view); +} + +class ProxyStyle : public QCommonStyle +{ +public: + ProxyStyle(QStyle *proxyStyle) : QCommonStyle() + { + m_proxyStyle = proxyStyle; + } + + int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const + { + return m_proxyStyle->pixelMetric(metric, option, widget); + } + +private: + QStyle *m_proxyStyle; +}; + +class StyledGraphicsWidget : public QGraphicsWidget +{ +public: + StyledGraphicsWidget(bool useOwnStyle) : QGraphicsWidget(), m_style(0) { + if (useOwnStyle) { + QStyle *oldStyle = style(); + m_style = new ProxyStyle(oldStyle); + setStyle(m_style); + } + + style()->pixelMetric(QStyle::PM_SmallIconSize); // crash when style() is still in widgetStyles + } + + ~StyledGraphicsWidget() { + delete m_style; + } + +private: + QStyle *m_style; +}; + +void tst_QGraphicsWidget::task243004_setStyleCrash() +{ + QGraphicsItem *item1 = new StyledGraphicsWidget(true); + delete item1; // item1 not removed from widgetStyles + + QGraphicsItem *item2 = new StyledGraphicsWidget(false); + delete item2; +} + +class GraphicsWidget_task250119 : public QGraphicsWidget +{ +public: + GraphicsWidget_task250119() + : shortcutEvents(0) + { + setFocusPolicy(Qt::StrongFocus); + resize(100, 100); + } + + int shortcutEvents; + +private: + bool event(QEvent *event) + { + if (event->type() == QEvent::Shortcut) + shortcutEvents++; + return QGraphicsWidget::event(event); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + if (hasFocus()) { + painter->setPen(QPen(Qt::black, 0, Qt::DashLine)); + painter->drawRect(rect()); + } + painter->setPen(QPen(Qt::black, 0, Qt::SolidLine)); + painter->fillRect(rect().adjusted(2, 2, -2, -2), Qt::yellow); + painter->drawRect(rect().adjusted(2, 2, -2, -2)); + } +}; + +void tst_QGraphicsWidget::task250119_shortcutContext() +{ + QGraphicsScene scene; + QGraphicsView view; + view.setScene(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); + + + // *** Event: *** + + GraphicsWidget_task250119 w_event; + scene.addItem(&w_event); + + const int id = w_event.grabShortcut(Qt::Key_A, Qt::WidgetWithChildrenShortcut); + w_event.setShortcutEnabled(id, true); + + w_event.setFocus(); + QTest::keyPress(&view, Qt::Key_A); + QCOMPARE(w_event.shortcutEvents, 1); + + w_event.clearFocus(); + QTest::keyPress(&view, Qt::Key_A); + QCOMPARE(w_event.shortcutEvents, 1); + + scene.removeItem(&w_event); + + + // *** Signal: *** + + GraphicsWidget_task250119 w_signal; + scene.addItem(&w_signal); + + QAction action(0); + action.setShortcut(Qt::Key_B); + action.setShortcutContext(Qt::WidgetWithChildrenShortcut); + QSignalSpy spy(&action, SIGNAL(triggered())); + + w_signal.addAction(&action); + + w_signal.setFocus(); + QTest::keyPress(&view, Qt::Key_B); + QCOMPARE(spy.count(), 1); + + w_signal.clearFocus(); + QTest::keyPress(&view, Qt::Key_B); + QCOMPARE(spy.count(), 1); + + scene.removeItem(&w_signal); +} + +class ClippingAndTransformsScene : public QGraphicsScene +{ +public: + QList<QGraphicsItem *> drawnItems; +protected: + void drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], + const QStyleOptionGraphicsItem options[], QWidget *widget = 0) + { + drawnItems.clear(); + for (int i = 0; i < numItems; ++i) + drawnItems << items[i]; + QGraphicsScene::drawItems(painter, numItems, items, options, widget); + } +}; + +class RectWidget : public QGraphicsWidget +{ +public: + + RectWidget(Qt::GlobalColor color, QGraphicsItem *parent=0) : QGraphicsWidget(parent), mColor(color) {} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->setBrush(QBrush(mColor)); + painter->drawRect(boundingRect()); + } + + Qt::GlobalColor mColor; +}; + +class RectItem : public QGraphicsItem +{ +public: + + RectItem(Qt::GlobalColor color, QGraphicsItem *parent=0) : QGraphicsItem(parent), mColor(color) {} + + QRectF boundingRect() const + {return QRectF(10,10,50,50);} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->setBrush(QBrush(mColor)); + painter->drawRect(boundingRect()); + } + + Qt::GlobalColor mColor; +}; + +void tst_QGraphicsWidget::ensureClipping() +{ + ClippingAndTransformsScene scene; + scene.setSceneRect(-50, -50, 200, 200); + + //A root that clip children + RectWidget *clipWidget = new RectWidget(Qt::black); + scene.addItem(clipWidget); + + clipWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + //a child + RectWidget *childWidget = new RectWidget(Qt::red, clipWidget); + clipWidget->setGeometry(QRectF(10, 10, 100, 100)); + childWidget->setGeometry(QRectF(25, 25, 50, 50)); + + //We put a QGraphicsItem to be sure this one is also paint + RectItem *childitem = new RectItem(Qt::blue, clipWidget); + + QGraphicsView view(&scene); + view.setOptimizationFlag(QGraphicsView::IndirectPainting); + view.show(); + QTest::qWaitForWindowShown(&view); + + QList<QGraphicsItem *> expected; + expected << clipWidget << childWidget << childitem; + QTRY_VERIFY(scene.drawnItems.contains(clipWidget)); + QVERIFY(scene.drawnItems.contains(childWidget)); + QVERIFY(scene.drawnItems.contains(childitem)); +} + +class ItemChangeTester : public QGraphicsWidget +{ +public: + ItemChangeTester() + { setFlag(ItemSendsGeometryChanges); clear(); } + ItemChangeTester(QGraphicsItem *parent) : QGraphicsWidget(parent) + { setFlag(ItemSendsGeometryChanges); clear(); } + + void clear() + { + changes.clear(); + values.clear(); + oldValues.clear(); + } + QList<GraphicsItemChange> changes; + QList<QVariant> values; + QList<QVariant> oldValues; +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value) + { + changes << change; + values << value; + switch (change) { + case QGraphicsItem::ItemPositionChange: + oldValues << pos(); + break; + case QGraphicsItem::ItemPositionHasChanged: + break; + default: + break; + } + return value; + } +}; + +void tst_QGraphicsWidget::widgetSendsGeometryChanges() +{ + ItemChangeTester widget; + widget.setFlags(0); + widget.clear(); + + QPointF pos(10, 10); + widget.setPos(pos); + + QCOMPARE(widget.pos(), pos); + QCOMPARE(widget.changes.size(), 0); + + widget.setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); + QCOMPARE(widget.changes.size(), 2); + + widget.setPos(QPointF()); + QCOMPARE(widget.changes.size(), 4); + + QCOMPARE(widget.pos(), QPointF()); + + QRectF geometry(20, 20, 50, 50); + widget.setGeometry(geometry); + QCOMPARE(widget.changes.size(), 6); + + QCOMPARE(widget.geometry(), geometry); + + QCOMPARE(widget.changes, QList<QGraphicsItem::GraphicsItemChange>() + << QGraphicsItem::ItemFlagsChange + << QGraphicsItem::ItemFlagsHaveChanged + << QGraphicsItem::ItemPositionChange + << QGraphicsItem::ItemPositionHasChanged + << QGraphicsItem::ItemPositionChange + << QGraphicsItem::ItemPositionHasChanged); +} + +class HFWWidget : public QGraphicsWidget +{ +public: + HFWWidget() : QGraphicsWidget(0, Qt::Window) + { + QSizePolicy sp; + sp.setHeightForWidth(true); + setSizePolicy(sp); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + Q_UNUSED(option); + Q_UNUSED(widget); + qreal w = rect().width(); + QRectF box(0, 0, w, 2400/w); + painter->drawRoundRect(box); + painter->drawLine(box.topLeft(), box.bottomRight()); + painter->drawLine(box.bottomLeft(), box.topRight()); + } + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const + { + qreal w = constraint.width(); + switch (which) { + case Qt::MinimumSize: + if (w >= 0 && constraint.height() < 0) { + // keep the same area of 60x40 = 2400 + return QSizeF(w, 2400.0/w); + } else { + return QSizeF(10, 10); + } + break; + case Qt::PreferredSize: + return QSizeF(48.989794, 48.989794); + default: + break; + } + return QGraphicsWidget::sizeHint(which, constraint); + } +}; + +void tst_QGraphicsWidget::respectHFW() +{ +#if defined(Q_OS_WINCE) || defined(Q_OS_MAC) || defined(Q_WS_QWS) + qDebug("This test is platform dependent, it fails on wince, mac and qws. Please fix."); +#else + QGraphicsScene scene; + HFWWidget *window = new HFWWidget; + scene.addItem(window); + QGraphicsView *view = new QGraphicsView(&scene); + view->resize(400, 400); + view->setSceneRect(-100, -100, 300,300); + + view->show(); + window->setGeometry(0, 0, 70, 70); + QTest::qWaitForWindowShown(view); + + { // here we go - simulate a interactive resize of the window + QTest::mouseMove(view, view->mapFromScene(71, 71)); // bottom right corner + + QTest::mousePress(view->viewport(), Qt::LeftButton, 0, view->mapFromScene(71, 71), 200); + view->grabMouse(); + // move both mouse cursor and set correct event in order to emulate resize + QTest::mouseMove(view->viewport(), view->mapFromScene(60, 30), 200); + QMouseEvent e = QMouseEvent(QEvent::MouseMove, + view->mapFromScene(60, 20), + Qt::NoButton, + Qt::LeftButton, + Qt::NoModifier); + QApplication::sendEvent(view->viewport(), &e); + view->releaseMouse(); + } + const QSizeF winSize = window->size(); + qreal minHFW = window->effectiveSizeHint(Qt::MinimumSize, QSizeF(winSize.width(), -1)).height(); + QTRY_VERIFY(qAbs(minHFW - winSize.height()) < 1); +#endif +} + +class PolishWidget : public QGraphicsWidget +{ +public: + + PolishWidget(Qt::GlobalColor color, QGraphicsItem *parent=0) : + QGraphicsWidget(parent), mColor(color) + { + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->setBrush(QBrush(mColor)); + painter->drawRect(boundingRect()); + } + + void polishEvent() + { + if (!parentWidget()) { + //We add a child in the polish event for the parent + PolishWidget *childWidget = new PolishWidget(Qt::black, this); + childWidget->setGeometry(QRectF(10,10,30,30)); + } + + QGraphicsWidget::polishEvent(); + mColor = Qt::red; + update(); + numberOfPolish++; + } + + static int numberOfPolish; + +private: + Qt::GlobalColor mColor; +}; + +int PolishWidget::numberOfPolish = 0; + +void tst_QGraphicsWidget::addChildInpolishEvent() +{ + QGraphicsScene scene; + + PolishWidget *parentWidget = new PolishWidget(Qt::white); + scene.addItem(parentWidget); + + QGraphicsView view(&scene); + view.resize(200, 200); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(PolishWidget::numberOfPolish, 2); +} + +void tst_QGraphicsWidget::polishEvent() +{ + class MyGraphicsWidget : public QGraphicsWidget + { public: + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) + { events << QEvent::Paint; } + void polishEvent() + { events << QEvent::Polish; } + QList<QEvent::Type> events; + }; + + QGraphicsScene scene; + + MyGraphicsWidget *widget = new MyGraphicsWidget; + scene.addItem(widget); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + // Make sure the item is painted. + QTRY_VERIFY(widget->events.contains(QEvent::Paint)); + + // Make sure the item got polish before paint. + QCOMPARE(widget->events.at(0), QEvent::Polish); +} + +void tst_QGraphicsWidget::polishEvent2() +{ + class MyGraphicsWidget : public QGraphicsWidget + { public: + void polishEvent() + { events << QEvent::Polish; } + QList<QEvent::Type> events; + }; + + QGraphicsScene scene; + + MyGraphicsWidget *widget = new MyGraphicsWidget; + widget->hide(); + scene.addItem(widget); + + widget->events.clear(); + + // Make sure the item got polish event. + QTRY_VERIFY(widget->events.contains(QEvent::Polish)); +} + +void tst_QGraphicsWidget::autoFillBackground() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + QCOMPARE(widget->autoFillBackground(), false); + widget->setAutoFillBackground(true); + QCOMPARE(widget->autoFillBackground(), true); + + const QColor color(Qt::red); + const QRect rect(0, 0, 1, 1); + + QGraphicsScene scene; + scene.addItem(widget); + widget->setGeometry(rect); + + QPalette palette = widget->palette(); + palette.setColor(QPalette::Window, color); + widget->setPalette(palette); + + QImage image(rect.size(), QImage::Format_RGB32); + QPainter painter; + painter.begin(&image); + scene.render(&painter, rect, rect); + painter.end(); + QCOMPARE(image.pixel(0, 0), color.rgb()); +} + +void tst_QGraphicsWidget::initialShow() +{ + class MyGraphicsWidget : public QGraphicsWidget + { public: + MyGraphicsWidget() : repaints(0) {} + int repaints; + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget*) { ++repaints; } + void polishEvent() { update(); } + }; + + QGraphicsScene scene; + MyGraphicsWidget *widget = new MyGraphicsWidget; + + QGraphicsView view(&scene); + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + + scene.addItem(widget); + + QTRY_COMPARE(widget->repaints, 1); +} + +void tst_QGraphicsWidget::initialShow2() +{ + class MyGraphicsWidget : public QGraphicsWidget + { public: + MyGraphicsWidget() : repaints(0) {} + int repaints; + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget*) { ++repaints; } + void polishEvent() { update(); } + }; + + // Don't let paint events triggered by the windowing system + // influence our test case. We're only interested in knowing + // whether a QGraphicsWidget generates an additional repaint + // on the initial show. Hence create a dummy scenario to find out + // how many repaints we should expect. + QGraphicsScene dummyScene(0, 0, 200, 200); + dummyScene.addItem(new QGraphicsRectItem(0, 0, 100, 100)); + + QGraphicsView *dummyView = new QGraphicsView(&dummyScene); + dummyView->setWindowFlags(Qt::X11BypassWindowManagerHint); + EventSpy paintSpy(dummyView->viewport(), QEvent::Paint); + dummyView->show(); + QTest::qWaitForWindowShown(dummyView); + const int expectedRepaintCount = paintSpy.count(); + delete dummyView; + dummyView = 0; + + MyGraphicsWidget *widget = new MyGraphicsWidget; + widget->resize(100, 100); + + QGraphicsScene scene(0, 0, 200, 200); + scene.addItem(widget); + + QGraphicsView view(&scene); + view.setWindowFlags(view.windowFlags()|Qt::X11BypassWindowManagerHint); + view.show(); + QTest::qWaitForWindowShown(&view); + + QTRY_COMPARE(widget->repaints, expectedRepaintCount); +} + +void tst_QGraphicsWidget::itemChangeEvents() +{ + class TestGraphicsWidget : public QGraphicsWidget + { public: + TestGraphicsWidget() : QGraphicsWidget() {} + QHash<QEvent::Type, QVariant> valueDuringEvents; + bool event(QEvent *event) { + Q_UNUSED(event); + switch (event->type()) { + case QEvent::EnabledChange: { + valueDuringEvents.insert(QEvent::EnabledChange, isEnabled()); + break; + } + case QEvent::ParentAboutToChange: { + valueDuringEvents.insert(QEvent::ParentAboutToChange, qVariantFromValue(parentItem())); + break; + } + case QEvent::ParentChange: { + valueDuringEvents.insert(QEvent::ParentChange, qVariantFromValue(parentItem())); + break; + } + case QEvent::CursorChange: { + valueDuringEvents.insert(QEvent::CursorChange, int(cursor().shape())); + break; + } + case QEvent::ToolTipChange: { + valueDuringEvents.insert(QEvent::ToolTipChange, toolTip()); + break; + } + default: { + break; + } + } + return true; + } + void showEvent(QShowEvent *event) { + Q_UNUSED(event); + valueDuringEvents.insert(QEvent::Show, isVisible()); + } + void hideEvent(QHideEvent *event) { + Q_UNUSED(event); + valueDuringEvents.insert(QEvent::Hide, isVisible()); + } + }; + + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *parent = new QGraphicsWidget; + scene.addItem(parent); + view.show(); + QTest::qWaitForWindowShown(&view); + + TestGraphicsWidget *item = new TestGraphicsWidget; + item->setParentItem(parent); + // ParentAboutToChange should be triggered before the parent has changed + QTRY_COMPARE(qVariantValue<QGraphicsItem *>(item->valueDuringEvents.value(QEvent::ParentAboutToChange)), + static_cast<QGraphicsItem *>(0)); + // ParentChange should be triggered after the parent has changed + QTRY_COMPARE(qVariantValue<QGraphicsItem *>(item->valueDuringEvents.value(QEvent::ParentChange)), + static_cast<QGraphicsItem *>(parent)); + + // ShowEvent should be triggered before the item is shown + QTRY_VERIFY(!item->valueDuringEvents.value(QEvent::Show).toBool()); + + // HideEvent should be triggered after the item is hidden + QVERIFY(item->isVisible()); + item->setVisible(false); + QVERIFY(!item->isVisible()); + QTRY_VERIFY(!item->valueDuringEvents.value(QEvent::Hide).toBool()); + + // CursorChange should be triggered after the cursor has changed + item->setCursor(Qt::PointingHandCursor); + QTRY_COMPARE(item->valueDuringEvents.value(QEvent::CursorChange).toInt(), int(item->cursor().shape())); + + // ToolTipChange should be triggered after the tooltip has changed + item->setToolTip("tooltipText"); + QTRY_COMPARE(item->valueDuringEvents.value(QEvent::ToolTipChange).toString(), item->toolTip()); + + // EnabledChange should be triggered after the enabled state has changed + QVERIFY(item->isEnabled()); + item->setEnabled(false); + QVERIFY(!item->isEnabled()); + QTRY_VERIFY(!item->valueDuringEvents.value(QEvent::EnabledChange).toBool()); +} + +void tst_QGraphicsWidget::itemSendGeometryPosChangesDeactivated() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget *item = new QGraphicsWidget; + scene.addItem(item); + view.show(); + QTest::qWaitForWindowShown(&view); + item->setGeometry(QRectF(0, 0, 50, 50)); + QTRY_COMPARE(item->geometry(), QRectF(0, 0, 50, 50)); + + item->setFlag(QGraphicsItem::ItemSendsGeometryChanges, false); + item->setGeometry(QRectF(0, 0, 60, 60)); + QCOMPARE(item->geometry(), QRectF(0, 0, 60, 60)); + QCOMPARE(item->pos(), QPointF(0, 0)); + item->setPos(QPointF(10, 10)); + QCOMPARE(item->pos(), QPointF(10, 10)); + QCOMPARE(item->geometry(), QRectF(10, 10, 60, 60)); + + item->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, false); + item->setGeometry(QRectF(0, 0, 60, 60)); + QCOMPARE(item->geometry(), QRectF(0, 0, 60, 60)); + QCOMPARE(item->pos(), QPointF(0, 0)); + item->setPos(QPointF(10, 10)); + QCOMPARE(item->pos(), QPointF(10, 10)); + QCOMPARE(item->geometry(), QRectF(10, 10, 60, 60)); +} + +void tst_QGraphicsWidget::QT_BUG_6544_tabFocusFirstUnsetWhenRemovingItems() +{ + QGraphicsScene scene; + QGraphicsWidget* parent1 = new QGraphicsWidget; + QGraphicsWidget* child1_0 = new QGraphicsWidget; + QGraphicsWidget* child1_1 = new QGraphicsWidget; + + QGraphicsWidget* parent2 = new QGraphicsWidget; + + // Add the parent and child to the scene. + scene.addItem(parent1); + child1_0->setParentItem(parent1); + child1_1->setParentItem(parent1); + + // Hide and show the child. + child1_0->setParentItem(NULL); + scene.removeItem(child1_0); + + // Remove parent from the scene. + scene.removeItem(parent1); + + delete child1_0; + delete child1_1; + delete parent1; + + // Add an item into the scene. + scene.addItem(parent2); + + //This should not crash +} +void tst_QGraphicsWidget::QT_BUG_12056_tabFocusFirstUnsetWhenRemovingItems() +{ + QGraphicsScene scene; + QGraphicsWidget* item1 = new QGraphicsWidget; + QGraphicsWidget* item2 = new QGraphicsWidget; + QGraphicsWidget* item3 = new QGraphicsWidget; + + scene.addItem(item1); + scene.addItem(item2); + + scene.removeItem(item2); + scene.removeItem(item1); + delete item2; + delete item1; + + scene.addItem(item3); + + //This should not crash +} + +QTEST_MAIN(tst_QGraphicsWidget) +#include "tst_qgraphicswidget.moc" |