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..4ae446e 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;
@@ -386,41 +397,60 @@ namespace XNodeEditor {
/// Duplicate selected nodes and select the duplicates
public void DuplicateSelectedNodes() {
- UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length];
+ // 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() {
+ copyBuffer = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray();
+ }
+
+ public void PasteNodes(Vector2 pos) {
+ InsertDuplicateNodes(copyBuffer, pos);
+ }
+
+ private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) {
+ if (nodes == null || nodes.Length == 0) return;
+
+ // 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 < 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;
- }
+ 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);
+ 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 < 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);
+ 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++) {
+ 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);
+ 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);
}
}
}
+ // Select the new nodes
Selection.objects = newNodes;
}
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);
}