From 629474d0424ca9b0aa5abeea6b13938ed0782285 Mon Sep 17 00:00:00 2001 From: juliocp Date: Mon, 5 Oct 2020 16:25:08 -0300 Subject: [PATCH 1/7] Added Noodle Thickness at preferences The default value for the thickness of the noodle can be modified from xNode preferences. This value is only effective if the user does not make an override from the EditorGraph at GetNoodleThickness(...). Many users do not like to edit from the script, this will help in the creation of my new layout template, similar to the new graphView from unity --- Scripts/Editor/NodeEditorPreferences.cs | 3 +++ Scripts/Editor/NodeGraphEditor.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index e5fa238..bffec1c 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -42,6 +42,8 @@ namespace XNodeEditor { [SerializeField] private string typeColorsData = ""; [NonSerialized] public Dictionary typeColors = new Dictionary(); [FormerlySerializedAs("noodleType")] public NoodlePath noodlePath = NoodlePath.Curvy; + public float noodleThickness = 2f; + public NoodleStroke noodleStroke = NoodleStroke.Full; private Texture2D _gridTexture; @@ -160,6 +162,7 @@ namespace XNodeEditor { EditorGUILayout.LabelField("Node", EditorStyles.boldLabel); settings.highlightColor = EditorGUILayout.ColorField("Selection", settings.highlightColor); settings.noodlePath = (NoodlePath) EditorGUILayout.EnumPopup("Noodle path", (Enum) settings.noodlePath); + settings.noodleThickness = EditorGUILayout.FloatField(new GUIContent("Noodle thickness", "Noodle Thickness of the node connections"), settings.noodleThickness); settings.noodleStroke = (NoodleStroke) EditorGUILayout.EnumPopup("Noodle stroke", (Enum) settings.noodleStroke); settings.portTooltips = EditorGUILayout.Toggle("Port Tooltips", settings.portTooltips); settings.dragToCreate = EditorGUILayout.Toggle(new GUIContent("Drag to Create", "Drag a port connection anywhere on the grid to create and connect a node"), settings.dragToCreate); diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 01de70e..a8bde28 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -125,7 +125,7 @@ namespace XNodeEditor { /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) { - return 5f; + return NodeEditorPreferences.GetSettings().noodleThickness; } public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) { From 7aefddfaaeb9774c3509f441d2d2f2bcb7498983 Mon Sep 17 00:00:00 2001 From: juliocp Date: Mon, 5 Oct 2020 16:52:28 -0300 Subject: [PATCH 2/7] Default background and grid Color Changed the default background and grid colors of the graph in the preferences. This will maintain the new layout of nodes and ports uniform with the background. (More Clean) --- Scripts/Editor/NodeEditorPreferences.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index bffec1c..d16dfd0 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -20,10 +20,10 @@ namespace XNodeEditor { [System.Serializable] public class Settings : ISerializationCallbackReceiver { - [SerializeField] private Color32 _gridLineColor = new Color(0.45f, 0.45f, 0.45f); + [SerializeField] private Color32 _gridLineColor = new Color(.23f, .23f, .23f); public Color32 gridLineColor { get { return _gridLineColor; } set { _gridLineColor = value; _gridTexture = null; _crossTexture = null; } } - [SerializeField] private Color32 _gridBgColor = new Color(0.18f, 0.18f, 0.18f); + [SerializeField] private Color32 _gridBgColor = new Color(.19f, .19f, .19f); public Color32 gridBgColor { get { return _gridBgColor; } set { _gridBgColor = value; _gridTexture = null; } } [Obsolete("Use maxZoom instead")] From 7c301c88a77fe8b54cc8a4b6184746d572ccd064 Mon Sep 17 00:00:00 2001 From: juliocp Date: Mon, 5 Oct 2020 21:44:46 -0300 Subject: [PATCH 3/7] Virtual GetPortBackgroundColor at NodeGraph Exposed virtual method to control the color of the background Port. Now it is possible to override in the NodeGraph. Will be used to spotlight compatible ports! --- Scripts/Editor/NodeEditorGUILayout.cs | 9 +-- Scripts/Editor/NodeGraphEditor.cs | 105 ++++++++++++++++++-------- 2 files changed, 75 insertions(+), 39 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 19e9ca5..1e17f4a 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -165,8 +165,7 @@ namespace XNodeEditor { rect.size = new Vector2(16, 16); - NodeEditor editor = NodeEditor.GetEditor(port.node, NodeEditorWindow.current); - Color backgroundColor = editor.GetTint(); + Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); @@ -219,8 +218,7 @@ namespace XNodeEditor { Rect rect = new Rect(position, new Vector2(16, 16)); - NodeEditor editor = NodeEditor.GetEditor(port.node, NodeEditorWindow.current); - Color backgroundColor = editor.GetTint(); + Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); @@ -246,8 +244,7 @@ namespace XNodeEditor { rect.size = new Vector2(16, 16); - NodeEditor editor = NodeEditor.GetEditor(port.node, NodeEditorWindow.current); - Color backgroundColor = editor.GetTint(); + Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index a8bde28..8b671e2 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? @@ -17,28 +19,32 @@ namespace XNodeEditor { /// Called when opened by NodeEditorWindow public virtual void OnOpen() { } - + /// Called when NodeEditorWindow gains focus public virtual void OnWindowFocus() { } /// Called when NodeEditorWindow loses focus public virtual void OnWindowFocusLost() { } - 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 @@ -48,7 +54,8 @@ namespace XNodeEditor { } /// The order by which the menu items are displayed. - public virtual int GetNodeMenuOrder(Type type) { + public virtual int GetNodeMenuOrder(Type type) + { //Check if type has the CreateNodeMenuAttribute XNode.Node.CreateNodeMenuAttribute attrib; if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path @@ -58,10 +65,12 @@ 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); var nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); - for (int i = 0; i < nodeTypes.Length; i++) { + for (int i = 0; i < nodeTypes.Length; i++) + { Type type = nodeTypes[i]; //Get node context menu path @@ -71,14 +80,16 @@ namespace XNodeEditor { // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; bool disallowed = false; - if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) { + if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) + { int typeCount = target.nodes.Count(x => x.GetType() == type); if (typeCount >= disallowAttrib.max) disallowed = true; } // Add node entry to context menu if (disallowed) menu.AddItem(new GUIContent(path), false, null); - else menu.AddItem(new GUIContent(path), false, () => { + else menu.AddItem(new GUIContent(path), false, () => + { XNode.Node node = CreateNode(type, pos); NodeEditorWindow.current.AutoConnect(node); }); @@ -93,11 +104,13 @@ namespace XNodeEditor { /// Returned gradient is used to color noodles /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. - public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) { + public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) + { Gradient grad = new Gradient(); // If dragging the noodle, draw solid, slightly transparent - if (input == null) { + if (input == null) + { Color a = GetTypeColor(output.ValueType); grad.SetKeys( new GradientColorKey[] { new GradientColorKey(a, 0f) }, @@ -105,11 +118,13 @@ namespace XNodeEditor { ); } // If normal, draw gradient fading from one input color to the other - else { + else + { Color a = GetTypeColor(output.ValueType); Color b = GetTypeColor(input.ValueType); // If any port is hovered, tint white - if (window.hoveredPort == output || window.hoveredPort == input) { + if (window.hoveredPort == output || window.hoveredPort == input) + { a = Color.Lerp(a, Color.white, 0.8f); b = Color.Lerp(b, Color.white, 0.8f); } @@ -124,34 +139,48 @@ namespace XNodeEditor { /// Returned float is used for noodle thickness /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. - public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) { + public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) + { return NodeEditorPreferences.GetSettings().noodleThickness; } - public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) { + public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) + { return NodeEditorPreferences.GetSettings().noodlePath; } - public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) { + public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) + { return NodeEditorPreferences.GetSettings().noodleStroke; } /// Returned color is used to color ports - public virtual Color GetPortColor(XNode.NodePort port) { + public virtual Color GetPortColor(XNode.NodePort port) + { return GetTypeColor(port.ValueType); } + /// The returned color is used to color the background of the door. + /// Usually used for outer edge effect + public virtual Color GetPortBackgroundColor(XNode.NodePort port) + { + return Color.gray; + } + /// Returns generated color for a type. This color is editable in preferences - public virtual Color GetTypeColor(Type type) { + public virtual Color GetTypeColor(Type type) + { return NodeEditorPreferences.GetTypeColor(type); } /// Override to display custom tooltips - public virtual string GetPortTooltip(XNode.NodePort port) { + public virtual string GetPortTooltip(XNode.NodePort port) + { Type portType = port.ValueType; string tooltip = ""; tooltip = portType.PrettyName(); - if (port.IsOutput) { + if (port.IsOutput) + { object obj = port.node.GetValue(port); tooltip += " = " + (obj != null ? obj.ToString() : "null"); } @@ -159,12 +188,14 @@ namespace XNodeEditor { } /// Deal with objects dropped into the graph through DragAndDrop - public virtual void OnDropObjects(UnityEngine.Object[] objects) { + public virtual void OnDropObjects(UnityEngine.Object[] objects) + { if (GetType() != typeof(NodeGraphEditor)) Debug.Log("No OnDropObjects override defined for " + GetType()); } /// Create a node and save it in the graph asset - public virtual XNode.Node CreateNode(Type type, Vector2 position) { + public virtual XNode.Node CreateNode(Type type, Vector2 position) + { Undo.RecordObject(target, "Create Node"); XNode.Node node = target.AddNode(type); Undo.RegisterCreatedObjectUndo(node, "Create Node"); @@ -177,7 +208,8 @@ namespace XNodeEditor { } /// Creates a copy of the original node in the graph - public virtual XNode.Node CopyNode(XNode.Node original) { + public virtual XNode.Node CopyNode(XNode.Node original) + { Undo.RecordObject(target, "Duplicate Node"); XNode.Node node = target.CopyNode(original); Undo.RegisterCreatedObjectUndo(node, "Duplicate Node"); @@ -188,13 +220,16 @@ namespace XNodeEditor { } /// Return false for nodes that can't be removed - public virtual bool CanRemove(XNode.Node node) { + public virtual bool CanRemove(XNode.Node node) + { // Check graph attributes to see if this node is required Type graphType = target.GetType(); XNode.NodeGraph.RequireNodeAttribute[] attribs = Array.ConvertAll( graphType.GetCustomAttributes(typeof(XNode.NodeGraph.RequireNodeAttribute), true), x => x as XNode.NodeGraph.RequireNodeAttribute); - if (attribs.Any(x => x.Requires(node.GetType()))) { - if (target.nodes.Count(x => x.GetType() == node.GetType()) <= 1) { + if (attribs.Any(x => x.Requires(node.GetType()))) + { + if (target.nodes.Count(x => x.GetType() == node.GetType()) <= 1) + { return false; } } @@ -202,7 +237,8 @@ namespace XNodeEditor { } /// Safely remove a node and all its connections. - public virtual void RemoveNode(XNode.Node node) { + public virtual void RemoveNode(XNode.Node node) + { if (!CanRemove(node)) return; // Remove the node @@ -218,18 +254,21 @@ namespace XNodeEditor { [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; } } From cce4b440910149a81f2c095527ead637f62adebc Mon Sep 17 00:00:00 2001 From: juliocp Date: Tue, 6 Oct 2020 11:11:29 -0300 Subject: [PATCH 4/7] Update NodeEditorAction.cs I made these variables public to be able to work with more information from GraphEditor. They are only Boolean and do not pose any danger. - IsDraggingPort: - IsHoveringPort - IsHoveringNode - IsHoveringReroute --- Scripts/Editor/NodeEditorAction.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 9bcbdc3..cade4e0 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -14,10 +14,11 @@ namespace XNodeEditor { 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; } } - private bool IsHoveringReroute { get { return hoveredReroute.port != null; } } + public bool IsDraggingPort { get { return draggedOutput != null; } } + public bool IsHoveringPort { get { return hoveredPort != null; } } + public bool IsHoveringNode { get { return hoveredNode != null; } } + public bool IsHoveringReroute { get { return hoveredReroute.port != null; } } + private XNode.Node hoveredNode = null; [NonSerialized] public XNode.NodePort hoveredPort = null; [NonSerialized] private XNode.NodePort draggedOutput = null; From e820157fad3e0dbd107ecc6e6dca97a6f43ba2ad Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 11:16:25 -0300 Subject: [PATCH 5/7] Some useful methods for research at NodeEditorAction Some useful methods for research. Now with these methods and the previous commit it is now possible to play not only with the theme of xNode but also with the functionality of ports, nodes and noodles. --- Scripts/Editor/NodeEditorAction.cs | 351 ++++++++++++++++++++--------- 1 file changed, 246 insertions(+), 105 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index cade4e0..738a602 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -5,8 +5,10 @@ using UnityEditor; using UnityEngine; using XNodeEditor.Internal; -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; } @@ -19,12 +21,26 @@ namespace XNodeEditor { public bool IsHoveringNode { get { return hoveredNode != null; } } public bool IsHoveringReroute { get { return hoveredReroute.port != null; } } + /// + /// Return the dragged port or null if not exist + /// + public XNode.NodePort DraggedOutputPort { get { XNode.NodePort result = draggedOutput; return result; } } + /// + /// Return the Hovered port or null if not exist + /// + public XNode.NodePort HoveredPort { get { XNode.NodePort result = hoveredPort; return result; } } + /// + /// Return the Hovered node or null if not exist + /// + public XNode.Node HoveredNode { get { XNode.Node result = hoveredNode; return result; } } + private XNode.Node hoveredNode = null; [NonSerialized] public XNode.NodePort hoveredPort = null; [NonSerialized] private XNode.NodePort draggedOutput = null; [NonSerialized] private XNode.NodePort draggedOutputTarget = null; [NonSerialized] private XNode.NodePort autoConnectOutput = null; - [NonSerialized] private List draggedOutputReroutes = new List(); + [NonSerialized] private List draggedOutputReroutes = new List(); + private RerouteReference hoveredReroute = new RerouteReference(); public List selectedReroutes = new List(); private Vector2 dragBoxStart; @@ -35,14 +51,17 @@ namespace XNodeEditor { private Vector2 lastMousePosition; private float dragThreshold = 1f; - 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.Generic; - if (e.type == EventType.DragPerform) { + if (e.type == EventType.DragPerform) + { DragAndDrop.AcceptDrag(); graphEditor.OnDropObjects(DragAndDrop.objectReferences); } @@ -58,52 +77,68 @@ 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 (e.button == 0) + { + if (IsDraggingPort) + { // Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously - if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) { + if (IsHoveringPort && hoveredPort.IsInput && !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; Undo.RecordObject(node, "Moved 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 +147,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,9 +176,12 @@ namespace XNodeEditor { selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } - } else if (e.button == 1 || e.button == 2) { + } + else if (e.button == 1 || e.button == 2) + { //check drag threshold for larger screens - if (e.delta.magnitude > dragThreshold) { + if (e.delta.magnitude > dragThreshold) + { panOffset += e.delta * zoom; isPanning = true; } @@ -145,17 +189,23 @@ namespace XNodeEditor { 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; autoConnectOutput = hoveredPort; - } else { + } + else + { hoveredPort.VerifyConnections(); autoConnectOutput = null; - if (hoveredPort.IsConnected) { + if (hoveredPort.IsConnected) + { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); @@ -166,25 +216,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; } @@ -196,9 +254,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; } @@ -206,24 +266,29 @@ 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 && draggedOutput.CanConnectTo(draggedOutputTarget)) { + if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) + { 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); } } // Open context menu for auto-connection if there is no target node - else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) { + else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) + { GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); @@ -233,13 +298,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; } @@ -247,44 +317,61 @@ 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); autoConnectOutput = null; 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) + { autoConnectOutput = null; GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); @@ -299,18 +386,27 @@ namespace XNodeEditor { case EventType.KeyDown: if (EditorGUIUtility.editingTextField) break; else if (e.keyCode == KeyCode.F) Home(); - if (NodeEditorUtilities.IsMac()) { + if (NodeEditorUtilities.IsMac()) + { if (e.keyCode == KeyCode.Return) RenameSelectedNode(); - } else { + } + else + { if (e.keyCode == KeyCode.F2) RenameSelectedNode(); } - if (e.keyCode == KeyCode.A) { - if (Selection.objects.Any(x => graph.nodes.Contains(x as XNode.Node))) { - foreach (XNode.Node node in graph.nodes) { + if (e.keyCode == KeyCode.A) + { + if (Selection.objects.Any(x => graph.nodes.Contains(x as XNode.Node))) + { + foreach (XNode.Node node in graph.nodes) + { DeselectNode(node); } - } else { - foreach (XNode.Node node in graph.nodes) { + } + else + { + foreach (XNode.Node node in graph.nodes) + { SelectNode(node, true); } } @@ -319,19 +415,28 @@ namespace XNodeEditor { 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 (NodeEditorUtilities.IsMac() && e.commandName == "Delete") { + } + else if (NodeEditorUtilities.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(); - } else if (e.commandName == "Copy") { + } + else if (e.commandName == "Copy") + { if (e.type == EventType.ExecuteCommand) CopySelectedNodes(); e.Use(); - } else if (e.commandName == "Paste") { + } + else if (e.commandName == "Paste") + { if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition)); e.Use(); } @@ -339,7 +444,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; } @@ -347,45 +453,57 @@ namespace XNodeEditor { } } - 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 selected nodes in focus. If no nodes are present, resets view and zoom to to origin - public void Home() { + public void Home() + { var nodes = Selection.objects.Where(o => o is XNode.Node).Cast().ToList(); - if (nodes.Count > 0) { + if (nodes.Count > 0) + { Vector2 minPos = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y))); Vector2 maxPos = nodes.Select(x => x.position + (nodeSizes.ContainsKey(x) ? nodeSizes[x] : Vector2.zero)).Aggregate((x, y) => new Vector2(Mathf.Max(x.x, y.x), Mathf.Max(x.y, y.y))); panOffset = -(minPos + (maxPos - minPos) / 2f); - } else { + } + else + { 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); } @@ -393,29 +511,37 @@ 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() + { // 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(); if (selectedNodes == null || selectedNodes.Length == 0) return; @@ -424,15 +550,18 @@ namespace XNodeEditor { InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30)); } - public void CopySelectedNodes() { + 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) { + public void PasteNodes(Vector2 pos) + { InsertDuplicateNodes(copyBuffer, pos); } - private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) { + private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) + { if (nodes == null || nodes.Length == 0) return; // Get top-left node @@ -441,14 +570,16 @@ namespace XNodeEditor { UnityEngine.Object[] newNodes = new UnityEngine.Object[nodes.Length]; Dictionary substitutes = new Dictionary(); - for (int i = 0; i < nodes.Length; i++) { + for (int i = 0; i < nodes.Length; i++) + { XNode.Node srcNode = nodes[i]; if (srcNode == null) continue; // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; Type nodeType = srcNode.GetType(); - if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) { + if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) + { int typeCount = graph.nodes.Count(x => x.GetType() == nodeType); if (typeCount >= disallowAttrib.max) continue; } @@ -460,16 +591,20 @@ namespace XNodeEditor { } // Walk through the selected nodes again, recreate connections, using the new nodes - for (int i = 0; i < nodes.Length; i++) { + 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++) { + 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.UpdatePorts(); newNodeOut.UpdatePorts(); inputPort = newNodeIn.GetInputPort(inputPort.fieldName); @@ -479,14 +614,16 @@ namespace XNodeEditor { } } } - EditorUtility.SetDirty(graph); + EditorUtility.SetDirty(graph); // Select the new nodes Selection.objects = newNodes; } /// Draw a connection as we are dragging it - public void DrawDraggedConnection() { - if (IsDraggingPort) { + public void DrawDraggedConnection() + { + if (IsDraggingPort) + { Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null); float thickness = graphEditor.GetNoodleThickness(draggedOutput, null); NoodlePath path = graphEditor.GetNoodlePath(draggedOutput, null); @@ -496,7 +633,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); @@ -510,7 +648,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); @@ -521,7 +660,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); @@ -534,7 +674,8 @@ namespace XNodeEditor { } /// Attempt to connect dragged output to target node - public void AutoConnect(XNode.Node node) { + public void AutoConnect(XNode.Node node) + { if (autoConnectOutput == null) return; // Find input port of same type From 72c7ca9d0495fb31e5ab11dfdb1f377e67c3acff Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 16:33:10 -0300 Subject: [PATCH 6/7] Port Padding BugFIX Added the separate port padding to control the port's offset position in relation to the node content padding Currently, if I try to create a custom theme for node, i will be stuck with the port padding, because currently there are not separate padding from the node, --- Scripts/Editor/NodeEditorGUILayout.cs | 297 +++++++++++++++++--------- Scripts/Editor/NodeEditorResources.cs | 8 +- 2 files changed, 203 insertions(+), 102 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 1e17f4a..077de1d 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -7,20 +7,24 @@ using UnityEditor; using UnityEditorInternal; using UnityEngine; -namespace XNodeEditor { +namespace XNodeEditor +{ /// xNode-specific version of - public static class NodeEditorGUILayout { + public static class NodeEditorGUILayout + { private static readonly Dictionary> reorderableListCache = new Dictionary>(); private static int reorderableListIndex = -1; /// Make a field for a serialized property. Automatically displays relevant node port. - public static void PropertyField(SerializedProperty property, bool includeChildren = true, params GUILayoutOption[] options) { - PropertyField(property, (GUIContent) null, includeChildren, options); + public static void PropertyField(SerializedProperty property, bool includeChildren = true, params GUILayoutOption[] options) + { + PropertyField(property, (GUIContent)null, includeChildren, options); } /// Make a field for a serialized property. Automatically displays relevant node port. - public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) { + public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) + { if (property == null) throw new NullReferenceException(); XNode.Node node = property.serializedObject.targetObject as XNode.Node; XNode.NodePort port = node.GetPort(property.name); @@ -28,28 +32,33 @@ namespace XNodeEditor { } /// Make a field for a serialized property. Manual node port override. - public static void PropertyField(SerializedProperty property, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { + public static void PropertyField(SerializedProperty property, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) + { PropertyField(property, null, port, includeChildren, options); } /// Make a field for a serialized property. Manual node port override. - public static void PropertyField(SerializedProperty property, GUIContent label, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { + public static void PropertyField(SerializedProperty property, GUIContent label, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) + { if (property == null) throw new NullReferenceException(); // If property is not a port, display a regular property field if (port == null) EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); - else { + else + { Rect rect = new Rect(); List propertyAttributes = NodeEditorUtilities.GetCachedPropertyAttribs(port.node.GetType(), property.name); // If property is an input, display a regular property field and put a port handle on the left side - if (port.direction == XNode.NodePort.IO.Input) { + if (port.direction == XNode.NodePort.IO.Input) + { // Get data from [Input] attribute XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected; XNode.Node.InputAttribute inputAttribute; bool dynamicPortList = false; - if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) { + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) + { dynamicPortList = inputAttribute.dynamicPortList; showBacking = inputAttribute.backingValue; } @@ -60,30 +69,40 @@ namespace XNodeEditor { float spacePadding = 0; string tooltip = null; - foreach (var attr in propertyAttributes) { - if (attr is SpaceAttribute) { + foreach (var attr in propertyAttributes) + { + if (attr is SpaceAttribute) + { if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); else spacePadding += (attr as SpaceAttribute).height; - } else if (attr is HeaderAttribute) { - if (usePropertyAttributes) { + } + else if (attr is HeaderAttribute) + { + if (usePropertyAttributes) + { //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; position = EditorGUI.IndentedRect(position); GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); - } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; - } else if (attr is TooltipAttribute) { - tooltip = (attr as TooltipAttribute).tooltip; + } + else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } + else if (attr is TooltipAttribute) + { + tooltip = (attr as TooltipAttribute).tooltip; } } - if (dynamicPortList) { + if (dynamicPortList) + { Type type = GetType(property); XNode.Node.ConnectionType connectionType = inputAttribute != null ? inputAttribute.connectionType : XNode.Node.ConnectionType.Multiple; DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType); return; } - switch (showBacking) { + switch (showBacking) + { case XNode.Node.ShowBackingValue.Unconnected: // Display a label if port is connected if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName, tooltip)); @@ -101,14 +120,18 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position - new Vector2(16, -spacePadding); + float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + rect.position = rect.position - new Vector2(16 + paddingLeft, -spacePadding); // If property is an output, display a text label and put a port handle on the right side - } else if (port.direction == XNode.NodePort.IO.Output) { + } + else if (port.direction == XNode.NodePort.IO.Output) + { // Get data from [Output] attribute XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected; XNode.Node.OutputAttribute outputAttribute; bool dynamicPortList = false; - if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) { + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) + { dynamicPortList = outputAttribute.dynamicPortList; showBacking = outputAttribute.backingValue; } @@ -119,30 +142,40 @@ namespace XNodeEditor { float spacePadding = 0; string tooltip = null; - foreach (var attr in propertyAttributes) { - if (attr is SpaceAttribute) { + foreach (var attr in propertyAttributes) + { + if (attr is SpaceAttribute) + { if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); else spacePadding += (attr as SpaceAttribute).height; - } else if (attr is HeaderAttribute) { - if (usePropertyAttributes) { + } + else if (attr is HeaderAttribute) + { + if (usePropertyAttributes) + { //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; position = EditorGUI.IndentedRect(position); GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); - } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; - } else if (attr is TooltipAttribute) { - tooltip = (attr as TooltipAttribute).tooltip; + } + else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } + else if (attr is TooltipAttribute) + { + tooltip = (attr as TooltipAttribute).tooltip; } } - if (dynamicPortList) { + if (dynamicPortList) + { Type type = GetType(property); XNode.Node.ConnectionType connectionType = outputAttribute != null ? outputAttribute.connectionType : XNode.Node.ConnectionType.Multiple; DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType); return; } - switch (showBacking) { + switch (showBacking) + { case XNode.Node.ShowBackingValue.Unconnected: // Display a label if port is connected if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName, tooltip), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); @@ -160,6 +193,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); + rect.width += NodeEditorResources.styles.outputPort.padding.right; rect.position = rect.position + new Vector2(rect.width, spacePadding); } @@ -175,45 +209,53 @@ namespace XNodeEditor { } } - private static System.Type GetType(SerializedProperty property) { + private static System.Type GetType(SerializedProperty property) + { System.Type parentType = property.serializedObject.targetObject.GetType(); System.Reflection.FieldInfo fi = parentType.GetFieldInfo(property.name); return fi.FieldType; } /// Make a simple port field. - public static void PortField(XNode.NodePort port, params GUILayoutOption[] options) { + public static void PortField(XNode.NodePort port, params GUILayoutOption[] options) + { PortField(null, port, options); } /// Make a simple port field. - public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) { + public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) + { if (port == null) return; if (options == null) options = new GUILayoutOption[] { GUILayout.MinWidth(30) }; Vector2 position = Vector3.zero; GUIContent content = label != null ? label : new GUIContent(ObjectNames.NicifyVariableName(port.fieldName)); // If property is an input, display a regular property field and put a port handle on the left side - if (port.direction == XNode.NodePort.IO.Input) { + if (port.direction == XNode.NodePort.IO.Input) + { // Display a label EditorGUILayout.LabelField(content, options); Rect rect = GUILayoutUtility.GetLastRect(); - position = rect.position - new Vector2(16, 0); + float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + position = rect.position - new Vector2(16 + paddingLeft, 0); } // If property is an output, display a text label and put a port handle on the right side - else if (port.direction == XNode.NodePort.IO.Output) { + else if (port.direction == XNode.NodePort.IO.Output) + { // Display a label EditorGUILayout.LabelField(content, NodeEditorResources.OutputPort, options); Rect rect = GUILayoutUtility.GetLastRect(); + rect.width += NodeEditorResources.styles.outputPort.padding.right; position = rect.position + new Vector2(rect.width, 0); } PortField(position, port); } /// Make a simple port field. - public static void PortField(Vector2 position, XNode.NodePort port) { + public static void PortField(Vector2 position, XNode.NodePort port) + { if (port == null) return; Rect rect = new Rect(position, new Vector2(16, 16)); @@ -228,17 +270,23 @@ namespace XNodeEditor { } /// Add a port field to previous layout element. - public static void AddPortField(XNode.NodePort port) { + public static void AddPortField(XNode.NodePort port) + { if (port == null) return; Rect rect = new Rect(); // If property is an input, display a regular property field and put a port handle on the left side - if (port.direction == XNode.NodePort.IO.Input) { + if (port.direction == XNode.NodePort.IO.Input) + { rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position - new Vector2(16, 0); + float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + rect.position = rect.position - new Vector2(16 + paddingLeft, 0); // If property is an output, display a text label and put a port handle on the right side - } else if (port.direction == XNode.NodePort.IO.Output) { + } + else if (port.direction == XNode.NodePort.IO.Output) + { rect = GUILayoutUtility.GetLastRect(); + rect.width += NodeEditorResources.styles.outputPort.padding.right; rect.position = rect.position + new Vector2(rect.width, 0); } @@ -254,14 +302,16 @@ namespace XNodeEditor { } /// Draws an input and an output port on the same line - public static void PortPair(XNode.NodePort input, XNode.NodePort output) { + public static void PortPair(XNode.NodePort input, XNode.NodePort output) + { GUILayout.BeginHorizontal(); NodeEditorGUILayout.PortField(input, GUILayout.MinWidth(0)); NodeEditorGUILayout.PortField(output, GUILayout.MinWidth(0)); GUILayout.EndHorizontal(); } - public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor) { + public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor) + { Color col = GUI.color; GUI.color = backgroundColor; GUI.DrawTexture(rect, NodeEditorResources.dotOuter); @@ -270,24 +320,28 @@ namespace XNodeEditor { GUI.color = col; } -#region Obsolete + #region Obsolete [Obsolete("Use IsDynamicPortListPort instead")] - public static bool IsInstancePortListPort(XNode.NodePort port) { + public static bool IsInstancePortListPort(XNode.NodePort port) + { return IsDynamicPortListPort(port); } [Obsolete("Use DynamicPortList instead")] - public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { + public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) + { DynamicPortList(fieldName, type, serializedObject, io, connectionType, typeConstraint, onCreation); } -#endregion + #endregion /// Is this port part of a DynamicPortList? - public static bool IsDynamicPortListPort(XNode.NodePort port) { + public static bool IsDynamicPortListPort(XNode.NodePort port) + { string[] parts = port.fieldName.Split(' '); if (parts.Length != 2) return false; Dictionary cache; - if (reorderableListCache.TryGetValue(port.node, out cache)) { + if (reorderableListCache.TryGetValue(port.node, out cache)) + { ReorderableList list; if (cache.TryGetValue(parts[0], out list)) return true; } @@ -300,30 +354,36 @@ namespace XNodeEditor { /// The serializedObject of the node /// Connection type of added dynamic ports /// Called on the list on creation. Use this if you want to customize the created ReorderableList - public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { + public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) + { XNode.Node node = serializedObject.targetObject as XNode.Node; - var indexedPorts = node.DynamicPorts.Select(x => { + var indexedPorts = node.DynamicPorts.Select(x => + { string[] split = x.fieldName.Split(' '); - if (split != null && split.Length == 2 && split[0] == fieldName) { + if (split != null && split.Length == 2 && split[0] == fieldName) + { int i = -1; - if (int.TryParse(split[1], out i)) { + if (int.TryParse(split[1], out i)) + { return new { index = i, port = x }; } } - return new { index = -1, port = (XNode.NodePort) null }; + return new { index = -1, port = (XNode.NodePort)null }; }).Where(x => x.port != null); List dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList(); node.UpdatePorts(); - + ReorderableList list = null; Dictionary rlc; - if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) { + if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) + { if (!rlc.TryGetValue(fieldName, out list)) list = null; } // If a ReorderableList isn't cached for this array, do so. - if (list == null) { + if (list == null) + { SerializedProperty arrayData = serializedObject.FindProperty(fieldName); list = CreateReorderableList(fieldName, dynamicPorts, arrayData, type, serializedObject, io, connectionType, typeConstraint, onCreation); if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) rlc.Add(fieldName, list); @@ -331,56 +391,70 @@ namespace XNodeEditor { } list.list = dynamicPorts; list.DoLayoutList(); - + } - private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) { + private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) + { bool hasArrayData = arrayData != null && arrayData.isArray; XNode.Node node = serializedObject.targetObject as XNode.Node; ReorderableList list = new ReorderableList(dynamicPorts, null, true, true, true, true); string label = arrayData != null ? arrayData.displayName : ObjectNames.NicifyVariableName(fieldName); list.drawElementCallback = - (Rect rect, int index, bool isActive, bool isFocused) => { + (Rect rect, int index, bool isActive, bool isFocused) => + { XNode.NodePort port = node.GetPort(fieldName + " " + index); - if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { - if (arrayData.arraySize <= index) { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) + { + if (arrayData.arraySize <= index) + { EditorGUI.LabelField(rect, "Array[" + index + "] data out of range"); return; } SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); EditorGUI.PropertyField(rect, itemData, true); - } else EditorGUI.LabelField(rect, port != null ? port.fieldName : ""); - if (port != null) { - Vector2 pos = rect.position + (port.IsOutput?new Vector2(rect.width + 6, 0) : new Vector2(-36, 0)); + } + else EditorGUI.LabelField(rect, port != null ? port.fieldName : ""); + if (port != null) + { + Vector2 pos = rect.position + (port.IsOutput ? new Vector2(rect.width + 6, 0) : new Vector2(-36, 0)); NodeEditorGUILayout.PortField(pos, port); } }; list.elementHeightCallback = - (int index) => { - if (hasArrayData) { + (int index) => + { + if (hasArrayData) + { if (arrayData.arraySize <= index) return EditorGUIUtility.singleLineHeight; SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); return EditorGUI.GetPropertyHeight(itemData); - } else return EditorGUIUtility.singleLineHeight; + } + else return EditorGUIUtility.singleLineHeight; }; list.drawHeaderCallback = - (Rect rect) => { + (Rect rect) => + { EditorGUI.LabelField(rect, label); }; list.onSelectCallback = - (ReorderableList rl) => { + (ReorderableList rl) => + { reorderableListIndex = rl.index; }; list.onReorderCallback = - (ReorderableList rl) => { + (ReorderableList rl) => + { bool hasRect = false; bool hasNewRect = false; Rect rect = Rect.zero; Rect newRect = Rect.zero; // Move up - if (rl.index > reorderableListIndex) { - for (int i = reorderableListIndex; i < rl.index; ++i) { + if (rl.index > reorderableListIndex) + { + for (int i = reorderableListIndex; i < rl.index; ++i) + { XNode.NodePort port = node.GetPort(fieldName + " " + i); XNode.NodePort nextPort = node.GetPort(fieldName + " " + (i + 1)); port.SwapConnections(nextPort); @@ -388,13 +462,15 @@ namespace XNodeEditor { // Swap cached positions to mitigate twitching hasRect = NodeEditorWindow.current.portConnectionPoints.TryGetValue(port, out rect); hasNewRect = NodeEditorWindow.current.portConnectionPoints.TryGetValue(nextPort, out newRect); - NodeEditorWindow.current.portConnectionPoints[port] = hasNewRect?newRect:rect; - NodeEditorWindow.current.portConnectionPoints[nextPort] = hasRect?rect:newRect; + NodeEditorWindow.current.portConnectionPoints[port] = hasNewRect ? newRect : rect; + NodeEditorWindow.current.portConnectionPoints[nextPort] = hasRect ? rect : newRect; } } // Move down - else { - for (int i = reorderableListIndex; i > rl.index; --i) { + else + { + for (int i = reorderableListIndex; i > rl.index; --i) + { XNode.NodePort port = node.GetPort(fieldName + " " + i); XNode.NodePort nextPort = node.GetPort(fieldName + " " + (i - 1)); port.SwapConnections(nextPort); @@ -402,8 +478,8 @@ namespace XNodeEditor { // Swap cached positions to mitigate twitching hasRect = NodeEditorWindow.current.portConnectionPoints.TryGetValue(port, out rect); hasNewRect = NodeEditorWindow.current.portConnectionPoints.TryGetValue(nextPort, out newRect); - NodeEditorWindow.current.portConnectionPoints[port] = hasNewRect?newRect:rect; - NodeEditorWindow.current.portConnectionPoints[nextPort] = hasRect?rect:newRect; + NodeEditorWindow.current.portConnectionPoints[port] = hasNewRect ? newRect : rect; + NodeEditorWindow.current.portConnectionPoints[nextPort] = hasRect ? rect : newRect; } } // Apply changes @@ -411,7 +487,8 @@ namespace XNodeEditor { serializedObject.Update(); // Move array data if there is any - if (hasArrayData) { + if (hasArrayData) + { arrayData.MoveArrayElement(reorderableListIndex, rl.index); } @@ -422,7 +499,8 @@ namespace XNodeEditor { EditorApplication.delayCall += NodeEditorWindow.current.Repaint; }; list.onAddCallback = - (ReorderableList rl) => { + (ReorderableList rl) => + { // Add dynamic port postfixed with an index number string newName = fieldName + " 0"; int i = 0; @@ -432,39 +510,51 @@ namespace XNodeEditor { else node.AddDynamicInput(type, connectionType, typeConstraint, newName); serializedObject.Update(); EditorUtility.SetDirty(node); - if (hasArrayData) { + if (hasArrayData) + { arrayData.InsertArrayElementAtIndex(arrayData.arraySize); } serializedObject.ApplyModifiedProperties(); }; list.onRemoveCallback = - (ReorderableList rl) => { + (ReorderableList rl) => + { - var indexedPorts = node.DynamicPorts.Select(x => { + var indexedPorts = node.DynamicPorts.Select(x => + { string[] split = x.fieldName.Split(' '); - if (split != null && split.Length == 2 && split[0] == fieldName) { + if (split != null && split.Length == 2 && split[0] == fieldName) + { int i = -1; - if (int.TryParse(split[1], out i)) { + if (int.TryParse(split[1], out i)) + { return new { index = i, port = x }; } } - return new { index = -1, port = (XNode.NodePort) null }; + return new { index = -1, port = (XNode.NodePort)null }; }).Where(x => x.port != null); dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList(); int index = rl.index; - if (dynamicPorts[index] == null) { + if (dynamicPorts[index] == null) + { Debug.LogWarning("No port found at index " + index + " - Skipped"); - } else if (dynamicPorts.Count <= index) { + } + else if (dynamicPorts.Count <= index) + { Debug.LogWarning("DynamicPorts[" + index + "] out of range. Length was " + dynamicPorts.Count + " - Skipped"); - } else { + } + else + { // Clear the removed ports connections dynamicPorts[index].ClearConnections(); // Move following connections one step up to replace the missing connection - for (int k = index + 1; k < dynamicPorts.Count(); k++) { - for (int j = 0; j < dynamicPorts[k].ConnectionCount; j++) { + for (int k = index + 1; k < dynamicPorts.Count(); k++) + { + for (int j = 0; j < dynamicPorts[k].ConnectionCount; j++) + { XNode.NodePort other = dynamicPorts[k].GetConnection(j); dynamicPorts[k].Disconnect(other); dynamicPorts[k - 1].Connect(other); @@ -476,16 +566,20 @@ namespace XNodeEditor { EditorUtility.SetDirty(node); } - if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { - if (arrayData.arraySize <= index) { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) + { + if (arrayData.arraySize <= index) + { Debug.LogWarning("Attempted to remove array index " + index + " where only " + arrayData.arraySize + " exist - Skipped"); Debug.Log(rl.list[0]); return; } arrayData.DeleteArrayElementAtIndex(index); // Error handling. If the following happens too often, file a bug report at https://github.com/Siccity/xNode/issues - if (dynamicPorts.Count <= arrayData.arraySize) { - while (dynamicPorts.Count <= arrayData.arraySize) { + if (dynamicPorts.Count <= arrayData.arraySize) + { + while (dynamicPorts.Count <= arrayData.arraySize) + { arrayData.DeleteArrayElementAtIndex(arrayData.arraySize - 1); } UnityEngine.Debug.LogWarning("Array size exceeded dynamic ports size. Excess items removed."); @@ -495,9 +589,11 @@ namespace XNodeEditor { } }; - if (hasArrayData) { + if (hasArrayData) + { int dynamicPortCount = dynamicPorts.Count; - while (dynamicPortCount < arrayData.arraySize) { + while (dynamicPortCount < arrayData.arraySize) + { // Add dynamic port postfixed with an index number string newName = arrayData.name + " 0"; int i = 0; @@ -507,7 +603,8 @@ namespace XNodeEditor { EditorUtility.SetDirty(node); dynamicPortCount++; } - while (arrayData.arraySize < dynamicPortCount) { + while (arrayData.arraySize < dynamicPortCount) + { arrayData.InsertArrayElementAtIndex(arrayData.arraySize); } serializedObject.ApplyModifiedProperties(); diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs index 0a84e0a..9a5cc1e 100644 --- a/Scripts/Editor/NodeEditorResources.cs +++ b/Scripts/Editor/NodeEditorResources.cs @@ -18,7 +18,7 @@ namespace XNodeEditor { public static Styles _styles = null; public static GUIStyle OutputPort { get { return new GUIStyle(EditorStyles.label) { alignment = TextAnchor.UpperRight }; } } public class Styles { - public GUIStyle inputPort, nodeHeader, nodeBody, tooltip, nodeHighlight; + public GUIStyle inputPort, outputPort, nodeHeader, nodeBody, tooltip, nodeHighlight; public Styles() { GUIStyle baseStyle = new GUIStyle("Label"); @@ -26,7 +26,11 @@ namespace XNodeEditor { inputPort = new GUIStyle(baseStyle); inputPort.alignment = TextAnchor.UpperLeft; - inputPort.padding.left = 10; + inputPort.padding.left = 0; + + outputPort = new GUIStyle(baseStyle); + outputPort.alignment = TextAnchor.UpperRight; + outputPort.padding.right = 0; nodeHeader = new GUIStyle(); nodeHeader.alignment = TextAnchor.MiddleCenter; From cde52ff21aae0b1822a992d3aa13350f838b4992 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 12 Oct 2020 09:40:40 +0200 Subject: [PATCH 7/7] Formatting --- Scripts/Editor/NodeEditorAction.cs | 349 ++++++++---------------- Scripts/Editor/NodeEditorGUI.cs | 2 +- Scripts/Editor/NodeEditorGUILayout.cs | 269 ++++++------------ Scripts/Editor/NodeEditorPreferences.cs | 4 +- Scripts/Editor/NodeGraphEditor.cs | 101 +++---- 5 files changed, 234 insertions(+), 491 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 738a602..e78aaec 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -5,10 +5,8 @@ using UnityEditor; using UnityEngine; using XNodeEditor.Internal; -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; } @@ -21,17 +19,11 @@ namespace XNodeEditor public bool IsHoveringNode { get { return hoveredNode != null; } } public bool IsHoveringReroute { get { return hoveredReroute.port != null; } } - /// - /// Return the dragged port or null if not exist - /// + /// Return the dragged port or null if not exist public XNode.NodePort DraggedOutputPort { get { XNode.NodePort result = draggedOutput; return result; } } - /// - /// Return the Hovered port or null if not exist - /// + /// Return the Hovered port or null if not exist public XNode.NodePort HoveredPort { get { XNode.NodePort result = hoveredPort; return result; } } - /// - /// Return the Hovered node or null if not exist - /// + /// Return the Hovered node or null if not exist public XNode.Node HoveredNode { get { XNode.Node result = hoveredNode; return result; } } private XNode.Node hoveredNode = null; @@ -39,7 +31,7 @@ namespace XNodeEditor [NonSerialized] private XNode.NodePort draggedOutput = null; [NonSerialized] private XNode.NodePort draggedOutputTarget = null; [NonSerialized] private XNode.NodePort autoConnectOutput = null; - [NonSerialized] private List draggedOutputReroutes = new List(); + [NonSerialized] private List draggedOutputReroutes = new List(); private RerouteReference hoveredReroute = new RerouteReference(); public List selectedReroutes = new List(); @@ -51,17 +43,14 @@ namespace XNodeEditor private Vector2 lastMousePosition; private float dragThreshold = 1f; - 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.Generic; - if (e.type == EventType.DragPerform) - { + if (e.type == EventType.DragPerform) { DragAndDrop.AcceptDrag(); graphEditor.OnDropObjects(DragAndDrop.objectReferences); } @@ -77,68 +66,52 @@ 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 (e.button == 0) { + if (IsDraggingPort) { // Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously - if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) - { + if (IsHoveringPort && hoveredPort.IsInput && !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; Undo.RecordObject(node, "Moved 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; } @@ -147,28 +120,22 @@ 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); } @@ -176,12 +143,9 @@ namespace XNodeEditor selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } - } - else if (e.button == 1 || e.button == 2) - { + } else if (e.button == 1 || e.button == 2) { //check drag threshold for larger screens - if (e.delta.magnitude > dragThreshold) - { + if (e.delta.magnitude > dragThreshold) { panOffset += e.delta * zoom; isPanning = true; } @@ -189,23 +153,17 @@ namespace XNodeEditor 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; autoConnectOutput = hoveredPort; - } - else - { + } else { hoveredPort.VerifyConnections(); autoConnectOutput = null; - if (hoveredPort.IsConnected) - { + if (hoveredPort.IsConnected) { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); @@ -216,33 +174,25 @@ 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; } @@ -254,11 +204,9 @@ 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; } @@ -266,29 +214,24 @@ 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 && draggedOutput.CanConnectTo(draggedOutputTarget)) - { + if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) { 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); } } // Open context menu for auto-connection if there is no target node - else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) - { + else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) { GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); @@ -298,18 +241,13 @@ 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; } @@ -317,61 +255,44 @@ 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); autoConnectOutput = null; 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) { autoConnectOutput = null; GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); @@ -386,27 +307,18 @@ namespace XNodeEditor case EventType.KeyDown: if (EditorGUIUtility.editingTextField) break; else if (e.keyCode == KeyCode.F) Home(); - if (NodeEditorUtilities.IsMac()) - { + if (NodeEditorUtilities.IsMac()) { if (e.keyCode == KeyCode.Return) RenameSelectedNode(); - } - else - { + } else { if (e.keyCode == KeyCode.F2) RenameSelectedNode(); } - if (e.keyCode == KeyCode.A) - { - if (Selection.objects.Any(x => graph.nodes.Contains(x as XNode.Node))) - { - foreach (XNode.Node node in graph.nodes) - { + if (e.keyCode == KeyCode.A) { + if (Selection.objects.Any(x => graph.nodes.Contains(x as XNode.Node))) { + foreach (XNode.Node node in graph.nodes) { DeselectNode(node); } - } - else - { - foreach (XNode.Node node in graph.nodes) - { + } else { + foreach (XNode.Node node in graph.nodes) { SelectNode(node, true); } } @@ -415,28 +327,19 @@ namespace XNodeEditor 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 (NodeEditorUtilities.IsMac() && e.commandName == "Delete") - { + } else if (NodeEditorUtilities.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(); - } - else if (e.commandName == "Copy") - { + } else if (e.commandName == "Copy") { if (e.type == EventType.ExecuteCommand) CopySelectedNodes(); e.Use(); - } - else if (e.commandName == "Paste") - { + } else if (e.commandName == "Paste") { if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition)); e.Use(); } @@ -444,8 +347,7 @@ 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; } @@ -453,57 +355,45 @@ namespace XNodeEditor } } - 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 selected nodes in focus. If no nodes are present, resets view and zoom to to origin - public void Home() - { + public void Home() { var nodes = Selection.objects.Where(o => o is XNode.Node).Cast().ToList(); - if (nodes.Count > 0) - { + if (nodes.Count > 0) { Vector2 minPos = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y))); Vector2 maxPos = nodes.Select(x => x.position + (nodeSizes.ContainsKey(x) ? nodeSizes[x] : Vector2.zero)).Aggregate((x, y) => new Vector2(Mathf.Max(x.x, y.x), Mathf.Max(x.y, y.y))); panOffset = -(minPos + (maxPos - minPos) / 2f); - } - else - { + } else { 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); } @@ -511,37 +401,29 @@ 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() { // 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(); if (selectedNodes == null || selectedNodes.Length == 0) return; @@ -550,18 +432,15 @@ namespace XNodeEditor InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30)); } - public void CopySelectedNodes() - { + 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) - { + public void PasteNodes(Vector2 pos) { InsertDuplicateNodes(copyBuffer, pos); } - private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) - { + private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) { if (nodes == null || nodes.Length == 0) return; // Get top-left node @@ -570,16 +449,14 @@ namespace XNodeEditor UnityEngine.Object[] newNodes = new UnityEngine.Object[nodes.Length]; Dictionary substitutes = new Dictionary(); - for (int i = 0; i < nodes.Length; i++) - { + for (int i = 0; i < nodes.Length; i++) { XNode.Node srcNode = nodes[i]; if (srcNode == null) continue; // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; Type nodeType = srcNode.GetType(); - if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) - { + if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) { int typeCount = graph.nodes.Count(x => x.GetType() == nodeType); if (typeCount >= disallowAttrib.max) continue; } @@ -591,20 +468,16 @@ namespace XNodeEditor } // Walk through the selected nodes again, recreate connections, using the new nodes - for (int i = 0; i < nodes.Length; i++) - { + 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++) - { + 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.UpdatePorts(); newNodeOut.UpdatePorts(); inputPort = newNodeIn.GetInputPort(inputPort.fieldName); @@ -620,10 +493,8 @@ namespace XNodeEditor } /// Draw a connection as we are dragging it - public void DrawDraggedConnection() - { - if (IsDraggingPort) - { + public void DrawDraggedConnection() { + if (IsDraggingPort) { Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null); float thickness = graphEditor.GetNoodleThickness(draggedOutput, null); NoodlePath path = graphEditor.GetNoodlePath(draggedOutput, null); @@ -633,8 +504,7 @@ 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); @@ -648,8 +518,7 @@ 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); @@ -660,8 +529,7 @@ 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); @@ -674,8 +542,7 @@ namespace XNodeEditor } /// Attempt to connect dragged output to target node - public void AutoConnect(XNode.Node node) - { + public void AutoConnect(XNode.Node node) { if (autoConnectOutput == null) return; // Find input port of same type @@ -691,4 +558,4 @@ namespace XNodeEditor autoConnectOutput = null; } } -} +} \ No newline at end of file diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 21d6d0f..d914eff 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -556,7 +556,7 @@ namespace XNodeEditor { } private void DrawTooltip() { - if (!NodeEditorPreferences.GetSettings().portTooltips || graphEditor is null) + if (!NodeEditorPreferences.GetSettings().portTooltips || graphEditor == null) return; string tooltip = null; if (hoveredPort != null) { diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 077de1d..1b74a32 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -7,24 +7,20 @@ using UnityEditor; using UnityEditorInternal; using UnityEngine; -namespace XNodeEditor -{ +namespace XNodeEditor { /// xNode-specific version of - public static class NodeEditorGUILayout - { + public static class NodeEditorGUILayout { private static readonly Dictionary> reorderableListCache = new Dictionary>(); private static int reorderableListIndex = -1; /// Make a field for a serialized property. Automatically displays relevant node port. - public static void PropertyField(SerializedProperty property, bool includeChildren = true, params GUILayoutOption[] options) - { - PropertyField(property, (GUIContent)null, includeChildren, options); + public static void PropertyField(SerializedProperty property, bool includeChildren = true, params GUILayoutOption[] options) { + PropertyField(property, (GUIContent) null, includeChildren, options); } /// Make a field for a serialized property. Automatically displays relevant node port. - public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) - { + public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) { if (property == null) throw new NullReferenceException(); XNode.Node node = property.serializedObject.targetObject as XNode.Node; XNode.NodePort port = node.GetPort(property.name); @@ -32,33 +28,28 @@ namespace XNodeEditor } /// Make a field for a serialized property. Manual node port override. - public static void PropertyField(SerializedProperty property, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) - { + public static void PropertyField(SerializedProperty property, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { PropertyField(property, null, port, includeChildren, options); } /// Make a field for a serialized property. Manual node port override. - public static void PropertyField(SerializedProperty property, GUIContent label, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) - { + public static void PropertyField(SerializedProperty property, GUIContent label, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { if (property == null) throw new NullReferenceException(); // If property is not a port, display a regular property field if (port == null) EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); - else - { + else { Rect rect = new Rect(); List propertyAttributes = NodeEditorUtilities.GetCachedPropertyAttribs(port.node.GetType(), property.name); // If property is an input, display a regular property field and put a port handle on the left side - if (port.direction == XNode.NodePort.IO.Input) - { + if (port.direction == XNode.NodePort.IO.Input) { // Get data from [Input] attribute XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected; XNode.Node.InputAttribute inputAttribute; bool dynamicPortList = false; - if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) - { + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) { dynamicPortList = inputAttribute.dynamicPortList; showBacking = inputAttribute.backingValue; } @@ -69,40 +60,30 @@ namespace XNodeEditor float spacePadding = 0; string tooltip = null; - foreach (var attr in propertyAttributes) - { - if (attr is SpaceAttribute) - { + foreach (var attr in propertyAttributes) { + if (attr is SpaceAttribute) { if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); else spacePadding += (attr as SpaceAttribute).height; - } - else if (attr is HeaderAttribute) - { - if (usePropertyAttributes) - { + } else if (attr is HeaderAttribute) { + if (usePropertyAttributes) { //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; position = EditorGUI.IndentedRect(position); GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); - } - else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; - } - else if (attr is TooltipAttribute) - { + } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } else if (attr is TooltipAttribute) { tooltip = (attr as TooltipAttribute).tooltip; } } - if (dynamicPortList) - { + if (dynamicPortList) { Type type = GetType(property); XNode.Node.ConnectionType connectionType = inputAttribute != null ? inputAttribute.connectionType : XNode.Node.ConnectionType.Multiple; DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType); return; } - switch (showBacking) - { + switch (showBacking) { case XNode.Node.ShowBackingValue.Unconnected: // Display a label if port is connected if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName, tooltip)); @@ -123,15 +104,12 @@ namespace XNodeEditor float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; rect.position = rect.position - new Vector2(16 + paddingLeft, -spacePadding); // If property is an output, display a text label and put a port handle on the right side - } - else if (port.direction == XNode.NodePort.IO.Output) - { + } else if (port.direction == XNode.NodePort.IO.Output) { // Get data from [Output] attribute XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected; XNode.Node.OutputAttribute outputAttribute; bool dynamicPortList = false; - if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) - { + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) { dynamicPortList = outputAttribute.dynamicPortList; showBacking = outputAttribute.backingValue; } @@ -142,40 +120,30 @@ namespace XNodeEditor float spacePadding = 0; string tooltip = null; - foreach (var attr in propertyAttributes) - { - if (attr is SpaceAttribute) - { + foreach (var attr in propertyAttributes) { + if (attr is SpaceAttribute) { if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); else spacePadding += (attr as SpaceAttribute).height; - } - else if (attr is HeaderAttribute) - { - if (usePropertyAttributes) - { + } else if (attr is HeaderAttribute) { + if (usePropertyAttributes) { //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; position = EditorGUI.IndentedRect(position); GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); - } - else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; - } - else if (attr is TooltipAttribute) - { + } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } else if (attr is TooltipAttribute) { tooltip = (attr as TooltipAttribute).tooltip; } } - if (dynamicPortList) - { + if (dynamicPortList) { Type type = GetType(property); XNode.Node.ConnectionType connectionType = outputAttribute != null ? outputAttribute.connectionType : XNode.Node.ConnectionType.Multiple; DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType); return; } - switch (showBacking) - { + switch (showBacking) { case XNode.Node.ShowBackingValue.Unconnected: // Display a label if port is connected if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName, tooltip), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); @@ -209,30 +177,26 @@ namespace XNodeEditor } } - private static System.Type GetType(SerializedProperty property) - { + private static System.Type GetType(SerializedProperty property) { System.Type parentType = property.serializedObject.targetObject.GetType(); System.Reflection.FieldInfo fi = parentType.GetFieldInfo(property.name); return fi.FieldType; } /// Make a simple port field. - public static void PortField(XNode.NodePort port, params GUILayoutOption[] options) - { + public static void PortField(XNode.NodePort port, params GUILayoutOption[] options) { PortField(null, port, options); } /// Make a simple port field. - public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) - { + public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) { if (port == null) return; if (options == null) options = new GUILayoutOption[] { GUILayout.MinWidth(30) }; Vector2 position = Vector3.zero; GUIContent content = label != null ? label : new GUIContent(ObjectNames.NicifyVariableName(port.fieldName)); // If property is an input, display a regular property field and put a port handle on the left side - if (port.direction == XNode.NodePort.IO.Input) - { + if (port.direction == XNode.NodePort.IO.Input) { // Display a label EditorGUILayout.LabelField(content, options); @@ -241,8 +205,7 @@ namespace XNodeEditor position = rect.position - new Vector2(16 + paddingLeft, 0); } // If property is an output, display a text label and put a port handle on the right side - else if (port.direction == XNode.NodePort.IO.Output) - { + else if (port.direction == XNode.NodePort.IO.Output) { // Display a label EditorGUILayout.LabelField(content, NodeEditorResources.OutputPort, options); @@ -254,8 +217,7 @@ namespace XNodeEditor } /// Make a simple port field. - public static void PortField(Vector2 position, XNode.NodePort port) - { + public static void PortField(Vector2 position, XNode.NodePort port) { if (port == null) return; Rect rect = new Rect(position, new Vector2(16, 16)); @@ -270,21 +232,17 @@ namespace XNodeEditor } /// Add a port field to previous layout element. - public static void AddPortField(XNode.NodePort port) - { + public static void AddPortField(XNode.NodePort port) { if (port == null) return; Rect rect = new Rect(); // If property is an input, display a regular property field and put a port handle on the left side - if (port.direction == XNode.NodePort.IO.Input) - { + if (port.direction == XNode.NodePort.IO.Input) { rect = GUILayoutUtility.GetLastRect(); float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; rect.position = rect.position - new Vector2(16 + paddingLeft, 0); // If property is an output, display a text label and put a port handle on the right side - } - else if (port.direction == XNode.NodePort.IO.Output) - { + } else if (port.direction == XNode.NodePort.IO.Output) { rect = GUILayoutUtility.GetLastRect(); rect.width += NodeEditorResources.styles.outputPort.padding.right; rect.position = rect.position + new Vector2(rect.width, 0); @@ -302,16 +260,14 @@ namespace XNodeEditor } /// Draws an input and an output port on the same line - public static void PortPair(XNode.NodePort input, XNode.NodePort output) - { + public static void PortPair(XNode.NodePort input, XNode.NodePort output) { GUILayout.BeginHorizontal(); NodeEditorGUILayout.PortField(input, GUILayout.MinWidth(0)); NodeEditorGUILayout.PortField(output, GUILayout.MinWidth(0)); GUILayout.EndHorizontal(); } - public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor) - { + public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor) { Color col = GUI.color; GUI.color = backgroundColor; GUI.DrawTexture(rect, NodeEditorResources.dotOuter); @@ -320,28 +276,24 @@ namespace XNodeEditor GUI.color = col; } - #region Obsolete +#region Obsolete [Obsolete("Use IsDynamicPortListPort instead")] - public static bool IsInstancePortListPort(XNode.NodePort port) - { + public static bool IsInstancePortListPort(XNode.NodePort port) { return IsDynamicPortListPort(port); } [Obsolete("Use DynamicPortList instead")] - public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) - { + public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { DynamicPortList(fieldName, type, serializedObject, io, connectionType, typeConstraint, onCreation); } - #endregion +#endregion /// Is this port part of a DynamicPortList? - public static bool IsDynamicPortListPort(XNode.NodePort port) - { + public static bool IsDynamicPortListPort(XNode.NodePort port) { string[] parts = port.fieldName.Split(' '); if (parts.Length != 2) return false; Dictionary cache; - if (reorderableListCache.TryGetValue(port.node, out cache)) - { + if (reorderableListCache.TryGetValue(port.node, out cache)) { ReorderableList list; if (cache.TryGetValue(parts[0], out list)) return true; } @@ -354,22 +306,18 @@ namespace XNodeEditor /// The serializedObject of the node /// Connection type of added dynamic ports /// Called on the list on creation. Use this if you want to customize the created ReorderableList - public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) - { + public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { XNode.Node node = serializedObject.targetObject as XNode.Node; - var indexedPorts = node.DynamicPorts.Select(x => - { + var indexedPorts = node.DynamicPorts.Select(x => { string[] split = x.fieldName.Split(' '); - if (split != null && split.Length == 2 && split[0] == fieldName) - { + if (split != null && split.Length == 2 && split[0] == fieldName) { int i = -1; - if (int.TryParse(split[1], out i)) - { + if (int.TryParse(split[1], out i)) { return new { index = i, port = x }; } } - return new { index = -1, port = (XNode.NodePort)null }; + return new { index = -1, port = (XNode.NodePort) null }; }).Where(x => x.port != null); List dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList(); @@ -377,13 +325,11 @@ namespace XNodeEditor ReorderableList list = null; Dictionary rlc; - if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) - { + if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) { if (!rlc.TryGetValue(fieldName, out list)) list = null; } // If a ReorderableList isn't cached for this array, do so. - if (list == null) - { + if (list == null) { SerializedProperty arrayData = serializedObject.FindProperty(fieldName); list = CreateReorderableList(fieldName, dynamicPorts, arrayData, type, serializedObject, io, connectionType, typeConstraint, onCreation); if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) rlc.Add(fieldName, list); @@ -394,67 +340,53 @@ namespace XNodeEditor } - private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) - { + private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) { bool hasArrayData = arrayData != null && arrayData.isArray; XNode.Node node = serializedObject.targetObject as XNode.Node; ReorderableList list = new ReorderableList(dynamicPorts, null, true, true, true, true); string label = arrayData != null ? arrayData.displayName : ObjectNames.NicifyVariableName(fieldName); list.drawElementCallback = - (Rect rect, int index, bool isActive, bool isFocused) => - { + (Rect rect, int index, bool isActive, bool isFocused) => { XNode.NodePort port = node.GetPort(fieldName + " " + index); - if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) - { - if (arrayData.arraySize <= index) - { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { + if (arrayData.arraySize <= index) { EditorGUI.LabelField(rect, "Array[" + index + "] data out of range"); return; } SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); EditorGUI.PropertyField(rect, itemData, true); - } - else EditorGUI.LabelField(rect, port != null ? port.fieldName : ""); - if (port != null) - { + } else EditorGUI.LabelField(rect, port != null ? port.fieldName : ""); + if (port != null) { Vector2 pos = rect.position + (port.IsOutput ? new Vector2(rect.width + 6, 0) : new Vector2(-36, 0)); NodeEditorGUILayout.PortField(pos, port); } }; list.elementHeightCallback = - (int index) => - { - if (hasArrayData) - { + (int index) => { + if (hasArrayData) { if (arrayData.arraySize <= index) return EditorGUIUtility.singleLineHeight; SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); return EditorGUI.GetPropertyHeight(itemData); - } - else return EditorGUIUtility.singleLineHeight; + } else return EditorGUIUtility.singleLineHeight; }; list.drawHeaderCallback = - (Rect rect) => - { + (Rect rect) => { EditorGUI.LabelField(rect, label); }; list.onSelectCallback = - (ReorderableList rl) => - { + (ReorderableList rl) => { reorderableListIndex = rl.index; }; list.onReorderCallback = - (ReorderableList rl) => - { + (ReorderableList rl) => { bool hasRect = false; bool hasNewRect = false; Rect rect = Rect.zero; Rect newRect = Rect.zero; // Move up - if (rl.index > reorderableListIndex) - { - for (int i = reorderableListIndex; i < rl.index; ++i) - { + if (rl.index > reorderableListIndex) { + for (int i = reorderableListIndex; i < rl.index; ++i) { XNode.NodePort port = node.GetPort(fieldName + " " + i); XNode.NodePort nextPort = node.GetPort(fieldName + " " + (i + 1)); port.SwapConnections(nextPort); @@ -467,10 +399,8 @@ namespace XNodeEditor } } // Move down - else - { - for (int i = reorderableListIndex; i > rl.index; --i) - { + else { + for (int i = reorderableListIndex; i > rl.index; --i) { XNode.NodePort port = node.GetPort(fieldName + " " + i); XNode.NodePort nextPort = node.GetPort(fieldName + " " + (i - 1)); port.SwapConnections(nextPort); @@ -487,8 +417,7 @@ namespace XNodeEditor serializedObject.Update(); // Move array data if there is any - if (hasArrayData) - { + if (hasArrayData) { arrayData.MoveArrayElement(reorderableListIndex, rl.index); } @@ -499,8 +428,7 @@ namespace XNodeEditor EditorApplication.delayCall += NodeEditorWindow.current.Repaint; }; list.onAddCallback = - (ReorderableList rl) => - { + (ReorderableList rl) => { // Add dynamic port postfixed with an index number string newName = fieldName + " 0"; int i = 0; @@ -510,51 +438,39 @@ namespace XNodeEditor else node.AddDynamicInput(type, connectionType, typeConstraint, newName); serializedObject.Update(); EditorUtility.SetDirty(node); - if (hasArrayData) - { + if (hasArrayData) { arrayData.InsertArrayElementAtIndex(arrayData.arraySize); } serializedObject.ApplyModifiedProperties(); }; list.onRemoveCallback = - (ReorderableList rl) => - { + (ReorderableList rl) => { - var indexedPorts = node.DynamicPorts.Select(x => - { + var indexedPorts = node.DynamicPorts.Select(x => { string[] split = x.fieldName.Split(' '); - if (split != null && split.Length == 2 && split[0] == fieldName) - { + if (split != null && split.Length == 2 && split[0] == fieldName) { int i = -1; - if (int.TryParse(split[1], out i)) - { + if (int.TryParse(split[1], out i)) { return new { index = i, port = x }; } } - return new { index = -1, port = (XNode.NodePort)null }; + return new { index = -1, port = (XNode.NodePort) null }; }).Where(x => x.port != null); dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList(); int index = rl.index; - if (dynamicPorts[index] == null) - { + if (dynamicPorts[index] == null) { Debug.LogWarning("No port found at index " + index + " - Skipped"); - } - else if (dynamicPorts.Count <= index) - { + } else if (dynamicPorts.Count <= index) { Debug.LogWarning("DynamicPorts[" + index + "] out of range. Length was " + dynamicPorts.Count + " - Skipped"); - } - else - { + } else { // Clear the removed ports connections dynamicPorts[index].ClearConnections(); // Move following connections one step up to replace the missing connection - for (int k = index + 1; k < dynamicPorts.Count(); k++) - { - for (int j = 0; j < dynamicPorts[k].ConnectionCount; j++) - { + for (int k = index + 1; k < dynamicPorts.Count(); k++) { + for (int j = 0; j < dynamicPorts[k].ConnectionCount; j++) { XNode.NodePort other = dynamicPorts[k].GetConnection(j); dynamicPorts[k].Disconnect(other); dynamicPorts[k - 1].Connect(other); @@ -566,20 +482,16 @@ namespace XNodeEditor EditorUtility.SetDirty(node); } - if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) - { - if (arrayData.arraySize <= index) - { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { + if (arrayData.arraySize <= index) { Debug.LogWarning("Attempted to remove array index " + index + " where only " + arrayData.arraySize + " exist - Skipped"); Debug.Log(rl.list[0]); return; } arrayData.DeleteArrayElementAtIndex(index); // Error handling. If the following happens too often, file a bug report at https://github.com/Siccity/xNode/issues - if (dynamicPorts.Count <= arrayData.arraySize) - { - while (dynamicPorts.Count <= arrayData.arraySize) - { + if (dynamicPorts.Count <= arrayData.arraySize) { + while (dynamicPorts.Count <= arrayData.arraySize) { arrayData.DeleteArrayElementAtIndex(arrayData.arraySize - 1); } UnityEngine.Debug.LogWarning("Array size exceeded dynamic ports size. Excess items removed."); @@ -589,11 +501,9 @@ namespace XNodeEditor } }; - if (hasArrayData) - { + if (hasArrayData) { int dynamicPortCount = dynamicPorts.Count; - while (dynamicPortCount < arrayData.arraySize) - { + while (dynamicPortCount < arrayData.arraySize) { // Add dynamic port postfixed with an index number string newName = arrayData.name + " 0"; int i = 0; @@ -603,8 +513,7 @@ namespace XNodeEditor EditorUtility.SetDirty(node); dynamicPortCount++; } - while (arrayData.arraySize < dynamicPortCount) - { + while (arrayData.arraySize < dynamicPortCount) { arrayData.InsertArrayElementAtIndex(arrayData.arraySize); } serializedObject.ApplyModifiedProperties(); @@ -614,4 +523,4 @@ namespace XNodeEditor return list; } } -} +} \ No newline at end of file diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index d16dfd0..6165072 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -43,7 +43,7 @@ namespace XNodeEditor { [NonSerialized] public Dictionary typeColors = new Dictionary(); [FormerlySerializedAs("noodleType")] public NoodlePath noodlePath = NoodlePath.Curvy; public float noodleThickness = 2f; - + public NoodleStroke noodleStroke = NoodleStroke.Full; private Texture2D _gridTexture; @@ -103,7 +103,7 @@ namespace XNodeEditor { public static SettingsProvider CreateXNodeSettingsProvider() { SettingsProvider provider = new SettingsProvider("Preferences/Node Editor", SettingsScope.User) { guiHandler = (searchContext) => { XNodeEditor.NodeEditorPreferences.PreferencesGUI(); }, - keywords = new HashSet(new [] { "xNode", "node", "editor", "graph", "connections", "noodles", "ports" }) + keywords = new HashSet(new [] { "xNode", "node", "editor", "graph", "connections", "noodles", "ports" }) }; return provider; } diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 8b671e2..dcc92a9 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -4,12 +4,10 @@ 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? @@ -26,25 +24,21 @@ namespace XNodeEditor /// Called when NodeEditorWindow loses focus public virtual void OnWindowFocusLost() { } - 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 @@ -54,8 +48,7 @@ namespace XNodeEditor } /// The order by which the menu items are displayed. - public virtual int GetNodeMenuOrder(Type type) - { + public virtual int GetNodeMenuOrder(Type type) { //Check if type has the CreateNodeMenuAttribute XNode.Node.CreateNodeMenuAttribute attrib; if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path @@ -65,12 +58,10 @@ 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); var nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); - for (int i = 0; i < nodeTypes.Length; i++) - { + for (int i = 0; i < nodeTypes.Length; i++) { Type type = nodeTypes[i]; //Get node context menu path @@ -80,16 +71,14 @@ namespace XNodeEditor // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; bool disallowed = false; - if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) - { + if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) { int typeCount = target.nodes.Count(x => x.GetType() == type); if (typeCount >= disallowAttrib.max) disallowed = true; } // Add node entry to context menu if (disallowed) menu.AddItem(new GUIContent(path), false, null); - else menu.AddItem(new GUIContent(path), false, () => - { + else menu.AddItem(new GUIContent(path), false, () => { XNode.Node node = CreateNode(type, pos); NodeEditorWindow.current.AutoConnect(node); }); @@ -104,13 +93,11 @@ namespace XNodeEditor /// Returned gradient is used to color noodles /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. - public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) - { + public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) { Gradient grad = new Gradient(); // If dragging the noodle, draw solid, slightly transparent - if (input == null) - { + if (input == null) { Color a = GetTypeColor(output.ValueType); grad.SetKeys( new GradientColorKey[] { new GradientColorKey(a, 0f) }, @@ -118,13 +105,11 @@ namespace XNodeEditor ); } // If normal, draw gradient fading from one input color to the other - else - { + else { Color a = GetTypeColor(output.ValueType); Color b = GetTypeColor(input.ValueType); // If any port is hovered, tint white - if (window.hoveredPort == output || window.hoveredPort == input) - { + if (window.hoveredPort == output || window.hoveredPort == input) { a = Color.Lerp(a, Color.white, 0.8f); b = Color.Lerp(b, Color.white, 0.8f); } @@ -139,48 +124,40 @@ namespace XNodeEditor /// Returned float is used for noodle thickness /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. - public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) - { + public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) { return NodeEditorPreferences.GetSettings().noodleThickness; } - public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) - { + public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) { return NodeEditorPreferences.GetSettings().noodlePath; } - public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) - { + public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) { return NodeEditorPreferences.GetSettings().noodleStroke; } /// Returned color is used to color ports - public virtual Color GetPortColor(XNode.NodePort port) - { + public virtual Color GetPortColor(XNode.NodePort port) { return GetTypeColor(port.ValueType); } /// The returned color is used to color the background of the door. /// Usually used for outer edge effect - public virtual Color GetPortBackgroundColor(XNode.NodePort port) - { + public virtual Color GetPortBackgroundColor(XNode.NodePort port) { return Color.gray; } /// Returns generated color for a type. This color is editable in preferences - public virtual Color GetTypeColor(Type type) - { + public virtual Color GetTypeColor(Type type) { return NodeEditorPreferences.GetTypeColor(type); } /// Override to display custom tooltips - public virtual string GetPortTooltip(XNode.NodePort port) - { + public virtual string GetPortTooltip(XNode.NodePort port) { Type portType = port.ValueType; string tooltip = ""; tooltip = portType.PrettyName(); - if (port.IsOutput) - { + if (port.IsOutput) { object obj = port.node.GetValue(port); tooltip += " = " + (obj != null ? obj.ToString() : "null"); } @@ -188,14 +165,12 @@ namespace XNodeEditor } /// Deal with objects dropped into the graph through DragAndDrop - public virtual void OnDropObjects(UnityEngine.Object[] objects) - { + public virtual void OnDropObjects(UnityEngine.Object[] objects) { if (GetType() != typeof(NodeGraphEditor)) Debug.Log("No OnDropObjects override defined for " + GetType()); } /// Create a node and save it in the graph asset - public virtual XNode.Node CreateNode(Type type, Vector2 position) - { + public virtual XNode.Node CreateNode(Type type, Vector2 position) { Undo.RecordObject(target, "Create Node"); XNode.Node node = target.AddNode(type); Undo.RegisterCreatedObjectUndo(node, "Create Node"); @@ -208,8 +183,7 @@ namespace XNodeEditor } /// Creates a copy of the original node in the graph - public virtual XNode.Node CopyNode(XNode.Node original) - { + public virtual XNode.Node CopyNode(XNode.Node original) { Undo.RecordObject(target, "Duplicate Node"); XNode.Node node = target.CopyNode(original); Undo.RegisterCreatedObjectUndo(node, "Duplicate Node"); @@ -220,16 +194,13 @@ namespace XNodeEditor } /// Return false for nodes that can't be removed - public virtual bool CanRemove(XNode.Node node) - { + public virtual bool CanRemove(XNode.Node node) { // Check graph attributes to see if this node is required Type graphType = target.GetType(); XNode.NodeGraph.RequireNodeAttribute[] attribs = Array.ConvertAll( graphType.GetCustomAttributes(typeof(XNode.NodeGraph.RequireNodeAttribute), true), x => x as XNode.NodeGraph.RequireNodeAttribute); - if (attribs.Any(x => x.Requires(node.GetType()))) - { - if (target.nodes.Count(x => x.GetType() == node.GetType()) <= 1) - { + if (attribs.Any(x => x.Requires(node.GetType()))) { + if (target.nodes.Count(x => x.GetType() == node.GetType()) <= 1) { return false; } } @@ -237,8 +208,7 @@ namespace XNodeEditor } /// Safely remove a node and all its connections. - public virtual void RemoveNode(XNode.Node node) - { + public virtual void RemoveNode(XNode.Node node) { if (!CanRemove(node)) return; // Remove the node @@ -254,23 +224,20 @@ namespace XNodeEditor [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; } } } -} +} \ No newline at end of file