summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2018-12-10 10:17:25 +0100
committerPaul Lemire <paul.lemire@kdab.com>2018-12-12 05:40:40 +0000
commite3fbebe61111dfe670ffe19c96e313157df7331f (patch)
tree2b61cb59a84e4d31350cb1049bd71efc7a95941e
parentc9ce0deeb8f2bb79c446e41584f753f1b1bfe17f (diff)
QObjectPicker and QPickingSettings NearestPriorityPick picking mode
Add a new priority property on QObjectPicker and a new QPickingSettings mode. This will select the result with the highest priority if there are several results on a given ray. If we have several results with identical properties, the result with the closest distance is selected. [ChangeLog][Qt3DRender] QObjectPicker: add a priority based result selection Change-Id: I7af12db6e163c3c2d9dad696e6d9f9bbbee064ed Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r--src/render/frontend/qpickingsettings.cpp8
-rw-r--r--src/render/frontend/qpickingsettings.h3
-rw-r--r--src/render/jobs/pickboundingvolumejob.cpp16
-rw-r--r--src/render/jobs/pickboundingvolumeutils.cpp132
-rw-r--r--src/render/jobs/pickboundingvolumeutils_p.h14
-rw-r--r--src/render/jobs/raycastingjob.cpp6
-rw-r--r--src/render/picking/objectpicker.cpp15
-rw-r--r--src/render/picking/objectpicker_p.h5
-rw-r--r--src/render/picking/qobjectpicker.cpp37
-rw-r--r--src/render/picking/qobjectpicker.h5
-rw-r--r--src/render/picking/qobjectpicker_p.h3
-rw-r--r--tests/auto/render/objectpicker/tst_objectpicker.cpp36
-rw-r--r--tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc1
-rw-r--r--tests/auto/render/pickboundingvolumejob/testscene_priorityoverlapping.qml135
-rw-r--r--tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp105
-rw-r--r--tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp160
16 files changed, 648 insertions, 33 deletions
diff --git a/src/render/frontend/qpickingsettings.cpp b/src/render/frontend/qpickingsettings.cpp
index 66d3fc912..84e61e141 100644
--- a/src/render/frontend/qpickingsettings.cpp
+++ b/src/render/frontend/qpickingsettings.cpp
@@ -199,6 +199,9 @@ void QPickingSettings::setPickMethod(QPickingSettings::PickMethod pickMethod)
* \value NearestPick Only the nearest entity to picking ray origin intersected by the picking ray
* is picked (default).
* \value AllPicks All entities that intersect the picking ray are picked.
+ * \value PriorityPick Selects the entity whose object picker has the highest
+ * value. If several object pickers have the same priority, the closest one on
+ * the ray is selected.
*
* \sa Qt3DRender::QPickEvent
*/
@@ -211,6 +214,7 @@ void QPickingSettings::setPickMethod(QPickingSettings::PickMethod pickMethod)
\list
\li PickingSettings.NearestPick
\li PickingSettings.AllPicks
+ \li PickingSettings.NearestPriorityPick
\endlist
\sa Qt3DRender::QPickingSettings::PickResultMode
@@ -225,6 +229,10 @@ void QPickingSettings::setPickMethod(QPickingSettings::PickMethod pickMethod)
When setting the pick method to AllPicks, events will be triggered for all the
entities with a QObjectPicker along the ray.
+ When setting the pick method to NearestPriorityPick, events will be
+ triggered for the nearest highest priority picker. This can be used when a
+ given element should always be selected even if others are in front of it.
+
If a QObjectPicker is assigned to an entity with multiple children, an event will
be triggered for each child entity that intersects the ray.
*/
diff --git a/src/render/frontend/qpickingsettings.h b/src/render/frontend/qpickingsettings.h
index 9c8a2c856..741f918b0 100644
--- a/src/render/frontend/qpickingsettings.h
+++ b/src/render/frontend/qpickingsettings.h
@@ -73,7 +73,8 @@ public:
enum PickResultMode {
NearestPick,
- AllPicks
+ AllPicks,
+ NearestPriorityPick
};
Q_ENUM(PickResultMode) // LCOV_EXCL_LINE
diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp
index 2050b8772..96ec11b4e 100644
--- a/src/render/jobs/pickboundingvolumejob.cpp
+++ b/src/render/jobs/pickboundingvolumejob.cpp
@@ -211,7 +211,6 @@ bool PickBoundingVolumeJob::runHelper()
const bool edgePickingRequested = (m_renderSettings->pickMethod() & QPickingSettings::LinePicking);
const bool pointPickingRequested = (m_renderSettings->pickMethod() & QPickingSettings::PointPicking);
const bool primitivePickingRequested = pointPickingRequested | edgePickingRequested | trianglePickingRequested;
- const bool allHitsRequested = (m_renderSettings->pickResultMode() == QPickingSettings::AllPicks);
const bool frontFaceRequested =
m_renderSettings->faceOrientationPickingMode() != QPickingSettings::BackFace;
const bool backFaceRequested =
@@ -237,7 +236,7 @@ bool PickBoundingVolumeJob::runHelper()
// has moved out of the viewport In case of a button released
// outside of the viewport, we still want to notify the
// lastCurrent entity about this.
- dispatchPickEvents(event.second, PickingUtils::HitList(), eventButton, eventButtons, eventModifiers, allHitsRequested);
+ dispatchPickEvents(event.second, PickingUtils::HitList(), eventButton, eventButtons, eventModifiers, m_renderSettings->pickResultMode());
continue;
}
@@ -249,14 +248,16 @@ bool PickBoundingVolumeJob::runHelper()
gathererFunctor.m_backFaceRequested = backFaceRequested;
gathererFunctor.m_manager = m_manager;
gathererFunctor.m_ray = ray;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), allHitsRequested);
+ gathererFunctor.m_entityToPriorityTable = entityPicker.entityToPriorityTable();
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), m_renderSettings->pickResultMode());
}
if (edgePickingRequested) {
PickingUtils::LineCollisionGathererFunctor gathererFunctor;
gathererFunctor.m_manager = m_manager;
gathererFunctor.m_ray = ray;
gathererFunctor.m_pickWorldSpaceTolerance = pickWorldSpaceTolerance;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), allHitsRequested);
+ gathererFunctor.m_entityToPriorityTable = entityPicker.entityToPriorityTable();
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), m_renderSettings->pickResultMode());
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
}
if (pointPickingRequested) {
@@ -264,19 +265,20 @@ bool PickBoundingVolumeJob::runHelper()
gathererFunctor.m_manager = m_manager;
gathererFunctor.m_ray = ray;
gathererFunctor.m_pickWorldSpaceTolerance = pickWorldSpaceTolerance;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), allHitsRequested);
+ gathererFunctor.m_entityToPriorityTable = entityPicker.entityToPriorityTable();
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), m_renderSettings->pickResultMode());
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
}
if (!primitivePickingRequested) {
sphereHits << entityPicker.hits();
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
- if (!allHitsRequested)
+ if (m_renderSettings->pickResultMode() != QPickingSettings::AllPicks)
sphereHits = { sphereHits.front() };
}
}
// Dispatch events based on hit results
- dispatchPickEvents(event.second, sphereHits, eventButton, eventButtons, eventModifiers, allHitsRequested);
+ dispatchPickEvents(event.second, sphereHits, eventButton, eventButtons, eventModifiers, m_renderSettings->pickResultMode());
}
}
diff --git a/src/render/jobs/pickboundingvolumeutils.cpp b/src/render/jobs/pickboundingvolumeutils.cpp
index 5fed946d6..23e495ecb 100644
--- a/src/render/jobs/pickboundingvolumeutils.cpp
+++ b/src/render/jobs/pickboundingvolumeutils.cpp
@@ -55,6 +55,7 @@
#include <vector>
#include <algorithm>
+#include <functional>
QT_BEGIN_NAMESPACE
@@ -418,6 +419,46 @@ HitList reduceToFirstHit(HitList &result, const HitList &intermediate)
return result;
}
+
+struct HighestPriorityHitReducer
+{
+ // No need to protect this from concurrent access as the table
+ // is read only
+ const QHash<Qt3DCore::QNodeId, int> entityToPriorityTable;
+
+ HitList operator()(HitList &result, const HitList &intermediate)
+ {
+ // Sort by priority first
+ // If we have equal priorities, we then sort by distance
+
+ if (!intermediate.empty()) {
+ if (result.empty())
+ result.push_back(intermediate.front());
+ int currentPriority = entityToPriorityTable.value(result.front().m_entityId, 0);
+ float closest = result.front().m_distance;
+
+ for (const auto &v : intermediate) {
+ const int newEntryPriority = entityToPriorityTable.value(v.m_entityId, 0);
+ if (newEntryPriority > currentPriority) {
+ result.push_front(v);
+ currentPriority = newEntryPriority;
+ closest = v.m_distance;
+ } else if (newEntryPriority == currentPriority) {
+ if (v.m_distance < closest) {
+ result.push_front(v);
+ closest = v.m_distance;
+ currentPriority = newEntryPriority;
+ }
+ }
+ }
+
+ while (result.size() > 1)
+ result.pop_back();
+ }
+ return result;
+ }
+};
+
HitList reduceToAllHits(HitList &results, const HitList &intermediate)
{
if (!intermediate.empty())
@@ -492,9 +533,22 @@ struct MapFunctorHolder
} // anonymous
-HitList EntityCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities, bool allHitsRequested)
-{
- const auto reducerOp = allHitsRequested ? PickingUtils::reduceToAllHits : PickingUtils::reduceToFirstHit;
+HitList EntityCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities,
+ Qt3DRender::QPickingSettings::PickResultMode mode)
+{
+ std::function<HitList (HitList &, const HitList &)> reducerOp;
+ switch (mode) {
+ case QPickingSettings::AllPicks:
+ reducerOp = PickingUtils::reduceToAllHits;
+ break;
+ case QPickingSettings::NearestPriorityPick:
+ reducerOp = HighestPriorityHitReducer{ m_entityToPriorityTable };
+ break;
+ case QPickingSettings::NearestPick:
+ reducerOp = PickingUtils::reduceToFirstHit;
+ break;
+ }
+
const MapFunctorHolder holder(this);
#if QT_CONFIG(concurrent)
return QtConcurrent::blockingMappedReduced<HitList>(entities, holder, reducerOp);
@@ -519,9 +573,22 @@ HitList EntityCollisionGathererFunctor::pick(const Entity *entity) const
return result;
}
-HitList TriangleCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities, bool allHitsRequested)
-{
- const auto reducerOp = allHitsRequested ? PickingUtils::reduceToAllHits : PickingUtils::reduceToFirstHit;
+HitList TriangleCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities,
+ Qt3DRender::QPickingSettings::PickResultMode mode)
+{
+ std::function<HitList (HitList &, const HitList &)> reducerOp;
+ switch (mode) {
+ case QPickingSettings::AllPicks:
+ reducerOp = PickingUtils::reduceToAllHits;
+ break;
+ case QPickingSettings::NearestPriorityPick:
+ reducerOp = HighestPriorityHitReducer { m_entityToPriorityTable };
+ break;
+ case QPickingSettings::NearestPick:
+ reducerOp = PickingUtils::reduceToFirstHit;
+ break;
+ }
+
const MapFunctorHolder holder(this);
#if QT_CONFIG(concurrent)
return QtConcurrent::blockingMappedReduced<HitList>(entities, holder, reducerOp);
@@ -553,9 +620,22 @@ HitList TriangleCollisionGathererFunctor::pick(const Entity *entity) const
return result;
}
-HitList LineCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities, bool allHitsRequested)
-{
- const auto reducerOp = allHitsRequested ? PickingUtils::reduceToAllHits : PickingUtils::reduceToFirstHit;
+HitList LineCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities,
+ Qt3DRender::QPickingSettings::PickResultMode mode)
+{
+ std::function<HitList (HitList &, const HitList &)> reducerOp;
+ switch (mode) {
+ case QPickingSettings::AllPicks:
+ reducerOp = PickingUtils::reduceToAllHits;
+ break;
+ case QPickingSettings::NearestPriorityPick:
+ reducerOp = HighestPriorityHitReducer { m_entityToPriorityTable };
+ break;
+ case QPickingSettings::NearestPick:
+ reducerOp = PickingUtils::reduceToFirstHit;
+ break;
+ }
+
const MapFunctorHolder holder(this);
#if QT_CONFIG(concurrent)
return QtConcurrent::blockingMappedReduced<HitList>(entities, holder, reducerOp);
@@ -586,9 +666,22 @@ HitList LineCollisionGathererFunctor::pick(const Entity *entity) const
return result;
}
-HitList PointCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities, bool allHitsRequested)
-{
- const auto reducerOp = allHitsRequested ? PickingUtils::reduceToAllHits : PickingUtils::reduceToFirstHit;
+HitList PointCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities,
+ Qt3DRender::QPickingSettings::PickResultMode mode)
+{
+ std::function<HitList (HitList &, const HitList &)> reducerOp;
+ switch (mode) {
+ case QPickingSettings::AllPicks:
+ reducerOp = PickingUtils::reduceToAllHits;
+ break;
+ case QPickingSettings::NearestPriorityPick:
+ reducerOp = HighestPriorityHitReducer { m_entityToPriorityTable };
+ break;
+ case QPickingSettings::NearestPick:
+ reducerOp = PickingUtils::reduceToFirstHit;
+ break;
+ }
+
const MapFunctorHolder holder(this);
#if QT_CONFIG(concurrent)
return QtConcurrent::blockingMappedReduced<HitList>(entities, holder, reducerOp);
@@ -641,15 +734,17 @@ bool HierarchicalEntityPicker::collectHits(NodeManagers *manager, Entity *root)
{
m_hits.clear();
m_entities.clear();
+ m_entityToPriorityTable.clear();
QRayCastingService rayCasting;
struct EntityData {
Entity* entity;
bool hasObjectPicker;
Qt3DCore::QNodeIdVector recursiveLayers;
+ int priority;
};
std::vector<EntityData> worklist;
- worklist.push_back({root, !root->componentHandle<ObjectPicker>().isNull(), {}});
+ worklist.push_back({root, !root->componentHandle<ObjectPicker>().isNull(), {}, 0});
LayerManager *layerManager = manager->layerManager();
@@ -710,6 +805,8 @@ bool HierarchicalEntityPicker::collectHits(NodeManagers *manager, Entity *root)
if (accepted && queryResult.m_distance >= 0.f && (current.hasObjectPicker || !m_objectPickersRequired)) {
m_entities.push_back(current.entity);
m_hits.push_back(queryResult);
+ // Record entry for entity/priority
+ m_entityToPriorityTable.insert(current.entity->peerId(), current.priority);
}
Qt3DCore::QNodeIdVector recursiveLayers;
@@ -722,9 +819,12 @@ bool HierarchicalEntityPicker::collectHits(NodeManagers *manager, Entity *root)
// and pick children
const auto children = current.entity->children();
- for (auto child: children)
- worklist.push_back({child, current.hasObjectPicker || !child->componentHandle<ObjectPicker>().isNull(),
- current.recursiveLayers + recursiveLayers});
+ for (Entity *child: children) {
+ ObjectPicker *childPicker = child->renderComponent<ObjectPicker>();
+ worklist.push_back({child, current.hasObjectPicker || childPicker,
+ current.recursiveLayers + recursiveLayers,
+ (childPicker ? childPicker->priority() : current.priority)});
+ }
}
return !m_hits.empty();
diff --git a/src/render/jobs/pickboundingvolumeutils_p.h b/src/render/jobs/pickboundingvolumeutils_p.h
index 780c16cc8..3fc4517c3 100644
--- a/src/render/jobs/pickboundingvolumeutils_p.h
+++ b/src/render/jobs/pickboundingvolumeutils_p.h
@@ -55,6 +55,7 @@
#include <Qt3DRender/QAbstractRayCaster>
#include <Qt3DRender/private/qray3d_p.h>
#include <Qt3DRender/private/qraycastingservice_p.h>
+#include <Qt3DRender/qpickingsettings.h>
QT_BEGIN_NAMESPACE
@@ -124,6 +125,7 @@ public:
bool collectHits(NodeManagers *manager, Entity *root);
inline HitList hits() const { return m_hits; }
inline QVector<Entity *> entities() const { return m_entities; }
+ inline QHash<Qt3DCore::QNodeId, int> entityToPriorityTable() const { return m_entityToPriorityTable; }
private:
RayCasting::QRay3D m_ray;
@@ -132,6 +134,7 @@ private:
bool m_objectPickersRequired;
Qt3DCore::QNodeIdVector m_layerIds;
QAbstractRayCaster::FilterMode m_filterMode;
+ QHash<Qt3DCore::QNodeId, int> m_entityToPriorityTable;
};
struct Q_AUTOTEST_EXPORT AbstractCollisionGathererFunctor
@@ -142,8 +145,9 @@ struct Q_AUTOTEST_EXPORT AbstractCollisionGathererFunctor
bool m_objectPickersRequired = true;
NodeManagers *m_manager = nullptr;
RayCasting::QRay3D m_ray;
+ QHash<Qt3DCore::QNodeId, int> m_entityToPriorityTable;
- virtual HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) = 0;
+ virtual HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) = 0;
// This define is required to work with QtConcurrent
typedef HitList result_type;
@@ -156,7 +160,7 @@ struct Q_AUTOTEST_EXPORT AbstractCollisionGathererFunctor
struct Q_AUTOTEST_EXPORT EntityCollisionGathererFunctor : public AbstractCollisionGathererFunctor
{
- HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) override;
+ HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) override;
HitList pick(const Entity *entity) const override;
};
@@ -165,7 +169,7 @@ struct Q_AUTOTEST_EXPORT TriangleCollisionGathererFunctor : public AbstractColli
bool m_frontFaceRequested;
bool m_backFaceRequested;
- HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) override;
+ HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) override;
HitList pick(const Entity *entity) const override;
};
@@ -173,7 +177,7 @@ struct Q_AUTOTEST_EXPORT LineCollisionGathererFunctor : public AbstractCollision
{
float m_pickWorldSpaceTolerance;
- HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) override;
+ HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) override;
HitList pick(const Entity *entity) const override;
};
@@ -181,7 +185,7 @@ struct Q_AUTOTEST_EXPORT PointCollisionGathererFunctor : public AbstractCollisio
{
float m_pickWorldSpaceTolerance;
- HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) override;
+ HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) override;
HitList pick(const Entity *entity) const override;
};
diff --git a/src/render/jobs/raycastingjob.cpp b/src/render/jobs/raycastingjob.cpp
index e76b9fe8d..70c7ac374 100644
--- a/src/render/jobs/raycastingjob.cpp
+++ b/src/render/jobs/raycastingjob.cpp
@@ -183,7 +183,7 @@ bool RayCastingJob::runHelper()
gathererFunctor.m_manager = m_manager;
gathererFunctor.m_ray = ray;
gathererFunctor.m_objectPickersRequired = false;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), true);
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), QPickingSettings::AllPicks);
}
if (edgePickingRequested) {
PickingUtils::LineCollisionGathererFunctor gathererFunctor;
@@ -191,7 +191,7 @@ bool RayCastingJob::runHelper()
gathererFunctor.m_ray = ray;
gathererFunctor.m_pickWorldSpaceTolerance = pickWorldSpaceTolerance;
gathererFunctor.m_objectPickersRequired = false;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), true);
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), QPickingSettings::AllPicks);
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
}
if (pointPickingRequested) {
@@ -200,7 +200,7 @@ bool RayCastingJob::runHelper()
gathererFunctor.m_ray = ray;
gathererFunctor.m_pickWorldSpaceTolerance = pickWorldSpaceTolerance;
gathererFunctor.m_objectPickersRequired = false;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), true);
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), QPickingSettings::AllPicks);
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
}
if (!primitivePickingRequested) {
diff --git a/src/render/picking/objectpicker.cpp b/src/render/picking/objectpicker.cpp
index 76f00993c..43e308d20 100644
--- a/src/render/picking/objectpicker.cpp
+++ b/src/render/picking/objectpicker.cpp
@@ -53,6 +53,7 @@ namespace Render {
ObjectPicker::ObjectPicker()
: BackendNode(QBackendNode::ReadWrite)
+ , m_priority(0)
, m_isPressed(false)
, m_hoverEnabled(false)
, m_dragEnabled(false)
@@ -70,6 +71,7 @@ void ObjectPicker::cleanup()
m_isPressed = false;
m_hoverEnabled = false;
m_dragEnabled = false;
+ m_priority = 0;
notifyJob();
}
@@ -79,6 +81,7 @@ void ObjectPicker::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr
const auto &data = typedChange->data;
m_hoverEnabled = data.hoverEnabled;
m_dragEnabled = data.dragEnabled;
+ m_priority = data.priority;
notifyJob();
}
@@ -97,6 +100,8 @@ void ObjectPicker::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
m_hoverEnabled = propertyChange->value().toBool();
} else if (propertyChange->propertyName() == QByteArrayLiteral("dragEnabled")) {
m_dragEnabled = propertyChange->value().toBool();
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("priority")) {
+ m_priority = propertyChange->value().toInt();
}
markDirty(AbstractRenderer::AllDirty);
@@ -175,6 +180,16 @@ void ObjectPicker::onExited()
notifyObservers(e);
}
+void ObjectPicker::setPriority(int priority)
+{
+ m_priority = priority;
+}
+
+int ObjectPicker::priority() const
+{
+ return m_priority;
+}
+
} // Render
} // Qt3DRender
diff --git a/src/render/picking/objectpicker_p.h b/src/render/picking/objectpicker_p.h
index b9c308afb..7389a4b53 100644
--- a/src/render/picking/objectpicker_p.h
+++ b/src/render/picking/objectpicker_p.h
@@ -81,10 +81,15 @@ public:
void onEntered();
void onExited();
+ // Needed for unit tests
+ void setPriority(int priority);
+ int priority() const;
+
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final;
void notifyJob();
+ int m_priority;
bool m_isPressed;
bool m_hoverEnabled;
bool m_dragEnabled;
diff --git a/src/render/picking/qobjectpicker.cpp b/src/render/picking/qobjectpicker.cpp
index c3671d018..a0b6d8dcd 100644
--- a/src/render/picking/qobjectpicker.cpp
+++ b/src/render/picking/qobjectpicker.cpp
@@ -266,6 +266,23 @@ void QObjectPicker::setDragEnabled(bool dragEnabled)
}
/*!
+ * Sets the picker's priority to \a priority. This is used when the pick result
+ * mode on QPickingSettings is set to QPickingSettings::NearestPriorityPick.
+ * Picking results are sorted by highest priority and shortest picking
+ * distance.
+ *
+ * \since 5.13
+ */
+void QObjectPicker::setPriority(int priority)
+{
+ Q_D(QObjectPicker);
+ if (priority != d->m_priority) {
+ d->m_priority = priority;
+ emit priorityChanged(priority);
+ }
+}
+
+/*!
\qmlproperty bool Qt3D.Render::ObjectPicker::dragEnabled
*/
/*!
@@ -312,6 +329,25 @@ bool QObjectPicker::isPressed() const
return d->m_pressed;
}
+/*!
+ \qmlproperty int Qt3D.Render::ObjectPicker::priority
+
+ The priority to be used when filtering pick results by priority when
+ PickingSettings.pickResultMode is set to PickingSettings.PriorityPick.
+*/
+/*!
+ \property Qt3DRender::QObjectPicker::priority
+
+ The priority to be used when filtering pick results by priority when
+ QPickingSettings::pickResultMode is set to
+ QPickingSettings::NearestPriorityPick.
+*/
+int QObjectPicker::priority() const
+{
+ Q_D(const QObjectPicker);
+ return d->m_priority;
+}
+
/*! \internal */
void QObjectPicker::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change)
{
@@ -465,6 +501,7 @@ Qt3DCore::QNodeCreatedChangeBasePtr QObjectPicker::createNodeCreationChange() co
Q_D(const QObjectPicker);
data.hoverEnabled = d->m_hoverEnabled;
data.dragEnabled = d->m_dragEnabled;
+ data.priority = d->m_priority;
return creationChange;
}
diff --git a/src/render/picking/qobjectpicker.h b/src/render/picking/qobjectpicker.h
index 9f3b138c3..1d15f6092 100644
--- a/src/render/picking/qobjectpicker.h
+++ b/src/render/picking/qobjectpicker.h
@@ -58,6 +58,7 @@ class QT3DRENDERSHARED_EXPORT QObjectPicker : public Qt3DCore::QComponent
Q_PROPERTY(bool dragEnabled READ isDragEnabled WRITE setDragEnabled NOTIFY dragEnabledChanged)
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged)
+ Q_PROPERTY(int priority READ priority WRITE setPriority NOTIFY priorityChanged REVISION 13)
public:
explicit QObjectPicker(QNode *parent = nullptr);
@@ -69,9 +70,12 @@ public:
bool containsMouse() const;
bool isPressed() const;
+ int priority() const;
+
public Q_SLOTS:
void setHoverEnabled(bool hoverEnabled);
void setDragEnabled(bool dragEnabled);
+ void setPriority(int priority);
Q_SIGNALS:
void pressed(Qt3DRender::QPickEvent *pick);
@@ -84,6 +88,7 @@ Q_SIGNALS:
void dragEnabledChanged(bool dragEnabled);
void pressedChanged(bool pressed);
void containsMouseChanged(bool containsMouse);
+ void priorityChanged(int priority);
protected:
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override;
diff --git a/src/render/picking/qobjectpicker_p.h b/src/render/picking/qobjectpicker_p.h
index 3c48b9419..384062bef 100644
--- a/src/render/picking/qobjectpicker_p.h
+++ b/src/render/picking/qobjectpicker_p.h
@@ -69,6 +69,7 @@ public:
, m_pressed(false)
, m_containsMouse(false)
, m_acceptedLastPressedEvent(true)
+ , m_priority(0)
{
m_shareable = false;
}
@@ -79,6 +80,7 @@ public:
bool m_pressed;
bool m_containsMouse;
bool m_acceptedLastPressedEvent;
+ int m_priority;
enum EventType {
Pressed,
@@ -102,6 +104,7 @@ struct QObjectPickerData
{
bool hoverEnabled;
bool dragEnabled;
+ int priority;
};
} // namespace Qt3DRender
diff --git a/tests/auto/render/objectpicker/tst_objectpicker.cpp b/tests/auto/render/objectpicker/tst_objectpicker.cpp
index c1b06ccd8..644849102 100644
--- a/tests/auto/render/objectpicker/tst_objectpicker.cpp
+++ b/tests/auto/render/objectpicker/tst_objectpicker.cpp
@@ -47,6 +47,7 @@ private Q_SLOTS:
Qt3DRender::Render::ObjectPicker objectPicker;
Qt3DRender::QObjectPicker picker;
picker.setHoverEnabled(true);
+ picker.setPriority(883);
// WHEN
simulateInitialization(&picker, &objectPicker);
@@ -54,6 +55,7 @@ private Q_SLOTS:
// THEN
QVERIFY(!objectPicker.peerId().isNull());
QCOMPARE(objectPicker.isHoverEnabled(), true);
+ QCOMPARE(objectPicker.priority(), 883);
}
void checkInitialAndCleanedUpState()
@@ -64,10 +66,14 @@ private Q_SLOTS:
// THEN
QVERIFY(objectPicker.peerId().isNull());
QCOMPARE(objectPicker.isHoverEnabled(), false);
+ QCOMPARE(objectPicker.isDragEnabled(), false);
+ QCOMPARE(objectPicker.priority(), 0);
// GIVEN
Qt3DRender::QObjectPicker picker;
picker.setHoverEnabled(true);
+ picker.setDragEnabled(true);
+ picker.setPriority(1584);
// WHEN
simulateInitialization(&picker, &objectPicker);
@@ -75,6 +81,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(objectPicker.isHoverEnabled(), false);
+ QCOMPARE(objectPicker.isDragEnabled(), false);
+ QCOMPARE(objectPicker.priority(), 0);
}
void checkPropertyChanges()
@@ -95,6 +103,34 @@ private Q_SLOTS:
QCOMPARE(objectPicker.isHoverEnabled(), true);
QVERIFY(renderer.dirtyBits() != 0);
}
+ {
+ Qt3DRender::Render::ObjectPicker objectPicker;
+ objectPicker.setRenderer(&renderer);
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr updateChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ updateChange->setValue(true);
+ updateChange->setPropertyName("dragEnabled");
+ objectPicker.sceneChangeEvent(updateChange);
+
+ // THEN
+ QCOMPARE(objectPicker.isDragEnabled(), true);
+ QVERIFY(renderer.dirtyBits() != 0);
+ }
+ {
+ Qt3DRender::Render::ObjectPicker objectPicker;
+ objectPicker.setRenderer(&renderer);
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr updateChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ updateChange->setValue(15);
+ updateChange->setPropertyName("priority");
+ objectPicker.sceneChangeEvent(updateChange);
+
+ // THEN
+ QCOMPARE(objectPicker.priority(), 15);
+ QVERIFY(renderer.dirtyBits() != 0);
+ }
}
void checkBackendPropertyNotifications()
diff --git a/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc b/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc
index feef480e2..e1506de86 100644
--- a/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc
+++ b/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc
@@ -10,5 +10,6 @@
<file>testscene_parententity.qml</file>
<file>testscene_viewports.qml</file>
<file>testscene_cameraposition.qml</file>
+ <file>testscene_priorityoverlapping.qml</file>
</qresource>
</RCC>
diff --git a/tests/auto/render/pickboundingvolumejob/testscene_priorityoverlapping.qml b/tests/auto/render/pickboundingvolumejob/testscene_priorityoverlapping.qml
new file mode 100644
index 000000000..7cacb3d2d
--- /dev/null
+++ b/tests/auto/render/pickboundingvolumejob/testscene_priorityoverlapping.qml
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt3D.Core 2.0
+import Qt3D.Render 2.13
+import Qt3D.Extras 2.0
+import QtQuick.Window 2.0
+
+Entity {
+ id: sceneRoot
+
+ Window {
+ id: win
+ width: 600
+ height: 600
+ visible: true
+ }
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 45
+ nearPlane : 0.1
+ farPlane : 1000.0
+ position: Qt.vector3d( 0.0, 0.0, -40.0 )
+ upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
+ viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: Viewport {
+ normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
+
+ RenderSurfaceSelector {
+ surface: win
+
+ ClearBuffers {
+ buffers : ClearBuffers.ColorDepthBuffer
+ NoDraw {}
+ }
+
+ CameraSelector {
+ camera: camera
+ }
+ }
+ }
+ pickingSettings {
+ pickResultMode: PickingSettings.NearestPriorityPick
+ pickMethod: PickingSettings.TrianglePicking
+ faceOrientationPickingMode: PickingSettings.FrontAndBackFace
+ }
+ }
+ ]
+
+ CuboidMesh { id: cubeMesh }
+ PhongMaterial { id: material }
+
+ // Entity 1
+ Entity {
+ property ObjectPicker picker: ObjectPicker {
+ id: picker1
+ objectName: "Picker1"
+ }
+
+ property Transform transform: Transform {
+ translation: Qt.vector3d(0, 0, 0)
+ scale: 2.0
+ }
+
+ components: [cubeMesh, material, picker1, transform]
+ }
+
+ // Entity 2
+ Entity {
+ property ObjectPicker picker: ObjectPicker {
+ id: picker2
+ objectName: "Picker2"
+ }
+
+ property Transform transform: Transform {
+ translation: Qt.vector3d(0, 0, 10)
+ scale: 2.5
+ }
+
+ components: [cubeMesh, material, picker2, transform]
+ }
+}
diff --git a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
index 60b60eb6e..5e51c8aa7 100644
--- a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
+++ b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
@@ -1473,6 +1473,111 @@ private Q_SLOTS:
arbiter.events.clear();
}
+ void checkPriorityPicking()
+ {
+ // GIVEN
+ QmlSceneReader sceneReader(QUrl("qrc:/testscene_priorityoverlapping.qml"));
+ QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(sceneReader.root()));
+ QVERIFY(root);
+
+ QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data()));
+ TestArbiter arbiter1;
+ TestArbiter arbiter2;
+
+ // Runs Required jobs
+ runRequiredJobs(test.data());
+
+ // THEN
+ QList<Qt3DRender::QObjectPicker *> pickers = root->findChildren<Qt3DRender::QObjectPicker *>();
+ QCOMPARE(pickers.size(), 2);
+
+ Qt3DRender::QObjectPicker *picker1 = nullptr;
+ Qt3DRender::QObjectPicker *picker2 = nullptr;
+ if (pickers.first()->objectName() == QLatin1String("Picker1")) {
+ picker1 = pickers.first();
+ picker2 = pickers.last();
+ } else {
+ picker1 = pickers.last();
+ picker2 = pickers.first();
+ }
+
+ Qt3DRender::Render::ObjectPicker *backendPicker1 = test->nodeManagers()->objectPickerManager()->lookupResource(picker1->id());
+ QVERIFY(backendPicker1);
+ Qt3DCore::QBackendNodePrivate::get(backendPicker1)->setArbiter(&arbiter1);
+
+ Qt3DRender::Render::ObjectPicker *backendPicker2 = test->nodeManagers()->objectPickerManager()->lookupResource(picker2->id());
+ QVERIFY(backendPicker2);
+ Qt3DCore::QBackendNodePrivate::get(backendPicker2)->setArbiter(&arbiter2);
+
+
+ // WHEN both have priority == 0, select closest
+ {
+ Qt3DRender::Render::PickBoundingVolumeJob pickBVJob;
+ initializePickBoundingVolumeJob(&pickBVJob, test.data());
+
+ // WHEN -> Pressed on object
+ QList<QPair<QObject *, QMouseEvent>> events;
+ events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(300.0f, 300.0f),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)});
+ pickBVJob.setMouseEvents(events);
+ bool earlyReturn = !pickBVJob.runHelper();
+
+ // THEN -> Select picker with highest priority
+ QVERIFY(!earlyReturn);
+ QVERIFY(backendPicker1->isPressed());
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter1.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "pressed");
+
+ QVERIFY(!backendPicker2->isPressed());
+ QVERIFY(arbiter2.events.isEmpty());
+
+ events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(300.0f, 300.0f),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)});
+ pickBVJob.setMouseEvents(events);
+ pickBVJob.runHelper();
+ arbiter1.events.clear();
+ arbiter2.events.clear();
+
+ QVERIFY(!backendPicker1->isPressed());
+ QVERIFY(!backendPicker2->isPressed());
+ }
+
+ // WHEN furthest one has higher priority, select furthest one
+ {
+ backendPicker2->setPriority(1000);
+ QCOMPARE(backendPicker2->priority(), 1000);
+
+ Qt3DRender::Render::PickBoundingVolumeJob pickBVJob;
+ initializePickBoundingVolumeJob(&pickBVJob, test.data());
+
+ // WHEN -> Pressed on object
+ QList<QPair<QObject *, QMouseEvent>> events;
+ events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(300.0f, 300.0f),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)});
+ pickBVJob.setMouseEvents(events);
+ bool earlyReturn = !pickBVJob.runHelper();
+
+ // THEN -> Select picker with highest priority
+ QVERIFY(!earlyReturn);
+ QVERIFY(backendPicker2->isPressed());
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter2.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "pressed");
+
+ QVERIFY(!backendPicker1->isPressed());
+ QVERIFY(arbiter1.events.isEmpty());
+
+ events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(300.0f, 300.0f),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)});
+ pickBVJob.setMouseEvents(events);
+ pickBVJob.runHelper();
+ arbiter1.events.clear();
+ arbiter2.events.clear();
+
+ QVERIFY(!backendPicker1->isPressed());
+ QVERIFY(!backendPicker2->isPressed());
+ }
+ }
+
};
QTEST_MAIN(tst_PickBoundingVolumeJob)
diff --git a/tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp b/tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp
index 6714d8a06..bd486774c 100644
--- a/tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp
+++ b/tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp
@@ -31,8 +31,10 @@
#include <Qt3DCore/private/qnode_p.h>
#include <Qt3DCore/private/qscene_p.h>
#include <Qt3DRender/QObjectPicker>
+#include <Qt3DRender/private/qobjectpicker_p.h>
#include <Qt3DRender/QPickEvent>
-
+#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
+#include <Qt3DCore/qnodecreatedchange.h>
#include "testpostmanarbiter.h"
class MyObjectPicker : public Qt3DRender::QObjectPicker
@@ -71,6 +73,162 @@ public:
private Q_SLOTS:
+ void checkInitialState()
+ {
+ // GIVEN
+ Qt3DRender::QObjectPicker picker;
+
+ // THEN
+ QCOMPARE(picker.priority(), 0);
+ QCOMPARE(picker.isDragEnabled(), false);
+ QCOMPARE(picker.isHoverEnabled(), false);
+ }
+
+ void checkCreationData()
+ {
+ // GIVEN
+ Qt3DRender::QObjectPicker picker;
+
+ picker.setPriority(1584);
+ picker.setDragEnabled(true);
+ picker.setHoverEnabled(true);
+
+ // WHEN
+ QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges;
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&picker);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QObjectPickerData>>(creationChanges.first());
+ const Qt3DRender::QObjectPickerData cloneData = creationChangeData->data;
+
+ QCOMPARE(cloneData.priority, 1584);
+ QCOMPARE(cloneData.hoverEnabled, true);
+ QCOMPARE(cloneData.dragEnabled, true);
+ QCOMPARE(picker.id(), creationChangeData->subjectId());
+ QCOMPARE(picker.isEnabled(), true);
+ QCOMPARE(picker.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(picker.metaObject(), creationChangeData->metaObject());
+ }
+
+ // WHEN
+ picker.setEnabled(false);
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&picker);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QObjectPickerData>>(creationChanges.first());
+ const Qt3DRender::QObjectPickerData cloneData = creationChangeData->data;
+
+ QCOMPARE(cloneData.priority, 1584);
+ QCOMPARE(cloneData.hoverEnabled, true);
+ QCOMPARE(cloneData.dragEnabled, true);
+ QCOMPARE(picker.id(), creationChangeData->subjectId());
+ QCOMPARE(picker.isEnabled(), false);
+ QCOMPARE(picker.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(picker.metaObject(), creationChangeData->metaObject());
+ }
+ }
+
+ void checkPropertyUpdate()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QObjectPicker picker;
+ arbiter.setArbiterOnNode(&picker);
+
+ {
+ {
+ // WHEN
+ picker.setPriority(883);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ QCOMPARE(picker.priority(), 883);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "priority");
+ QCOMPARE(change->value().value<int>(), picker.priority());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ picker.setPriority(883);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+ {
+ {
+ // WHEN
+ picker.setDragEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ QCOMPARE(picker.isDragEnabled(), true);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "dragEnabled");
+ QCOMPARE(change->value().value<bool>(), picker.isDragEnabled());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ picker.setDragEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+ {
+ {
+ // WHEN
+ picker.setHoverEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ QCOMPARE(picker.isHoverEnabled(), true);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "hoverEnabled");
+ QCOMPARE(change->value().value<bool>(), picker.isHoverEnabled());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ picker.setHoverEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+ }
+
void checkCloning_data()
{
QTest::addColumn<Qt3DRender::QObjectPicker *>("objectPicker");