mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2024-11-29 23:45:50 +08:00
5075 lines
174 KiB
C++
5075 lines
174 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
#include <QTest>
|
|
#include <QSignalSpy>
|
|
#include <QTimer>
|
|
|
|
#include <qgraphicsitem.h>
|
|
#include <qgraphicsscene.h>
|
|
#include <qgraphicssceneevent.h>
|
|
#include <qgraphicsview.h>
|
|
#include <qgraphicswidget.h>
|
|
#include <qgraphicsproxywidget.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <QtWidgets/QLabel>
|
|
#include <QtWidgets/QStyleFactory>
|
|
#include <QtWidgets/QCommonStyle>
|
|
#include <QtGui/QPainterPath>
|
|
#include <QtWidgets/QRubberBand>
|
|
#include <QtWidgets/QScrollBar>
|
|
#include <QtWidgets/QStyleOption>
|
|
#include <QtWidgets/QBoxLayout>
|
|
#include <QtWidgets/QStyle>
|
|
#include <QtWidgets/QPushButton>
|
|
#include <QtWidgets/QScroller>
|
|
#if QT_CONFIG(opengl)
|
|
#include <QtOpenGLWidgets/QOpenGLWidget>
|
|
#endif
|
|
#include <private/qgraphicsscene_p.h>
|
|
#include <private/qgraphicsview_p.h>
|
|
#include "../../../shared/platforminputcontext.h"
|
|
#include <private/qinputmethod_p.h>
|
|
#include <private/qguiapplication_p.h>
|
|
#include <qpa/qplatformintegration.h>
|
|
|
|
#include "tst_qgraphicsview.h"
|
|
|
|
#include <QtTest/private/qtesthelpers_p.h>
|
|
|
|
#include <QtWidgets/private/qapplication_p.h>
|
|
|
|
using namespace QTestPrivate;
|
|
|
|
Q_DECLARE_METATYPE(ExpectedValueDescription)
|
|
Q_DECLARE_METATYPE(QList<int>)
|
|
Q_DECLARE_METATYPE(QList<QRectF>)
|
|
Q_DECLARE_METATYPE(QTransform)
|
|
Q_DECLARE_METATYPE(QPainterPath)
|
|
Q_DECLARE_METATYPE(Qt::ScrollBarPolicy)
|
|
Q_DECLARE_METATYPE(ScrollBarCount)
|
|
|
|
#ifdef Q_OS_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, {}, {});
|
|
QApplication::sendEvent(widget, &event);
|
|
}
|
|
|
|
static void sendMouseMove(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::NoButton, Qt::MouseButtons buttons = {})
|
|
{
|
|
QTest::mouseMove(widget, point);
|
|
QMouseEvent event(QEvent::MouseMove, point, widget->mapToGlobal(point), button, buttons, {});
|
|
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, {}, {});
|
|
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) override
|
|
{
|
|
Q_UNUSED(watched);
|
|
if (event->type() == spied)
|
|
++_count;
|
|
return false;
|
|
}
|
|
|
|
int _count;
|
|
QEvent::Type spied;
|
|
};
|
|
|
|
#if defined QT_BUILD_INTERNAL
|
|
class FriendlyGraphicsScene : public QGraphicsScene
|
|
{
|
|
friend class tst_QGraphicsView;
|
|
Q_DECLARE_PRIVATE(QGraphicsScene);
|
|
};
|
|
#endif
|
|
|
|
class tst_QGraphicsView : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
tst_QGraphicsView()
|
|
: platformName(QGuiApplication::platformName().toLower())
|
|
{ }
|
|
private slots:
|
|
void cleanup();
|
|
void construction();
|
|
void renderHints();
|
|
void alignment();
|
|
void interactive();
|
|
void scene();
|
|
void setScene();
|
|
void deleteScene();
|
|
void sceneRect();
|
|
void sceneRect_growing();
|
|
void setSceneRect();
|
|
void viewport();
|
|
#if QT_CONFIG(opengl)
|
|
void openGLViewport();
|
|
#endif
|
|
void dragMode_scrollHand();
|
|
void dragMode_rubberBand();
|
|
void rubberBandSelectionMode();
|
|
void rubberBandExtendSelection();
|
|
void rotated_rubberBand();
|
|
void backgroundBrush();
|
|
void foregroundBrush();
|
|
void matrix();
|
|
void matrix_convenience();
|
|
void matrix_combine();
|
|
void centerOnPoint();
|
|
void centerOnItem();
|
|
void ensureVisibleRect();
|
|
void fitInView();
|
|
void itemsAtPoint();
|
|
#if defined QT_BUILD_INTERNAL
|
|
void itemsAtPosition_data();
|
|
void itemsAtPosition();
|
|
#endif
|
|
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();
|
|
#if QT_CONFIG(wheelevent)
|
|
void wheelEvent();
|
|
#endif
|
|
#ifndef QT_NO_CURSOR
|
|
void cursor();
|
|
void cursor2();
|
|
#endif
|
|
void transformationAnchor();
|
|
void resizeAnchor();
|
|
void viewportUpdateMode();
|
|
void viewportUpdateMode2();
|
|
#if QT_CONFIG(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 acceptMouseDoubleClickEvent();
|
|
void forwardMousePress();
|
|
void forwardMouseDoubleClick();
|
|
void replayMouseMove();
|
|
void itemsUnderMouse();
|
|
void embeddedViews();
|
|
void embeddedViewsWithFocus();
|
|
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 task239729_noViewUpdate_data();
|
|
void task239729_noViewUpdate();
|
|
void task239047_fitInViewSmallViewport();
|
|
void task245469_itemsAtPointWithClip();
|
|
void task253415_reconnectUpdateSceneOnSceneChanged();
|
|
void task255529_transformationAnchorMouseAndViewportMargins();
|
|
void task259503_scrollingArtifacts();
|
|
void QTBUG_4151_clipAndIgnore_data();
|
|
void QTBUG_4151_clipAndIgnore();
|
|
void QTBUG_5859_exposedRect();
|
|
void hoverLeave();
|
|
void QTBUG_16063_microFocusRect();
|
|
void QTBUG_70255_scrollTo();
|
|
#ifndef QT_NO_CURSOR
|
|
void QTBUG_7438_cursor();
|
|
#endif
|
|
void resizeContentsOnItemDrag_data();
|
|
void resizeContentsOnItemDrag();
|
|
|
|
public slots:
|
|
void dummySlot() {}
|
|
|
|
private:
|
|
QString platformName;
|
|
};
|
|
|
|
void tst_QGraphicsView::cleanup()
|
|
{
|
|
// ensure not even skipped tests with custom input context leave it dangling
|
|
QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
|
|
inputMethodPrivate->testContext = 0;
|
|
}
|
|
|
|
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.transform(), QTransform());
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
}
|
|
|
|
class TestItem : public QGraphicsItem
|
|
{
|
|
public:
|
|
QRectF boundingRect() const override
|
|
{ return QRectF(-10, -10, 20, 20); }
|
|
|
|
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
|
|
{ hints = painter->renderHints(); painter->drawRect(boundingRect()); }
|
|
|
|
bool sceneEvent(QEvent *event) override
|
|
{
|
|
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({});
|
|
QCOMPARE(view.renderHints(), 0);
|
|
|
|
TestItem *item = new TestItem;
|
|
QGraphicsScene scene;
|
|
scene.addItem(item);
|
|
|
|
view.setScene(&scene);
|
|
|
|
view.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing);
|
|
QCOMPARE(view.renderHints(), QPainter::TextAntialiasing | QPainter::Antialiasing);
|
|
|
|
QCOMPARE(item->hints, 0);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
view.update();
|
|
QTRY_COMPARE(item->hints, view.renderHints());
|
|
|
|
view.setRenderHints(QPainter::Antialiasing);
|
|
QCOMPARE(view.renderHints(), QPainter::Antialiasing);
|
|
|
|
view.update();
|
|
QTRY_COMPARE(item->hints, view.renderHints());
|
|
}
|
|
|
|
void tst_QGraphicsView::alignment()
|
|
{
|
|
QGraphicsScene scene;
|
|
scene.addRect(QRectF(-10, -10, 20, 20));
|
|
|
|
QGraphicsView view(&scene);
|
|
setFrameless(&view);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
for (int j = 0; j < 3; ++j) {
|
|
Qt::Alignment alignment;
|
|
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()
|
|
{
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
QSKIP("Window activation is not supported");
|
|
|
|
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);
|
|
view.setFixedSize(300, 300);
|
|
QCOMPARE(item->events.size(), 0);
|
|
view.show();
|
|
view.activateWindow();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
|
|
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);
|
|
#ifndef QT_NO_CONTEXTMENU
|
|
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);
|
|
#endif // QT_NO_CONTEXTMENU
|
|
}
|
|
|
|
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);
|
|
#ifndef QT_NO_CONTEXTMENU
|
|
QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, itemPoint, view.mapToGlobal(itemPoint));
|
|
QApplication::sendEvent(view.viewport(), &contextEvent);
|
|
QCOMPARE(item->events.size(), 501);
|
|
QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu);
|
|
#endif // QT_NO_CONTEXTMENU
|
|
}
|
|
}
|
|
|
|
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(), nullptr);
|
|
}
|
|
|
|
void tst_QGraphicsView::setScene()
|
|
{
|
|
QGraphicsScene scene(-1000, -1000, 2000, 2000);
|
|
|
|
QGraphicsView view(&scene);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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);
|
|
|
|
QTRY_VERIFY(!view.horizontalScrollBar()->isVisible());
|
|
QTRY_VERIFY(!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(), nullptr);
|
|
QCOMPARE(view2.scene(), nullptr);
|
|
QCOMPARE(view3.scene(), nullptr);
|
|
}
|
|
|
|
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));
|
|
item->setPen(QPen(Qt::black, 0));
|
|
|
|
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(QLatin1String("(0, ") + QString::number((i - 50) * 20))->setPos(0, (i - 50) * 20);
|
|
|
|
QGraphicsView view(&scene, &toplevel);
|
|
view.setFixedSize(200, 200);
|
|
toplevel.show();
|
|
QVERIFY(QTest::qWaitForWindowActive(&toplevel));
|
|
|
|
int size = 200;
|
|
scene.setSceneRect(-size, -size, size * 2, size * 2);
|
|
QCOMPARE(view.sceneRect(), scene.sceneRect());
|
|
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
QPointer<QWidget> widget = new QWidget;
|
|
view.setViewport(widget);
|
|
QCOMPARE(view.viewport(), (QWidget *)widget);
|
|
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
view.setViewport(0);
|
|
QVERIFY(widget.isNull());
|
|
QVERIFY(view.viewport() != 0);
|
|
QVERIFY(view.viewport() != widget);
|
|
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
}
|
|
|
|
#if QT_CONFIG(opengl)
|
|
void tst_QGraphicsView::openGLViewport()
|
|
{
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
|
|
QSKIP("QOpenGL is not supported on this platform.");
|
|
|
|
QGraphicsScene scene;
|
|
scene.setBackgroundBrush(Qt::white);
|
|
scene.addText("GraphicsView");
|
|
scene.addEllipse(QRectF(400, 50, 50, 50));
|
|
scene.addEllipse(QRectF(-100, -400, 50, 50));
|
|
scene.addEllipse(QRectF(50, -100, 50, 50));
|
|
scene.addEllipse(QRectF(-100, 50, 50, 50));
|
|
|
|
QGraphicsView view(&scene);
|
|
view.setSceneRect(-400, -400, 800, 800);
|
|
view.resize(400, 400);
|
|
|
|
QOpenGLWidget *glw = new QOpenGLWidget;
|
|
QSignalSpy spy1(glw, SIGNAL(resized()));
|
|
QSignalSpy spy2(glw, SIGNAL(frameSwapped()));
|
|
|
|
view.setViewport(glw);
|
|
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QTRY_VERIFY(spy1.size() > 0);
|
|
QTRY_VERIFY(spy2.size() >= spy1.size());
|
|
spy1.clear();
|
|
spy2.clear();
|
|
|
|
// Now test for resize (QTBUG-52419). This is special when the viewport is
|
|
// a QOpenGLWidget since the underlying FBO must also be maintained.
|
|
view.resize(300, 300);
|
|
QTRY_VERIFY(spy1.size() > 0);
|
|
QTRY_VERIFY(spy2.size() >= spy1.size());
|
|
// There is no sane way to check if the framebuffer contents got updated
|
|
// (grabFramebuffer is no good for the viewport case as that does not go
|
|
// through paintGL). So skip the actual verification.
|
|
}
|
|
#endif
|
|
|
|
void tst_QGraphicsView::dragMode_scrollHand()
|
|
{
|
|
for (int j = 0; j < 2; ++j) {
|
|
QGraphicsView view;
|
|
setFrameless(&view);
|
|
QCOMPARE(view.dragMode(), QGraphicsView::NoDrag);
|
|
|
|
view.setSceneRect(-1000, -1000, 2000, 2000);
|
|
view.setFixedSize(100, 100);
|
|
view.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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
|
|
auto pos = view.viewport()->rect().center();
|
|
QMouseEvent event(QEvent::MouseButtonPress, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
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
|
|
auto pos = view.viewport()->rect().center() + QPoint(10, 0);
|
|
QMouseEvent event(QEvent::MouseMove, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
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
|
|
auto pos = view.viewport()->rect().center() + QPoint(10, 10);
|
|
QMouseEvent event(QEvent::MouseMove, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
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
|
|
auto pos = view.viewport()->rect().center() + QPoint(10, 10);
|
|
QMouseEvent event(QEvent::MouseButtonRelease, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
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
|
|
auto pos = view.viewport()->rect().center() + QPoint(10, 10);
|
|
QMouseEvent event(QEvent::MouseButtonPress, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
QApplication::sendEvent(view.viewport(), &event);
|
|
}
|
|
{
|
|
// Release
|
|
auto pos = view.viewport()->rect().center() + QPoint(10, 10);
|
|
QMouseEvent event(QEvent::MouseButtonRelease, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
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);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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
|
|
auto pos = view.viewport()->rect().center();
|
|
QMouseEvent event(QEvent::MouseButtonPress, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
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
|
|
auto pos = view.viewport()->rect().center() + QPoint(100, 0);
|
|
QMouseEvent event(QEvent::MouseMove, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
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(!view.findChild<QRubberBand *>());
|
|
|
|
{
|
|
// Move
|
|
auto pos = view.viewport()->rect().center() + QPoint(100, 100);
|
|
QMouseEvent event(QEvent::MouseMove, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
event.setAccepted(true);
|
|
QApplication::sendEvent(view.viewport(), &event);
|
|
QVERIFY(event.isAccepted());
|
|
}
|
|
QCOMPARE(view.horizontalScrollBar()->value(), horizontalScrollBarValue);
|
|
QCOMPARE(view.verticalScrollBar()->value(), verticalScrollBarValue);
|
|
|
|
{
|
|
// Release
|
|
auto pos = view.viewport()->rect().center() + QPoint(100, 100);
|
|
QMouseEvent event(QEvent::MouseButtonRelease, pos,
|
|
view.viewport()->mapToGlobal(pos),
|
|
Qt::LeftButton, Qt::LeftButton, {});
|
|
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
|
|
|
|
if (view.scene())
|
|
QCOMPARE(scene.selectedItems().size(), 1);
|
|
|
|
view.setScene(&scene);
|
|
view.centerOn(0, 0);
|
|
}
|
|
}
|
|
|
|
void tst_QGraphicsView::rubberBandSelectionMode()
|
|
{
|
|
QWidget toplevel;
|
|
setFrameless(&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);
|
|
|
|
QVERIFY(scene.selectedItems().isEmpty());
|
|
sendMousePress(view.viewport(), QPoint(), Qt::LeftButton);
|
|
sendMouseMove(view.viewport(), view.viewport()->rect().center(),
|
|
Qt::LeftButton, Qt::LeftButton);
|
|
QCOMPARE(scene.selectedItems(), {rect});
|
|
sendMouseRelease(view.viewport(), QPoint(), Qt::LeftButton);
|
|
|
|
view.setRubberBandSelectionMode(Qt::ContainsItemShape);
|
|
QCOMPARE(view.rubberBandSelectionMode(), Qt::ContainsItemShape);
|
|
sendMousePress(view.viewport(), QPoint(), Qt::LeftButton);
|
|
QVERIFY(scene.selectedItems().isEmpty());
|
|
sendMouseMove(view.viewport(), view.viewport()->rect().center(),
|
|
Qt::LeftButton, Qt::LeftButton);
|
|
QVERIFY(scene.selectedItems().isEmpty());
|
|
sendMouseMove(view.viewport(), view.viewport()->rect().bottomRight(),
|
|
Qt::LeftButton, Qt::LeftButton);
|
|
QCOMPARE(scene.selectedItems(), {rect});
|
|
}
|
|
|
|
void tst_QGraphicsView::rubberBandExtendSelection()
|
|
{
|
|
QWidget toplevel;
|
|
setFrameless(&toplevel);
|
|
|
|
QGraphicsScene scene(0, 0, 1000, 1000);
|
|
|
|
QGraphicsView view(&scene, &toplevel);
|
|
view.setDragMode(QGraphicsView::RubberBandDrag);
|
|
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);
|
|
|
|
QGraphicsItem *item1 = scene.addRect(10, 10, 100, 100);
|
|
QGraphicsItem *item2 = scene.addRect(10, 120, 100, 100);
|
|
QGraphicsItem *item3 = scene.addRect(10, 230, 100, 100);
|
|
|
|
item1->setFlag(QGraphicsItem::ItemIsSelectable);
|
|
item2->setFlag(QGraphicsItem::ItemIsSelectable);
|
|
item3->setFlag(QGraphicsItem::ItemIsSelectable);
|
|
|
|
// select first item
|
|
item1->setSelected(true);
|
|
QCOMPARE(scene.selectedItems(), {item1});
|
|
|
|
// first rubberband without modifier key
|
|
sendMousePress(view.viewport(), view.mapFromScene(20, 115), Qt::LeftButton);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(20, 300), Qt::LeftButton, Qt::LeftButton);
|
|
QVERIFY(!item1->isSelected());
|
|
QVERIFY(item2->isSelected());
|
|
QVERIFY(item3->isSelected());
|
|
sendMouseRelease(view.viewport(), QPoint(), Qt::LeftButton);
|
|
|
|
scene.clearSelection();
|
|
|
|
// select first item
|
|
item1->setSelected(true);
|
|
QVERIFY(item1->isSelected());
|
|
|
|
// now rubberband with modifier key
|
|
{
|
|
QPoint clickPoint = view.mapFromScene(20, 115);
|
|
QMouseEvent event(QEvent::MouseButtonPress, clickPoint, view.viewport()->mapToGlobal(clickPoint),
|
|
Qt::LeftButton, {}, Qt::ControlModifier);
|
|
QApplication::sendEvent(view.viewport(), &event);
|
|
}
|
|
sendMouseMove(view.viewport(), view.mapFromScene(20, 300), Qt::LeftButton, Qt::LeftButton);
|
|
QVERIFY(item1->isSelected());
|
|
QVERIFY(item2->isSelected());
|
|
QVERIFY(item3->isSelected());
|
|
}
|
|
|
|
void tst_QGraphicsView::rotated_rubberBand()
|
|
{
|
|
QWidget toplevel;
|
|
setFrameless(&toplevel);
|
|
|
|
QGraphicsScene scene;
|
|
const int dim = 3;
|
|
for (int i = 0; i < dim; i++) {
|
|
for (int j = 0; j < dim; j ++) {
|
|
QGraphicsRectItem *rect = new QGraphicsRectItem(i * 20, j * 20, 10, 10);
|
|
rect->setFlag(QGraphicsItem::ItemIsSelectable);
|
|
rect->setData(0, (i == j));
|
|
scene.addItem(rect);
|
|
}
|
|
}
|
|
|
|
QGraphicsView view(&scene, &toplevel);
|
|
QCOMPARE(view.rubberBandSelectionMode(), Qt::IntersectsItemShape);
|
|
view.setDragMode(QGraphicsView::RubberBandDrag);
|
|
view.resize(120, 120);
|
|
view.rotate(45);
|
|
toplevel.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&toplevel));
|
|
|
|
// 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);
|
|
|
|
QVERIFY(scene.selectedItems().isEmpty());
|
|
int midWidth = view.viewport()->width() / 2;
|
|
sendMousePress(view.viewport(), QPoint(midWidth - 2, 0), Qt::LeftButton);
|
|
sendMouseMove(view.viewport(), QPoint(midWidth + 2, view.viewport()->height()),
|
|
Qt::LeftButton, Qt::LeftButton);
|
|
QCOMPARE(scene.selectedItems().size(), dim);
|
|
const auto items = scene.items();
|
|
for (const QGraphicsItem *item : items)
|
|
QCOMPARE(item->isSelected(), item->data(0).toBool());
|
|
sendMouseRelease(view.viewport(), QPoint(), Qt::LeftButton);
|
|
}
|
|
|
|
void tst_QGraphicsView::backgroundBrush()
|
|
{
|
|
QGraphicsScene scene;
|
|
QGraphicsView view(&scene);
|
|
scene.setBackgroundBrush(Qt::blue);
|
|
QCOMPARE(scene.backgroundBrush(), QBrush(Qt::blue));
|
|
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
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.transform(), QTransform());
|
|
|
|
// Check the convenience functions
|
|
view.rotate(90);
|
|
QCOMPARE(view.transform(), QTransform().rotate(90));
|
|
view.scale(2, 2);
|
|
QCOMPARE(view.transform(), QTransform().scale(2, 2) * QTransform().rotate(90));
|
|
view.shear(1.2, 1.2);
|
|
QCOMPARE(view.transform(), QTransform().shear(1.2, 1.2) * QTransform().scale(2, 2) * QTransform().rotate(90));
|
|
view.translate(1, 1);
|
|
QCOMPARE(view.transform(), QTransform().translate(1, 1) * QTransform().shear(1.2, 1.2) * QTransform().scale(2, 2) * QTransform().rotate(90));
|
|
}
|
|
|
|
void tst_QGraphicsView::matrix_combine()
|
|
{
|
|
// Check matrix combining
|
|
QGraphicsView view;
|
|
QCOMPARE(view.transform(), QTransform());
|
|
view.setTransform(QTransform().rotate(90), true);
|
|
view.setTransform(QTransform().rotate(90), true);
|
|
view.setTransform(QTransform().rotate(90), true);
|
|
view.setTransform(QTransform().rotate(90), true);
|
|
QCOMPARE(view.transform(), QTransform());
|
|
|
|
view.resetTransform();
|
|
QCOMPARE(view.transform(), QTransform());
|
|
view.setTransform(QTransform().rotate(90), false);
|
|
view.setTransform(QTransform().rotate(90), false);
|
|
view.setTransform(QTransform().rotate(90), false);
|
|
view.setTransform(QTransform().rotate(90), false);
|
|
QCOMPARE(view.transform(), QTransform().rotate(90));
|
|
}
|
|
|
|
void tst_QGraphicsView::centerOnPoint()
|
|
{
|
|
QWidget toplevel;
|
|
setFrameless(&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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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]->setTransform(QTransform().rotate(30), true);
|
|
items[1]->setTransform(QTransform().rotate(-30), true);
|
|
|
|
QGraphicsView view(&scene);
|
|
view.setSceneRect(-400, -400, 800, 800);
|
|
view.setFixedSize(400, 200);
|
|
|
|
view.showNormal();
|
|
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));
|
|
}
|
|
|
|
#if defined QT_BUILD_INTERNAL
|
|
void tst_QGraphicsView::itemsAtPosition_data()
|
|
{
|
|
QTest::addColumn<float>("rotation");
|
|
QTest::addColumn<float>("scale");
|
|
QTest::addColumn<QPoint>("viewPos");
|
|
QTest::addColumn<bool>("ignoreTransform");
|
|
QTest::addColumn<bool>("hit");
|
|
QTest::newRow("scaled + ignore transform, no hit") << 0.0f << 1000.0f << QPoint(0, 0) << true << false;
|
|
QTest::newRow("scaled + ignore transform, hit") << 0.0f << 1000.0f << QPoint(100, 100) << true << true;
|
|
QTest::newRow("rotated + scaled, no hit") << 45.0f << 2.0f << QPoint(90, 90) << false << false;
|
|
QTest::newRow("rotated + scaled, hit") << 45.0f << 2.0f << QPoint(100, 100) << false << true;
|
|
}
|
|
|
|
void tst_QGraphicsView::itemsAtPosition()
|
|
{
|
|
QFETCH(float, rotation);
|
|
QFETCH(float, scale);
|
|
QFETCH(QPoint, viewPos);
|
|
QFETCH(bool, ignoreTransform);
|
|
QFETCH(bool, hit);
|
|
|
|
FriendlyGraphicsScene scene;
|
|
scene.setSceneRect(QRect(-100, -100, 200, 200));
|
|
QGraphicsItem *item = scene.addRect(-5, -5, 10, 10);
|
|
|
|
if (ignoreTransform)
|
|
item->setFlag(QGraphicsItem::ItemIgnoresTransformations);
|
|
|
|
QGraphicsView view;
|
|
view.setFrameStyle(QFrame::NoFrame);
|
|
view.resize(200, 200);
|
|
view.scale(scale, scale);
|
|
view.rotate(rotation);
|
|
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
view.setScene(&scene);
|
|
view.showNormal();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
QPoint screenPos = view.viewport()->mapToGlobal(viewPos);
|
|
QPointF scenePos = view.mapToScene(viewPos);
|
|
QGraphicsScenePrivate *viewPrivate = scene.d_func();
|
|
QList<QGraphicsItem *> items;
|
|
items = viewPrivate->itemsAtPosition(screenPos, scenePos, view.viewport());
|
|
QCOMPARE(!items.empty(), hit);
|
|
}
|
|
#endif
|
|
|
|
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();
|
|
|
|
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 = nullptr) override
|
|
{
|
|
++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));
|
|
rect->setPen(QPen(Qt::black, 0));
|
|
scene.addItem(rect);
|
|
|
|
QGraphicsView view(&scene);
|
|
view.setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, !adjustForAntialiasing);
|
|
view.setRenderHint(QPainter::Antialiasing, adjustForAntialiasing);
|
|
view.setFrameStyle(0);
|
|
view.resize(300, 300);
|
|
view.showNormal();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
QTRY_VERIFY(rect->numPaints > 0);
|
|
|
|
QCoreApplication::processEvents(); // Process all queued paint events
|
|
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();
|
|
|
|
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();
|
|
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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");
|
|
|
|
// 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);
|
|
QSize viewSize(300,300);
|
|
|
|
view.setFixedSize(viewSize);
|
|
topLevel.show();
|
|
QApplication::processEvents();
|
|
QVERIFY(view.isVisible());
|
|
QCOMPARE(view.size(), viewSize);
|
|
|
|
// First once without setting the scene rect
|
|
#ifdef Q_PROCESSOR_ARM
|
|
const int step = 20;
|
|
#else
|
|
const int step = 5;
|
|
#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);
|
|
setFrameless(&view);
|
|
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);
|
|
setFrameless(&view);
|
|
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();
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
|
|
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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
QCOMPARE(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, {});
|
|
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, {}, {});
|
|
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);
|
|
|
|
QTest::keyPress(view.viewport(), Qt::Key_Space);
|
|
QCOMPARE(item->events.size(), 9);
|
|
QCOMPARE(item->events.at(item->events.size() - 2), QEvent::ShortcutOverride);
|
|
QCOMPARE(item->events.last(), QEvent::KeyPress);
|
|
}
|
|
|
|
#if QT_CONFIG(wheelevent)
|
|
class MouseWheelScene : public QGraphicsScene
|
|
{
|
|
public:
|
|
Qt::Orientation orientation;
|
|
|
|
void wheelEvent(QGraphicsSceneWheelEvent *event) override
|
|
{
|
|
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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
QCOMPARE(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())),
|
|
QPoint(), QPoint(120, 0), Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
|
|
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())),
|
|
QPoint(), QPoint(0, 120), Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
|
|
QApplication::sendEvent(view.viewport(), &event);
|
|
QCOMPARE(scene.orientation, Qt::Vertical);
|
|
}
|
|
|
|
QCOMPARE(spy.count(), 2);
|
|
QVERIFY(widget->hasFocus());
|
|
}
|
|
#endif // QT_CONFIG(wheelevent)
|
|
|
|
#ifndef QT_NO_CURSOR
|
|
void tst_QGraphicsView::cursor()
|
|
{
|
|
QGraphicsScene scene;
|
|
QGraphicsItem *item = scene.addRect(QRectF(-10, -10, 20, 20));
|
|
item->setCursor(Qt::IBeamCursor);
|
|
|
|
QGraphicsView view(&scene);
|
|
view.setFixedSize(400, 400);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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
|
|
|
|
#ifndef QT_NO_CURSOR
|
|
void tst_QGraphicsView::cursor2()
|
|
{
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-30, -30));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(0, 0));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-30, -30));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(0, 0));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-15, 0));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor);
|
|
|
|
view.setDragMode(QGraphicsView::ScrollHandDrag);
|
|
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-30, -30));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::OpenHandCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(0, 0));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-15, -15));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::OpenHandCursor);
|
|
|
|
view.setDragMode(QGraphicsView::NoDrag);
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::ArrowCursor);
|
|
view.viewport()->setCursor(Qt::PointingHandCursor);
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor);
|
|
|
|
item2->setCursor(Qt::SizeAllCursor);
|
|
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-30, -30));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-15, -15));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::SizeAllCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(0, 0));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-15, -15));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::SizeAllCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(0, 0));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-30, -30));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::PointingHandCursor);
|
|
|
|
view.setDragMode(QGraphicsView::ScrollHandDrag);
|
|
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-30, -30));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::OpenHandCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(0, 0));
|
|
QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor);
|
|
sendMouseMove(view.viewport(), view.mapFromScene(-15, -15));
|
|
QTRY_COMPARE(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);
|
|
setFrameless(&view);
|
|
|
|
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);
|
|
setFrameless(&view);
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
view.resize(100, 100);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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 = nullptr) : QGraphicsView(s) {}
|
|
CustomView(QGraphicsScene *s, QWidget *parent)
|
|
: QGraphicsView(s, parent) {}
|
|
QList<QRegion> lastUpdateRegions;
|
|
bool painted;
|
|
|
|
protected:
|
|
void paintEvent(QPaintEvent *event) override
|
|
{
|
|
lastUpdateRegions << event->region();
|
|
painted = true;
|
|
QGraphicsView::paintEvent(event);
|
|
}
|
|
};
|
|
|
|
void tst_QGraphicsView::viewportUpdateMode()
|
|
{
|
|
QGraphicsScene scene(0, 0, 100, 100);
|
|
scene.setBackgroundBrush(Qt::red);
|
|
|
|
CustomView view;
|
|
QScreen *screen = QGuiApplication::primaryScreen();
|
|
view.setFixedSize(QSize(500, 500).boundedTo(screen->availableGeometry().size())); // 500 is too big for all common smartphones
|
|
view.setScene(&scene);
|
|
QCOMPARE(view.viewportUpdateMode(), QGraphicsView::MinimalViewportUpdate);
|
|
|
|
// Show the view, and initialize our test.
|
|
view.show();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&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));
|
|
|
|
// 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().rectCount(), 2);
|
|
QCOMPARE(view.lastUpdateRegions.last().begin()[0].size(), QSize(14, 14));
|
|
QCOMPARE(view.lastUpdateRegions.last().begin()[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().rectCount(), 1);
|
|
QCOMPARE(view.lastUpdateRegions.last().begin()[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().rectCount(), 1);
|
|
QCOMPARE(view.lastUpdateRegions.last().begin()[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
|
|
const QMargins margins = view.contentsMargins();
|
|
view.resize(200 + margins.left() + margins.right(), 200 + margins.top() + margins.bottom());
|
|
toplevel.show();
|
|
QApplicationPrivate::setActiveWindow(&toplevel);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&toplevel));
|
|
QVERIFY(QTest::qWaitForWindowActive(&toplevel));
|
|
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();
|
|
QTRY_COMPARE(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
|
|
}
|
|
|
|
#if QT_CONFIG(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::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);
|
|
QCOMPARE(view.optimizationFlags(), QGraphicsView::OptimizationFlags(QGraphicsView::DontAdjustForAntialiasing));
|
|
}
|
|
|
|
class MessUpPainterItem : public QGraphicsRectItem
|
|
{
|
|
public:
|
|
using QGraphicsRectItem::QGraphicsRectItem;
|
|
bool dirtyPainter = false;
|
|
bool receivedPaintEvent = false;
|
|
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *w) override
|
|
{
|
|
receivedPaintEvent = true;
|
|
dirtyPainter = (painter->pen().color() != w->palette().color(w->foregroundRole()));
|
|
painter->setPen(Qt::red);
|
|
}
|
|
};
|
|
|
|
class MyGraphicsView : public QGraphicsView
|
|
{
|
|
public:
|
|
MyGraphicsView(QGraphicsScene * scene) : QGraphicsView(scene)
|
|
{ }
|
|
|
|
void drawBackground(QPainter * painter, const QRectF & rect) override
|
|
{
|
|
painter->setCompositionMode(QPainter::CompositionMode_Source);
|
|
painter->drawRect(rect);
|
|
}
|
|
|
|
void drawItems (QPainter * painter, int numItems, QGraphicsItem *items[], const QStyleOptionGraphicsItem options[]) override
|
|
{
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
parent->receivedPaintEvent = false;
|
|
child->receivedPaintEvent = false;
|
|
view.viewport()->update();
|
|
|
|
QTRY_VERIFY(parent->receivedPaintEvent);
|
|
QTRY_VERIFY(child->receivedPaintEvent);
|
|
QVERIFY(!parent->dirtyPainter);
|
|
QVERIFY(!child->dirtyPainter);
|
|
|
|
view.setOptimizationFlags(QGraphicsView::DontSavePainterState);
|
|
parent->receivedPaintEvent = false;
|
|
child->receivedPaintEvent = false;
|
|
view.viewport()->update();
|
|
|
|
QTRY_VERIFY(parent->receivedPaintEvent);
|
|
QTRY_VERIFY(child->receivedPaintEvent);
|
|
QVERIFY(!parent->dirtyPainter);
|
|
QVERIFY(child->dirtyPainter);
|
|
|
|
MyGraphicsView painter(&scene);
|
|
painter.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&painter));
|
|
|
|
MyGraphicsView painter2(&scene);
|
|
painter2.setOptimizationFlag(QGraphicsView::DontSavePainterState,true);
|
|
painter2.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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 &) override
|
|
{ transformInDrawBackground = p->worldTransform(); opacityInDrawBackground = p->opacity(); }
|
|
|
|
void drawForeground(QPainter *p, const QRectF &) override
|
|
{ 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.
|
|
QGraphicsRectItem *rectA = scene.addRect(0, 0, 20, 20);
|
|
QGraphicsRectItem *rectB = scene.addRect(50, 50, 20, 20);
|
|
|
|
rectA->setTransform(QTransform::fromScale(2, 2));
|
|
rectA->setPen(QPen(Qt::black, 0));
|
|
rectB->setTransform(QTransform::fromTranslate(200, 200));
|
|
rectB->setPen(QPen(Qt::black, 0));
|
|
|
|
const auto items = scene.items();
|
|
for (QGraphicsItem *item : 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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
// Make sure the view is repainted; otherwise the tests below will fail.
|
|
view.viewport()->update();
|
|
QTRY_VERIFY(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) override
|
|
{
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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();
|
|
}
|
|
|
|
// Simulates motif scrollbar for range tests
|
|
class FauxMotifStyle : public QCommonStyle {
|
|
public:
|
|
int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const override
|
|
{
|
|
if (hint == QStyle::SH_ScrollView_FrameOnlyAroundContents)
|
|
return true;
|
|
return QCommonStyle::styleHint(hint, option, widget, returnData);
|
|
}
|
|
|
|
int pixelMetric(PixelMetric m, const QStyleOption *opt, const QWidget *widget) const override
|
|
{
|
|
if (m == QStyle::PM_ScrollView_ScrollBarSpacing)
|
|
return 4;
|
|
return QCommonStyle::pixelMetric(m, opt, widget);
|
|
}
|
|
};
|
|
|
|
void tst_QGraphicsView::scrollBarRanges()
|
|
{
|
|
QFETCH(QByteArray, style);
|
|
QFETCH(QSize, viewportSize);
|
|
QFETCH(QRectF, sceneRect);
|
|
QFETCH(ScrollBarCount, sceneRectOffsetFactors);
|
|
QFETCH(QTransform, transform);
|
|
QFETCH(Qt::ScrollBarPolicy, hbarpolicy);
|
|
QFETCH(Qt::ScrollBarPolicy, vbarpolicy);
|
|
QFETCH(ExpectedValueDescription, hmin);
|
|
QFETCH(ExpectedValueDescription, hmax);
|
|
QFETCH(ExpectedValueDescription, vmin);
|
|
QFETCH(ExpectedValueDescription, vmax);
|
|
QFETCH(bool, useStyledPanel);
|
|
|
|
if (useStyledPanel && style == "macOS" && platformName == QStringLiteral("cocoa"))
|
|
QSKIP("Insignificant on OSX");
|
|
|
|
QScopedPointer<QStyle> stylePtr;
|
|
|
|
QGraphicsScene scene;
|
|
QGraphicsView view(&scene);
|
|
view.setRenderHint(QPainter::Antialiasing);
|
|
view.setTransform(transform);
|
|
view.setFrameStyle(useStyledPanel ? QFrame::StyledPanel : QFrame::NoFrame);
|
|
|
|
if (style == "motif")
|
|
stylePtr.reset(new FauxMotifStyle);
|
|
else
|
|
stylePtr.reset(QStyleFactory::create(QLatin1String(style)));
|
|
view.setStyle(stylePtr.data());
|
|
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.showNormal();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
const int offset = view.style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, 0);
|
|
|
|
QRectF actualSceneRect;
|
|
actualSceneRect.setLeft(sceneRect.left() + sceneRectOffsetFactors.left * offset);
|
|
actualSceneRect.setWidth(sceneRect.width() + sceneRectOffsetFactors.right * offset);
|
|
actualSceneRect.setTop(sceneRect.top() + sceneRectOffsetFactors.top * offset);
|
|
actualSceneRect.setHeight(sceneRect.height() + sceneRectOffsetFactors.bottom * offset);
|
|
scene.setSceneRect(actualSceneRect);
|
|
scene.addRect(actualSceneRect, QPen(Qt::blue), QBrush(QColor(Qt::green)));
|
|
|
|
int expectedHmin = hmin.value + hmin.scrollBarExtentsToAdd * offset;
|
|
int expectedVmin = vmin.value + vmin.scrollBarExtentsToAdd * offset;
|
|
int expectedHmax = hmax.value + hmax.scrollBarExtentsToAdd * offset;
|
|
int expectedVmax = vmax.value + vmax.scrollBarExtentsToAdd* offset;
|
|
if (useStyledPanel && view.style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) {
|
|
int spacing = view.style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing);
|
|
expectedHmin += hmin.spacingsToAdd * spacing;
|
|
expectedVmin += vmin.spacingsToAdd * spacing;
|
|
expectedHmax += hmax.spacingsToAdd * spacing;
|
|
expectedVmax += vmax.spacingsToAdd * spacing;
|
|
}
|
|
QCOMPARE(view.horizontalScrollBar()->minimum(), expectedHmin);
|
|
QCOMPARE(view.verticalScrollBar()->minimum(), expectedVmin);
|
|
QCOMPARE(view.horizontalScrollBar()->maximum(), expectedHmax);
|
|
QCOMPARE(view.verticalScrollBar()->maximum(), expectedVmax);
|
|
}
|
|
|
|
class TestView : public QGraphicsView
|
|
{
|
|
public:
|
|
TestView(QGraphicsScene *scene)
|
|
: QGraphicsView(scene), pressAccepted(false), doubleClickAccepted(false)
|
|
{ }
|
|
|
|
bool pressAccepted;
|
|
bool doubleClickAccepted;
|
|
|
|
protected:
|
|
void mousePressEvent(QMouseEvent *event) override
|
|
{
|
|
QGraphicsView::mousePressEvent(event);
|
|
pressAccepted = event->isAccepted();
|
|
}
|
|
void mouseDoubleClickEvent(QMouseEvent *event) override
|
|
{
|
|
QGraphicsView::mouseDoubleClickEvent(event);
|
|
doubleClickAccepted = event->isAccepted();
|
|
}
|
|
};
|
|
|
|
void tst_QGraphicsView::acceptMousePressEvent()
|
|
{
|
|
QGraphicsScene scene;
|
|
|
|
TestView view(&scene);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
QTest::mouseClick(view.viewport(), Qt::LeftButton);
|
|
QVERIFY(!view.pressAccepted);
|
|
|
|
QSignalSpy spy(&scene, &QGraphicsScene::changed);
|
|
scene.addRect(0, 0, 2000, 2000)->setFlag(QGraphicsItem::ItemIsMovable);
|
|
QVERIFY(spy.wait());
|
|
|
|
QTest::mouseClick(view.viewport(), Qt::LeftButton);
|
|
QVERIFY(view.pressAccepted);
|
|
}
|
|
|
|
void tst_QGraphicsView::acceptMouseDoubleClickEvent()
|
|
{
|
|
QGraphicsScene scene;
|
|
|
|
TestView view(&scene);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
QTest::mouseDClick(view.viewport(), Qt::LeftButton);
|
|
QVERIFY(!view.doubleClickAccepted);
|
|
|
|
QSignalSpy spy(&scene, &QGraphicsScene::changed);
|
|
scene.addRect(0, 0, 2000, 2000)->setFlag(QGraphicsItem::ItemIsMovable);
|
|
QVERIFY(spy.wait());
|
|
|
|
QTest::mouseDClick(view.viewport(), Qt::LeftButton);
|
|
QVERIFY(view.doubleClickAccepted);
|
|
}
|
|
|
|
class TestWidget : public QWidget
|
|
{
|
|
public:
|
|
TestWidget()
|
|
: QWidget(), pressForwarded(false), doubleClickForwarded(false)
|
|
{ }
|
|
|
|
bool pressForwarded;
|
|
bool doubleClickForwarded;
|
|
|
|
protected:
|
|
void mousePressEvent(QMouseEvent *event) override
|
|
{
|
|
QWidget::mousePressEvent(event);
|
|
pressForwarded = true;
|
|
}
|
|
void mouseDoubleClickEvent(QMouseEvent *event) override
|
|
{
|
|
QWidget::mouseDoubleClickEvent(event);
|
|
doubleClickForwarded = true;
|
|
}
|
|
};
|
|
|
|
void tst_QGraphicsView::forwardMousePress()
|
|
{
|
|
TestWidget widget;
|
|
QGraphicsScene scene;
|
|
QGraphicsView view(&scene);
|
|
QHBoxLayout layout;
|
|
widget.setLayout(&layout);
|
|
layout.addWidget(&view);
|
|
widget.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&widget));
|
|
|
|
widget.pressForwarded = false;
|
|
QTest::mouseClick(view.viewport(), Qt::LeftButton);
|
|
QVERIFY(widget.pressForwarded);
|
|
|
|
scene.addRect(0, 0, 2000, 2000);
|
|
|
|
qApp->processEvents(); // ensure scene rect is updated
|
|
|
|
widget.pressForwarded = false;
|
|
QTest::mouseClick(view.viewport(), Qt::LeftButton);
|
|
QVERIFY(widget.pressForwarded);
|
|
}
|
|
|
|
void tst_QGraphicsView::forwardMouseDoubleClick()
|
|
{
|
|
TestWidget widget;
|
|
QGraphicsScene scene;
|
|
QGraphicsView view(&scene);
|
|
QHBoxLayout layout;
|
|
widget.setLayout(&layout);
|
|
layout.addWidget(&view);
|
|
widget.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&widget));
|
|
|
|
widget.doubleClickForwarded = false;
|
|
QTest::mouseDClick(view.viewport(), Qt::LeftButton);
|
|
QVERIFY(widget.doubleClickForwarded);
|
|
|
|
scene.addRect(0, 0, 2000, 2000);
|
|
|
|
qApp->processEvents(); // ensure scene rect is updated
|
|
|
|
widget.doubleClickForwarded = false;
|
|
QTest::mouseDClick(view.viewport(), Qt::LeftButton);
|
|
QVERIFY(widget.doubleClickForwarded);
|
|
}
|
|
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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 = nullptr)
|
|
: QGraphicsTextItem(text, parent) {}
|
|
QRectF exposedRect;
|
|
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
|
|
{
|
|
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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
QCOMPARE(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) override
|
|
{ ++presses; QGraphicsScene::mousePressEvent(event); }
|
|
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override
|
|
{ ++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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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) override
|
|
{
|
|
++mouseMoves;
|
|
QGraphicsView::mouseMoveEvent(event);
|
|
const auto items = scene()->items();
|
|
for (QGraphicsItem *item : 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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
view.viewport()->setMouseTracking(true);
|
|
|
|
QCOMPARE(view.mouseMoves, 0);
|
|
{
|
|
auto pos = view.mapFromScene(25, 25);
|
|
QMouseEvent event(QEvent::MouseMove, pos, view.viewport()->mapToGlobal(pos), Qt::NoButton, {}, {});
|
|
QApplication::sendEvent(view.viewport(), &event);
|
|
}
|
|
QCOMPARE(view.mouseMoves, 1);
|
|
QTest::qWait(25);
|
|
QTRY_COMPARE(view.mouseMoves, 1);
|
|
QTest::qWait(25);
|
|
{
|
|
auto pos = view.mapFromScene(25, 25);
|
|
QMouseEvent event(QEvent::MouseMove, pos, view.viewport()->mapToGlobal(pos), Qt::NoButton, {}, {});
|
|
QApplication::sendEvent(view.viewport(), &event);
|
|
}
|
|
QCOMPARE(view.mouseMoves, 2);
|
|
QTest::qWait(15);
|
|
}
|
|
|
|
void tst_QGraphicsView::task207546_focusCrash()
|
|
{
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
QSKIP("Window activation is not supported");
|
|
|
|
class _Widget : public QWidget
|
|
{
|
|
public:
|
|
bool focusNextPrevChild(bool next) override { 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();
|
|
widget.activateWindow();
|
|
QApplicationPrivate::setActiveWindow(&widget);
|
|
QVERIFY(QTest::qWaitForWindowActive(&widget));
|
|
QCOMPARE(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,
|
|
view.viewport()->mapToGlobal(origPos), Qt::LeftButton, {}, {});
|
|
QMouseEvent move(QEvent::MouseMove, step1Pos,
|
|
view.viewport()->mapToGlobal(step1Pos), Qt::LeftButton, {}, {});
|
|
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,
|
|
view.viewport()->mapToGlobal(step1Pos), Qt::LeftButton, {}, {});
|
|
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,
|
|
view.viewport()->mapToGlobal(step2Pos), Qt::LeftButton, {}, {});
|
|
QApplication::sendEvent(view.viewport(), &move);
|
|
}
|
|
|
|
// Check that no draggin has occurred...
|
|
QCOMPARE(basePos, view.mapFromScene(0, 0));
|
|
}
|
|
|
|
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 = nullptr;
|
|
|
|
if (a) {
|
|
view = new QGraphicsView(&scene);
|
|
connect(&scene, SIGNAL(changed(QList<QRectF>)), &cl, SLOT(changed(QList<QRectF>)));
|
|
} else {
|
|
connect(&scene, SIGNAL(changed(QList<QRectF>)), &cl, SLOT(changed(QList<QRectF>)));
|
|
view = new QGraphicsView(&scene);
|
|
}
|
|
|
|
EventSpy spy(view->viewport(), QEvent::Paint);
|
|
QCOMPARE(spy.count(), 0);
|
|
|
|
view->show();
|
|
QApplicationPrivate::setActiveWindow(view);
|
|
QVERIFY(QTest::qWaitForWindowActive(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;
|
|
setFrameless(&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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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);
|
|
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 *) override
|
|
{
|
|
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->setTransform(QTransform::fromTranslate(5, 5), true);
|
|
|
|
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;
|
|
|
|
QCOMPARE(a, b);
|
|
delete v1;
|
|
}
|
|
|
|
/*!
|
|
Verify that a nested graphics view and embedded widgets receive window
|
|
activation and focus correctly.
|
|
|
|
See QTBUG-94091.
|
|
*/
|
|
void tst_QGraphicsView::embeddedViewsWithFocus()
|
|
{
|
|
class FocusWidget : public QWidget
|
|
{
|
|
public:
|
|
FocusWidget() { setFocusPolicy(Qt::StrongFocus); }
|
|
QSize sizeHint() const override { return QSize(100, 100); }
|
|
|
|
int focusCount = 0;
|
|
protected:
|
|
void mousePressEvent(QMouseEvent *) override {} // accept event to avoid warning
|
|
void focusInEvent(QFocusEvent *) override { ++focusCount; }
|
|
void focusOutEvent(QFocusEvent *) override { --focusCount; }
|
|
};
|
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
|
|
|
QGraphicsScene innerScene;
|
|
FocusWidget *innerWidget = new FocusWidget;
|
|
innerScene.addWidget(innerWidget);
|
|
QGraphicsView *innerView = new QGraphicsView(&innerScene);
|
|
|
|
QGraphicsScene outerScene;
|
|
FocusWidget *outerWidget = new FocusWidget;
|
|
QGraphicsProxyWidget *outerProxy = outerScene.addWidget(outerWidget);
|
|
QGraphicsProxyWidget *nestedProxy = outerScene.addWidget(innerView);
|
|
outerProxy->setPos(0, 0);
|
|
nestedProxy->setPos(0, outerWidget->sizeHint().height());
|
|
QGraphicsView outerView(&outerScene);
|
|
outerView.show();
|
|
outerView.activateWindow();
|
|
QVERIFY(QTest::qWaitForWindowActive(&outerView));
|
|
const QPoint outerCenter(QPoint(innerWidget->sizeHint().width() / 2,
|
|
innerWidget->sizeHint().height() / 2));
|
|
const QPoint innerCenter(outerCenter + QPoint(0, innerWidget->sizeHint().height()));
|
|
QCOMPARE(outerView.itemAt(outerCenter), outerProxy);
|
|
QCOMPARE(outerView.itemAt(innerCenter), nestedProxy);
|
|
QVERIFY(outerScene.isActive());
|
|
QVERIFY(innerScene.isActive());
|
|
|
|
QCOMPARE(outerWidget->focusCount, 0);
|
|
QCOMPARE(innerWidget->focusCount, 0);
|
|
QTest::mouseClick(outerView.viewport(), Qt::LeftButton, {}, outerCenter);
|
|
QCOMPARE(outerWidget->focusCount, 1);
|
|
QCOMPARE(innerWidget->focusCount, 0);
|
|
QTest::mouseClick(outerView.viewport(), Qt::LeftButton, {}, innerCenter);
|
|
QCOMPARE(outerWidget->focusCount, 0);
|
|
QCOMPARE(innerWidget->focusCount, 1);
|
|
}
|
|
|
|
void tst_QGraphicsView::scrollAfterResize_data()
|
|
{
|
|
QTest::addColumn<bool>("reverse");
|
|
QTest::addColumn<QTransform>("x1");
|
|
QTest::addColumn<QTransform>("x2");
|
|
QTest::addColumn<QTransform>("x3");
|
|
|
|
QStyle *style = QStyleFactory::create("windows");
|
|
|
|
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);
|
|
delete style;
|
|
}
|
|
|
|
void tst_QGraphicsView::scrollAfterResize()
|
|
{
|
|
QFETCH(bool, reverse);
|
|
QFETCH(QTransform, x1);
|
|
QFETCH(QTransform, x2);
|
|
QFETCH(QTransform, x3);
|
|
|
|
QStyle *style = QStyleFactory::create("windows");
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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);
|
|
delete style;
|
|
}
|
|
|
|
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, this));
|
|
rect = scene()->addRect(0, 0, 10, 10);
|
|
rect->setPos(50, 50);
|
|
rect->setPen(QPen(Qt::black, 0));
|
|
painted = false;
|
|
}
|
|
QRegion lastPaintedRegion;
|
|
QGraphicsRectItem *rect;
|
|
bool painted;
|
|
void waitForPaintEvent()
|
|
{
|
|
QTimer::singleShot(2000, &eventLoop, SLOT(quit()));
|
|
eventLoop.exec();
|
|
}
|
|
protected:
|
|
QEventLoop eventLoop;
|
|
void paintEvent(QPaintEvent *event) override
|
|
{
|
|
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.showNormal();
|
|
if (changedConnected)
|
|
QObject::connect(view.scene(), SIGNAL(changed(QList<QRectF>)), this, SLOT(dummySlot()));
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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);
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
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);
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&toplevel));
|
|
|
|
QImage before(view.viewport()->size(), QImage::Format_ARGB32);
|
|
view.viewport()->render(&before);
|
|
|
|
item->setPos(20, 0);
|
|
view.centerOn(item);
|
|
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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);
|
|
auto pos = view.viewport()->rect().center();
|
|
QMouseEvent event(QEvent::MouseMove, pos, view.viewport()->mapToGlobal(pos),
|
|
Qt::NoButton, Qt::MouseButtons(Qt::NoButton), {});
|
|
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) override
|
|
{
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QApplication::processEvents();
|
|
QTRY_VERIFY(view.painted);
|
|
|
|
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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&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
|
|
const QMargins margins = view.contentsMargins();
|
|
view.resize(200 + margins.left() + margins.right(), 200 + margins.top() + margins.bottom());
|
|
toplevel.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&toplevel));
|
|
|
|
|
|
QApplicationPrivate::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);
|
|
#else
|
|
Q_UNUSED(updateRect);
|
|
#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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&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
|
|
|
|
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 */) override
|
|
{
|
|
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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
QCOMPARE(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));
|
|
}
|
|
|
|
void tst_QGraphicsView::inputContextReset()
|
|
{
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
QSKIP("Window activation is not supported");
|
|
|
|
PlatformInputContext inputContext;
|
|
QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
|
|
inputMethodPrivate->testContext = &inputContext;
|
|
|
|
QGraphicsScene scene;
|
|
QGraphicsView view(&scene);
|
|
QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled));
|
|
|
|
view.show();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
|
|
|
|
QGraphicsItem *item1 = new QGraphicsRectItem;
|
|
item1->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemAcceptsInputMethod);
|
|
|
|
inputContext.m_resetCallCount = 0;
|
|
inputContext.m_commitCallCount = 0;
|
|
scene.addItem(item1);
|
|
QCOMPARE(inputContext.m_resetCallCount, 0);
|
|
QCOMPARE(inputContext.m_commitCallCount, 0);
|
|
|
|
scene.setFocusItem(item1);
|
|
QCOMPARE(scene.focusItem(), (QGraphicsItem *)item1);
|
|
QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled));
|
|
QCOMPARE(inputContext.m_resetCallCount, 0);
|
|
QCOMPARE(inputContext.m_commitCallCount, 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.
|
|
// QEXPECT_FAIL("", "QTBUG-22454", Abort);
|
|
QCOMPARE(inputContext.m_resetCallCount + inputContext.m_commitCallCount, 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.m_resetCallCount = 0;
|
|
inputContext.m_commitCallCount = 0;
|
|
scene.setFocusItem(item2);
|
|
QCOMPARE(inputContext.m_resetCallCount, 0);
|
|
QCOMPARE(inputContext.m_commitCallCount, 0);
|
|
|
|
scene.setFocusItem(item1);
|
|
QCOMPARE(inputContext.m_resetCallCount, 0);
|
|
QCOMPARE(inputContext.m_commitCallCount, 0);
|
|
|
|
// test changing between between items that accept input methods.
|
|
item2->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemAcceptsInputMethod);
|
|
scene.setFocusItem(item2);
|
|
QCOMPARE(inputContext.m_resetCallCount + inputContext.m_commitCallCount, 1);
|
|
}
|
|
|
|
void tst_QGraphicsView::indirectPainting()
|
|
{
|
|
class MyScene : public QGraphicsScene
|
|
{ public:
|
|
MyScene() : QGraphicsScene(), drawCount(0) {}
|
|
void drawItems(QPainter *, int, QGraphicsItem **, const QStyleOptionGraphicsItem *, QWidget *) override
|
|
{ ++drawCount; }
|
|
int drawCount;
|
|
};
|
|
|
|
MyScene scene;
|
|
QGraphicsItem *item = scene.addRect(0, 0, 50, 50);
|
|
|
|
QGraphicsView view(&scene);
|
|
view.setOptimizationFlag(QGraphicsView::IndirectPainting);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QTRY_VERIFY(scene.drawCount > 0);
|
|
|
|
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 &) override
|
|
{
|
|
compositionMode = painter->compositionMode();
|
|
painted = true;
|
|
}
|
|
};
|
|
|
|
QGraphicsScene dummy;
|
|
MyView view(&dummy);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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);
|
|
}
|
|
|
|
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();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
const bool isActiveWindow = QTest::qWaitForWindowActive(&view);
|
|
if (!isActiveWindow)
|
|
QSKIP("Window activation failed, skipping test", Abort);
|
|
// This is highly unstable (observed to pass on Windows and some Linux configurations).
|
|
#ifndef Q_OS_MAC
|
|
for (int i = 0; i < 4; ++i) {
|
|
QPoint mouseViewPos(20, 20);
|
|
sendMouseMove(view.viewport(), mouseViewPos);
|
|
|
|
QPointF mouseScenePos = view.mapToScene(mouseViewPos);
|
|
view.setTransform(QTransform().scale(5, 5).rotate(5, Qt::ZAxis), true);
|
|
|
|
qreal slack = 1;
|
|
|
|
QPointF newMouseScenePos = view.mapToScene(mouseViewPos);
|
|
|
|
const qreal dx = qAbs(newMouseScenePos.x() - mouseScenePos.x());
|
|
const qreal dy = qAbs(newMouseScenePos.y() - mouseScenePos.y());
|
|
const QByteArray message = QString::fromLatin1("QTBUG-22455, distance: dx=%1, dy=%2 slack=%3 (%4).").
|
|
arg(dx).arg(dy).arg(slack).arg(qApp->style()->metaObject()->className()).toLocal8Bit();
|
|
if (i == 9 || (dx < slack && dy < slack)) {
|
|
QVERIFY2(dx < slack && dy < slack, message.constData());
|
|
break;
|
|
}
|
|
|
|
QTest::qWait(100);
|
|
}
|
|
#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) override
|
|
{
|
|
QGraphicsView::paintEvent(event);
|
|
|
|
if (itSTimeToTest)
|
|
{
|
|
QEXPECT_FAIL("", "QTBUG-24296", Continue);
|
|
QCOMPARE(event->region(), updateRegion);
|
|
}
|
|
}
|
|
};
|
|
|
|
SAGraphicsView view(&scene);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
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);
|
|
setFrameless(&view);
|
|
view.setFrameStyle(0);
|
|
view.resize(75, 75);
|
|
view.show();
|
|
view.activateWindow();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
QCOMPARE(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) override
|
|
{ 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) override
|
|
{ lastExposedRect = option->exposedRect; }
|
|
QRectF lastExposedRect;
|
|
};
|
|
|
|
CustomScene scene(QRectF(0,0,50,50));
|
|
|
|
CustomRectItem item(scene.sceneRect());
|
|
|
|
scene.addItem(&item);
|
|
|
|
QGraphicsView view(&scene);
|
|
view.scale(4.15, 4.15);
|
|
view.showNormal();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
|
|
view.viewport()->update(10,10,20,20);
|
|
QTRY_COMPARE(item.lastExposedRect, scene.lastBackgroundExposedRect);
|
|
}
|
|
|
|
#ifndef QT_NO_CURSOR
|
|
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();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&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()
|
|
{
|
|
setRect(0, 0, 100, 100);
|
|
setAcceptHoverEvents(true);
|
|
}
|
|
|
|
bool sceneEvent(QEvent *event) override
|
|
{
|
|
if (!checkEvents) // ensures that we don't look at stray events before we are ready
|
|
return QGraphicsRectItem::sceneEvent(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 = false;
|
|
bool receivedLeaveEvent = false;
|
|
QWidget *enterWidget = nullptr;
|
|
QWidget *leaveWidget = nullptr;
|
|
bool checkEvents = false;
|
|
};
|
|
|
|
void tst_QGraphicsView::hoverLeave()
|
|
{
|
|
QGraphicsScene scene;
|
|
QGraphicsView view(&scene);
|
|
view.resize(160, 160);
|
|
GraphicsItemWithHover *item = new GraphicsItemWithHover;
|
|
scene.addItem(item);
|
|
|
|
view.showNormal();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
QWindow *viewWindow = view.window()->windowHandle();
|
|
QPoint posOutsideItem = view.mapFromScene(item->mapToScene(0, 0)) - QPoint(5, 0);
|
|
QPoint posOutsideItemGlobal = view.mapToGlobal(posOutsideItem);
|
|
QPoint posOutsideItemInWindow = viewWindow->mapFromGlobal(posOutsideItemGlobal);
|
|
QTest::mouseMove(viewWindow, posOutsideItemInWindow);
|
|
|
|
item->checkEvents = true;
|
|
QPoint posInItemGlobal = view.mapToGlobal(view.mapFromScene(item->mapToScene(10, 10)));
|
|
QTest::mouseMove(viewWindow, viewWindow->mapFromGlobal(posInItemGlobal));
|
|
QTRY_VERIFY(item->receivedEnterEvent);
|
|
QCOMPARE(item->enterWidget, view.viewport());
|
|
|
|
QTest::mouseMove(viewWindow, posOutsideItemInWindow);
|
|
|
|
QTRY_VERIFY(item->receivedLeaveEvent);
|
|
QCOMPARE(item->leaveWidget, view.viewport());
|
|
}
|
|
|
|
class IMItem : public QGraphicsRectItem
|
|
{
|
|
public:
|
|
IMItem(QGraphicsItem *parent = nullptr):
|
|
QGraphicsRectItem(QRectF(0, 0, 20, 20), parent)
|
|
{
|
|
setFlag(QGraphicsItem::ItemIsFocusable, true);
|
|
setFlag(QGraphicsItem::ItemAcceptsInputMethod, true);
|
|
setPen(Qt::NoPen); // Avoid adding a half pixel border to the rect.
|
|
}
|
|
|
|
QVariant inputMethodQuery(Qt::InputMethodQuery) const override
|
|
{
|
|
return mf;
|
|
}
|
|
|
|
static QRectF mf;
|
|
};
|
|
|
|
QRectF IMItem::mf(1.5, 1.6, 10, 10);
|
|
|
|
void tst_QGraphicsView::QTBUG_16063_microFocusRect()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
QGraphicsScene scene;
|
|
IMItem *item = new IMItem();
|
|
scene.addItem(item);
|
|
|
|
QGraphicsView view(&scene);
|
|
setFrameless(&view);
|
|
|
|
view.setFixedSize(40, 40);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
QVERIFY(QTest::qWaitForWindowActive(&view));
|
|
|
|
scene.setFocusItem(item);
|
|
view.setFocus();
|
|
QRectF mfv = view.inputMethodQuery(Qt::ImCursorRectangle).toRectF();
|
|
QCOMPARE(mfv, IMItem::mf.translated(-view.mapToScene(view.sceneRect().toRect()).boundingRect().topLeft()));
|
|
}
|
|
|
|
void tst_QGraphicsView::QTBUG_70255_scrollTo()
|
|
{
|
|
QGraphicsView view;
|
|
QGraphicsScene scene;
|
|
view.setFixedSize(200, 200);
|
|
scene.setSceneRect(0, 0, 1000, 1000);
|
|
QGraphicsRectItem item;
|
|
item.setRect(-20, -20, 40, 40);
|
|
item.setFlag(QGraphicsItem::ItemIsMovable, true);
|
|
scene.addItem(&item);
|
|
view.setScene(&scene);
|
|
view.centerOn(0, 0);
|
|
|
|
view.show();
|
|
QApplicationPrivate::setActiveWindow(&view);
|
|
if (!QTest::qWaitForWindowExposed(&view) || !QTest::qWaitForWindowActive(&view))
|
|
QSKIP("Failed to show and activate window");
|
|
|
|
QPoint point = view.mapFromScene(0, 0);
|
|
QCOMPARE(point, QPoint(0, 0));
|
|
|
|
QScroller::scroller(&view)->scrollTo(QPointF(0, 500), 100);
|
|
QTest::qWait(200);
|
|
|
|
point = view.mapFromScene(0, 0);
|
|
QCOMPARE(point, QPoint(0, -500));
|
|
}
|
|
|
|
void tst_QGraphicsView::resizeContentsOnItemDrag_data()
|
|
{
|
|
QTest::addColumn<Qt::Alignment>("alignment");
|
|
QTest::addColumn<Qt::Orientation>("orientation");
|
|
QTest::addRow("Center right") << Qt::Alignment(Qt::AlignCenter) << Qt::Horizontal;
|
|
QTest::addRow("Center down") << Qt::Alignment(Qt::AlignCenter) << Qt::Vertical;
|
|
QTest::addRow("BottomLeft right") << (Qt::AlignBottom | Qt::AlignLeft) << Qt::Horizontal;
|
|
QTest::addRow("TopRight down") << (Qt::AlignTop | Qt::AlignRight) << Qt::Vertical;
|
|
}
|
|
|
|
void tst_QGraphicsView::resizeContentsOnItemDrag()
|
|
{
|
|
QFETCH(Qt::Alignment, alignment);
|
|
QFETCH(Qt::Orientation, orientation);
|
|
|
|
QGraphicsView view;
|
|
QGraphicsScene scene;
|
|
view.setFixedSize(200, 200);
|
|
view.setScene(&scene);
|
|
|
|
view.setAlignment(alignment);
|
|
|
|
class MovableItem : public QGraphicsEllipseItem
|
|
{
|
|
public:
|
|
using QGraphicsEllipseItem::QGraphicsEllipseItem;
|
|
|
|
QList<QPointF> scenePositions;
|
|
|
|
protected:
|
|
void mousePressEvent(QGraphicsSceneMouseEvent *event) override
|
|
{
|
|
scenePositions << event->scenePos();
|
|
}
|
|
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override
|
|
{
|
|
scenePositions << event->scenePos();
|
|
QGraphicsEllipseItem::mouseMoveEvent(event);
|
|
}
|
|
};
|
|
|
|
MovableItem item(-10, -10, 20, 20);
|
|
item.setFlags(QGraphicsItem::ItemIsMovable);
|
|
scene.addItem(&item);
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
// Position the item near the relevant edge of the view, with a few pixels
|
|
// to go until the scrollbars should be showing.
|
|
if (orientation == Qt::Horizontal)
|
|
item.setPos(view.width() - item.rect().width() - 5, 0);
|
|
else
|
|
item.setPos(0, view.height() - item.rect().height() - 5);
|
|
QApplication::processEvents(); // queued connection used to trigger recalculateContentSize
|
|
QPoint mousePos = view.mapFromScene(item.pos());
|
|
|
|
QTest::mousePress(view.viewport(), Qt::LeftButton, {}, mousePos);
|
|
QCOMPARE(item.scenePositions.count(), 1);
|
|
QCOMPARE(item.scenePositions.takeLast(), view.mapToScene(mousePos));
|
|
|
|
auto lastItemPos = item.pos();
|
|
auto lastScenePos = view.mapToScene(mousePos);
|
|
int overshoot = 0;
|
|
const QScrollBar *scrollBar = orientation == Qt::Horizontal
|
|
? view.horizontalScrollBar()
|
|
: view.verticalScrollBar();
|
|
// Drag the item until the scroll bars become visible, and then for a few more pixels.
|
|
// Verify that the item doesn't jump when the scrollbar shows.
|
|
while (overshoot < 10) {
|
|
if (orientation == Qt::Horizontal)
|
|
mousePos.rx() += 1;
|
|
else
|
|
mousePos.ry() += 1;
|
|
QTest::mouseMove(view.viewport(), mousePos);
|
|
QApplication::processEvents(); // queued connection used to trigger recalculateContentSize
|
|
const bool scrollbarAvailable = scrollBar->maximum() > scrollBar->minimum();
|
|
bool allowMoreEvents = false;
|
|
if (scrollbarAvailable) {
|
|
if (!overshoot) {
|
|
QTRY_VERIFY(scrollBar->isVisible());
|
|
// scrollbar becoming visible triggers event replay, so we get more than one
|
|
allowMoreEvents = true;
|
|
}
|
|
++overshoot;
|
|
}
|
|
if (allowMoreEvents)
|
|
QCOMPARE_GE(item.scenePositions.count(), 1);
|
|
else
|
|
QCOMPARE(item.scenePositions.count(), 1);
|
|
const auto scenePos = item.scenePositions.takeLast();
|
|
item.scenePositions.clear();
|
|
|
|
const auto same = orientation == Qt::Horizontal ? &QPointF::y : &QPointF::x;
|
|
const auto moved = orientation == Qt::Horizontal ? &QPointF::x : &QPointF::y;
|
|
QCOMPARE((item.pos().*same)(), (lastItemPos.*same)());
|
|
QCOMPARE_GE((item.pos().*moved)() - (lastItemPos.*moved)(), 1);
|
|
QCOMPARE_LE((item.pos().*moved)() - (lastItemPos.*moved)(), 2);
|
|
lastItemPos = item.pos();
|
|
|
|
QCOMPARE((scenePos.*same)(), (lastScenePos.*same)());
|
|
QCOMPARE_GE((scenePos.*moved)() - (lastScenePos.*moved)(), 1);
|
|
QCOMPARE_LE((scenePos.*moved)() - (lastScenePos.*moved)(), 2);
|
|
lastScenePos = scenePos;
|
|
}
|
|
}
|
|
|
|
QTEST_MAIN(tst_QGraphicsView)
|
|
#include "tst_qgraphicsview.moc"
|