1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <QtGui/QPainterPath>
32#include <QtWidgets/qgraphicsscene.h>
33#include <private/qgraphicsscenebsptreeindex_p.h>
34#include <private/qgraphicssceneindex_p.h>
35#include <private/qgraphicsscenelinearindex_p.h>
36
37class tst_QGraphicsSceneIndex : public QObject
38{
39 Q_OBJECT
40public slots:
41 void initTestCase();
42
43private slots:
44 void scatteredItems_data();
45 void scatteredItems();
46 void overlappedItems_data();
47 void overlappedItems();
48 void movingItems_data();
49 void movingItems();
50 void connectedToSceneRectChanged();
51 void items();
52 void boundingRectPointIntersection_data();
53 void boundingRectPointIntersection();
54 void removeItems();
55 void clear();
56
57private:
58 void common_data();
59 QGraphicsSceneIndex *createIndex(const QString &name);
60};
61
62void tst_QGraphicsSceneIndex::initTestCase()
63{
64}
65
66void tst_QGraphicsSceneIndex::common_data()
67{
68 QTest::addColumn<QString>(name: "indexMethod");
69
70 QTest::newRow(dataTag: "BSP") << QString("bsp");
71 QTest::newRow(dataTag: "Linear") << QString("linear");
72}
73
74QGraphicsSceneIndex *tst_QGraphicsSceneIndex::createIndex(const QString &indexMethod)
75{
76 QGraphicsSceneIndex *index = 0;
77 QGraphicsScene *scene = new QGraphicsScene();
78 if (indexMethod == "bsp")
79 index = new QGraphicsSceneBspTreeIndex(scene);
80
81 if (indexMethod == "linear")
82 index = new QGraphicsSceneLinearIndex(scene);
83
84 return index;
85}
86
87void tst_QGraphicsSceneIndex::scatteredItems_data()
88{
89 common_data();
90}
91
92void tst_QGraphicsSceneIndex::scatteredItems()
93{
94 QFETCH(QString, indexMethod);
95
96 QGraphicsScene scene;
97 scene.setItemIndexMethod(indexMethod == "linear" ? QGraphicsScene::NoIndex : QGraphicsScene::BspTreeIndex);
98
99 for (int i = 0; i < 10; ++i)
100 scene.addRect(x: i*50, y: i*50, w: 40, h: 35);
101
102 QCOMPARE(scene.items(QPointF(5, 5)).count(), 1);
103 QCOMPARE(scene.items(QPointF(55, 55)).count(), 1);
104 QCOMPARE(scene.items(QPointF(-100, -100)).count(), 0);
105
106 QCOMPARE(scene.items(QRectF(0, 0, 10, 10)).count(), 1);
107 QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 10);
108 QCOMPARE(scene.items(QRectF(-100, -1000, 0, 0)).count(), 0);
109}
110
111void tst_QGraphicsSceneIndex::overlappedItems_data()
112{
113 common_data();
114}
115
116void tst_QGraphicsSceneIndex::overlappedItems()
117{
118 QFETCH(QString, indexMethod);
119
120 QGraphicsScene scene;
121 scene.setItemIndexMethod(indexMethod == "linear" ? QGraphicsScene::NoIndex : QGraphicsScene::BspTreeIndex);
122
123 for (int i = 0; i < 10; ++i)
124 for (int j = 0; j < 10; ++j)
125 scene.addRect(x: i*50, y: j*50, w: 200, h: 200)->setPen(QPen(Qt::black, 0));
126
127 QCOMPARE(scene.items(QPointF(5, 5)).count(), 1);
128 QCOMPARE(scene.items(QPointF(55, 55)).count(), 4);
129 QCOMPARE(scene.items(QPointF(105, 105)).count(), 9);
130 QCOMPARE(scene.items(QPointF(-100, -100)).count(), 0);
131
132 QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 100);
133 QCOMPARE(scene.items(QRectF(-100, -1000, 0, 0)).count(), 0);
134 QCOMPARE(scene.items(QRectF(0, 0, 200, 200)).count(), 16);
135 QCOMPARE(scene.items(QRectF(0, 0, 100, 100)).count(), 4);
136 QCOMPARE(scene.items(QRectF(0, 0, 1, 100)).count(), 2);
137 QCOMPARE(scene.items(QRectF(0, 0, 1, 1000)).count(), 10);
138}
139
140void tst_QGraphicsSceneIndex::movingItems_data()
141{
142 common_data();
143}
144
145void tst_QGraphicsSceneIndex::movingItems()
146{
147 QFETCH(QString, indexMethod);
148
149 QGraphicsScene scene;
150 scene.setItemIndexMethod(indexMethod == "linear" ? QGraphicsScene::NoIndex : QGraphicsScene::BspTreeIndex);
151
152 for (int i = 0; i < 10; ++i)
153 scene.addRect(x: i*50, y: i*50, w: 40, h: 35);
154
155 QGraphicsRectItem *box = scene.addRect(x: 0, y: 0, w: 10, h: 10);
156 QCOMPARE(scene.items(QPointF(5, 5)).count(), 2);
157 QCOMPARE(scene.items(QPointF(-1, -1)).count(), 0);
158 QCOMPARE(scene.items(QRectF(0, 0, 5, 5)).count(), 2);
159
160 box->setPos(ax: 10, ay: 10);
161 QCOMPARE(scene.items(QPointF(9, 9)).count(), 1);
162 QCOMPARE(scene.items(QPointF(15, 15)).count(), 2);
163 QCOMPARE(scene.items(QRectF(0, 0, 1, 1)).count(), 1);
164
165 box->setPos(ax: -5, ay: -5);
166 QCOMPARE(scene.items(QPointF(-1, -1)).count(), 1);
167 QCOMPARE(scene.items(QRectF(0, 0, 1, 1)).count(), 2);
168
169 QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 11);
170}
171
172void tst_QGraphicsSceneIndex::connectedToSceneRectChanged()
173{
174
175 class MyScene : public QGraphicsScene
176 {
177 public:
178 using QGraphicsScene::receivers;
179 };
180
181 MyScene scene; // Uses QGraphicsSceneBspTreeIndex by default.
182 QCOMPARE(scene.receivers(SIGNAL(sceneRectChanged(QRectF))), 1);
183
184 scene.setItemIndexMethod(QGraphicsScene::NoIndex); // QGraphicsSceneLinearIndex
185 QCOMPARE(scene.receivers(SIGNAL(sceneRectChanged(QRectF))), 1);
186}
187
188void tst_QGraphicsSceneIndex::items()
189{
190 QGraphicsScene scene;
191 QGraphicsItem *item1 = scene.addRect(x: 0, y: 0, w: 10, h: 10);
192 QGraphicsItem *item2 = scene.addRect(x: 10, y: 10, w: 10, h: 10);
193 QCOMPARE(scene.items().size(), 2);
194
195 // Move from unindexed items into bsp tree.
196 QTest::qWait(ms: 50);
197 QCOMPARE(scene.items().size(), 2);
198
199 // Add untransformable item.
200 QGraphicsItem *item3 = new QGraphicsRectItem(QRectF(20, 20, 10, 10));
201 item3->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
202 scene.addItem(item: item3);
203 QCOMPARE(scene.items().size(), 3);
204
205 // Move from unindexed items into untransformable items.
206 QTest::qWait(ms: 50);
207 QCOMPARE(scene.items().size(), 3);
208
209 // Move from untransformable items into unindexed items.
210 item3->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations, enabled: false);
211 QCOMPARE(scene.items().size(), 3);
212 QTest::qWait(ms: 50);
213 QCOMPARE(scene.items().size(), 3);
214
215 // Make all items untransformable.
216 item1->setFlag(flag: QGraphicsItem::ItemIgnoresTransformations);
217 item2->setParentItem(item1);
218 item3->setParentItem(item2);
219 QCOMPARE(scene.items().size(), 3);
220
221 // Move from unindexed items into untransformable items.
222 QTest::qWait(ms: 50);
223 QCOMPARE(scene.items().size(), 3);
224}
225
226class CustomShapeItem : public QGraphicsItem
227{
228public:
229 CustomShapeItem(const QPainterPath &shape) : QGraphicsItem(0), mShape(shape) {}
230
231 QPainterPath shape() const { return mShape; }
232 QRectF boundingRect() const { return mShape.boundingRect(); }
233 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) {}
234private:
235 QPainterPath mShape;
236};
237
238Q_DECLARE_METATYPE(Qt::ItemSelectionMode)
239Q_DECLARE_METATYPE(QPainterPath)
240
241void tst_QGraphicsSceneIndex::boundingRectPointIntersection_data()
242{
243 QTest::addColumn<QPainterPath>(name: "itemShape");
244 QTest::addColumn<Qt::ItemSelectionMode>(name: "mode");
245
246 QTest::newRow(dataTag: "zero shape - intersects rect") << QPainterPath() << Qt::IntersectsItemBoundingRect;
247 QTest::newRow(dataTag: "zero shape - contains rect") << QPainterPath() << Qt::ContainsItemBoundingRect;
248
249 QPainterPath triangle;
250 triangle.moveTo(x: 50, y: 0);
251 triangle.lineTo(x: 0, y: 50);
252 triangle.lineTo(x: 100, y: 50);
253 triangle.lineTo(x: 50, y: 0);
254 QTest::newRow(dataTag: "triangle shape - intersects rect") << triangle << Qt::IntersectsItemBoundingRect;
255 QTest::newRow(dataTag: "triangle shape - contains rect") << triangle << Qt::ContainsItemBoundingRect;
256
257 QPainterPath rect;
258 rect.addRect(rect: QRectF(0, 0, 100, 100));
259 QTest::newRow(dataTag: "rectangle shape - intersects rect") << rect << Qt::IntersectsItemBoundingRect;
260 QTest::newRow(dataTag: "rectangle shape - contains rect") << rect << Qt::ContainsItemBoundingRect;
261}
262
263void tst_QGraphicsSceneIndex::boundingRectPointIntersection()
264{
265 QFETCH(QPainterPath, itemShape);
266 QFETCH(Qt::ItemSelectionMode, mode);
267
268 QGraphicsScene scene;
269 CustomShapeItem *item = new CustomShapeItem(itemShape);
270 scene.addItem(item);
271 QList<QGraphicsItem*> items = scene.items(pos: QPointF(0, 0), mode, order: Qt::AscendingOrder);
272 QVERIFY(!items.isEmpty());
273 QCOMPARE(items.first(), item);
274}
275
276class RectWidget : public QGraphicsWidget
277{
278 Q_OBJECT
279public:
280 RectWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent)
281 {
282 }
283
284 void paint(QPainter *painter, const QStyleOptionGraphicsItem * /* option */, QWidget * /* widget */)
285 {
286 painter->setBrush(brush);
287 painter->drawRect(rect: boundingRect());
288 }
289public:
290 QBrush brush;
291};
292
293void tst_QGraphicsSceneIndex::removeItems()
294{
295 QGraphicsScene scene;
296
297 RectWidget *parent = new RectWidget;
298 parent->brush = QBrush(QColor(Qt::magenta));
299 parent->setGeometry(ax: 250, ay: 250, aw: 400, ah: 400);
300
301 RectWidget *widget = new RectWidget(parent);
302 widget->brush = QBrush(QColor(Qt::blue));
303 widget->setGeometry(ax: 10, ay: 10, aw: 200, ah: 200);
304
305 RectWidget *widgetChild1 = new RectWidget(widget);
306 widgetChild1->brush = QBrush(QColor(Qt::green));
307 widgetChild1->setGeometry(ax: 20, ay: 20, aw: 100, ah: 100);
308
309 RectWidget *widgetChild2 = new RectWidget(widgetChild1);
310 widgetChild2->brush = QBrush(QColor(Qt::yellow));
311 widgetChild2->setGeometry(ax: 25, ay: 25, aw: 50, ah: 50);
312
313 scene.addItem(item: parent);
314
315 QGraphicsView view(&scene);
316 view.resize(w: 600, h: 600);
317 view.show();
318 QApplication::setActiveWindow(&view);
319 QVERIFY(QTest::qWaitForWindowActive(&view));
320
321 scene.removeItem(item: widgetChild1);
322
323 delete widgetChild1;
324
325 //We move the parent
326 scene.items(rect: QRectF(295, 295, 50, 50));
327
328 //This should not crash
329}
330
331void tst_QGraphicsSceneIndex::clear()
332{
333 class MyItem : public QGraphicsItem
334 {
335 public:
336 MyItem(QGraphicsItem *parent = 0) : QGraphicsItem(parent), numPaints(0) {}
337 int numPaints;
338 protected:
339 QRectF boundingRect() const { return QRectF(0, 0, 10, 10); }
340 void paint(QPainter * /* painter */, const QStyleOptionGraphicsItem *, QWidget *)
341 { ++numPaints; }
342 };
343
344 QGraphicsScene scene;
345 scene.setSceneRect(x: 0, y: 0, w: 100, h: 100);
346 scene.addItem(item: new MyItem);
347
348 QGraphicsView view(&scene);
349 view.show();
350 qApp->setActiveWindow(&view);
351 QVERIFY(QTest::qWaitForWindowActive(&view));
352 scene.clear();
353
354 // Make sure the index is re-generated after QGraphicsScene::clear();
355 // otherwise no items will be painted.
356 MyItem *item = new MyItem;
357 scene.addItem(item);
358 qApp->processEvents();
359#ifdef Q_OS_WINRT
360 QEXPECT_FAIL("", "There is one additional paint event on WinRT - QTBUG-68297", Abort);
361#endif
362 QTRY_COMPARE(item->numPaints, 1);
363}
364
365QTEST_MAIN(tst_QGraphicsSceneIndex)
366#include "tst_qgraphicssceneindex.moc"
367

source code of qtbase/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp