aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2022-05-20 15:24:22 +0300
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2022-05-31 12:02:49 +0000
commitc7891864182f610d30c8b67601e3f4617d14a7eb (patch)
tree4850bfa946eba9b420f783da5cf9a8870a38a251
parentfc2f3983bc40184a855401871ab894c903a3f212 (diff)
QmlDesigner: Handle picking of models under View3D component properly
If a model defined inside the View3D component is picked on 3D editor, the parent View3D is selected instead as there is no instance for the model itself. This is similar to how Node based component picking works. Fixes: QDS-6934 Change-Id: I4f273972da8cb1c55f03cab323dd9804a5d10def Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
-rw-r--r--share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml11
-rw-r--r--share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml11
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp10
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h2
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp4
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h1
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp136
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h1
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp4
9 files changed, 161 insertions, 19 deletions
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml
index 904a062a62..cb9ece58b2 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml
@@ -329,6 +329,15 @@ Item {
function handleObjectClicked(object, multi)
{
+ if (object instanceof View3D) {
+ // View3D can be the resolved pick target in case the 3D editor is showing content
+ // of a component that has View3D as root. In that case locking is resolved on C++ side
+ // and we ignore multiselection.
+ selectObjects([]);
+ selectionChanged([object]);
+ return;
+ }
+
var clickedObject;
// Click on locked object is treated same as click on empty space
@@ -740,7 +749,7 @@ Item {
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
mouse.modifiers & Qt.ControlModifier);
- if (pickResult.objectHit) {
+ if (pickResult.objectHit && pickResult.objectHit instanceof Node) {
if (transformMode === EditView3D.TransformMode.Move)
freeDraggerArea = moveGizmo.freeDraggerArea;
else if (transformMode === EditView3D.TransformMode.Rotate)
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
index 25297f8fc6..3431a1ce3c 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
@@ -329,6 +329,15 @@ Item {
function handleObjectClicked(object, multi)
{
+ if (object instanceof View3D) {
+ // View3D can be the resolved pick target in case the 3D editor is showing content
+ // of a component that has View3D as root. In that case locking is resolved on C++ side
+ // and we ignore multiselection.
+ selectObjects([]);
+ selectionChanged([object]);
+ return;
+ }
+
var clickedObject;
// Click on locked object is treated same as click on empty space
@@ -892,7 +901,7 @@ Item {
handleObjectClicked(resolvedResult, mouse.modifiers & Qt.ControlModifier);
- if (pickResult.objectHit) {
+ if (pickResult.objectHit && pickResult.objectHit instanceof Node) {
if (transformMode === EditView3D.TransformMode.Move)
freeDraggerArea = moveGizmo.freeDraggerArea;
else if (transformMode === EditView3D.TransformMode.Rotate)
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
index 7e843012be..dd21622b5d 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
@@ -438,15 +438,15 @@ QQuick3DPickResult GeneralHelper::pickViewAt(QQuick3DViewport *view, float posX,
return QQuick3DPickResult();
}
-QQuick3DNode *GeneralHelper::resolvePick(QQuick3DNode *pickNode)
+QObject *GeneralHelper::resolvePick(QQuick3DNode *pickNode)
{
if (pickNode) {
- // Check if the picked node actually specifies another node as the pick target
+ // Check if the picked node actually specifies another object as the pick target
QVariant componentVar = pickNode->property("_pickTarget");
if (componentVar.isValid()) {
- auto componentNode = componentVar.value<QQuick3DNode *>();
- if (componentNode)
- return componentNode;
+ auto componentObj = componentVar.value<QObject *>();
+ if (componentObj)
+ return componentObj;
}
}
return pickNode;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
index 98974cfda9..5bb1fa1662 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
@@ -85,7 +85,7 @@ public:
Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property,
const QVariant& value);
Q_INVOKABLE QQuick3DPickResult pickViewAt(QQuick3DViewport *view, float posX, float posY);
- Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode);
+ Q_INVOKABLE QObject *resolvePick(QQuick3DNode *pickNode);
Q_INVOKABLE bool isLocked(QQuick3DNode *node) const;
Q_INVOKABLE bool isHidden(QQuick3DNode *node) const;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
index 8400c3d447..41c67f70b6 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
@@ -1561,6 +1561,10 @@ void NodeInstanceServer::handleInstanceHidden(const ServerNodeInstance &/*instan
{
}
+void NodeInstanceServer::handlePickTarget(const ServerNodeInstance &/*instance*/)
+{
+}
+
void NodeInstanceServer::setupState(qint32 stateInstanceId)
{
if (hasInstanceForId(stateInstanceId)) {
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h
index ba51f5e588..42f345ee4e 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h
@@ -226,6 +226,7 @@ public:
virtual void handleInstanceLocked(const ServerNodeInstance &instance, bool enable, bool checkAncestors);
virtual void handleInstanceHidden(const ServerNodeInstance &instance, bool enable, bool checkAncestors);
+ virtual void handlePickTarget(const ServerNodeInstance &instance);
virtual QImage grabWindow() = 0;
virtual QImage grabItem(QQuickItem *item) = 0;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
index c3fc4d4e07..975a6ded6b 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
@@ -553,7 +553,18 @@ void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &ob
auto obj = object.value<QObject *>();
if (obj) {
ServerNodeInstance instance = instanceForObject(obj);
- instanceList << instance;
+ // If instance is a View3D, make sure it is not locked
+ bool locked = false;
+ if (instance.isSubclassOf("QQuick3DViewport")) {
+ locked = instance.internalInstance()->isLockedInEditor();
+ auto parentInst = instance.parent();
+ while (!locked && parentInst.isValid()) {
+ locked = parentInst.internalInstance()->isLockedInEditor();
+ parentInst = parentInst.parent();
+ }
+ }
+ if (!locked)
+ instanceList << instance;
#ifdef QUICK3D_PARTICLES_MODULE
if (!skipSystemDeselect) {
auto particleSystem = parentParticleSystem(instance.internalObject());
@@ -1448,8 +1459,9 @@ void Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout()
void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout()
{
-#ifdef QUICK3D_MODULE
for (auto obj : std::as_const(m_dynamicObjectConstructors)) {
+#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1)
+#ifdef QUICK3D_MODULE
auto handleHiding = [this](QQuick3DNode *node) -> bool {
if (node && hasInstanceForObject(node)) {
ServerNodeInstance instance = instanceForObject(node);
@@ -1464,8 +1476,22 @@ void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout()
if (auto pickTarget = obj->property("_pickTarget").value<QQuick3DNode *>())
handleHiding(pickTarget);
}
- }
#endif
+#else
+ auto handlePicking = [this](QObject *object) -> bool {
+ if (object && hasInstanceForObject(object)) {
+ ServerNodeInstance instance = instanceForObject(object);
+ handlePickTarget(instance);
+ return true;
+ }
+ return false;
+ };
+ if (!handlePicking(obj)) {
+ if (auto pickTarget = obj->property("_pickTarget").value<QObject *>())
+ handlePicking(pickTarget);
+ }
+#endif
+ }
m_dynamicObjectConstructors.clear();
}
@@ -2328,9 +2354,9 @@ void Qt5InformationNodeInstanceServer::handleInstanceLocked(const ServerNodeInst
}
}
#else
- Q_UNUSED(instance);
- Q_UNUSED(enable);
- Q_UNUSED(checkAncestors);
+ Q_UNUSED(instance)
+ Q_UNUSED(enable)
+ Q_UNUSED(checkAncestors)
#endif
}
@@ -2384,6 +2410,7 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst
// Don't override explicit hide in children
handleInstanceHidden(quick3dInstance, edit3dHidden || isInstanceHidden, false);
} else {
+#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1)
// Children of components do not have instances, but will still need to be pickable
std::function<void(QQuick3DNode *)> checkChildren;
checkChildren = [&](QQuick3DNode *checkNode) {
@@ -2398,9 +2425,7 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst
value = QVariant::fromValue(node);
// Specify the actual pick target with dynamic property
checkModel->setProperty("_pickTarget", value);
-#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1)
checkModel->setPickable(!edit3dHidden);
-#endif
} else {
auto checkRepeater = qobject_cast<QQuick3DRepeater *>(checkNode);
auto checkLoader = qobject_cast<QQuick3DLoader *>(checkNode);
@@ -2432,13 +2457,102 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst
};
if (auto childNode = qobject_cast<QQuick3DNode *>(childItem))
checkChildren(childNode);
+#endif
}
}
}
#else
- Q_UNUSED(instance);
- Q_UNUSED(enable);
- Q_UNUSED(checkAncestors);
+ Q_UNUSED(instance)
+ Q_UNUSED(enable)
+ Q_UNUSED(checkAncestors)
+#endif
+}
+
+void Qt5InformationNodeInstanceServer::handlePickTarget(const ServerNodeInstance &instance)
+{
+#if defined(QUICK3D_MODULE) && (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1))
+ // Picking is dependent on hidden status prior to global picking support (<6.2.1), so it is
+ // handled in handleInstanceHidden() method in those builds
+
+ if (!ViewConfig::isQuick3DMode())
+ return;
+
+ QObject *obj = instance.internalObject();
+ QList<QQuick3DObject *> childItems;
+ if (auto node = qobject_cast<QQuick3DNode *>(obj)) {
+ childItems = node->childItems();
+ } else if (auto view = qobject_cast<QQuick3DViewport *>(obj)) {
+ // We only need to handle views that are components
+ // View is a component if its scene is not an instance and scene has node children that
+ // have no instances
+ QQuick3DNode *node = view->scene();
+ if (node) {
+ if (hasInstanceForObject(node))
+ return;
+ childItems = node->childItems();
+ bool allHaveInstance = true;
+ for (const auto &childItem : childItems) {
+ if (qobject_cast<QQuick3DNode *>(childItem) && !hasInstanceForObject(childItem)) {
+ allHaveInstance = false;
+ break;
+ }
+ }
+ if (allHaveInstance)
+ return;
+ }
+ } else {
+ return;
+ }
+
+ for (auto childItem : qAsConst(childItems)) {
+ if (!hasInstanceForObject(childItem)) {
+ // Children of components do not have instances, but will still need to be pickable
+ // and redirect their pick to the component
+ std::function<void(QQuick3DNode *)> checkChildren;
+ checkChildren = [&](QQuick3DNode *checkNode) {
+ const auto childItems = checkNode->childItems();
+ for (auto child : childItems) {
+ if (auto childNode = qobject_cast<QQuick3DNode *>(child))
+ checkChildren(childNode);
+ }
+ if (auto checkModel = qobject_cast<QQuick3DModel *>(checkNode)) {
+ // Specify the actual pick target with dynamic property
+ checkModel->setProperty("_pickTarget", QVariant::fromValue(obj));
+ } else {
+ auto checkRepeater = qobject_cast<QQuick3DRepeater *>(checkNode);
+ auto checkLoader = qobject_cast<QQuick3DLoader *>(checkNode);
+#if defined(QUICK3D_ASSET_UTILS_MODULE)
+ auto checkRunLoader = qobject_cast<QQuick3DRuntimeLoader *>(checkNode);
+ if (checkRepeater || checkLoader || checkRunLoader) {
+#else
+ if (checkRepeater || checkLoader) {
+#endif
+ // Repeaters/loaders may not yet have created their children, so we set
+ // _pickTarget on them and connect the notifier.
+ if (checkNode->property("_pickTarget").isNull()) {
+ if (checkRepeater) {
+ QObject::connect(checkRepeater, &QQuick3DRepeater::objectAdded,
+ this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject);
+#if defined(QUICK3D_ASSET_UTILS_MODULE)
+ } else if (checkRunLoader) {
+ QObject::connect(checkRunLoader, &QQuick3DRuntimeLoader::statusChanged,
+ this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject);
+#endif
+ } else {
+ QObject::connect(checkLoader, &QQuick3DLoader::loaded,
+ this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject);
+ }
+ }
+ checkNode->setProperty("_pickTarget", QVariant::fromValue(obj));
+ }
+ }
+ };
+ if (auto childNode = qobject_cast<QQuick3DNode *>(childItem))
+ checkChildren(childNode);
+ }
+ }
+#else
+ Q_UNUSED(instance)
#endif
}
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
index b6375076b6..291b2c3eec 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
@@ -80,6 +80,7 @@ public:
void handleInstanceLocked(const ServerNodeInstance &instance, bool enable, bool checkAncestors) override;
void handleInstanceHidden(const ServerNodeInstance &instance, bool enable, bool checkAncestors) override;
+ void handlePickTarget(const ServerNodeInstance &instance) override;
bool isInformationServer() const override;
void handleDynamicAddObject();
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
index a4fdde97ec..2f4be3e8e9 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
@@ -323,8 +323,12 @@ ServerNodeInstance ServerNodeInstance::create(NodeInstanceServer *nodeInstanceSe
instance.internalInstance()->initialize(instance.m_nodeInstance, instanceContainer.metaFlags());
+#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1)
// Handle hidden state to initialize pickable state
nodeInstanceServer->handleInstanceHidden(instance, false, false);
+#else
+ nodeInstanceServer->handlePickTarget(instance);
+#endif
return instance;
}