1/****************************************************************************
2**
3** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module 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#include <QtTest/QtTest>
30#include <Qt3DCore/qentity.h>
31#include <Qt3DCore/qtransform.h>
32#include <Qt3DCore/QPropertyUpdatedChange>
33#include <Qt3DCore/private/qscene_p.h>
34#include <Qt3DRender/private/qboundingvolumeprovider_p.h>
35#include <Qt3DRender/private/pickboundingvolumejob_p.h>
36#include <Qt3DRender/private/objectpicker_p.h>
37#include <Qt3DRender/qobjectpicker.h>
38#include <Qt3DRender/private/qobjectpicker_p.h>
39#include <Qt3DExtras/qspheremesh.h>
40#include <Qt3DRender/qattribute.h>
41#include <Qt3DRender/qbuffer.h>
42#include <Qt3DRender/qbufferdatagenerator.h>
43#include <Qt3DExtras/qspheregeometry.h>
44#include <Qt3DRender/qpickevent.h>
45
46using namespace Qt3DCore;
47using namespace Qt3DRender;
48using namespace Qt3DExtras;
49using namespace Qt3DRender::Render;
50
51class MyObjectPicker : public Qt3DRender::QObjectPicker
52{
53 Q_OBJECT
54public:
55 MyObjectPicker(Qt3DCore::QNode *parent = nullptr)
56 : Qt3DRender::QObjectPicker(parent)
57 {}
58
59 void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) final
60 {
61 Qt3DRender::QObjectPicker::sceneChangeEvent(change);
62 }
63};
64
65class PickableEntity : public QEntity
66{
67 Q_OBJECT
68public:
69 explicit PickableEntity(const QVector3D &position, float radius, QEntity *parent = nullptr)
70 : QEntity(parent)
71 , picker(new MyObjectPicker(this))
72 , mesh(new QSphereMesh(this))
73 , transform(new Qt3DCore::QTransform(this))
74 , acceptsEvents(true)
75 , pressedCalled(0)
76 , releasedCalled(0)
77 , clickedCalled(0)
78 {
79 mesh->setRadius(radius);
80 QSphereGeometry *g = static_cast<QSphereGeometry *>(mesh->geometry());
81 QAttribute *positionAttr = static_cast<QAttribute *>(g->attributes().first());
82 Qt3DRender::QBuffer *vertexBuffer = static_cast<Qt3DRender::QBuffer *>(positionAttr->buffer());
83
84 // Load the geometry
85 QT_WARNING_PUSH
86 QT_WARNING_DISABLE_DEPRECATED
87 const QByteArray data = (*vertexBuffer->dataGenerator())();
88 vertexBuffer->setData(data);
89 QT_WARNING_POP
90
91 transform->setTranslation(position);
92
93 addComponent(comp: picker);
94 addComponent(comp: mesh);
95 addComponent(comp: transform);
96
97 QObject::connect(sender: picker, signal: &QObjectPicker::pressed, receiver: this, slot: &PickableEntity::onPressed);
98 QObject::connect(sender: picker, signal: &QObjectPicker::released, receiver: this, slot: &PickableEntity::onReleased);
99 QObject::connect(sender: picker, signal: &QObjectPicker::clicked, receiver: this, slot: &PickableEntity::onClicked);
100 }
101
102 MyObjectPicker *picker;
103 QSphereMesh *mesh;
104 Qt3DCore::QTransform *transform;
105
106 bool acceptsEvents;
107 int pressedCalled;
108 int releasedCalled;
109 int clickedCalled;
110
111private Q_SLOTS:
112 void onPressed(QPickEvent *e)
113 {
114 e->setAccepted(acceptsEvents);
115 ++pressedCalled;
116 }
117
118 void onReleased(QPickEvent *e)
119 {
120 e->setAccepted(acceptsEvents);
121 if (acceptsEvents)
122 ++releasedCalled;
123 }
124
125 void onClicked(QPickEvent *e)
126 {
127 e->setAccepted(acceptsEvents);
128 ++clickedCalled;
129 }
130};
131
132class tst_Picking : public QObject
133{
134 Q_OBJECT
135public:
136 tst_Picking()
137 {
138 qRegisterMetaType<Qt3DCore::QNode*>();
139 }
140
141 ~tst_Picking() {}
142
143private Q_SLOTS:
144 void testEventPressedAcceptPropagation()
145 {
146 // GIVEN
147 Qt3DCore::QScene scene;
148 PickableEntity root(QVector3D(), 5.0f);
149 PickableEntity child1(QVector3D(), 5.0f, &root);
150 PickableEntity child2(QVector3D(), 5.0f, &root);
151 PickableEntity child11(QVector3D(), 5.0f, &child1);
152 Qt3DCore::QNodePrivate::get(q: root.picker)->setScene(&scene);
153 Qt3DCore::QNodePrivate::get(q: child1.picker)->setScene(&scene);
154 Qt3DCore::QNodePrivate::get(q: child2.picker)->setScene(&scene);
155 Qt3DCore::QNodePrivate::get(q: child11.picker)->setScene(&scene);
156
157 QCoreApplication::processEvents();
158
159 auto dpicker = [](QObjectPicker *node) {
160 return static_cast<QObjectPickerPrivate *>(QObjectPickerPrivate::get(q: node));
161 };
162
163 // WHEN
164 Qt3DRender::QPickEventPtr event(new Qt3DRender::QPickEvent());
165 dpicker(child11.picker)->pressedEvent(event: event.data());
166
167 // THEN
168 QCOMPARE(root.pressedCalled, 0);
169 QCOMPARE(child1.pressedCalled, 0);
170 QCOMPARE(child2.pressedCalled, 0);
171 QCOMPARE(child11.pressedCalled, 1);
172
173 // WHEN
174 child11.pressedCalled = 0;
175 child11.acceptsEvents = false;
176 dpicker(child11.picker)->pressedEvent(event: event.data());
177
178 // THEN
179 QCOMPARE(root.pressedCalled, 0);
180 QCOMPARE(child1.pressedCalled, 1);
181 QCOMPARE(child2.pressedCalled, 0);
182 QCOMPARE(child11.pressedCalled, 1);
183
184 // WHEN
185 child1.acceptsEvents = false;
186 child1.pressedCalled = 0;
187 child11.acceptsEvents = false;
188 child11.pressedCalled = 0;
189 dpicker(child11.picker)->pressedEvent(event: event.data());
190
191 // THEN
192 QCOMPARE(root.pressedCalled, 1);
193 QCOMPARE(child1.pressedCalled, 1);
194 QCOMPARE(child2.pressedCalled, 0);
195 QCOMPARE(child11.pressedCalled, 1);
196 }
197
198 void testEventReleasedAcceptPropagation()
199 {
200 // GIVEN
201 Qt3DCore::QScene scene;
202 PickableEntity root(QVector3D(), 5.0f);
203 PickableEntity child1(QVector3D(), 5.0f, &root);
204 PickableEntity child2(QVector3D(), 5.0f, &root);
205 PickableEntity child11(QVector3D(), 5.0f, &child1);
206 Qt3DCore::QNodePrivate::get(q: root.picker)->setScene(&scene);
207 Qt3DCore::QNodePrivate::get(q: child1.picker)->setScene(&scene);
208 Qt3DCore::QNodePrivate::get(q: child2.picker)->setScene(&scene);
209 Qt3DCore::QNodePrivate::get(q: child11.picker)->setScene(&scene);
210
211 QCoreApplication::processEvents();
212
213 auto dpicker = [](QObjectPicker *node) {
214 return static_cast<QObjectPickerPrivate *>(QObjectPickerPrivate::get(q: node));
215 };
216
217 // WHEN
218 Qt3DRender::QPickEventPtr event(new Qt3DRender::QPickEvent());
219 dpicker(child11.picker)->pressedEvent(event: event.data());
220 dpicker(child11.picker)->releasedEvent(event: event.data());
221
222 // THEN
223 QCOMPARE(root.releasedCalled, 0);
224 QCOMPARE(child1.releasedCalled, 0);
225 QCOMPARE(child2.releasedCalled, 0);
226 QCOMPARE(child11.releasedCalled, 1);
227
228 // WHEN
229 child11.releasedCalled = 0;
230 child11.pressedCalled = 0;
231 child11.acceptsEvents = false;
232 dpicker(child11.picker)->pressedEvent(event: event.data());
233 dpicker(child11.picker)->releasedEvent(event: event.data());
234
235 // THEN
236 QCOMPARE(child1.pressedCalled, 1);
237 QCOMPARE(child11.pressedCalled, 1);
238
239 QCOMPARE(root.releasedCalled, 0);
240 QCOMPARE(child1.releasedCalled, 1);
241 QCOMPARE(child2.releasedCalled, 0);
242 QCOMPARE(child11.releasedCalled, 0);
243 }
244
245 void testEventClickedAcceptPropagation()
246 {
247 // GIVEN
248 Qt3DCore::QScene scene;
249 PickableEntity root(QVector3D(), 5.0f);
250 PickableEntity child1(QVector3D(), 5.0f, &root);
251 PickableEntity child2(QVector3D(), 5.0f, &root);
252 PickableEntity child11(QVector3D(), 5.0f, &child1);
253 Qt3DCore::QNodePrivate::get(q: root.picker)->setScene(&scene);
254 Qt3DCore::QNodePrivate::get(q: child1.picker)->setScene(&scene);
255 Qt3DCore::QNodePrivate::get(q: child2.picker)->setScene(&scene);
256 Qt3DCore::QNodePrivate::get(q: child11.picker)->setScene(&scene);
257
258 QCoreApplication::processEvents();
259
260 auto dpicker = [](QObjectPicker *node) {
261 return static_cast<QObjectPickerPrivate *>(QObjectPickerPrivate::get(q: node));
262 };
263
264 // WHEN
265 Qt3DRender::QPickEventPtr event(new Qt3DRender::QPickEvent());
266 dpicker(child11.picker)->clickedEvent(event: event.data());
267
268 // THEN
269 QCOMPARE(root.clickedCalled, 0);
270 QCOMPARE(child1.clickedCalled, 0);
271 QCOMPARE(child2.clickedCalled, 0);
272 QCOMPARE(child11.clickedCalled, 1);
273
274 // WHEN
275 child11.clickedCalled = 0;
276 child11.acceptsEvents = false;
277 dpicker(child11.picker)->clickedEvent(event: event.data());
278
279 // THEN
280 QCOMPARE(root.clickedCalled, 0);
281 QCOMPARE(child1.clickedCalled, 1);
282 QCOMPARE(child2.clickedCalled, 0);
283 QCOMPARE(child11.clickedCalled, 1);
284
285 // WHEN
286 child1.acceptsEvents = false;
287 child1.clickedCalled = 0;
288 child11.acceptsEvents = false;
289 child11.clickedCalled = 0;
290 dpicker(child11.picker)->clickedEvent(event: event.data());
291
292 // THEN
293 QCOMPARE(root.clickedCalled, 1);
294 QCOMPARE(child1.clickedCalled, 1);
295 QCOMPARE(child2.clickedCalled, 0);
296 QCOMPARE(child11.clickedCalled, 1);
297 }
298};
299
300QTEST_MAIN(tst_Picking)
301
302#include "tst_picking.moc"
303

source code of qt3d/tests/auto/render/picking/tst_picking.cpp