diff options
author | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2022-05-20 15:24:22 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2022-05-31 12:02:49 +0000 |
commit | c7891864182f610d30c8b67601e3f4617d14a7eb (patch) | |
tree | 4850bfa946eba9b420f783da5cf9a8870a38a251 | |
parent | fc2f3983bc40184a855401871ab894c903a3f212 (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>
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; } |