From 598c75fb3aa181a6d4500bb11ef67f3a52bcbc83 Mon Sep 17 00:00:00 2001 From: ReDDarKwh Date: Sat, 15 Jun 2019 13:55:01 -0400 Subject: [PATCH] Added Drop event to Node Graph --- Scripts/Editor/NodeEditorAction.cs | 319 ++++++++++++++++++++--------- Scripts/Editor/NodeGraphEditor.cs | 64 ++++-- Scripts/NodeGraph.cs | 51 +++-- 3 files changed, 308 insertions(+), 126 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 792631a..464ed03 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -4,8 +4,10 @@ using System.Linq; using UnityEditor; using UnityEngine; -namespace XNodeEditor { - public partial class NodeEditorWindow { +namespace XNodeEditor +{ + public partial class NodeEditorWindow + { public enum NodeActivity { Idle, HoldNode, DragNode, HoldGrid, DragGrid } public static NodeActivity currentActivity = NodeActivity.Idle; public static bool isPanning { get; private set; } @@ -28,27 +30,50 @@ namespace XNodeEditor { private Rect selectionBox; private bool isDoubleClick = false; - private struct RerouteReference { + private struct RerouteReference + { public XNode.NodePort port; public int connectionIndex; public int pointIndex; - public RerouteReference(XNode.NodePort port, int connectionIndex, int pointIndex) { + public RerouteReference(XNode.NodePort port, int connectionIndex, int pointIndex) + { this.port = port; this.connectionIndex = connectionIndex; this.pointIndex = pointIndex; } public void InsertPoint(Vector2 pos) { port.GetReroutePoints(connectionIndex).Insert(pointIndex, pos); } - public void SetPoint(Vector2 pos) { port.GetReroutePoints(connectionIndex) [pointIndex] = pos; } + public void SetPoint(Vector2 pos) { port.GetReroutePoints(connectionIndex)[pointIndex] = pos; } public void RemovePoint() { port.GetReroutePoints(connectionIndex).RemoveAt(pointIndex); } - public Vector2 GetPoint() { return port.GetReroutePoints(connectionIndex) [pointIndex]; } + public Vector2 GetPoint() { return port.GetReroutePoints(connectionIndex)[pointIndex]; } } - public void Controls() { + public void Controls() + { wantsMouseMove = true; Event e = Event.current; - switch (e.type) { + switch (e.type) + { + + + case EventType.DragUpdated: + case EventType.DragPerform: + + + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + + if (e.type == EventType.DragPerform) + { + DragAndDrop.AcceptDrag(); + + foreach (object droppedItem in DragAndDrop.objectReferences) + { + graphEditor.DropItem(droppedItem); + } + } + break; + case EventType.MouseMove: break; case EventType.ScrollWheel: @@ -58,52 +83,69 @@ namespace XNodeEditor { if (NodeEditorPreferences.GetSettings().zoomToMouse) panOffset += (1 - oldZoom / zoom) * (WindowToGridPosition(e.mousePosition) + panOffset); break; case EventType.MouseDrag: - if (e.button == 0) { - if (IsDraggingPort) { - if (IsHoveringPort && hoveredPort.IsInput && draggedOutput.CanConnectTo(hoveredPort)) { - if (!draggedOutput.IsConnectedTo(hoveredPort)) { + if (e.button == 0) + { + if (IsDraggingPort) + { + if (IsHoveringPort && hoveredPort.IsInput && draggedOutput.CanConnectTo(hoveredPort)) + { + if (!draggedOutput.IsConnectedTo(hoveredPort)) + { draggedOutputTarget = hoveredPort; } - } else { + } + else + { draggedOutputTarget = null; } Repaint(); - } else if (currentActivity == NodeActivity.HoldNode) { + } + else if (currentActivity == NodeActivity.HoldNode) + { RecalculateDragOffsets(e); currentActivity = NodeActivity.DragNode; Repaint(); } - if (currentActivity == NodeActivity.DragNode) { + if (currentActivity == NodeActivity.DragNode) + { // Holding ctrl inverts grid snap bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap; if (e.control) gridSnap = !gridSnap; Vector2 mousePos = WindowToGridPosition(e.mousePosition); // Move selected nodes with offset - for (int i = 0; i < Selection.objects.Length; i++) { - if (Selection.objects[i] is XNode.Node) { + for (int i = 0; i < Selection.objects.Length; i++) + { + if (Selection.objects[i] is XNode.Node) + { XNode.Node node = Selection.objects[i] as XNode.Node; Vector2 initial = node.position; node.position = mousePos + dragOffset[i]; - if (gridSnap) { + if (gridSnap) + { node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8; node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8; } // Offset portConnectionPoints instantly if a node is dragged so they aren't delayed by a frame. Vector2 offset = node.position - initial; - if (offset.sqrMagnitude > 0) { - foreach (XNode.NodePort output in node.Outputs) { + if (offset.sqrMagnitude > 0) + { + foreach (XNode.NodePort output in node.Outputs) + { Rect rect; - if (portConnectionPoints.TryGetValue(output, out rect)) { + if (portConnectionPoints.TryGetValue(output, out rect)) + { rect.position += offset; portConnectionPoints[output] = rect; } } - foreach (XNode.NodePort input in node.Inputs) { + foreach (XNode.NodePort input in node.Inputs) + { Rect rect; - if (portConnectionPoints.TryGetValue(input, out rect)) { + if (portConnectionPoints.TryGetValue(input, out rect)) + { rect.position += offset; portConnectionPoints[input] = rect; } @@ -112,22 +154,28 @@ namespace XNodeEditor { } } // Move selected reroutes with offset - for (int i = 0; i < selectedReroutes.Count; i++) { + for (int i = 0; i < selectedReroutes.Count; i++) + { Vector2 pos = mousePos + dragOffset[Selection.objects.Length + i]; - if (gridSnap) { + if (gridSnap) + { pos.x = (Mathf.Round(pos.x / 16) * 16); pos.y = (Mathf.Round(pos.y / 16) * 16); } selectedReroutes[i].SetPoint(pos); } Repaint(); - } else if (currentActivity == NodeActivity.HoldGrid) { + } + else if (currentActivity == NodeActivity.HoldGrid) + { currentActivity = NodeActivity.DragGrid; preBoxSelection = Selection.objects; preBoxSelectionReroute = selectedReroutes.ToArray(); dragBoxStart = WindowToGridPosition(e.mousePosition); Repaint(); - } else if (currentActivity == NodeActivity.DragGrid) { + } + else if (currentActivity == NodeActivity.DragGrid) + { Vector2 boxStartPos = GridToWindowPosition(dragBoxStart); Vector2 boxSize = e.mousePosition - boxStartPos; if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); } @@ -135,22 +183,30 @@ namespace XNodeEditor { selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } - } else if (e.button == 1 || e.button == 2) { + } + else if (e.button == 1 || e.button == 2) + { panOffset += e.delta * zoom; isPanning = true; } break; case EventType.MouseDown: Repaint(); - if (e.button == 0) { + if (e.button == 0) + { draggedOutputReroutes.Clear(); - if (IsHoveringPort) { - if (hoveredPort.IsOutput) { + if (IsHoveringPort) + { + if (hoveredPort.IsOutput) + { draggedOutput = hoveredPort; - } else { + } + else + { hoveredPort.VerifyConnections(); - if (hoveredPort.IsConnected) { + if (hoveredPort.IsConnected) + { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); @@ -161,25 +217,33 @@ namespace XNodeEditor { if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node); } } - } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { + } + else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) + { // If mousedown on node header, select or deselect - if (!Selection.Contains(hoveredNode)) { + if (!Selection.Contains(hoveredNode)) + { SelectNode(hoveredNode, e.control || e.shift); if (!e.control && !e.shift) selectedReroutes.Clear(); - } else if (e.control || e.shift) DeselectNode(hoveredNode); + } + else if (e.control || e.shift) DeselectNode(hoveredNode); // Cache double click state, but only act on it in MouseUp - Except ClickCount only works in mouseDown. isDoubleClick = (e.clickCount == 2); e.Use(); currentActivity = NodeActivity.HoldNode; - } else if (IsHoveringReroute) { + } + else if (IsHoveringReroute) + { // If reroute isn't selected - if (!selectedReroutes.Contains(hoveredReroute)) { + if (!selectedReroutes.Contains(hoveredReroute)) + { // Add it if (e.control || e.shift) selectedReroutes.Add(hoveredReroute); // Select it - else { + else + { selectedReroutes = new List() { hoveredReroute }; Selection.activeObject = null; } @@ -191,9 +255,11 @@ namespace XNodeEditor { currentActivity = NodeActivity.HoldNode; } // If mousedown on grid background, deselect all - else if (!IsHoveringNode) { + else if (!IsHoveringNode) + { currentActivity = NodeActivity.HoldGrid; - if (!e.control && !e.shift) { + if (!e.control && !e.shift) + { selectedReroutes.Clear(); Selection.activeObject = null; } @@ -201,17 +267,21 @@ namespace XNodeEditor { } break; case EventType.MouseUp: - if (e.button == 0) { + if (e.button == 0) + { //Port drag release - if (IsDraggingPort) { + if (IsDraggingPort) + { //If connection is valid, save it - if (draggedOutputTarget != null) { + if (draggedOutputTarget != null) + { XNode.Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget); // ConnectionIndex can be -1 if the connection is removed instantly after creation int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget); - if (connectionIndex != -1) { + if (connectionIndex != -1) + { draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes); if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node); EditorUtility.SetDirty(graph); @@ -222,13 +292,18 @@ namespace XNodeEditor { draggedOutputTarget = null; EditorUtility.SetDirty(graph); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); - } else if (currentActivity == NodeActivity.DragNode) { + } + else if (currentActivity == NodeActivity.DragNode) + { IEnumerable nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node); foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); - } else if (!IsHoveringNode) { + } + else if (!IsHoveringNode) + { // If click outside node, release field focus - if (!isPanning) { + if (!isPanning) + { EditorGUI.FocusTextInControl(null); EditorGUIUtility.editingTextField = false; } @@ -236,43 +311,60 @@ namespace XNodeEditor { } // If click node header, select it. - if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) { + if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) + { selectedReroutes.Clear(); SelectNode(hoveredNode, false); // Double click to center node - if (isDoubleClick) { + if (isDoubleClick) + { Vector2 nodeDimension = nodeSizes.ContainsKey(hoveredNode) ? nodeSizes[hoveredNode] / 2 : Vector2.zero; panOffset = -hoveredNode.position - nodeDimension; } } // If click reroute, select it. - if (IsHoveringReroute && !(e.control || e.shift)) { + if (IsHoveringReroute && !(e.control || e.shift)) + { selectedReroutes = new List() { hoveredReroute }; Selection.activeObject = null; } Repaint(); currentActivity = NodeActivity.Idle; - } else if (e.button == 1 || e.button == 2) { - if (!isPanning) { - if (IsDraggingPort) { + } + else if (e.button == 1 || e.button == 2) + { + if (!isPanning) + { + if (IsDraggingPort) + { draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition)); - } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) { + } + else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) + { selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint()); selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port, selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1); - } else if (IsHoveringReroute) { + } + else if (IsHoveringReroute) + { ShowRerouteContextMenu(hoveredReroute); - } else if (IsHoveringPort) { + } + else if (IsHoveringPort) + { ShowPortContextMenu(hoveredPort); - } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { + } + else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) + { if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, false); GenericMenu menu = new GenericMenu(); NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places. - } else if (!IsHoveringNode) { + } + else if (!IsHoveringNode) + { GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); @@ -286,21 +378,29 @@ namespace XNodeEditor { case EventType.KeyDown: if (EditorGUIUtility.editingTextField) break; else if (e.keyCode == KeyCode.F) Home(); - if (IsMac()) { + if (IsMac()) + { if (e.keyCode == KeyCode.Return) RenameSelectedNode(); - } else { + } + else + { if (e.keyCode == KeyCode.F2) RenameSelectedNode(); } break; case EventType.ValidateCommand: case EventType.ExecuteCommand: - if (e.commandName == "SoftDelete") { + if (e.commandName == "SoftDelete") + { if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes(); e.Use(); - } else if (IsMac() && e.commandName == "Delete") { + } + else if (IsMac() && e.commandName == "Delete") + { if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes(); e.Use(); - } else if (e.commandName == "Duplicate") { + } + else if (e.commandName == "Duplicate") + { if (e.type == EventType.ExecuteCommand) DuplicateSelectedNodes(); e.Use(); } @@ -308,7 +408,8 @@ namespace XNodeEditor { break; case EventType.Ignore: // If release mouse outside window - if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { + if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) + { Repaint(); currentActivity = NodeActivity.Idle; } @@ -316,7 +417,8 @@ namespace XNodeEditor { } } - public bool IsMac() { + public bool IsMac() + { #if UNITY_2017_1_OR_NEWER return SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX; #else @@ -324,38 +426,47 @@ namespace XNodeEditor { #endif } - private void RecalculateDragOffsets(Event current) { + private void RecalculateDragOffsets(Event current) + { dragOffset = new Vector2[Selection.objects.Length + selectedReroutes.Count]; // Selected nodes - for (int i = 0; i < Selection.objects.Length; i++) { - if (Selection.objects[i] is XNode.Node) { + for (int i = 0; i < Selection.objects.Length; i++) + { + if (Selection.objects[i] is XNode.Node) + { XNode.Node node = Selection.objects[i] as XNode.Node; dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition); } } // Selected reroutes - for (int i = 0; i < selectedReroutes.Count; i++) { + for (int i = 0; i < selectedReroutes.Count; i++) + { dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition); } } /// Puts all nodes in focus. If no nodes are present, resets view to - public void Home() { + public void Home() + { zoom = 2; panOffset = Vector2.zero; } /// Remove nodes in the graph in Selection.objects - public void RemoveSelectedNodes() { + public void RemoveSelectedNodes() + { // We need to delete reroutes starting at the highest point index to avoid shifting indices selectedReroutes = selectedReroutes.OrderByDescending(x => x.pointIndex).ToList(); - for (int i = 0; i < selectedReroutes.Count; i++) { + for (int i = 0; i < selectedReroutes.Count; i++) + { selectedReroutes[i].RemovePoint(); } selectedReroutes.Clear(); - foreach (UnityEngine.Object item in Selection.objects) { - if (item is XNode.Node) { + foreach (UnityEngine.Object item in Selection.objects) + { + if (item is XNode.Node) + { XNode.Node node = item as XNode.Node; graphEditor.RemoveNode(node); } @@ -363,33 +474,43 @@ namespace XNodeEditor { } /// Initiate a rename on the currently selected node - public void RenameSelectedNode() { - if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { + public void RenameSelectedNode() + { + if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) + { XNode.Node node = Selection.activeObject as XNode.Node; Vector2 size; - if (nodeSizes.TryGetValue(node, out size)) { + if (nodeSizes.TryGetValue(node, out size)) + { RenamePopup.Show(Selection.activeObject, size.x); - } else { + } + else + { RenamePopup.Show(Selection.activeObject); } } } /// Draw this node on top of other nodes by placing it last in the graph.nodes list - public void MoveNodeToTop(XNode.Node node) { + public void MoveNodeToTop(XNode.Node node) + { int index; - while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) { + while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) + { graph.nodes[index] = graph.nodes[index + 1]; graph.nodes[index + 1] = node; } } /// Duplicate selected nodes and select the duplicates - public void DuplicateSelectedNodes() { + 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) { + 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); @@ -400,17 +521,22 @@ namespace XNodeEditor { } // 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) { + 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++) { + 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)) { + if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) + { newNodeIn.UpdateStaticPorts(); newNodeOut.UpdateStaticPorts(); inputPort = newNodeIn.GetInputPort(inputPort.fieldName); @@ -425,8 +551,10 @@ namespace XNodeEditor { } /// Draw a connection as we are dragging it - public void DrawDraggedConnection() { - if (IsDraggingPort) { + public void DrawDraggedConnection() + { + if (IsDraggingPort) + { Color col = NodeEditorPreferences.GetTypeColor(draggedOutput.ValueType); col.a = draggedOutputTarget != null ? 1.0f : 0.6f; @@ -434,7 +562,8 @@ namespace XNodeEditor { if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect)) return; List gridPoints = new List(); gridPoints.Add(fromRect.center); - for (int i = 0; i < draggedOutputReroutes.Count; i++) { + for (int i = 0; i < draggedOutputReroutes.Count; i++) + { gridPoints.Add(draggedOutputReroutes[i]); } if (draggedOutputTarget != null) gridPoints.Add(portConnectionPoints[draggedOutputTarget].center); @@ -448,7 +577,8 @@ namespace XNodeEditor { frcol.a = 0.6f; // Loop through reroute points again and draw the points - for (int i = 0; i < draggedOutputReroutes.Count; i++) { + for (int i = 0; i < draggedOutputReroutes.Count; i++) + { // Draw reroute point at position Rect rect = new Rect(draggedOutputReroutes[i], new Vector2(16, 16)); rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8); @@ -459,7 +589,8 @@ namespace XNodeEditor { } } - bool IsHoveringTitle(XNode.Node node) { + bool IsHoveringTitle(XNode.Node node) + { Vector2 mousePos = Event.current.mousePosition; //Get node position Vector2 nodePos = GridToWindowPosition(node.position); diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index c509d28..58abf01 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -4,10 +4,12 @@ using System.Linq; using UnityEditor; using UnityEngine; -namespace XNodeEditor { +namespace XNodeEditor +{ /// Base class to derive custom Node Graph editors from. Use this to override how graphs are drawn in the editor. [CustomNodeGraphEditor(typeof(XNode.NodeGraph))] - public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase { + public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase + { [Obsolete("Use window.position instead")] public Rect position { get { return window.position; } set { window.position = value; } } /// Are we currently renaming a node? @@ -18,21 +20,25 @@ namespace XNodeEditor { /// Called when opened by NodeEditorWindow public virtual void OnOpen() { } - public virtual Texture2D GetGridTexture() { + public virtual Texture2D GetGridTexture() + { return NodeEditorPreferences.GetSettings().gridTexture; } - public virtual Texture2D GetSecondaryGridTexture() { + public virtual Texture2D GetSecondaryGridTexture() + { return NodeEditorPreferences.GetSettings().crossTexture; } /// Return default settings for this graph type. This is the settings the user will load if no previous settings have been saved. - public virtual NodeEditorPreferences.Settings GetDefaultPreferences() { + public virtual NodeEditorPreferences.Settings GetDefaultPreferences() + { return new NodeEditorPreferences.Settings(); } /// Returns context node menu path. Null or empty strings for hidden nodes. - public virtual string GetNodeMenuName(Type type) { + public virtual string GetNodeMenuName(Type type) + { //Check if type has the CreateNodeMenuAttribute XNode.Node.CreateNodeMenuAttribute attrib; if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path @@ -42,16 +48,19 @@ namespace XNodeEditor { } /// Add items for the context menu when right-clicking this node. Override to add custom menu items. - public virtual void AddContextMenuItems(GenericMenu menu) { + public virtual void AddContextMenuItems(GenericMenu menu) + { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); - for (int i = 0; i < NodeEditorWindow.nodeTypes.Length; i++) { + for (int i = 0; i < NodeEditorWindow.nodeTypes.Length; i++) + { Type type = NodeEditorWindow.nodeTypes[i]; //Get node context menu path string path = GetNodeMenuName(type); if (string.IsNullOrEmpty(path)) continue; - menu.AddItem(new GUIContent(path), false, () => { + menu.AddItem(new GUIContent(path), false, () => + { CreateNode(type, pos); }); } @@ -60,19 +69,31 @@ namespace XNodeEditor { NodeEditorWindow.AddCustomContextMenuItems(menu, target); } - public virtual Color GetPortColor(XNode.NodePort port) { + public virtual Color GetPortColor(XNode.NodePort port) + { return GetTypeColor(port.ValueType); } - public virtual Color GetTypeColor(Type type) { + public virtual Color GetTypeColor(Type type) + { return NodeEditorPreferences.GetTypeColor(type); } + + + public virtual void DropItem(object droppedItem) + { + target.OnDrop(droppedItem, NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition)); + } + + /// Create a node and save it in the graph asset - public virtual void CreateNode(Type type, Vector2 position) { + public virtual void CreateNode(Type type, Vector2 position) + { XNode.Node node = target.AddNode(type); node.position = position; - if (string.IsNullOrEmpty(node.name)) { + if (string.IsNullOrEmpty(node.name)) + { // Automatically remove redundant 'Node' postfix string typeName = type.Name; if (typeName.EndsWith("Node")) typeName = typeName.Substring(0, typeName.LastIndexOf("Node")); @@ -84,7 +105,8 @@ namespace XNodeEditor { } /// Creates a copy of the original node in the graph - public XNode.Node CopyNode(XNode.Node original) { + public XNode.Node CopyNode(XNode.Node original) + { XNode.Node node = target.CopyNode(original); node.name = original.name; AssetDatabase.AddObjectToAsset(node, target); @@ -93,26 +115,32 @@ namespace XNodeEditor { } /// Safely remove a node and all its connections. - public virtual void RemoveNode(XNode.Node node) { + public virtual void RemoveNode(XNode.Node node) + { target.RemoveNode(node); UnityEngine.Object.DestroyImmediate(node, true); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } + + [AttributeUsage(AttributeTargets.Class)] public class CustomNodeGraphEditorAttribute : Attribute, - XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib { + XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib + { private Type inspectedType; public string editorPrefsKey; /// Tells a NodeGraphEditor which Graph type it is an editor for /// Type that this editor can edit /// Define unique key for unique layout settings instance - public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") { + public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") + { this.inspectedType = inspectedType; this.editorPrefsKey = editorPrefsKey; } - public Type GetInspectedType() { + public Type GetInspectedType() + { return inspectedType; } } diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs index 6a0cead..429cb26 100644 --- a/Scripts/NodeGraph.cs +++ b/Scripts/NodeGraph.cs @@ -2,22 +2,26 @@ using System.Collections.Generic; using UnityEngine; -namespace XNode { +namespace XNode +{ /// Base class for all node graphs [Serializable] - public abstract class NodeGraph : ScriptableObject { + public abstract class NodeGraph : ScriptableObject + { /// All nodes in the graph. /// See: [SerializeField] public List nodes = new List(); /// Add a node to the graph by type (convenience method - will call the System.Type version) - public T AddNode() where T : Node { + public T AddNode() where T : Node + { return AddNode(typeof(T)) as T; } /// Add a node to the graph by type - public virtual Node AddNode(Type type) { + public virtual Node AddNode(Type type) + { Node.graphHotfix = this; Node node = ScriptableObject.CreateInstance(type) as Node; node.graph = this; @@ -26,7 +30,8 @@ namespace XNode { } /// Creates a copy of the original node in the graph - public virtual Node CopyNode(Node original) { + public virtual Node CopyNode(Node original) + { Node.graphHotfix = this; Node node = ScriptableObject.Instantiate(original); node.graph = this; @@ -37,16 +42,20 @@ namespace XNode { /// Safely remove a node and all its connections /// The node to remove - public virtual void RemoveNode(Node node) { + public virtual void RemoveNode(Node node) + { node.ClearConnections(); nodes.Remove(node); if (Application.isPlaying) Destroy(node); } /// Remove all nodes and connections from the graph - public virtual void Clear() { - if (Application.isPlaying) { - for (int i = 0; i < nodes.Count; i++) { + public virtual void Clear() + { + if (Application.isPlaying) + { + for (int i = 0; i < nodes.Count; i++) + { Destroy(nodes[i]); } } @@ -54,11 +63,13 @@ namespace XNode { } /// Create a new deep copy of this graph - public virtual XNode.NodeGraph Copy() { + public virtual XNode.NodeGraph Copy() + { // Instantiate a new nodegraph instance NodeGraph graph = Instantiate(this); // Instantiate all nodes inside the graph - for (int i = 0; i < nodes.Count; i++) { + for (int i = 0; i < nodes.Count; i++) + { if (nodes[i] == null) continue; Node.graphHotfix = graph; Node node = Instantiate(nodes[i]) as Node; @@ -67,9 +78,11 @@ namespace XNode { } // Redirect all connections - for (int i = 0; i < graph.nodes.Count; i++) { + for (int i = 0; i < graph.nodes.Count; i++) + { if (graph.nodes[i] == null) continue; - foreach (NodePort port in graph.nodes[i].Ports) { + foreach (NodePort port in graph.nodes[i].Ports) + { port.Redirect(nodes, graph.nodes); } } @@ -77,9 +90,19 @@ namespace XNode { return graph; } - protected virtual void OnDestroy() { + protected virtual void OnDestroy() + { // Remove all nodes prior to graph destruction Clear(); } + + + /// Is called when something is dragged in the editor + /// The dropped object + public virtual void OnDrop(object droppedObject, Vector2 dropPosition) + { + Debug.LogWarning("No OnDrop(NodePort port) override defined for " + GetType()); + } + } } \ No newline at end of file