From 85341bf4cc073708919735137eba6ef91f613d58 Mon Sep 17 00:00:00 2001 From: MYTNB <31533351+MYTNB@users.noreply.github.com> Date: Tue, 11 Aug 2020 14:31:53 +0800 Subject: [PATCH 01/42] Add Disconnect(Node) Menu And Node Header Tooltip (#288) --- Scripts/Editor/NodeEditor.cs | 5 +++++ Scripts/Editor/NodeEditorGUI.cs | 33 ++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 36c3a6e..8afd227 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -104,6 +104,11 @@ namespace XNodeEditor { return NodeEditorResources.styles.nodeHighlight; } + /// Override to display custom node header tooltips + public virtual string GetHeaderTooltip() { + return null; + } + /// Add items for the context menu when right-clicking this node. Override to add custom menu items. public virtual void AddContextMenuItems(GenericMenu menu) { bool canRemove = true; diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 99cdecf..21d6d0f 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -112,6 +112,11 @@ namespace XNodeEditor { /// Show right-click context menu for hovered port void ShowPortContextMenu(XNode.NodePort hoveredPort) { GenericMenu contextMenu = new GenericMenu(); + foreach (var port in hoveredPort.GetConnections()) { + var name = port.node.name; + var index = hoveredPort.GetConnectionIndex(port); + contextMenu.AddItem(new GUIContent($"Disconnect({name})"), false, () => hoveredPort.Disconnect(index)); + } contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections()); contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); @@ -531,8 +536,8 @@ namespace XNodeEditor { if (e.type != EventType.Layout && currentActivity == NodeActivity.DragGrid) Selection.objects = preSelection.ToArray(); EndZoomed(position, zoom, topPadding); - //If a change in is detected in the selected node, call OnValidate method. - //This is done through reflection because OnValidate is only relevant in editor, + //If a change in is detected in the selected node, call OnValidate method. + //This is done through reflection because OnValidate is only relevant in editor, //and thus, the code should not be included in build. if (onValidate != null && EditorGUI.EndChangeCheck()) onValidate.Invoke(Selection.activeObject, null); } @@ -551,16 +556,22 @@ namespace XNodeEditor { } private void DrawTooltip() { - if (hoveredPort != null && NodeEditorPreferences.GetSettings().portTooltips && graphEditor != null) { - string tooltip = graphEditor.GetPortTooltip(hoveredPort); - if (string.IsNullOrEmpty(tooltip)) return; - GUIContent content = new GUIContent(tooltip); - Vector2 size = NodeEditorResources.styles.tooltip.CalcSize(content); - size.x += 8; - Rect rect = new Rect(Event.current.mousePosition - (size), size); - EditorGUI.LabelField(rect, content, NodeEditorResources.styles.tooltip); - Repaint(); + if (!NodeEditorPreferences.GetSettings().portTooltips || graphEditor is null) + return; + string tooltip = null; + if (hoveredPort != null) { + tooltip = graphEditor.GetPortTooltip(hoveredPort); } + else if (hoveredNode != null && IsHoveringNode && IsHoveringTitle(hoveredNode)) { + tooltip = NodeEditor.GetEditor(hoveredNode, this).GetHeaderTooltip(); + } + if (string.IsNullOrEmpty(tooltip)) return; + GUIContent content = new GUIContent(tooltip); + Vector2 size = NodeEditorResources.styles.tooltip.CalcSize(content); + size.x += 8; + Rect rect = new Rect(Event.current.mousePosition - (size), size); + EditorGUI.LabelField(rect, content, NodeEditorResources.styles.tooltip); + Repaint(); } } } From 1fb94b24133284c3878ab7c91a64fea8e01e6838 Mon Sep 17 00:00:00 2001 From: MYTNB <31533351+MYTNB@users.noreply.github.com> Date: Thu, 13 Aug 2020 19:08:58 +0800 Subject: [PATCH 02/42] Fix Port Disconnect Bug (#289) --- Scripts/NodePort.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index b2f1ad1..692e63f 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -307,11 +307,7 @@ namespace XNode { // Remove the other ports connection to this port NodePort otherPort = connections[i].Port; if (otherPort != null) { - for (int k = 0; k < otherPort.connections.Count; k++) { - if (otherPort.connections[k].Port == this) { - otherPort.connections.RemoveAt(i); - } - } + otherPort.connections.RemoveAll(it => { return it.Port == this; }); } // Remove this ports connection to the other connections.RemoveAt(i); From e2a0037b4d3d74b470ef8e979135d9fb497a085a Mon Sep 17 00:00:00 2001 From: Meng Fanye <519730356@qq.com> Date: Tue, 25 Aug 2020 15:54:23 +0800 Subject: [PATCH 03/42] fix: connections will break after InsertDuplicateNodes and restarting unity editor (#290) --- Scripts/Editor/NodeEditorAction.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index b112732..9bcbdc3 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -478,6 +478,7 @@ namespace XNodeEditor { } } } + EditorUtility.SetDirty(graph); // Select the new nodes Selection.objects = newNodes; } From b6528a255c45070c35841c1abdf5d64a43c74c0b Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 26 Aug 2020 21:49:48 +0200 Subject: [PATCH 04/42] Added InheritedAny for cases where a middle class can get inputs from child class and parent class (#293) --- Scripts/Node.cs | 2 ++ Scripts/NodePort.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 6744cc5..704e99d 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -51,6 +51,8 @@ namespace XNode { Strict, /// Allow connections where output value type is assignable from input value type (eg. Object --> ScriptableObject) InheritedInverse, + /// Allow connections where output value type is assignable from input value or input value type is assignable from output value type + InheritedAny } #region Obsolete diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index 692e63f..f7d6f97 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -273,10 +273,12 @@ namespace XNode { if (input.typeConstraint == XNode.Node.TypeConstraint.Inherited && !input.ValueType.IsAssignableFrom(output.ValueType)) return false; if (input.typeConstraint == XNode.Node.TypeConstraint.Strict && input.ValueType != output.ValueType) return false; if (input.typeConstraint == XNode.Node.TypeConstraint.InheritedInverse && !output.ValueType.IsAssignableFrom(input.ValueType)) return false; + if (input.typeConstraint == XNode.Node.TypeConstraint.InheritedAny && !input.ValueType.IsAssignableFrom(output.ValueType) && !output.ValueType.IsAssignableFrom(input.ValueType)) return false; // Check output type constraints if (output.typeConstraint == XNode.Node.TypeConstraint.Inherited && !input.ValueType.IsAssignableFrom(output.ValueType)) return false; if (output.typeConstraint == XNode.Node.TypeConstraint.Strict && input.ValueType != output.ValueType) return false; if (output.typeConstraint == XNode.Node.TypeConstraint.InheritedInverse && !output.ValueType.IsAssignableFrom(input.ValueType)) return false; + if (output.typeConstraint == XNode.Node.TypeConstraint.InheritedAny && !input.ValueType.IsAssignableFrom(output.ValueType) && !output.ValueType.IsAssignableFrom(input.ValueType)) return false; // Success return true; } From 123415bd833a3723bb98d16c5b31a57d1b85557d Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 26 Aug 2020 21:50:53 +0200 Subject: [PATCH 05/42] tooltips on label fields. serializedproperty.tooltip is broken and is always empty. (#294) --- Scripts/Editor/NodeEditorGUILayout.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 3574ace..19e9ca5 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -59,6 +59,7 @@ namespace XNodeEditor { (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); float spacePadding = 0; + string tooltip = null; foreach (var attr in propertyAttributes) { if (attr is SpaceAttribute) { if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); @@ -71,6 +72,8 @@ namespace XNodeEditor { 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; } } @@ -83,13 +86,13 @@ namespace XNodeEditor { 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)); + if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName, tooltip)); // Display an editable property field if port is not connected else EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); break; case XNode.Node.ShowBackingValue.Never: // Display a label - EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName)); + EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName, tooltip)); break; case XNode.Node.ShowBackingValue.Always: // Display an editable property field @@ -115,6 +118,7 @@ namespace XNodeEditor { (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); float spacePadding = 0; + string tooltip = null; foreach (var attr in propertyAttributes) { if (attr is SpaceAttribute) { if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); @@ -127,6 +131,8 @@ namespace XNodeEditor { 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; } } @@ -139,13 +145,13 @@ namespace XNodeEditor { 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), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); + if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName, tooltip), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); // Display an editable property field if port is not connected else EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); break; case XNode.Node.ShowBackingValue.Never: // Display a label - EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); + EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName, tooltip), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); break; case XNode.Node.ShowBackingValue.Always: // Display an editable property field From b5fb59a84eb69e5ce25ce08da5872054ab2a1817 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 26 Aug 2020 21:51:45 +0200 Subject: [PATCH 06/42] Added OpenOnCreate settings. (#295) Adds the option to prevent opening the graph editor when creating a new graph. Suggested in Issues #286 --- Scripts/Editor/NodeEditorPreferences.cs | 2 ++ Scripts/Editor/NodeEditorWindow.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 72eb8aa..e5fa238 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -35,6 +35,7 @@ namespace XNodeEditor { public Color32 highlightColor = new Color32(255, 255, 255, 255); public bool gridSnap = true; public bool autoSave = true; + public bool openOnCreate = true; public bool dragToCreate = true; public bool zoomToMouse = true; public bool portTooltips = true; @@ -149,6 +150,7 @@ namespace XNodeEditor { //Label EditorGUILayout.LabelField("System", EditorStyles.boldLabel); settings.autoSave = EditorGUILayout.Toggle(new GUIContent("Autosave", "Disable for better editor performance"), settings.autoSave); + settings.openOnCreate = EditorGUILayout.Toggle(new GUIContent("Open Editor on Create", "Disable to prevent openening the editor when creating a new graph"), settings.openOnCreate); if (GUI.changed) SavePrefs(key, settings); EditorGUILayout.Space(); } diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 4f0a102..a7ec96b 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -99,7 +99,7 @@ namespace XNodeEditor { private static void OnSelectionChanged() { XNode.NodeGraph nodeGraph = Selection.activeObject as XNode.NodeGraph; if (nodeGraph && !AssetDatabase.Contains(nodeGraph)) { - Open(nodeGraph); + if (NodeEditorPreferences.GetSettings().openOnCreate) Open(nodeGraph); } } From 0303c53643c88f9e5ad9de1ce1a911e8dc9979c0 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 7 Sep 2020 09:09:32 +0200 Subject: [PATCH 07/42] Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0207ad3..e0ac093 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,10 @@ With a minimal footprint, it is ideal as a base for custom state machines, dialo * [Getting started](https://github.com/Siccity/xNode/wiki/Getting%20Started) - create your very first node node and graph * [Examples branch](https://github.com/Siccity/xNode/tree/examples) - look at other small projects -### Installing with Unity Package Manager +### Installation +
Instructions + +## Installing with Unity Package Manager ***Via Git URL*** *(Requires Unity version 2018.3.0b7 or above)* @@ -56,6 +59,13 @@ The package is available on the [openupm registry](https://openupm.com). It's re openupm add com.github.siccity.xnode ``` +## Installing with git +***Via Git Submodule*** +To add xNode as a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) in your existing git project, +run the following git command `git submodule add git@github.com:Siccity/xNode.git Assets/Submodules/xNode` + +
+ ### Node example: ```csharp // public classes deriving from Node are registered as nodes for use within a graph From ae998a4e07f4d72388a2557a84f294e55b92244d Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 7 Sep 2020 09:13:29 +0200 Subject: [PATCH 08/42] Update README.md --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e0ac093..d113695 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ With a minimal footprint, it is ideal as a base for custom state machines, dialo ### Installation
Instructions -## Installing with Unity Package Manager +### Installing with Unity Package Manager ***Via Git URL*** *(Requires Unity version 2018.3.0b7 or above)* @@ -59,10 +59,18 @@ The package is available on the [openupm registry](https://openupm.com). It's re openupm add com.github.siccity.xnode ``` -## Installing with git +### Installing with git ***Via Git Submodule*** + To add xNode as a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) in your existing git project, -run the following git command `git submodule add git@github.com:Siccity/xNode.git Assets/Submodules/xNode` +run the following git command from your project root: + +``` +git submodule add git@github.com:Siccity/xNode.git Assets/Submodules/xNode +``` + +### Installing 'the old way' +If no source control or package manager is available to you, you can simply copy/paste the source files into your assets folder.
From 22b379353016739d0d537ee3920299a00ce451f5 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 8 Sep 2020 18:43:32 +0200 Subject: [PATCH 09/42] Fixed mouse propagation in custom graph editors. Now you can use buttons and other controls. --- Scripts/Editor/NodeEditorGUI.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 21d6d0f..c0b1c5b 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -22,6 +22,7 @@ namespace XNodeEditor { Matrix4x4 m = GUI.matrix; if (graph == null) return; ValidateGraphEditor(); + if (e.isMouse) graphEditor.OnGUI(); Controls(); DrawGrid(position, zoom, panOffset); @@ -30,7 +31,7 @@ namespace XNodeEditor { DrawNodes(); DrawSelectionBox(); DrawTooltip(); - graphEditor.OnGUI(); + if (!e.isMouse) graphEditor.OnGUI(); // Run and reset onLateGUI if (onLateGUI != null) { From af34ea6c47fc6be5ff913b52b5be0ecee5b8775b Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Wed, 9 Sep 2020 11:53:51 +0200 Subject: [PATCH 10/42] Revert "Fixed mouse propagation in custom graph editors. Now you can use buttons and other controls." This reverts commit 22b379353016739d0d537ee3920299a00ce451f5. --- Scripts/Editor/NodeEditorGUI.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index c0b1c5b..21d6d0f 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -22,7 +22,6 @@ namespace XNodeEditor { Matrix4x4 m = GUI.matrix; if (graph == null) return; ValidateGraphEditor(); - if (e.isMouse) graphEditor.OnGUI(); Controls(); DrawGrid(position, zoom, panOffset); @@ -31,7 +30,7 @@ namespace XNodeEditor { DrawNodes(); DrawSelectionBox(); DrawTooltip(); - if (!e.isMouse) graphEditor.OnGUI(); + graphEditor.OnGUI(); // Run and reset onLateGUI if (onLateGUI != null) { From 2c35e5ddb3579a0d9ba220ca2233259382636163 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 24 Sep 2020 21:50:47 +0200 Subject: [PATCH 11/42] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index d113695..3eadd3e 100644 --- a/README.md +++ b/README.md @@ -110,5 +110,10 @@ public class MathNode : Node { } ``` +### Plugins +Plugins are repositories that add functionality to xNode +* [xNodeGroups](https://github.com/Siccity/xNodeGroups): adds resizable groups + +### Community Join the [Discord](https://discord.gg/qgPrHv4 "Join Discord server") server to leave feedback or get support. Feel free to also leave suggestions/requests in the [issues](https://github.com/Siccity/xNode/issues "Go to Issues") page. From 629474d0424ca9b0aa5abeea6b13938ed0782285 Mon Sep 17 00:00:00 2001 From: juliocp Date: Mon, 5 Oct 2020 16:25:08 -0300 Subject: [PATCH 12/42] 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 13/42] 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 14/42] 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 15/42] 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 16/42] 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 17/42] 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 1b32e2ec0352f27647163c1a6f390540b94380a8 Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 19:00:56 -0300 Subject: [PATCH 18/42] Added new methods at NodeEditorUtilities _ HasCompatiblePortType: Looking for ports with value Type compatible with a given type. - GetCompatibleNodesTypes: Filter only node types that contains some port value type compatible with an given type --- Scripts/Editor/NodeEditorUtilities.cs | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index ac12e33..900e2e2 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -127,6 +127,64 @@ namespace XNodeEditor { return methods.Count() > 0; } + /// + /// Looking for ports with value Type compatible with a given type. + /// + /// Node to search + /// Type to find compatiblities + /// + /// True if NodeType has some port with value type compatible + public static bool HasCompatiblePortType(Type nodeType, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) + { + Type findType = typeof(XNode.Node.InputAttribute); + if (direction == XNode.NodePort.IO.Output) + findType = typeof(XNode.Node.OutputAttribute); + + //Get All fields from node type and we go filter only field with portAttribute. + //This way is possible to know the values of the all ports and if have some with compatible value tue + foreach (FieldInfo f in XNode.NodeDataCache.GetNodeFields(nodeType)) + { + var portAttribute = f.GetCustomAttributes(findType, false).FirstOrDefault(); + if (portAttribute != null) + { + if (IsCastableTo(f.FieldType, compatibleType)) + { + return true; + } + } + } + + return false; + } + + /// + /// Filter only node types that contains some port value type compatible with an given type + /// + /// List with all nodes type to filter + /// Compatible Type to Filter + /// Return Only Node Types with ports compatible, or an empty list + public static List GetCompatibleNodesTypes(Type[] nodeTypes, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) + { + //Result List + List filteredTypes = new List(); + + //Return empty list + if (nodeTypes == null) { return filteredTypes; } + if (compatibleType == null) { return filteredTypes; } + + //Find compatiblity + foreach (Type findType in nodeTypes) + { + if (HasCompatiblePortType(findType, compatibleType, direction)) + { + filteredTypes.Add(findType); + } + } + + return filteredTypes; + } + + /// Return a prettiefied type name. public static string PrettyName(this Type type) { if (type == null) return "null"; From 2d91e45dba95cba5ede5263b5e49da78ab2cc826 Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 19:12:35 -0300 Subject: [PATCH 19/42] Added parameter to AddContexMenuItens - Added parameters to get contextMenu with compatibles node filter. These parameters are auto-filled and do not interfere with the standard operation of this function. > Param: Type compatibleType: Use it to filter only nodes with ports value type, compatible with this type: Default null. > Param: NodePor.IO: Direction of the compatiblity: (Default Input) --- Scripts/Editor/NodeGraphEditor.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 8b671e2..b7ed32d 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -64,11 +64,24 @@ namespace XNodeEditor return 0; } - /// Add items for the context menu when right-clicking this node. Override to add custom menu items. - public virtual void AddContextMenuItems(GenericMenu menu) + /// + /// Add items for the context menu when right-clicking this node. + /// Override to add custom menu items. + /// + /// + /// Use it to filter only nodes with ports value type, compatible with this type + /// Direction of the compatiblity + public virtual void AddContextMenuItems(GenericMenu menu, Type compatibleType = null, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); - var nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); + + Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); + + if (compatibleType != null) + { + nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); + } + for (int i = 0; i < nodeTypes.Length; i++) { Type type = nodeTypes[i]; @@ -101,6 +114,7 @@ namespace XNodeEditor menu.AddCustomContextMenuItems(target); } + /// 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. From 37d82ad2157ddd3122ec52d9c3e9d537f15a6c16 Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 19:31:07 -0300 Subject: [PATCH 20/42] Added DragToCreate compatible filter - Added preference to filter the nodes from the draggable context menu, to show only nodes that have ports compatible with the dragged port - Minor modification in NodeEditorActions, to allow the filter - Minor modification in NodeGraphEditor to use or not the filter, based in preferences --- Scripts/Editor/NodeEditorAction.cs | 2 +- Scripts/Editor/NodeEditorPreferences.cs | 11 +++++++++++ Scripts/Editor/NodeGraphEditor.cs | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 738a602..438299e 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -290,7 +290,7 @@ namespace XNodeEditor else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) { GenericMenu menu = new GenericMenu(); - graphEditor.AddContextMenuItems(menu); + graphEditor.AddContextMenuItems(menu, draggedOutput.ValueType); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } //Release dragged connection diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index d16dfd0..73f2552 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -37,6 +37,7 @@ namespace XNodeEditor { public bool autoSave = true; public bool openOnCreate = true; public bool dragToCreate = true; + public bool dragToCreateFilter = true; public bool zoomToMouse = true; public bool portTooltips = true; [SerializeField] private string typeColorsData = ""; @@ -166,6 +167,16 @@ namespace XNodeEditor { 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); + + //Drag to Create Filter + int oldIndent = EditorGUI.indentLevel; + EditorGUI.indentLevel = oldIndent + 1; + GUI.enabled = settings.dragToCreate; + settings.dragToCreateFilter = EditorGUILayout.Toggle(new GUIContent("Filter", "Only show nodes that are compatible with the dragged port"), settings.dragToCreateFilter); + GUI.enabled = true; + EditorGUI.indentLevel = oldIndent; + + //END if (GUI.changed) { SavePrefs(key, settings); NodeEditorWindow.RepaintAll(); diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index b7ed32d..5890309 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -77,7 +77,7 @@ namespace XNodeEditor Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); - if (compatibleType != null) + if (compatibleType != null && NodeEditorPreferences.GetSettings().dragToCreateFilter) { nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); } From a03c4c5d0e7e30225b48621d6fef9835172971e2 Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 20:57:40 -0300 Subject: [PATCH 21/42] Added filtered node at port contextMenu Added compatible menus when the right mouse button is clicked on the port and preferences filter is active --- Scripts/Editor/NodeEditorGUI.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 21d6d0f..eb6811f 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -118,6 +118,16 @@ namespace XNodeEditor { contextMenu.AddItem(new GUIContent($"Disconnect({name})"), false, () => hoveredPort.Disconnect(index)); } contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections()); + //Get compatible nodes with this port + if (NodeEditorPreferences.GetSettings().dragToCreateFilter) + { + contextMenu.AddSeparator(""); + + if (hoveredPort.direction == XNode.NodePort.IO.Input) + graphEditor.AddContextMenuItems(contextMenu, hoveredPort.ValueType, XNode.NodePort.IO.Output); + else + graphEditor.AddContextMenuItems(contextMenu, hoveredPort.ValueType, XNode.NodePort.IO.Input); + } contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } From b0f20cafa4e7c539d3cbf363c2d8bcb097904c3a Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 21:08:31 -0300 Subject: [PATCH 22/42] renamed dragToCreate to createFilter - Renamed the preferences dragToCreateFilter to createFilter - Removed dragToCreate as a condition to use createFilter --- Scripts/Editor/NodeEditorGUI.cs | 2 +- Scripts/Editor/NodeEditorPreferences.cs | 14 ++++---------- Scripts/Editor/NodeGraphEditor.cs | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index eb6811f..73097b4 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -119,7 +119,7 @@ namespace XNodeEditor { } contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections()); //Get compatible nodes with this port - if (NodeEditorPreferences.GetSettings().dragToCreateFilter) + if (NodeEditorPreferences.GetSettings().createFilter) { contextMenu.AddSeparator(""); diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 73f2552..ef42e7a 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -37,7 +37,7 @@ namespace XNodeEditor { public bool autoSave = true; public bool openOnCreate = true; public bool dragToCreate = true; - public bool dragToCreateFilter = true; + public bool createFilter = true; public bool zoomToMouse = true; public bool portTooltips = true; [SerializeField] private string typeColorsData = ""; @@ -166,15 +166,9 @@ namespace XNodeEditor { 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); - - //Drag to Create Filter - int oldIndent = EditorGUI.indentLevel; - EditorGUI.indentLevel = oldIndent + 1; - GUI.enabled = settings.dragToCreate; - settings.dragToCreateFilter = EditorGUILayout.Toggle(new GUIContent("Filter", "Only show nodes that are compatible with the dragged port"), settings.dragToCreateFilter); - GUI.enabled = true; - EditorGUI.indentLevel = oldIndent; + 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); + settings.createFilter = EditorGUILayout.Toggle(new GUIContent("Create Filter", "Only show nodes that are compatible with the selected port"), settings.createFilter); + //END if (GUI.changed) { diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 5890309..dd9203c 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -77,7 +77,7 @@ namespace XNodeEditor Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); - if (compatibleType != null && NodeEditorPreferences.GetSettings().dragToCreateFilter) + if (compatibleType != null && NodeEditorPreferences.GetSettings().createFilter) { nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); } From cde52ff21aae0b1822a992d3aa13350f838b4992 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 12 Oct 2020 09:40:40 +0200 Subject: [PATCH 23/42] 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 From fdd9b24eca32a945ef43c6e8901f869581b5990b Mon Sep 17 00:00:00 2001 From: juliocp Date: Mon, 12 Oct 2020 21:17:53 -0300 Subject: [PATCH 24/42] Added Virtual portStyle - Added the GetPortStyle(...) as virtual method at GraphEditor. With this, users can customize the texture for different ports, and they can also modify the padding without having to overwrite the default xNode style. This prevents many problems when working with multiple graphics. The style properties used from Style is: - padding-left. - padding-right. - normal.background, to border texture. - active.background, to dot texture. --- Scripts/Editor/NodeEditorAction.cs | 336 ++++++++++++++++++-------- Scripts/Editor/NodeEditorGUI.cs | 6 +- Scripts/Editor/NodeEditorGUILayout.cs | 303 +++++++++++++++-------- Scripts/Editor/NodeEditorResources.cs | 4 + Scripts/Editor/NodeGraphEditor.cs | 111 ++++++--- 5 files changed, 524 insertions(+), 236 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index e78aaec..820aae3 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; } @@ -43,14 +45,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); } @@ -66,52 +71,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; } @@ -120,22 +141,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); } @@ -143,9 +170,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; } @@ -153,17 +183,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); @@ -174,25 +210,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; } @@ -204,9 +248,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; } @@ -214,24 +260,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)); @@ -241,13 +292,18 @@ namespace XNodeEditor { draggedOutputTarget = null; EditorUtility.SetDirty(graph); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); - } else if (currentActivity == NodeActivity.DragNode) { + } + else if (currentActivity == NodeActivity.DragNode) + { IEnumerable nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node); foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); - } else if (!IsHoveringNode) { + } + else if (!IsHoveringNode) + { // If click outside node, release field focus - if (!isPanning) { + if (!isPanning) + { EditorGUI.FocusTextInControl(null); EditorGUIUtility.editingTextField = false; } @@ -255,44 +311,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); @@ -307,18 +380,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); } } @@ -327,19 +409,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(); } @@ -347,7 +438,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; } @@ -355,45 +447,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); } @@ -401,29 +505,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; @@ -432,15 +544,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 @@ -449,14 +564,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; } @@ -468,16 +585,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); @@ -493,8 +614,10 @@ 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); @@ -504,7 +627,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); @@ -512,24 +636,27 @@ namespace XNodeEditor { DrawNoodle(gradient, path, stroke, thickness, gridPoints); + GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(draggedOutput); Color bgcol = Color.black; Color frcol = gradient.colorKeys[0].color; bgcol.a = 0.6f; 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); rect = GridToWindowRect(rect); - NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol); + NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol, portStyle.normal.background, portStyle.active.background); } } } - bool IsHoveringTitle(XNode.Node node) { + bool IsHoveringTitle(XNode.Node node) + { Vector2 mousePos = Event.current.mousePosition; //Get node position Vector2 nodePos = GridToWindowPosition(node.position); @@ -542,7 +669,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 diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index d914eff..379dc5e 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -334,6 +334,8 @@ namespace XNodeEditor { if (!_portConnectionPoints.TryGetValue(output, out fromRect)) continue; Color portColor = graphEditor.GetPortColor(output); + GUIStyle portStyle = graphEditor.GetPortStyle(output); + for (int k = 0; k < output.ConnectionCount; k++) { XNode.NodePort input = output.GetConnection(k); @@ -367,11 +369,11 @@ namespace XNodeEditor { // Draw selected reroute points with an outline if (selectedReroutes.Contains(rerouteRef)) { GUI.color = NodeEditorPreferences.GetSettings().highlightColor; - GUI.DrawTexture(rect, NodeEditorResources.dotOuter); + GUI.DrawTexture(rect, portStyle.normal.background); } GUI.color = portColor; - GUI.DrawTexture(rect, NodeEditorResources.dot); + GUI.DrawTexture(rect, portStyle.active.background); if (rect.Overlaps(selectionBox)) selection.Add(rerouteRef); if (rect.Contains(mousePos)) hoveredReroute = rerouteRef; diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 1b74a32..9a6cc1b 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) { + } + 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,15 +120,18 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + float paddingLeft = NodeEditorWindow.current.graphEditor.GetPortStyle(port).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; } @@ -120,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) { + } + 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)); @@ -161,7 +193,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.width += NodeEditorResources.styles.outputPort.padding.right; + rect.width += NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.right; rect.position = rect.position + new Vector2(rect.width, spacePadding); } @@ -169,7 +201,8 @@ namespace XNodeEditor { Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); - DrawPortHandle(rect, backgroundColor, col); + GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(port); + DrawPortHandle(rect, backgroundColor, col, portStyle.normal.background, portStyle.active.background); // Register the handle position Vector2 portPos = rect.center; @@ -177,54 +210,62 @@ 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(); - float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + float paddingLeft = NodeEditorWindow.current.graphEditor.GetPortStyle(port).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; + rect.width += NodeEditorWindow.current.graphEditor.GetPortStyle(port).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)); Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); - DrawPortHandle(rect, backgroundColor, col); + GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(port); + + DrawPortHandle(rect, backgroundColor, col, portStyle.normal.background, portStyle.active.background); // Register the handle position Vector2 portPos = rect.center; @@ -232,19 +273,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(); - float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + float paddingLeft = NodeEditorWindow.current.graphEditor.GetPortStyle(port).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.width += NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.right; rect.position = rect.position + new Vector2(rect.width, 0); } @@ -252,7 +297,9 @@ namespace XNodeEditor { Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); - DrawPortHandle(rect, backgroundColor, col); + GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(port); + + DrawPortHandle(rect, backgroundColor, col, portStyle.normal.background, portStyle.active.background); // Register the handle position Vector2 portPos = rect.center; @@ -260,40 +307,55 @@ 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) { + /// + /// Draw the port + /// + /// position and size + /// color for background texture of the port. Normaly used to Border + /// + /// texture for border of the dot port + /// texture for the dot port + public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor, Texture2D border, Texture2D dot) + { Color col = GUI.color; GUI.color = backgroundColor; - GUI.DrawTexture(rect, NodeEditorResources.dotOuter); + GUI.DrawTexture(rect, border); GUI.color = typeColor; - GUI.DrawTexture(rect, NodeEditorResources.dot); + GUI.DrawTexture(rect, dot); 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; } @@ -306,18 +368,22 @@ 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(); @@ -325,11 +391,13 @@ 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); @@ -340,53 +408,67 @@ 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); @@ -399,8 +481,10 @@ 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); @@ -417,7 +501,8 @@ namespace XNodeEditor { serializedObject.Update(); // Move array data if there is any - if (hasArrayData) { + if (hasArrayData) + { arrayData.MoveArrayElement(reorderableListIndex, rl.index); } @@ -428,7 +513,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; @@ -438,39 +524,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); @@ -482,16 +580,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."); @@ -501,9 +603,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; @@ -513,7 +617,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 9a5cc1e..26a79ce 100644 --- a/Scripts/Editor/NodeEditorResources.cs +++ b/Scripts/Editor/NodeEditorResources.cs @@ -27,10 +27,14 @@ namespace XNodeEditor { inputPort = new GUIStyle(baseStyle); inputPort.alignment = TextAnchor.UpperLeft; inputPort.padding.left = 0; + inputPort.active.background = dot; + inputPort.normal.background = dotOuter; outputPort = new GUIStyle(baseStyle); outputPort.alignment = TextAnchor.UpperRight; outputPort.padding.right = 0; + outputPort.active.background = dot; + outputPort.normal.background = dotOuter; nodeHeader = new GUIStyle(); nodeHeader.alignment = TextAnchor.MiddleCenter; diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index dcc92a9..1d248f5 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? @@ -24,21 +26,25 @@ 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 @@ -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 @@ -71,14 +78,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 +102,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 +116,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,40 +137,66 @@ 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 Style is used to configure the paddings and icon texture of the ports. + /// Use these properties to customize your port style. + /// + /// The properties used is: + /// [Left and Right], [Background] = border texture, + /// and [Background] = dot texture; + /// + /// the owner of the style + /// + public virtual GUIStyle GetPortStyle(XNode.NodePort port) + { + if (port.direction == XNode.NodePort.IO.Input) + return NodeEditorResources.styles.inputPort; + + return NodeEditorResources.styles.outputPort; + } + /// 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"); } @@ -165,12 +204,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"); @@ -183,7 +224,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"); @@ -194,13 +236,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; } } @@ -208,7 +253,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 @@ -224,18 +270,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 6aacfbfd2d12c0d322a32f10b78c7b87a131688c Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Sat, 14 Nov 2020 16:25:16 +0100 Subject: [PATCH 25/42] SceneGraph draw default inspector --- Scripts/Editor/SceneGraphEditor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/Editor/SceneGraphEditor.cs b/Scripts/Editor/SceneGraphEditor.cs index 9fb1c67..dd290a8 100644 --- a/Scripts/Editor/SceneGraphEditor.cs +++ b/Scripts/Editor/SceneGraphEditor.cs @@ -53,6 +53,7 @@ namespace XNodeEditor { GUI.color = Color.white; } } + DrawDefaultInspector(); } private void OnEnable() { From d4deeed45b2898721d80b0f70be9b60f56d61ec0 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 17 Nov 2020 11:06:35 +0100 Subject: [PATCH 26/42] Added support in preferences for default node tint --- Scripts/Editor/NodeEditor.cs | 4 +--- Scripts/Editor/NodeEditorPreferences.cs | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 8afd227..ddfd626 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -14,8 +14,6 @@ namespace XNodeEditor { [CustomNodeEditor(typeof(XNode.Node))] public class NodeEditor : XNodeEditor.Internal.NodeEditorBase { - private readonly Color DEFAULTCOLOR = new Color32(90, 97, 105, 255); - /// Fires every whenever a node was modified through the editor public static Action onUpdateNode; public readonly static Dictionary portPositions = new Dictionary(); @@ -93,7 +91,7 @@ namespace XNodeEditor { Color color; if (type.TryGetAttributeTint(out color)) return color; // Return default color (grey) - else return DEFAULTCOLOR; + else return NodeEditorPreferences.GetSettings().tintColor; } public virtual GUIStyle GetBodyStyle() { diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 6165072..ace03b9 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -32,6 +32,7 @@ namespace XNodeEditor { [UnityEngine.Serialization.FormerlySerializedAs("zoomOutLimit")] public float maxZoom = 5f; public float minZoom = 1f; + public Color32 tintColor = new Color32(90, 97, 105, 255); public Color32 highlightColor = new Color32(255, 255, 255, 255); public bool gridSnap = true; public bool autoSave = true; @@ -160,6 +161,7 @@ namespace XNodeEditor { private static void NodeSettingsGUI(string key, Settings settings) { //Label EditorGUILayout.LabelField("Node", EditorStyles.boldLabel); + settings.tintColor = EditorGUILayout.ColorField("Tint", settings.tintColor); 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); From 75a04920b1a5fd33a15a77f158c9f405d21791f7 Mon Sep 17 00:00:00 2001 From: Kailey Joanette Date: Tue, 15 Dec 2020 19:13:44 -0500 Subject: [PATCH 27/42] Fixed an issue with Undo / Redo not working correctly with Odin PropertyTree --- Scripts/Editor/NodeEditor.cs | 24 +++++++++++++++++++++--- Scripts/Editor/NodeEditorBase.cs | 12 +++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index ddfd626..a562fdf 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -39,10 +39,28 @@ namespace XNodeEditor { string[] excludes = { "m_Script", "graph", "position", "ports" }; #if ODIN_INSPECTOR - InspectorUtilities.BeginDrawPropertyTree(objectTree, true); - GUIHelper.PushLabelWidth(84); - objectTree.Draw(true); + try + { +#if ODIN_INSPECTOR_3 + objectTree.BeginDraw( true ); +#else + InspectorUtilities.BeginDrawPropertyTree(objectTree, true); +#endif + } + catch ( ArgumentNullException ) + { + objectTree.EndDraw(); + NodeEditor.DestroyEditor(this.target); + return; + } + + GUIHelper.PushLabelWidth( 84 ); + objectTree.Draw( true ); +#if ODIN_INSPECTOR_3 + objectTree.EndDraw(); +#else InspectorUtilities.EndDrawPropertyTree(objectTree); +#endif GUIHelper.PopLabelWidth(); #else diff --git a/Scripts/Editor/NodeEditorBase.cs b/Scripts/Editor/NodeEditorBase.cs index 1fc28c7..e556a10 100644 --- a/Scripts/Editor/NodeEditorBase.cs +++ b/Scripts/Editor/NodeEditorBase.cs @@ -24,7 +24,7 @@ namespace XNodeEditor.Internal { private PropertyTree _objectTree; public PropertyTree objectTree { get { - if (this._objectTree == null) { + if (this._objectTree == null){ try { bool wasInEditor = NodeEditor.inNodeEditor; NodeEditor.inNodeEditor = true; @@ -58,6 +58,16 @@ namespace XNodeEditor.Internal { return editor; } + public static void DestroyEditor( K target ) + { + if ( target == null ) return; + T editor; + if ( editors.TryGetValue( target, out editor ) ) + { + editors.Remove( target ); + } + } + private static Type GetEditorType(Type type) { if (type == null) return null; if (editorTypes == null) CacheCustomEditors(); From 822b7ae156485bc25a8baebca26fe1039aab221d Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 17 Dec 2020 13:43:39 +0100 Subject: [PATCH 28/42] Formatting --- Scripts/Editor/NodeEditorAction.cs | 333 ++++++++---------------- Scripts/Editor/NodeEditorGUI.cs | 6 +- Scripts/Editor/NodeEditorGUILayout.cs | 257 ++++++------------ Scripts/Editor/NodeEditorPreferences.cs | 5 +- Scripts/Editor/NodeEditorUtilities.cs | 21 +- Scripts/Editor/NodeGraphEditor.cs | 108 +++----- 6 files changed, 233 insertions(+), 497 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 85b0417..a2b95b7 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; } @@ -45,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); } @@ -71,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; } @@ -141,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); } @@ -170,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; } @@ -183,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); @@ -210,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; } @@ -248,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; } @@ -260,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, draggedOutput.ValueType); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); @@ -292,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; } @@ -311,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); @@ -380,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); } } @@ -409,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(); } @@ -438,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; } @@ -447,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); } @@ -505,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; @@ -544,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 @@ -564,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; } @@ -585,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); @@ -614,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); @@ -627,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); @@ -643,8 +519,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); @@ -655,8 +530,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); @@ -669,8 +543,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 diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 78c9614..ca726d5 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -119,8 +119,7 @@ namespace XNodeEditor { } contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections()); //Get compatible nodes with this port - if (NodeEditorPreferences.GetSettings().createFilter) - { + if (NodeEditorPreferences.GetSettings().createFilter) { contextMenu.AddSeparator(""); if (hoveredPort.direction == XNode.NodePort.IO.Input) @@ -573,8 +572,7 @@ namespace XNodeEditor { string tooltip = null; if (hoveredPort != null) { tooltip = graphEditor.GetPortTooltip(hoveredPort); - } - else if (hoveredNode != null && IsHoveringNode && IsHoveringTitle(hoveredNode)) { + } else if (hoveredNode != null && IsHoveringNode && IsHoveringTitle(hoveredNode)) { tooltip = NodeEditor.GetEditor(hoveredNode, this).GetHeaderTooltip(); } if (string.IsNullOrEmpty(tooltip)) return; diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 9a6cc1b..8c93cb2 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) - { + 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 = NodeEditorWindow.current.graphEditor.GetPortStyle(port).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)); @@ -210,30 +178,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); @@ -242,8 +206,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); @@ -255,8 +218,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)); @@ -273,21 +235,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 = NodeEditorWindow.current.graphEditor.GetPortStyle(port).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 += NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.right; rect.position = rect.position + new Vector2(rect.width, 0); @@ -307,8 +265,7 @@ 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)); @@ -323,8 +280,7 @@ namespace XNodeEditor /// /// texture for border of the dot port /// texture for the dot port - public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor, Texture2D border, Texture2D dot) - { + public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor, Texture2D border, Texture2D dot) { Color col = GUI.color; GUI.color = backgroundColor; GUI.DrawTexture(rect, border); @@ -336,26 +292,22 @@ namespace XNodeEditor #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 /// 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; } @@ -368,18 +320,14 @@ 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 }; } } @@ -391,13 +339,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); @@ -408,67 +354,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); @@ -481,10 +413,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); @@ -501,8 +431,7 @@ namespace XNodeEditor serializedObject.Update(); // Move array data if there is any - if (hasArrayData) - { + if (hasArrayData) { arrayData.MoveArrayElement(reorderableListIndex, rl.index); } @@ -513,8 +442,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; @@ -524,24 +452,19 @@ 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 }; } } @@ -551,24 +474,17 @@ namespace XNodeEditor 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); @@ -580,20 +496,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."); @@ -603,11 +515,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; @@ -617,8 +527,7 @@ 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/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 715ac6a..5113904 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -104,7 +104,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; } @@ -166,9 +166,8 @@ namespace XNodeEditor { 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); + 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); settings.createFilter = EditorGUILayout.Toggle(new GUIContent("Create Filter", "Only show nodes that are compatible with the selected port"), settings.createFilter); - //END if (GUI.changed) { diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index 900e2e2..753973b 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -134,21 +134,17 @@ namespace XNodeEditor { /// Type to find compatiblities /// /// True if NodeType has some port with value type compatible - public static bool HasCompatiblePortType(Type nodeType, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) - { + public static bool HasCompatiblePortType(Type nodeType, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { Type findType = typeof(XNode.Node.InputAttribute); if (direction == XNode.NodePort.IO.Output) findType = typeof(XNode.Node.OutputAttribute); //Get All fields from node type and we go filter only field with portAttribute. //This way is possible to know the values of the all ports and if have some with compatible value tue - foreach (FieldInfo f in XNode.NodeDataCache.GetNodeFields(nodeType)) - { + foreach (FieldInfo f in XNode.NodeDataCache.GetNodeFields(nodeType)) { var portAttribute = f.GetCustomAttributes(findType, false).FirstOrDefault(); - if (portAttribute != null) - { - if (IsCastableTo(f.FieldType, compatibleType)) - { + if (portAttribute != null) { + if (IsCastableTo(f.FieldType, compatibleType)) { return true; } } @@ -163,8 +159,7 @@ namespace XNodeEditor { /// List with all nodes type to filter /// Compatible Type to Filter /// Return Only Node Types with ports compatible, or an empty list - public static List GetCompatibleNodesTypes(Type[] nodeTypes, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) - { + public static List GetCompatibleNodesTypes(Type[] nodeTypes, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { //Result List List filteredTypes = new List(); @@ -173,10 +168,8 @@ namespace XNodeEditor { if (compatibleType == null) { return filteredTypes; } //Find compatiblity - foreach (Type findType in nodeTypes) - { - if (HasCompatiblePortType(findType, compatibleType, direction)) - { + foreach (Type findType in nodeTypes) { + if (HasCompatiblePortType(findType, compatibleType, direction)) { filteredTypes.Add(findType); } } diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 580e3a8..9b80df0 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,25 +58,22 @@ namespace XNodeEditor } /// - /// Add items for the context menu when right-clicking this node. + /// Add items for the context menu when right-clicking this node. /// Override to add custom menu items. /// /// /// Use it to filter only nodes with ports value type, compatible with this type /// Direction of the compatiblity - public virtual void AddContextMenuItems(GenericMenu menu, Type compatibleType = null, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) - { + public virtual void AddContextMenuItems(GenericMenu menu, Type compatibleType = null, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); - if (compatibleType != null && NodeEditorPreferences.GetSettings().createFilter) - { + if (compatibleType != null && NodeEditorPreferences.GetSettings().createFilter) { nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); } - for (int i = 0; i < nodeTypes.Length; i++) - { + for (int i = 0; i < nodeTypes.Length; i++) { Type type = nodeTypes[i]; @@ -94,16 +84,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); }); @@ -115,17 +103,14 @@ namespace XNodeEditor menu.AddCustomContextMenuItems(target); } - /// 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) }, @@ -133,13 +118,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); } @@ -154,24 +137,20 @@ 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); } @@ -185,8 +164,7 @@ namespace XNodeEditor /// /// the owner of the style /// - public virtual GUIStyle GetPortStyle(XNode.NodePort port) - { + public virtual GUIStyle GetPortStyle(XNode.NodePort port) { if (port.direction == XNode.NodePort.IO.Input) return NodeEditorResources.styles.inputPort; @@ -195,25 +173,21 @@ namespace XNodeEditor /// 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"); } @@ -221,14 +195,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"); @@ -241,8 +213,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"); @@ -253,16 +224,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; } } @@ -270,8 +238,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 @@ -287,21 +254,18 @@ 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 d58845ee154df5ae9236b1b9057312e05cae9ffd Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 26 Jan 2021 09:14:56 +0100 Subject: [PATCH 29/42] Fixed #299 - Typing in TextArea in custom node editor triggers Hotkey --- Scripts/Editor/NodeEditorAction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index a2b95b7..21c0612 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -305,7 +305,7 @@ namespace XNodeEditor { isDoubleClick = false; break; case EventType.KeyDown: - if (EditorGUIUtility.editingTextField) break; + if (EditorGUIUtility.editingTextField || GUIUtility.keyboardControl != 0) break; else if (e.keyCode == KeyCode.F) Home(); if (NodeEditorUtilities.IsMac()) { if (e.keyCode == KeyCode.Return) RenameSelectedNode(); From 75ee5d47fff5f9cca7af1e514c13e44a97272ec0 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Fri, 5 Feb 2021 13:33:33 +0100 Subject: [PATCH 30/42] Minor bugfix --- Scripts/NodePort.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index f7d6f97..9fa465e 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -301,7 +301,7 @@ namespace XNode { } // Trigger OnRemoveConnection node.OnRemoveConnection(this); - if (port != null) port.node.OnRemoveConnection(port); + if (port != null && port.IsConnectedTo(this)) port.node.OnRemoveConnection(port); } /// Disconnect this port from another port From 0f84990968746ca638fbf2400dd92c0f6fac7e58 Mon Sep 17 00:00:00 2001 From: ZLX Date: Mon, 15 Feb 2021 17:02:48 +0800 Subject: [PATCH 31/42] [FIX] Copy & Paste in node's TextField --- Scripts/Editor/NodeEditorAction.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 21c0612..b7fb3b4 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -337,11 +337,15 @@ namespace XNodeEditor { if (e.type == EventType.ExecuteCommand) DuplicateSelectedNodes(); e.Use(); } else if (e.commandName == "Copy") { - if (e.type == EventType.ExecuteCommand) CopySelectedNodes(); - e.Use(); + if (!EditorGUIUtility.editingTextField) { + if (e.type == EventType.ExecuteCommand) CopySelectedNodes(); + e.Use(); + } } else if (e.commandName == "Paste") { - if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition)); - e.Use(); + if (!EditorGUIUtility.editingTextField) { + if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition)); + e.Use(); + } } Repaint(); break; From ea2e190d98e8feaba143e1a890a86f193d3463bc Mon Sep 17 00:00:00 2001 From: GolfNorth Date: Sat, 20 Feb 2021 08:38:49 +0500 Subject: [PATCH 32/42] Fix issue for older Odin version --- Scripts/Editor/NodeEditor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index a562fdf..efe0072 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -49,7 +49,11 @@ namespace XNodeEditor { } catch ( ArgumentNullException ) { +#if ODIN_INSPECTOR_3 objectTree.EndDraw(); +#else + InspectorUtilities.EndDrawPropertyTree(objectTree); +#endif NodeEditor.DestroyEditor(this.target); return; } From 2f69c4350def2043f87cf94b2db9fb9388b516c1 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Sat, 1 May 2021 18:47:39 +0200 Subject: [PATCH 33/42] Added support for unitys FormerlySerializedAsAttribute. It reconnects ports that has the old name to its new name. --- Scripts/NodeDataCache.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index ba52e1b..e05d033 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -14,6 +14,7 @@ namespace XNode { if (!Initialized) BuildCache(); Dictionary staticPorts = new Dictionary(); + Dictionary formerlySerializedAs = new Dictionary(); Dictionary> removedPorts = new Dictionary>(); System.Type nodeType = node.GetType(); @@ -24,6 +25,11 @@ namespace XNode { for (int i = 0; i < typePortCache.Count; i++) { staticPorts.Add(typePortCache[i].fieldName, portDataCache[nodeType][i]); } + for (int i = 0; i < typePortCache.Count; i++) { + var fieldInfo = nodeType.GetField(typePortCache[i].fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var attribute = fieldInfo.GetCustomAttributes(true).FirstOrDefault(x => x is UnityEngine.Serialization.FormerlySerializedAsAttribute) as UnityEngine.Serialization.FormerlySerializedAsAttribute; + if (attribute != null) formerlySerializedAs.Add(attribute.oldName, typePortCache[i].fieldName); + } } // Cleanup port dict - Remove nonexisting static ports - update static port types @@ -43,6 +49,11 @@ namespace XNode { } // If port doesn't exist anymore, remove it else if (port.IsStatic) { + //See if the field is tagged with FormerlySerializedAs, if so add the port with its new field name to removedPorts + // so it can be reconnected in missing ports stage. + string newName = null; + if (formerlySerializedAs.TryGetValue(port.fieldName, out newName)) removedPorts.Add(newName, port.GetConnections()); + port.ClearConnections(); ports.Remove(port.fieldName); } From 3287575dcd3d03152c24454b0d962bd1ee33159e Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 4 May 2021 08:29:16 +0200 Subject: [PATCH 34/42] moved reflection code to cache part in initialization --- Scripts/NodeDataCache.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index e05d033..7061f17 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -7,6 +7,7 @@ namespace XNode { /// Precaches reflection data in editor so we won't have to do it runtime public static class NodeDataCache { private static PortDataCache portDataCache; + private static Dictionary> formerlySerializedAsCache; private static bool Initialized { get { return portDataCache != null; } } /// Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. @@ -14,10 +15,12 @@ namespace XNode { if (!Initialized) BuildCache(); Dictionary staticPorts = new Dictionary(); - Dictionary formerlySerializedAs = new Dictionary(); Dictionary> removedPorts = new Dictionary>(); System.Type nodeType = node.GetType(); + Dictionary formerlySerializedAs = null; + if (formerlySerializedAsCache != null) formerlySerializedAsCache.TryGetValue(nodeType, out formerlySerializedAs); + List dynamicListPorts = new List(); List typePortCache; @@ -25,11 +28,6 @@ namespace XNode { for (int i = 0; i < typePortCache.Count; i++) { staticPorts.Add(typePortCache[i].fieldName, portDataCache[nodeType][i]); } - for (int i = 0; i < typePortCache.Count; i++) { - var fieldInfo = nodeType.GetField(typePortCache[i].fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var attribute = fieldInfo.GetCustomAttributes(true).FirstOrDefault(x => x is UnityEngine.Serialization.FormerlySerializedAsAttribute) as UnityEngine.Serialization.FormerlySerializedAsAttribute; - if (attribute != null) formerlySerializedAs.Add(attribute.oldName, typePortCache[i].fieldName); - } } // Cleanup port dict - Remove nonexisting static ports - update static port types @@ -52,7 +50,7 @@ namespace XNode { //See if the field is tagged with FormerlySerializedAs, if so add the port with its new field name to removedPorts // so it can be reconnected in missing ports stage. string newName = null; - if (formerlySerializedAs.TryGetValue(port.fieldName, out newName)) removedPorts.Add(newName, port.GetConnections()); + if (formerlySerializedAs != null && formerlySerializedAs.TryGetValue(port.fieldName, out newName)) removedPorts.Add(newName, port.GetConnections()); port.ClearConnections(); ports.Remove(port.fieldName); @@ -188,6 +186,7 @@ namespace XNode { object[] attribs = fieldInfo[i].GetCustomAttributes(true); Node.InputAttribute inputAttrib = attribs.FirstOrDefault(x => x is Node.InputAttribute) as Node.InputAttribute; Node.OutputAttribute outputAttrib = attribs.FirstOrDefault(x => x is Node.OutputAttribute) as Node.OutputAttribute; + UnityEngine.Serialization.FormerlySerializedAsAttribute formerlySerializedAsAttribute = attribs.FirstOrDefault(x => x is UnityEngine.Serialization.FormerlySerializedAsAttribute) as UnityEngine.Serialization.FormerlySerializedAsAttribute; if (inputAttrib == null && outputAttrib == null) continue; @@ -196,6 +195,12 @@ namespace XNode { if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List()); portDataCache[nodeType].Add(new NodePort(fieldInfo[i])); } + + if(formerlySerializedAsAttribute != null) { + if (formerlySerializedAsCache == null) formerlySerializedAsCache = new Dictionary>(); + if (!formerlySerializedAsCache.ContainsKey(nodeType)) formerlySerializedAsCache.Add(nodeType, new Dictionary()); + formerlySerializedAsCache[nodeType].Add(formerlySerializedAsAttribute.oldName, fieldInfo[i].Name); + } } } From e1b564db1c85f611f871f15f8935ec62c1226b55 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 4 May 2021 08:38:25 +0200 Subject: [PATCH 35/42] added error handling --- Scripts/NodeDataCache.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index 7061f17..f865ab2 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -199,7 +199,9 @@ namespace XNode { if(formerlySerializedAsAttribute != null) { if (formerlySerializedAsCache == null) formerlySerializedAsCache = new Dictionary>(); if (!formerlySerializedAsCache.ContainsKey(nodeType)) formerlySerializedAsCache.Add(nodeType, new Dictionary()); - formerlySerializedAsCache[nodeType].Add(formerlySerializedAsAttribute.oldName, fieldInfo[i].Name); + + if (formerlySerializedAsCache[nodeType].ContainsKey(formerlySerializedAsAttribute.oldName)) Debug.LogError("Another FormerlySerializedAs with value '" + formerlySerializedAsAttribute.oldName + "' already exist on this node."); + else formerlySerializedAsCache[nodeType].Add(formerlySerializedAsAttribute.oldName, fieldInfo[i].Name); } } } From a3f1394078dc3ef15e5904815703c84d0053881a Mon Sep 17 00:00:00 2001 From: ZLX Date: Fri, 11 Jun 2021 13:35:52 +0800 Subject: [PATCH 36/42] Compatible with C# 4.0 language feature. --- Scripts/Editor/NodeEditorGUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index ca726d5..70c4cd1 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -115,7 +115,7 @@ namespace XNodeEditor { foreach (var port in hoveredPort.GetConnections()) { var name = port.node.name; var index = hoveredPort.GetConnectionIndex(port); - contextMenu.AddItem(new GUIContent($"Disconnect({name})"), false, () => hoveredPort.Disconnect(index)); + contextMenu.AddItem(new GUIContent(string.Format("Disconnect({0})", name)), false, () => hoveredPort.Disconnect(index)); } contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections()); //Get compatible nodes with this port From e17e9a8ba1921f00cf27597b736715e46a1eee2d Mon Sep 17 00:00:00 2001 From: ZLX Date: Fri, 11 Jun 2021 13:41:34 +0800 Subject: [PATCH 37/42] Fix node order in AddContextMenuItems with filter --- Scripts/Editor/NodeGraphEditor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 9b80df0..b6198ca 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; @@ -67,14 +66,15 @@ namespace XNodeEditor { public virtual void AddContextMenuItems(GenericMenu menu, Type compatibleType = null, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); - Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); + Type[] nodeTypes; if (compatibleType != null && NodeEditorPreferences.GetSettings().createFilter) { - nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); + nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).OrderBy(GetNodeMenuOrder).ToArray(); + } else { + nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(GetNodeMenuOrder).ToArray(); } for (int i = 0; i < nodeTypes.Length; i++) { - Type type = nodeTypes[i]; //Get node context menu path From 3666509002eb9107ec3982c777ab4b07f116cd0e Mon Sep 17 00:00:00 2001 From: Kailey Joanette Date: Sat, 25 Dec 2021 13:07:43 -0500 Subject: [PATCH 38/42] Advanced Generic menu that can be searched --- Scripts/Editor/AdvancedGenericMenu.cs | 206 +++++++++++++++++++++ Scripts/Editor/AdvancedGenericMenu.cs.meta | 11 ++ Scripts/Editor/NodeEditor.cs | 3 + Scripts/Editor/NodeEditorAction.cs | 3 + Scripts/Editor/NodeEditorGUI.cs | 3 + Scripts/Editor/NodeEditorReflection.cs | 3 + Scripts/Editor/NodeGraphEditor.cs | 3 + 7 files changed, 232 insertions(+) create mode 100644 Scripts/Editor/AdvancedGenericMenu.cs create mode 100644 Scripts/Editor/AdvancedGenericMenu.cs.meta diff --git a/Scripts/Editor/AdvancedGenericMenu.cs b/Scripts/Editor/AdvancedGenericMenu.cs new file mode 100644 index 0000000..549b028 --- /dev/null +++ b/Scripts/Editor/AdvancedGenericMenu.cs @@ -0,0 +1,206 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor.IMGUI.Controls; +using UnityEngine; +using static UnityEditor.GenericMenu; + +namespace XNodeEditor +{ + public class AdvancedGenericMenu : AdvancedDropdown + { + private class AdvancedGenericMenuItem : AdvancedDropdownItem + { + private MenuFunction func; + + private MenuFunction2 func2; + private object userData; + + public AdvancedGenericMenuItem( string name ) : base( name ) + { + } + + public AdvancedGenericMenuItem( string name, bool enabled, Texture2D icon, MenuFunction func ) : base( name ) + { + Set( enabled, icon, func ); + } + + public AdvancedGenericMenuItem( string name, bool enabled, Texture2D icon, MenuFunction2 func, object userData ) : base( name ) + { + Set( enabled, icon, func, userData ); + } + + public void Set( bool enabled, Texture2D icon, MenuFunction func ) + { + this.enabled = enabled; + this.icon = icon; + this.func = func; + } + + public void Set( bool enabled, Texture2D icon, MenuFunction2 func, object userData ) + { + this.enabled = enabled; + this.icon = icon; + this.func2 = func; + this.userData = userData; + } + + public void Run() + { + if ( func2 != null ) + func2( userData ); + else if ( func != null ) + func(); + } + } + + private List items = new List(); + + private AdvancedGenericMenuItem FindOrCreateItem( string name, AdvancedGenericMenuItem currentRoot = null ) + { + if ( string.IsNullOrWhiteSpace( name ) ) + return null; + + AdvancedGenericMenuItem item = null; + + string[] paths = name.Split( '/' ); + if ( currentRoot == null ) + { + item = items.FirstOrDefault( x => x != null && x.name == paths[0] ); + if ( item == null ) + items.Add( item = new AdvancedGenericMenuItem( name ) ); + } + else + { + item = currentRoot.children.OfType().FirstOrDefault( x => x.name == paths[0] ); + if ( item == null ) + currentRoot.AddChild( item = new AdvancedGenericMenuItem( name ) ); + } + + if ( paths.Length > 1 ) + return FindOrCreateItem( string.Join( "/", paths, 1, paths.Length - 1 ), item ); + + return item; + } + + private AdvancedGenericMenuItem FindParent( string name ) + { + string[] paths = name.Split( '/' ); + return FindOrCreateItem( string.Join( "/", paths, 0, paths.Length - 1 ) ); + } + + private string Name { get; set; } + + public AdvancedGenericMenu() : base( new AdvancedDropdownState() ) + { + Name = ""; + } + + public AdvancedGenericMenu( string name, AdvancedDropdownState state ) : base( state ) + { + Name = name; + } + + // + // Summary: + // Add a disabled item to the menu. + // + // Parameters: + // content: + // The GUIContent to display as a disabled menu item. + public void AddDisabledItem( GUIContent content ) + { + //var parent = FindParent( content.text ); + var item = FindOrCreateItem( content.text ); + item.Set( false, null, null ); + } + + // + // Summary: + // Add a disabled item to the menu. + // + // Parameters: + // content: + // The GUIContent to display as a disabled menu item. + // + // on: + // Specifies whether to show that the item is currently activated (i.e. a tick next + // to the item in the menu). + public void AddDisabledItem( GUIContent content, bool on ) + { + } + + public void AddItem( string name, bool on, MenuFunction func ) + { + AddItem( new GUIContent( name ), on, func ); + } + + public void AddItem( GUIContent content, bool on, MenuFunction func ) + { + //var parent = FindParent( content.text ); + var item = FindOrCreateItem( content.text ); + item.Set( true/*on*/, null, func ); + } + + public void AddItem( string name, bool on, MenuFunction2 func, object userData ) + { + AddItem( new GUIContent( name ), on, func, userData ); + } + + public void AddItem( GUIContent content, bool on, MenuFunction2 func, object userData ) + { + //var parent = FindParent( content.text ); + var item = FindOrCreateItem( content.text ); + item.Set( true/*on*/, null, func, userData ); + } + + // + // Summary: + // Add a seperator item to the menu. + // + // Parameters: + // path: + // The path to the submenu, if adding a separator to a submenu. When adding a separator + // to the top level of a menu, use an empty string as the path. + public void AddSeparator( string path = null ) + { + var parent = string.IsNullOrWhiteSpace( path ) ? null : FindParent( path ); + if ( parent == null ) + items.Add( null ); + else + parent.AddSeparator(); + } + + // + // Summary: + // Show the menu at the given screen rect. + // + // Parameters: + // position: + // The position at which to show the menu. + public void DropDown( Rect position ) + { + Show( position ); + } + + protected override AdvancedDropdownItem BuildRoot() + { + var root = new AdvancedDropdownItem( Name ); + + foreach ( var m in items ) + { + if ( m == null ) + root.AddSeparator(); + else + root.AddChild( m ); + } + + return root; + } + + protected override void ItemSelected( AdvancedDropdownItem item ) + { + if ( item is AdvancedGenericMenuItem gmItem ) + gmItem.Run(); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/AdvancedGenericMenu.cs.meta b/Scripts/Editor/AdvancedGenericMenu.cs.meta new file mode 100644 index 0000000..2c5c440 --- /dev/null +++ b/Scripts/Editor/AdvancedGenericMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ddde711109af02e42bfe8eb006577081 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index efe0072..0f92e61 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -8,6 +8,9 @@ using Sirenix.OdinInspector.Editor; using Sirenix.Utilities; using Sirenix.Utilities.Editor; #endif +#if USE_ADVANCED_GENERIC_MENU +using GenericMenu = XNodeEditor.AdvancedGenericMenu; +#endif namespace XNodeEditor { /// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index b7fb3b4..106590c 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -4,6 +4,9 @@ using System.Linq; using UnityEditor; using UnityEngine; using XNodeEditor.Internal; +#if USE_ADVANCED_GENERIC_MENU +using GenericMenu = XNodeEditor.AdvancedGenericMenu; +#endif namespace XNodeEditor { public partial class NodeEditorWindow { diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 70c4cd1..630ad4a 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -4,6 +4,9 @@ using System.Linq; using UnityEditor; using UnityEngine; using XNodeEditor.Internal; +#if USE_ADVANCED_GENERIC_MENU +using GenericMenu = XNodeEditor.AdvancedGenericMenu; +#endif namespace XNodeEditor { /// Contains GUI methods diff --git a/Scripts/Editor/NodeEditorReflection.cs b/Scripts/Editor/NodeEditorReflection.cs index 0a0a36a..2069302 100644 --- a/Scripts/Editor/NodeEditorReflection.cs +++ b/Scripts/Editor/NodeEditorReflection.cs @@ -5,6 +5,9 @@ using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; +#if USE_ADVANCED_GENERIC_MENU +using GenericMenu = XNodeEditor.AdvancedGenericMenu; +#endif namespace XNodeEditor { /// Contains reflection-related extensions built for xNode diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index b6198ca..80b1c1f 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -2,6 +2,9 @@ using System.Linq; using UnityEditor; using UnityEngine; +#if USE_ADVANCED_GENERIC_MENU +using GenericMenu = XNodeEditor.AdvancedGenericMenu; +#endif namespace XNodeEditor { /// Base class to derive custom Node Graph editors from. Use this to override how graphs are drawn in the editor. From 840eb7818d8f3fbf457523bcb8f5214783cc2918 Mon Sep 17 00:00:00 2001 From: Kailey Joanette Date: Sun, 26 Dec 2021 15:07:00 -0500 Subject: [PATCH 39/42] Fixed menu item naming Added min/maxwidth options --- Scripts/Editor/AdvancedGenericMenu.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/AdvancedGenericMenu.cs b/Scripts/Editor/AdvancedGenericMenu.cs index 549b028..943c52f 100644 --- a/Scripts/Editor/AdvancedGenericMenu.cs +++ b/Scripts/Editor/AdvancedGenericMenu.cs @@ -8,6 +8,9 @@ namespace XNodeEditor { public class AdvancedGenericMenu : AdvancedDropdown { + public static float? DefaultMinWidth = 200f; + public static float? DefaultMaxWidth = 300f; + private class AdvancedGenericMenuItem : AdvancedDropdownItem { private MenuFunction func; @@ -67,13 +70,13 @@ namespace XNodeEditor { item = items.FirstOrDefault( x => x != null && x.name == paths[0] ); if ( item == null ) - items.Add( item = new AdvancedGenericMenuItem( name ) ); + items.Add( item = new AdvancedGenericMenuItem( paths[0] ) ); } else { item = currentRoot.children.OfType().FirstOrDefault( x => x.name == paths[0] ); if ( item == null ) - currentRoot.AddChild( item = new AdvancedGenericMenuItem( name ) ); + currentRoot.AddChild( item = new AdvancedGenericMenuItem( paths[0] ) ); } if ( paths.Length > 1 ) @@ -179,6 +182,8 @@ namespace XNodeEditor // The position at which to show the menu. public void DropDown( Rect position ) { + position.width = Mathf.Clamp( position.width, DefaultMinWidth.HasValue ? DefaultMinWidth.Value : 1f, DefaultMaxWidth.HasValue ? DefaultMaxWidth.Value : Screen.width ); + Show( position ); } From f1cbe6191d8028ebfea549d3ca7043dbb90c7451 Mon Sep 17 00:00:00 2001 From: Kailey Joanette Date: Sun, 26 Dec 2021 15:12:46 -0500 Subject: [PATCH 40/42] Limit AdvancedGenericMenu to UNITY_2019_1_OR_NEWER --- Scripts/Editor/AdvancedGenericMenu.cs | 6 ++++-- Scripts/Editor/NodeEditor.cs | 2 +- Scripts/Editor/NodeEditorAction.cs | 2 +- Scripts/Editor/NodeEditorGUI.cs | 2 +- Scripts/Editor/NodeEditorReflection.cs | 2 +- Scripts/Editor/NodeGraphEditor.cs | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Scripts/Editor/AdvancedGenericMenu.cs b/Scripts/Editor/AdvancedGenericMenu.cs index 943c52f..a22fa83 100644 --- a/Scripts/Editor/AdvancedGenericMenu.cs +++ b/Scripts/Editor/AdvancedGenericMenu.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +#if UNITY_2019_1_OR_NEWER +using System.Collections.Generic; using System.Linq; using UnityEditor.IMGUI.Controls; using UnityEngine; @@ -208,4 +209,5 @@ namespace XNodeEditor gmItem.Run(); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 0f92e61..8522fc0 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -8,7 +8,7 @@ using Sirenix.OdinInspector.Editor; using Sirenix.Utilities; using Sirenix.Utilities.Editor; #endif -#if USE_ADVANCED_GENERIC_MENU +#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU using GenericMenu = XNodeEditor.AdvancedGenericMenu; #endif diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 106590c..5d8d32b 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -4,7 +4,7 @@ using System.Linq; using UnityEditor; using UnityEngine; using XNodeEditor.Internal; -#if USE_ADVANCED_GENERIC_MENU +#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU using GenericMenu = XNodeEditor.AdvancedGenericMenu; #endif diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 630ad4a..35b2e2a 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -4,7 +4,7 @@ using System.Linq; using UnityEditor; using UnityEngine; using XNodeEditor.Internal; -#if USE_ADVANCED_GENERIC_MENU +#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU using GenericMenu = XNodeEditor.AdvancedGenericMenu; #endif diff --git a/Scripts/Editor/NodeEditorReflection.cs b/Scripts/Editor/NodeEditorReflection.cs index 2069302..d401139 100644 --- a/Scripts/Editor/NodeEditorReflection.cs +++ b/Scripts/Editor/NodeEditorReflection.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; -#if USE_ADVANCED_GENERIC_MENU +#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU using GenericMenu = XNodeEditor.AdvancedGenericMenu; #endif diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 80b1c1f..a304c2c 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -2,7 +2,7 @@ using System.Linq; using UnityEditor; using UnityEngine; -#if USE_ADVANCED_GENERIC_MENU +#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU using GenericMenu = XNodeEditor.AdvancedGenericMenu; #endif From 46076527f850c10b7074558787299e015e7abc6b Mon Sep 17 00:00:00 2001 From: RomanZanevski <92728725+RomanZanevski@users.noreply.github.com> Date: Mon, 25 Apr 2022 16:24:08 +0200 Subject: [PATCH 41/42] Fix OnRemoveConnection from calling side If we use the original: if (port != null && port.IsConnectedTo(this)) port.node.OnRemoveConnection(port); we going to have always wrong condition, about "IsConnectedTo(this)" and port.node.OnRemoveConnection(port) just won't be called, cause we already have disconnected ports. So we need to do it exactly at the moment of disconnection. --- Scripts/NodePort.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index 9fa465e..6bcc638 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -296,12 +296,13 @@ namespace XNode { for (int i = 0; i < port.connections.Count; i++) { if (port.connections[i].Port == this) { port.connections.RemoveAt(i); + // Trigger OnRemoveConnection from this side port + port.node.OnRemoveConnection(port); } } } // Trigger OnRemoveConnection node.OnRemoveConnection(this); - if (port != null && port.IsConnectedTo(this)) port.node.OnRemoveConnection(port); } /// Disconnect this port from another port @@ -413,4 +414,4 @@ namespace XNode { } } } -} \ No newline at end of file +} From 36a1e8891b91770464e20609b9fe06c76e3379ac Mon Sep 17 00:00:00 2001 From: David Pilles Date: Fri, 8 Jul 2022 14:15:20 -0700 Subject: [PATCH 42/42] Fix DynamicPortList reordering operating on old data in NodeEditorGUILayout --- Scripts/Editor/NodeEditorGUILayout.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 8c93cb2..a0e3358 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -394,6 +394,7 @@ namespace XNodeEditor { }; list.onReorderCallback = (ReorderableList rl) => { + serializedObject.Update(); bool hasRect = false; bool hasNewRect = false; Rect rect = Rect.zero;