From 651d7f5d8504fa6d0311fb23dd172ade34d44da2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 23 Apr 2024 11:39:53 +0300 Subject: [PATCH] QmlDesigner: Select correct underlying View3D for "Edit in 3D View" If the selected node is not View3D, check if the topmost item in scenePosition is View3D and target that instead for "Edit in 3D View" action. Fixes: QDS-12347 Change-Id: I06b59f55246e828cced8dd3c15ee9b297e5edeb7 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../componentcore/designeractionmanager.cpp | 11 ++----- .../modelnodecontextmenu_helper.h | 28 ++++++++++++++++-- .../componentcore/modelnodeoperations.cpp | 29 ++++++++++++++++--- .../components/edit3d/edit3dview.cpp | 3 ++ .../components/edit3d/edit3dview.h | 1 + 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 4e32237ee95..8d2b2c43c2a 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -61,11 +61,6 @@ inline static QString captionForModelNode(const ModelNode &modelNode) return modelNode.id(); } -inline static bool contains(const QmlItemNode &node, const QPointF &position) -{ - return node.isValid() && node.instanceSceneTransform().mapRect(node.instanceBoundingRect()).contains(position); -} - DesignerActionManagerView *DesignerActionManager::view() { return m_designerActionManagerView; @@ -438,8 +433,8 @@ public: } for (const ModelNode &node : selectionContext().view()->allModelNodes()) { if (node != selectionContext().currentSingleSelectedNode() && node != parentNode - && contains(node, selectionContext().scenePosition()) && !node.isRootNode() - && !ModelUtils::isThisOrAncestorLocked(node)) { + && SelectionContextHelpers::contains(node, selectionContext().scenePosition()) + && !node.isRootNode() && !ModelUtils::isThisOrAncestorLocked(node)) { selectionContext().setTargetNode(node); QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node)); ActionTemplate *selectionAction = new ActionTemplate("SELECT", what, &ModelNodeOperations::select); @@ -1971,7 +1966,7 @@ void DesignerActionManager::createDefaultDesignerActions() QKeySequence(), Priorities::ComponentActions + 1, &editIn3dView, - &singleSelectionView3D, + &SelectionContextFunctors::always, // If action is visible, it is usable &singleSelectionView3D)); addDesignerAction(new ModelNodeContextMenuAction( diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index eb4915b1d0a..aec14e9d04b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -25,6 +25,16 @@ namespace QmlDesigner { using SelectionContextPredicate = std::function; using SelectionContextOperation = std::function; +namespace SelectionContextHelpers { + +inline bool contains(const QmlItemNode &node, const QPointF &position) +{ + return node.isValid() + && node.instanceSceneTransform().mapRect(node.instanceBoundingRect()).contains(position); +} + +} // namespace SelectionContextHelpers + namespace SelectionContextFunctors { inline bool always(const SelectionContext &) @@ -99,8 +109,22 @@ inline bool singleSelectionNotRoot(const SelectionContext &selectionState) inline bool singleSelectionView3D(const SelectionContext &selectionState) { - return selectionState.singleNodeIsSelected() - && selectionState.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D(); + if (selectionState.singleNodeIsSelected() + && selectionState.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) { + return true; + } + + // If currently selected node is not View3D, check if there is a View3D under the cursor. + if (!selectionState.scenePosition().isNull()) { + // Assumption is that last match in allModelNodes() list is the topmost one. + const QList allNodes = selectionState.view()->allModelNodes(); + for (int i = allNodes.size() - 1; i >= 0; --i) { + if (SelectionContextHelpers::contains(allNodes[i], selectionState.scenePosition())) + return allNodes[i].metaInfo().isQtQuick3DView3D(); + } + } + + return false; } inline bool selectionHasProperty(const SelectionContext &selectionState, const char *property) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index a5274c70e2d..c24ec9aa3e1 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1685,16 +1685,37 @@ void updateImported3DAsset(const SelectionContext &selectionContext) void editIn3dView(const SelectionContext &selectionContext) { - if (selectionContext.view() && selectionContext.hasSingleSelectedModelNode() + if (!selectionContext.view()) + return; + + ModelNode targetNode; + + if (selectionContext.hasSingleSelectedModelNode() && selectionContext.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) { + targetNode = selectionContext.currentSingleSelectedNode(); + } + + const QPointF scenePos = selectionContext.scenePosition(); + if (!targetNode.isValid() && !scenePos.isNull()) { + // If currently selected node is not View3D, check if there is a View3D under the cursor. + // Assumption is that last match in allModelNodes() list is the topmost one. + const QList allNodes = selectionContext.view()->allModelNodes(); + for (int i = allNodes.size() - 1; i >= 0; --i) { + if (SelectionContextHelpers::contains(allNodes[i], selectionContext.scenePosition())) { + if (allNodes[i].metaInfo().isQtQuick3DView3D()) + targetNode = allNodes[i]; + break; + } + } + } + + if (targetNode.isValid()) { QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true); - const QPointF scenePos = selectionContext.scenePosition(); if (scenePos.isNull()) { selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); } else { selectionContext.view()->emitCustomNotification("pick_3d_node_from_2d_scene", - {selectionContext.currentSingleSelectedNode()}, - {scenePos}); + {targetNode}, {scenePos}); } } } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index a20904b2e5d..4712b048b14 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -462,6 +462,7 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view, self->emitView3DAction(View3DActionType::GetNodeAtMainScenePos, QVariantList{data[0], nodeList[0].internalId()}); self->m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick; + self->m_pickView3dNode = nodeList[0]; }); } } @@ -514,6 +515,8 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos } else if (m_nodeAtPosReqType == NodeAtPosReqType::MainScenePick) { if (modelNode.isValid()) setSelectedModelNode(modelNode); + else if (m_pickView3dNode.isValid() && !m_pickView3dNode.isSelected()) + setSelectedModelNode(m_pickView3dNode); emitView3DAction(View3DActionType::AlignViewToCamera, true); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index fad87aae1f1..ade2ef6a8f9 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -189,6 +189,7 @@ private: QList m_splitToolStates; QList m_flyModeDisabledActions; ModelNode m_contextMenuPendingNode; + ModelNode m_pickView3dNode; double m_previousCameraSpeed = -1.; double m_previousCameraMultiplier = -1.;