diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 1921f458935..e75c791a197 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -191,6 +191,93 @@ bool DesignDocument::pasteSVG() return true; } +void DesignDocument::moveNodesToPosition(const QList &nodes, const std::optional &position) +{ + if (!nodes.size()) + return; + + QList movingNodes = nodes; + DesignDocumentView view{m_externalDependencies}; + currentModel()->attachView(&view); + + ModelNode targetNode; // the node that new nodes should be placed in + if (!view.selectedModelNodes().isEmpty()) + targetNode = view.firstSelectedModelNode(); + + // in case we copy and paste a selection we paste in the parent item + if ((view.selectedModelNodes().count() == movingNodes.count()) + && targetNode.hasParentProperty()) { + targetNode = targetNode.parentProperty().parentModelNode(); + } else if (view.selectedModelNodes().isEmpty()) { + // if selection is empty and copied nodes are all 3D nodes, paste them under the active scene + bool all3DNodes = Utils::allOf(movingNodes, [](const ModelNode &node) { + return node.metaInfo().isQtQuick3DNode(); + }); + + if (all3DNodes) { + auto data = rootModelNode().auxiliaryData(active3dSceneProperty); + if (data) { + if (int activeSceneId = data->toInt(); activeSceneId != -1) { + NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty( + rootModelNode().view(), activeSceneId); + targetNode = sceneNodeProperty.parentModelNode(); + } + } + } + } + + if (!targetNode.isValid()) + targetNode = view.rootModelNode(); + + for (const ModelNode &node : std::as_const(movingNodes)) { + for (const ModelNode &node2 : std::as_const(movingNodes)) { + if (node.isAncestorOf(node2)) + movingNodes.removeAll(node2); + } + } + + bool isSingleNode = movingNodes.size() == 1; + if (isSingleNode) { + ModelNode singleNode = movingNodes.first(); + if (targetNode.hasParentProperty() + && singleNode.simplifiedTypeName() == targetNode.simplifiedTypeName() + && singleNode.variantProperty("width").value() == targetNode.variantProperty("width").value() + && singleNode.variantProperty("height").value() == targetNode.variantProperty("height").value()) { + targetNode = targetNode.parentProperty().parentModelNode(); + } + } + + const PropertyName defaultPropertyName = targetNode.metaInfo().defaultPropertyName(); + if (!targetNode.metaInfo().property(defaultPropertyName).isListProperty()) + qWarning() << Q_FUNC_INFO << "Cannot reparent to" << targetNode; + NodeListProperty parentProperty = targetNode.nodeListProperty(defaultPropertyName); + QList pastedNodeList; + + const double scatterRange = 20.; + int offset = QRandomGenerator::global()->generateDouble() * scatterRange - scatterRange / 2; + + std::optional firstVisualNode; + QVector3D translationVect; + for (const ModelNode &node : std::as_const(movingNodes)) { + ModelNode pastedNode(view.insertModel(node)); + pastedNodeList.append(pastedNode); + parentProperty.reparentHere(pastedNode); + + QmlVisualNode visualNode(pastedNode); + if (!firstVisualNode.has_value() && visualNode.isValid()){ + firstVisualNode = visualNode; + translationVect = (position.has_value() && firstVisualNode.has_value()) + ? position.value() - firstVisualNode->position() + : QVector3D(); + } + visualNode.translate(translationVect); + visualNode.scatter(targetNode, offset); + } + + view.setSelectedModelNodes(pastedNodeList); + view.model()->clearMetaInfoCache(); +} + bool DesignDocument::inFileComponentModelActive() const { return bool(m_inFileComponentModel); @@ -492,46 +579,12 @@ void DesignDocument::cutSelected() deleteSelected(); } -static void scatterItem(const ModelNode &pastedNode, const ModelNode &targetNode, int offset = -2000) +void DesignDocument::paste() { - if (targetNode.metaInfo().isValid() && targetNode.metaInfo().isLayoutable()) - return; - - if (!(pastedNode.hasVariantProperty("x") && pastedNode.hasVariantProperty("y"))) - return; - - bool scatter = false; - for (const ModelNode &childNode : targetNode.directSubModelNodes()) { - if (childNode.variantProperty("x").value() == pastedNode.variantProperty("x").value() && - childNode.variantProperty("y").value() == pastedNode.variantProperty("y").value()) { - scatter = true; - break; - } - } - if (!scatter) - return; - - if (offset == -2000) { // scatter in range - double x = pastedNode.variantProperty("x").value().toDouble(); - double y = pastedNode.variantProperty("y").value().toDouble(); - - const double scatterRange = 20.; - x += QRandomGenerator::global()->generateDouble() * scatterRange - scatterRange / 2; - y += QRandomGenerator::global()->generateDouble() * scatterRange - scatterRange / 2; - - pastedNode.variantProperty("x").setValue(int(x)); - pastedNode.variantProperty("y").setValue(int(y)); - } else { // offset - int x = pastedNode.variantProperty("x").value().toInt(); - int y = pastedNode.variantProperty("y").value().toInt(); - x += offset; - y += offset; - pastedNode.variantProperty("x").setValue(x); - pastedNode.variantProperty("y").setValue(y); - } + pasteToPosition({}); } -void DesignDocument::paste() +void DesignDocument::pasteToPosition(const std::optional &position) { if (pasteSVG()) return; @@ -547,116 +600,21 @@ void DesignDocument::paste() DesignDocumentView view{m_externalDependencies}; pasteModel->attachView(&view); ModelNode rootNode(view.rootModelNode()); - QList selectedNodes = rootNode.directSubModelNodes(); - pasteModel->detachView(&view); if (rootNode.type() == "empty") return; - if (rootNode.id() == "__multi__selection__") { // pasting multiple objects - currentModel()->attachView(&view); + QList selectedNodes; + if (rootNode.id() == "__multi__selection__") + selectedNodes << rootNode.directSubModelNodes(); + else + selectedNodes << rootNode; - ModelNode targetNode; + pasteModel->detachView(&view); - if (!view.selectedModelNodes().isEmpty()) - targetNode = view.firstSelectedModelNode(); - - // in case we copy and paste a selection we paste in the parent item - if ((view.selectedModelNodes().count() == selectedNodes.count()) - && targetNode.hasParentProperty()) { - targetNode = targetNode.parentProperty().parentModelNode(); - } else if (view.selectedModelNodes().isEmpty()) { - // if selection is empty and copied nodes are all 3D nodes, paste them under the active scene - bool all3DNodes = std::find_if(selectedNodes.cbegin(), - selectedNodes.cend(), - [](const ModelNode &node) { - return !node.metaInfo().isQtQuick3DNode(); - }) - == selectedNodes.cend(); - if (all3DNodes) { - auto data = rootModelNode().auxiliaryData(active3dSceneProperty); - if (data) { - if (int activeSceneId = data->toInt(); activeSceneId != -1) { - NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty( - rootModelNode().view(), activeSceneId); - targetNode = sceneNodeProperty.parentModelNode(); - } - } - } - } - - if (!targetNode.isValid()) - targetNode = view.rootModelNode(); - - for (const ModelNode &node : std::as_const(selectedNodes)) { - for (const ModelNode &node2 : std::as_const(selectedNodes)) { - if (node.isAncestorOf(node2)) - selectedNodes.removeAll(node2); - } - } - - rewriterView()->executeInTransaction("DesignDocument::paste1", [&view, selectedNodes, targetNode]() { - QList pastedNodeList; - - const double scatterRange = 20.; - int offset = QRandomGenerator::global()->generateDouble() * scatterRange - scatterRange / 2; - - const auto defaultPropertyName = targetNode.metaInfo().defaultPropertyName(); - auto parentProperty = targetNode.nodeListProperty(defaultPropertyName); - for (const ModelNode &node : std::as_const(selectedNodes)) { - ModelNode pastedNode(view.insertModel(node)); - pastedNodeList.append(pastedNode); - scatterItem(pastedNode, targetNode, offset); - parentProperty.reparentHere(pastedNode); - } - - view.setSelectedModelNodes(pastedNodeList); - }); - - } else { // pasting single object - rewriterView()->executeInTransaction("DesignDocument::paste1", [this, &view, rootNode]() { - currentModel()->attachView(&view); - ModelNode pastedNode(view.insertModel(rootNode)); - ModelNode targetNode; - - if (!view.selectedModelNodes().isEmpty()) { - targetNode = view.firstSelectedModelNode(); - } else { - // if selection is empty and this is a 3D Node, paste it under the active scene - if (pastedNode.metaInfo().isQtQuick3DNode()) { - auto data = rootModelNode().auxiliaryData(active3dSceneProperty); - if (data) { - if (int activeSceneId = data->toInt(); activeSceneId != -1) { - NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty( - rootModelNode().view(), activeSceneId); - targetNode = sceneNodeProperty.parentModelNode(); - } - } - } - } - - if (!targetNode.isValid()) - targetNode = view.rootModelNode(); - - if (targetNode.hasParentProperty() && - pastedNode.simplifiedTypeName() == targetNode.simplifiedTypeName() && - pastedNode.variantProperty("width").value() == targetNode.variantProperty("width").value() && - pastedNode.variantProperty("height").value() == targetNode.variantProperty("height").value()) { - targetNode = targetNode.parentProperty().parentModelNode(); - } - - PropertyName defaultProperty(targetNode.metaInfo().defaultPropertyName()); - - scatterItem(pastedNode, targetNode); - if (targetNode.metaInfo().property(defaultProperty).isListProperty()) - targetNode.nodeListProperty(defaultProperty).reparentHere(pastedNode); - else - qWarning() << "Cannot reparent to" << targetNode; - - view.setSelectedModelNodes({pastedNode}); - }); - view.model()->clearMetaInfoCache(); - } + rewriterView()->executeInTransaction("DesignDocument::pasteToPosition", [this, selectedNodes, position]() { + moveNodesToPosition(selectedNodes, position); + }); } void DesignDocument::selectAll() diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 25cd03c164e..4750908f64a 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -99,6 +99,7 @@ public: void copySelected(); void cutSelected(); void paste(); + void pasteToPosition(const std::optional &position); void selectAll(); void undo(); void redo(); @@ -128,6 +129,7 @@ private: // functions ModelPointer createInFileComponentModel(); bool pasteSVG(); + void moveNodesToPosition(const QList &nodes, const std::optional &position); private: // variables ModelPointer m_documentModel;