From fefea30594b0b5f38b4777452fc43a9182bbcb8a Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Thu, 13 Jun 2019 18:36:40 +0200 Subject: [PATCH 1/3] Added support for copy paste in menu and ctrl+c / ctrl+v / cmd+c / cmd+v. --- Scripts/Editor/NodeEditor.cs | 1 + Scripts/Editor/NodeEditorAction.cs | 55 ++++++++++++++++++++++++++++++ Scripts/Editor/NodeGraphEditor.cs | 2 ++ 3 files changed, 58 insertions(+) diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 7652658..c3f2f9e 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -73,6 +73,7 @@ namespace XNodeEditor { } // Add actions to any number of selected nodes + menu.AddItem(new GUIContent("Copy"), false, NodeEditorWindow.current.CopySelectedNodes); menu.AddItem(new GUIContent("Duplicate"), false, NodeEditorWindow.current.DuplicateSelectedNodes); menu.AddItem(new GUIContent("Remove"), false, NodeEditorWindow.current.RemoveSelectedNodes); diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 792631a..f74744b 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -11,6 +11,8 @@ namespace XNodeEditor { public static bool isPanning { get; private set; } public static Vector2[] dragOffset; + public static XNode.Node[] copyBuffer = null; + private bool IsDraggingPort { get { return draggedOutput != null; } } private bool IsHoveringPort { get { return hoveredPort != null; } } private bool IsHoveringNode { get { return hoveredNode != null; } } @@ -27,6 +29,7 @@ namespace XNodeEditor { private RerouteReference[] preBoxSelectionReroute; private Rect selectionBox; private bool isDoubleClick = false; + private Vector2 lastMousePosition; private struct RerouteReference { public XNode.NodePort port; @@ -50,6 +53,8 @@ namespace XNodeEditor { Event e = Event.current; switch (e.type) { case EventType.MouseMove: + //Keyboard commands will not get correct mouse position from Event + lastMousePosition = e.mousePosition; break; case EventType.ScrollWheel: float oldZoom = zoom; @@ -303,6 +308,12 @@ namespace XNodeEditor { } else if (e.commandName == "Duplicate") { if (e.type == EventType.ExecuteCommand) DuplicateSelectedNodes(); e.Use(); + } else if (e.commandName == "Copy") { + if (e.type == EventType.ExecuteCommand) CopySelectedNodes(); + e.Use(); + } else if (e.commandName == "Paste") { + if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition)); + e.Use(); } Repaint(); break; @@ -424,6 +435,50 @@ namespace XNodeEditor { Selection.objects = newNodes; } + public void CopySelectedNodes() { + copyBuffer = Selection.objects.Where((o) => o != null && o is XNode.Node).Cast().ToArray(); + } + + public void PasteNodes(Vector2 pos) { + if (copyBuffer == null || copyBuffer.Length == 0) return; + + //Center paste around first node in list + Vector2 offset = pos - copyBuffer[0].position; + + UnityEngine.Object[] newNodes = new UnityEngine.Object[copyBuffer.Length]; + Dictionary substitutes = new Dictionary(); + for (int i = 0; i < copyBuffer.Length; i++) { + XNode.Node srcNode = copyBuffer[i] as XNode.Node; + if (srcNode == null) continue; + XNode.Node newNode = graphEditor.CopyNode(srcNode); + substitutes.Add(srcNode, newNode); + newNode.position = srcNode.position + offset; + newNodes[i] = newNode; + } + + // Walk through the selected nodes again, recreate connections, using the new nodes + for (int i = 0; i < copyBuffer.Length; i++) { + XNode.Node srcNode = copyBuffer[i] as XNode.Node; + if (srcNode == null) continue; + foreach (XNode.NodePort port in srcNode.Ports) { + for (int c = 0; c < port.ConnectionCount; c++) { + XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c); + XNode.NodePort outputPort = port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c); + + XNode.Node newNodeIn, newNodeOut; + if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) { + newNodeIn.UpdateStaticPorts(); + newNodeOut.UpdateStaticPorts(); + inputPort = newNodeIn.GetInputPort(inputPort.fieldName); + outputPort = newNodeOut.GetOutputPort(outputPort.fieldName); + } + if (!inputPort.IsConnectedTo(outputPort)) inputPort.Connect(outputPort); + } + } + } + Selection.objects = newNodes; + } + /// Draw a connection as we are dragging it public void DrawDraggedConnection() { if (IsDraggingPort) { diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index c509d28..8ece949 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -56,6 +56,8 @@ namespace XNodeEditor { }); } menu.AddSeparator(""); + if (NodeEditorWindow.copyBuffer != null && NodeEditorWindow.copyBuffer.Length > 0) menu.AddItem(new GUIContent("Paste"), false, () => NodeEditorWindow.current.PasteNodes(pos)); + else menu.AddDisabledItem(new GUIContent("Paste")); menu.AddItem(new GUIContent("Preferences"), false, () => NodeEditorWindow.OpenPreferences()); NodeEditorWindow.AddCustomContextMenuItems(menu, target); } From 6c38439e84f6cc23a208376191870850e770230c Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 18 Jun 2019 22:00:31 +0200 Subject: [PATCH 2/3] Built DuplicateSelectedNodes and PasteNodes together --- Scripts/Editor/NodeEditorAction.cs | 63 +++++++++--------------------- 1 file changed, 19 insertions(+), 44 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index f74744b..ada5992 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -397,42 +397,11 @@ namespace XNodeEditor { /// Duplicate selected nodes and select the duplicates public void DuplicateSelectedNodes() { - UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length]; - Dictionary substitutes = new Dictionary(); - for (int i = 0; i < Selection.objects.Length; i++) { - if (Selection.objects[i] is XNode.Node) { - XNode.Node srcNode = Selection.objects[i] as XNode.Node; - if (srcNode.graph != graph) continue; // ignore nodes selected in another graph - XNode.Node newNode = graphEditor.CopyNode(srcNode); - substitutes.Add(srcNode, newNode); - newNode.position = srcNode.position + new Vector2(30, 30); - newNodes[i] = newNode; - } - } - - // Walk through the selected nodes again, recreate connections, using the new nodes - for (int i = 0; i < Selection.objects.Length; i++) { - if (Selection.objects[i] is XNode.Node) { - XNode.Node srcNode = Selection.objects[i] as XNode.Node; - if (srcNode.graph != graph) continue; // ignore nodes selected in another graph - foreach (XNode.NodePort port in srcNode.Ports) { - for (int c = 0; c < port.ConnectionCount; c++) { - XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c); - XNode.NodePort outputPort = port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c); - - XNode.Node newNodeIn, newNodeOut; - if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) { - newNodeIn.UpdateStaticPorts(); - newNodeOut.UpdateStaticPorts(); - inputPort = newNodeIn.GetInputPort(inputPort.fieldName); - outputPort = newNodeOut.GetOutputPort(outputPort.fieldName); - } - if (!inputPort.IsConnectedTo(outputPort)) inputPort.Connect(outputPort); - } - } - } - } - Selection.objects = newNodes; + // Get selected nodes which are part of this graph + XNode.Node[] selectedNodes = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray(); + // Get top left node position + Vector2 topLeftNode = selectedNodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y))); + InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30)); } public void CopySelectedNodes() { @@ -440,15 +409,20 @@ namespace XNodeEditor { } public void PasteNodes(Vector2 pos) { - if (copyBuffer == null || copyBuffer.Length == 0) return; + InsertDuplicateNodes(copyBuffer, pos); + } - //Center paste around first node in list - Vector2 offset = pos - copyBuffer[0].position; + private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) { + if (nodes == null || nodes.Length == 0) return; - UnityEngine.Object[] newNodes = new UnityEngine.Object[copyBuffer.Length]; + // Get top-left node + Vector2 topLeftNode = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y))); + Vector2 offset = topLeft - topLeftNode; + + UnityEngine.Object[] newNodes = new UnityEngine.Object[nodes.Length]; Dictionary substitutes = new Dictionary(); - for (int i = 0; i < copyBuffer.Length; i++) { - XNode.Node srcNode = copyBuffer[i] as XNode.Node; + for (int i = 0; i < nodes.Length; i++) { + XNode.Node srcNode = nodes[i]; if (srcNode == null) continue; XNode.Node newNode = graphEditor.CopyNode(srcNode); substitutes.Add(srcNode, newNode); @@ -457,8 +431,8 @@ namespace XNodeEditor { } // Walk through the selected nodes again, recreate connections, using the new nodes - for (int i = 0; i < copyBuffer.Length; i++) { - XNode.Node srcNode = copyBuffer[i] as XNode.Node; + for (int i = 0; i < nodes.Length; i++) { + XNode.Node srcNode = nodes[i]; if (srcNode == null) continue; foreach (XNode.NodePort port in srcNode.Ports) { for (int c = 0; c < port.ConnectionCount; c++) { @@ -476,6 +450,7 @@ namespace XNodeEditor { } } } + // Select the new nodes Selection.objects = newNodes; } From 56e84b7c92e932a8f3895d6cd8d9ce27e41ad390 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 18 Jun 2019 22:17:56 +0200 Subject: [PATCH 3/3] Changed so CopySelectedNodes uses the same Linq-select as DuplicateSelectedNodes does. --- Scripts/Editor/NodeEditorAction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index ada5992..4ae446e 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -405,7 +405,7 @@ namespace XNodeEditor { } public void CopySelectedNodes() { - copyBuffer = Selection.objects.Where((o) => o != null && o is XNode.Node).Cast().ToArray(); + copyBuffer = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray(); } public void PasteNodes(Vector2 pos) {