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.;