From 23e369d38fbcf4ce688753c2e89a6694cbd5e75a Mon Sep 17 00:00:00 2001 From: Lumos Date: Sun, 22 Dec 2019 15:43:25 +0100 Subject: [PATCH 01/34] Fix auto-connection menu opening on invalid connections where a target node is present --- Scripts/Editor/NodeEditorAction.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 2581676..9fcc42c 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -58,10 +58,9 @@ namespace XNodeEditor { case EventType.MouseDrag: if (e.button == 0) { if (IsDraggingPort) { - if (IsHoveringPort && hoveredPort.IsInput && draggedOutput.CanConnectTo(hoveredPort)) { - if (!draggedOutput.IsConnectedTo(hoveredPort)) { - draggedOutputTarget = hoveredPort; - } + // 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)) { + draggedOutputTarget = hoveredPort; } else { draggedOutputTarget = null; } @@ -205,8 +204,8 @@ namespace XNodeEditor { if (e.button == 0) { //Port drag release if (IsDraggingPort) { - //If connection is valid, save it - if (draggedOutputTarget != null) { + // If connection is valid, save it + if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) { XNode.Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget); @@ -218,8 +217,8 @@ namespace XNodeEditor { EditorUtility.SetDirty(graph); } } - // Open context menu for auto-connection - else if (NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) { + // Open context menu for auto-connection if there is no target node + 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)); From e98bd85531201803209cbcf1fa009738d1101ea7 Mon Sep 17 00:00:00 2001 From: Gahwon Lee Date: Tue, 24 Dec 2019 10:39:36 -0600 Subject: [PATCH 02/34] better odin support in xNode editors --- Scripts/Editor/GraphAndNodeEditor.cs | 35 ++++++++++++++++++++++++---- Scripts/Editor/NodeEditor.cs | 2 +- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/Scripts/Editor/GraphAndNodeEditor.cs b/Scripts/Editor/GraphAndNodeEditor.cs index bfc809a..c13f782 100644 --- a/Scripts/Editor/GraphAndNodeEditor.cs +++ b/Scripts/Editor/GraphAndNodeEditor.cs @@ -1,13 +1,24 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using UnityEditor; +using UnityEditor; using UnityEngine; +#if ODIN_INSPECTOR +using Sirenix.OdinInspector.Editor; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +#endif namespace XNodeEditor { /// Override graph inspector to show an 'Open Graph' button at the top [CustomEditor(typeof(XNode.NodeGraph), true)] +#if ODIN_INSPECTOR + public class GlobalGraphEditor : OdinEditor { + public override void OnInspectorGUI() { + if (GUILayout.Button("Edit graph", GUILayout.Height(40))) { + NodeEditorWindow.Open(serializedObject.targetObject as XNode.NodeGraph); + } + base.OnInspectorGUI(); + } + } +#else public class GlobalGraphEditor : Editor { public override void OnInspectorGUI() { serializedObject.Update(); @@ -24,8 +35,21 @@ namespace XNodeEditor { serializedObject.ApplyModifiedProperties(); } } +#endif [CustomEditor(typeof(XNode.Node), true)] +#if ODIN_INSPECTOR + public class GlobalNodeEditor : OdinEditor { + public override void OnInspectorGUI() { + if (GUILayout.Button("Edit graph", GUILayout.Height(40))) { + SerializedProperty graphProp = serializedObject.FindProperty("graph"); + NodeEditorWindow w = NodeEditorWindow.Open(graphProp.objectReferenceValue as XNode.NodeGraph); + w.Home(); // Focus selected node + } + base.OnInspectorGUI(); + } + } +#else public class GlobalNodeEditor : Editor { public override void OnInspectorGUI() { serializedObject.Update(); @@ -45,4 +69,5 @@ namespace XNodeEditor { serializedObject.ApplyModifiedProperties(); } } +#endif } \ No newline at end of file diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index eae80cd..b237a21 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -21,7 +21,7 @@ namespace XNodeEditor { public readonly static Dictionary portPositions = new Dictionary(); #if ODIN_INSPECTOR - internal static bool inNodeEditor = false; + protected internal static bool inNodeEditor = false; #endif public virtual void OnHeaderGUI() { From b5260923417bb9fffe7f84c82fb4c5731226cfb8 Mon Sep 17 00:00:00 2001 From: Lumos Date: Thu, 2 Jan 2020 19:48:36 +0100 Subject: [PATCH 03/34] Added a virtual OnRename function to base nodes, triggered when a node gets renamed. (#225) --- Scripts/Editor/NodeEditor.cs | 7 ++++++- Scripts/Editor/RenamePopup.cs | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index b237a21..edf66d6 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -129,9 +129,14 @@ namespace XNodeEditor { public void Rename(string newName) { if (newName == null || newName.Trim() == "") newName = NodeEditorUtilities.NodeDefaultName(target.GetType()); target.name = newName; + OnRename(); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); } - + + /// Called after this node's name has changed. + public virtual void OnRename() { } + + [AttributeUsage(AttributeTargets.Class)] public class CustomNodeEditorAttribute : Attribute, XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib { diff --git a/Scripts/Editor/RenamePopup.cs b/Scripts/Editor/RenamePopup.cs index 564374e..f245d4e 100644 --- a/Scripts/Editor/RenamePopup.cs +++ b/Scripts/Editor/RenamePopup.cs @@ -49,6 +49,7 @@ namespace XNodeEditor { if (input == null || input.Trim() == "") { if (GUILayout.Button("Revert to default") || (e.isKey && e.keyCode == KeyCode.Return)) { target.name = NodeEditorUtilities.NodeDefaultName(target.GetType()); + NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename(); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); Close(); target.TriggerOnValidate(); @@ -58,6 +59,7 @@ namespace XNodeEditor { else { if (GUILayout.Button("Apply") || (e.isKey && e.keyCode == KeyCode.Return)) { target.name = input; + NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename(); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); Close(); target.TriggerOnValidate(); From ac7403b8766b53152e47294d114c594d8cf6acc8 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 6 Jan 2020 09:12:58 +0100 Subject: [PATCH 04/34] Removed Allowmultiple from Output and Input attributes --- Scripts/Node.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/Node.cs b/Scripts/Node.cs index a07679a..28e389d 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -262,7 +262,7 @@ namespace XNode { #region Attributes /// Mark a serializable field as an input port. You can access this through - [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Field)] public class InputAttribute : Attribute { public ShowBackingValue backingValue; public ConnectionType connectionType; @@ -285,7 +285,7 @@ namespace XNode { } /// Mark a serializable field as an output port. You can access this through - [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Field)] public class OutputAttribute : Attribute { public ShowBackingValue backingValue; public ConnectionType connectionType; From 7e93ffe4b75a6cab453223d20c5e03f4d7c57f7f Mon Sep 17 00:00:00 2001 From: Lumos Date: Tue, 21 Jan 2020 07:01:57 +0000 Subject: [PATCH 05/34] Fix for the dynamic port "serialisation desync" problem. (#223) * Exclude strings from array types manually to prevent them from being treated as char arrays. --- Scripts/Editor/NodeEditorAction.cs | 4 +-- Scripts/Editor/NodeEditorGUILayout.cs | 7 +++-- Scripts/Node.cs | 6 ++-- Scripts/NodeDataCache.cs | 43 ++++++++++++++++++++++++++- Scripts/NodePort.cs | 15 ++++++++-- 5 files changed, 64 insertions(+), 11 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 9fcc42c..8021a16 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -456,8 +456,8 @@ namespace XNodeEditor { XNode.Node newNodeIn, newNodeOut; if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) { - newNodeIn.UpdateStaticPorts(); - newNodeOut.UpdateStaticPorts(); + newNodeIn.UpdatePorts(); + newNodeOut.UpdatePorts(); inputPort = newNodeIn.GetInputPort(inputPort.fieldName); outputPort = newNodeOut.GetOutputPort(outputPort.fieldName); } diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index f9333db..0b8a0cd 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -312,6 +312,8 @@ namespace XNodeEditor { }).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)) { @@ -326,6 +328,7 @@ 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) { @@ -337,7 +340,7 @@ namespace XNodeEditor { list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { XNode.NodePort port = node.GetPort(fieldName + " " + index); - if (hasArrayData) { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { if (arrayData.arraySize <= index) { EditorGUI.LabelField(rect, "Array[" + index + "] data out of range"); return; @@ -465,7 +468,7 @@ namespace XNodeEditor { EditorUtility.SetDirty(node); } - if (hasArrayData) { + 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]); diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 28e389d..4103d67 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -119,12 +119,12 @@ namespace XNode { protected void OnEnable() { if (graphHotfix != null) graph = graphHotfix; graphHotfix = null; - UpdateStaticPorts(); + UpdatePorts(); Init(); } - /// Update static ports to reflect class fields. This happens automatically on enable. - public void UpdateStaticPorts() { + /// Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. This happens automatically on enable or on redrawing a dynamic port list. + public void UpdatePorts() { NodeDataCache.UpdatePorts(this, ports); } diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index 02e35a1..24458ec 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -9,7 +9,7 @@ namespace XNode { private static PortDataCache portDataCache; private static bool Initialized { get { return portDataCache != null; } } - /// Update static ports to reflect class fields. + /// Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. public static void UpdatePorts(Node node, Dictionary ports) { if (!Initialized) BuildCache(); @@ -17,6 +17,8 @@ namespace XNode { Dictionary> removedPorts = new Dictionary>(); System.Type nodeType = node.GetType(); + List dynamicListPorts = new List(); + List typePortCache; if (portDataCache.TryGetValue(nodeType, out typePortCache)) { for (int i = 0; i < typePortCache.Count; i++) { @@ -25,6 +27,7 @@ namespace XNode { } // Cleanup port dict - Remove nonexisting static ports - update static port types + // AND update dynamic ports (albeit only those in lists) too, in order to enforce proper serialisation. // Loop through current node ports foreach (NodePort port in ports.Values.ToList()) { // If port still exists, check it it has been changed @@ -43,6 +46,10 @@ namespace XNode { port.ClearConnections(); ports.Remove(port.fieldName); } + // If the port is dynamic and is managed by a dynamic port list, flag it for reference updates + else if (IsDynamicListPort(port)) { + dynamicListPorts.Add(port); + } } // Add missing ports foreach (NodePort staticPort in staticPorts.Values) { @@ -60,8 +67,42 @@ namespace XNode { ports.Add(staticPort.fieldName, port); } } + + // Finally, make sure dynamic list port settings correspond to the settings of their "backing port" + foreach (NodePort listPort in dynamicListPorts) { + // At this point we know that ports here are dynamic list ports + // which have passed name/"backing port" checks, ergo we can proceed more safely. + string backingPortName = listPort.fieldName.Split(' ')[0]; + NodePort backingPort = staticPorts[backingPortName]; + + // Update port constraints. Creating a new port instead will break the editor, mandating the need for setters. + listPort.ValueType = backingPort.ValueType; + listPort.direction = backingPort.direction; + listPort.connectionType = backingPort.connectionType; + listPort.typeConstraint = backingPort.typeConstraint; + } } + /// Returns true if the given port is in a dynamic port list. + private static bool IsDynamicListPort(NodePort port) { + // Ports flagged as "dynamicPortList = true" end up having a "backing port" and a name with an index, but we have + // no guarantee that a dynamic port called "output 0" is an element in a list backed by a static "output" port. + // Thus, we need to check for attributes... (but at least we don't need to look at all fields this time) + string[] fieldNameParts = port.fieldName.Split(' '); + if (fieldNameParts.Length != 2) return false; + + FieldInfo backingPortInfo = port.node.GetType().GetField(fieldNameParts[0]); + if (backingPortInfo == null) return false; + + object[] attribs = backingPortInfo.GetCustomAttributes(true); + return attribs.Any(x => { + Node.InputAttribute inputAttribute = x as Node.InputAttribute; + Node.OutputAttribute outputAttribute = x as Node.OutputAttribute; + return inputAttribute != null && inputAttribute.dynamicPortList || + outputAttribute != null && outputAttribute.dynamicPortList; + }); + } + /// Cache node types private static void BuildCache() { portDataCache = new PortDataCache(); diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index 58a3bd6..b2f1ad1 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -19,9 +19,18 @@ namespace XNode { } } - public IO direction { get { return _direction; } } - public Node.ConnectionType connectionType { get { return _connectionType; } } - public Node.TypeConstraint typeConstraint { get { return _typeConstraint; } } + public IO direction { + get { return _direction; } + internal set { _direction = value; } + } + public Node.ConnectionType connectionType { + get { return _connectionType; } + internal set { _connectionType = value; } + } + public Node.TypeConstraint typeConstraint { + get { return _typeConstraint; } + internal set { _typeConstraint = value; } + } /// Is this port connected to anytihng? public bool IsConnected { get { return connections.Count != 0; } } From cf8c70203b0624c7ef402bc71d5f6a392894b3cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rey=20M=C3=A9ndez?= Date: Sun, 2 Feb 2020 14:36:21 -0300 Subject: [PATCH 06/34] Fix stack overflow when using obsolete method Node.AddInstanceInput (#231) --- Scripts/Node.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 4103d67..beb5f77 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -65,7 +65,7 @@ namespace XNode { [Obsolete("Use AddDynamicInput instead")] public NodePort AddInstanceInput(Type type, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) { - return AddInstanceInput(type, connectionType, typeConstraint, fieldName); + return AddDynamicInput(type, connectionType, typeConstraint, fieldName); } [Obsolete("Use AddDynamicOutput instead")] From f9200ae6a8ef31dc9c3244e8ae0b90d63c16bb64 Mon Sep 17 00:00:00 2001 From: Lumos Date: Thu, 13 Feb 2020 21:48:59 +0000 Subject: [PATCH 07/34] Change the serialised type of dynamic port lists defined as lists/arrays to match the respective element type. (#232) This is logical, given that xNode already draws such dynamic ports as belonging to the element type. --- Scripts/NodeDataCache.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index 24458ec..71931b1 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -76,13 +76,28 @@ namespace XNode { NodePort backingPort = staticPorts[backingPortName]; // Update port constraints. Creating a new port instead will break the editor, mandating the need for setters. - listPort.ValueType = backingPort.ValueType; + listPort.ValueType = GetBackingValueType(backingPort.ValueType); listPort.direction = backingPort.direction; listPort.connectionType = backingPort.connectionType; listPort.typeConstraint = backingPort.typeConstraint; } } + /// + /// Extracts the underlying types from arrays and lists, the only collections for dynamic port lists + /// currently supported. If the given type is not applicable (i.e. if the dynamic list port was not + /// defined as an array or a list), returns the given type itself. + /// + private static System.Type GetBackingValueType(System.Type portValType) { + if (portValType.HasElementType) { + return portValType.GetElementType(); + } + if (portValType.IsGenericType && portValType.GetGenericTypeDefinition() == typeof(List<>)) { + return portValType.GetGenericArguments()[0]; + } + return portValType; + } + /// Returns true if the given port is in a dynamic port list. private static bool IsDynamicListPort(NodePort port) { // Ports flagged as "dynamicPortList = true" end up having a "backing port" and a name with an index, but we have From c9e1ef1c922000b8798b2198f8fa318c44adca07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6khan=20Kurt?= Date: Fri, 14 Feb 2020 01:38:17 +0300 Subject: [PATCH 08/34] Skip Microsoft assemblies while building node cache (#234) --- Scripts/NodeDataCache.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index 71931b1..ad55f7b 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -137,6 +137,7 @@ namespace XNode { case "UnityEngine": case "System": case "mscorlib": + case "Microsoft": continue; default: nodeTypes.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray()); From 1ef389689307a761d0b22434cb408a65344df85c Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 25 Feb 2020 09:13:14 +0100 Subject: [PATCH 09/34] Potential fix for the main asset/rename problem in newer versions of Unity --- Scripts/Editor/RenamePopup.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/RenamePopup.cs b/Scripts/Editor/RenamePopup.cs index f245d4e..5429270 100644 --- a/Scripts/Editor/RenamePopup.cs +++ b/Scripts/Editor/RenamePopup.cs @@ -50,9 +50,10 @@ namespace XNodeEditor { if (GUILayout.Button("Revert to default") || (e.isKey && e.keyCode == KeyCode.Return)) { target.name = NodeEditorUtilities.NodeDefaultName(target.GetType()); NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename(); + AssetDatabase.SetMainObject((target as XNode.Node).graph, AssetDatabase.GetAssetPath(target)); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); Close(); - target.TriggerOnValidate(); + target.TriggerOnValidate(); } } // Rename asset to input text @@ -60,9 +61,10 @@ namespace XNodeEditor { if (GUILayout.Button("Apply") || (e.isKey && e.keyCode == KeyCode.Return)) { target.name = input; NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename(); + AssetDatabase.SetMainObject((target as XNode.Node).graph, AssetDatabase.GetAssetPath(target)); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); Close(); - target.TriggerOnValidate(); + target.TriggerOnValidate(); } } } From bbfc44b04fdb4c4926ee36764654ad661ef3f412 Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Fri, 28 Feb 2020 22:52:30 +0100 Subject: [PATCH 10/34] Added fix for duplicate node fields found * Added fix for duplicate node fields found while iterating through base fields; on 2019.3 using .Net 20 getting the fields of the node type will return all base type fields as well. This results in the iteration up through the base classes and adding their instance fields resulting in duplicates which causes errors on node creation when assigning port information. --- Scripts/NodeDataCache.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index ad55f7b..ba52e1b 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -156,7 +156,14 @@ namespace XNode { // GetFields doesnt return inherited private fields, so walk through base types and pick those up System.Type tempType = nodeType; while ((tempType = tempType.BaseType) != typeof(XNode.Node)) { - fieldInfo.AddRange(tempType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)); + FieldInfo[] parentFields = tempType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + for (int i = 0; i < parentFields.Length; i++) { + // Ensure that we do not already have a member with this type and name + FieldInfo parentField = parentFields[i]; + if (fieldInfo.TrueForAll(x => x.Name != parentField.Name)) { + fieldInfo.Add(parentField); + } + } } return fieldInfo; } From 11b7f4ac4444220d7075f9a07e387477f31d0bc7 Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Sat, 7 Mar 2020 21:30:46 +0100 Subject: [PATCH 11/34] Added NRE check for ValidateGraphEditor * Added NRE check to prevent exception when NodeGraphEditor.GetEditor returns null and an existing non-null graph editor is already present. --- Scripts/Editor/NodeEditorWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 1f64653..4fc1136 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -97,7 +97,7 @@ namespace XNodeEditor { /// Make sure the graph editor is assigned and to the right object private void ValidateGraphEditor() { NodeGraphEditor graphEditor = NodeGraphEditor.GetEditor(graph, this); - if (this.graphEditor != graphEditor) { + if (this.graphEditor != graphEditor && graphEditor != null) { this.graphEditor = graphEditor; graphEditor.OnOpen(); } From 9add4e11b29fba2086094f61c9d84c6694436727 Mon Sep 17 00:00:00 2001 From: Jeff Campbell <1663648+jeffcampbellmakesgames@users.noreply.github.com> Date: Sun, 8 Mar 2020 12:56:57 +0100 Subject: [PATCH 12/34] Added fix for rename bug with v2 AssetDatabase (#239) * Added fix for NodeGraph rename bug with v2 AssetDatabase where renaming a graph asset can sometimes result in it being swapped as the main asset with one of the node sub assets. --- .../Editor/GraphRenameFixAssetProcessor.cs | 35 +++++++++++++++++++ .../GraphRenameFixAssetProcessor.cs.meta | 11 ++++++ 2 files changed, 46 insertions(+) create mode 100644 Scripts/Editor/GraphRenameFixAssetProcessor.cs create mode 100644 Scripts/Editor/GraphRenameFixAssetProcessor.cs.meta diff --git a/Scripts/Editor/GraphRenameFixAssetProcessor.cs b/Scripts/Editor/GraphRenameFixAssetProcessor.cs new file mode 100644 index 0000000..264e8b1 --- /dev/null +++ b/Scripts/Editor/GraphRenameFixAssetProcessor.cs @@ -0,0 +1,35 @@ +using UnityEditor; +using XNode; + +namespace XNodeEditor { + /// + /// This asset processor resolves an issue with the new v2 AssetDatabase system present on 2019.3 and later. When + /// renaming a asset, it appears that sometimes the v2 AssetDatabase will swap which asset + /// is the main asset (present at top level) between the and one of its + /// sub-assets. As a workaround until Unity fixes this, this asset processor checks all renamed assets and if it + /// finds a case where a has been made the main asset it will swap it back to being a sub-asset + /// and rename the node to the default name for that node type. + /// + internal sealed class GraphRenameFixAssetProcessor : AssetPostprocessor { + private static void OnPostprocessAllAssets( + string[] importedAssets, + string[] deletedAssets, + string[] movedAssets, + string[] movedFromAssetPaths) { + for (int i = 0; i < movedAssets.Length; i++) { + Node nodeAsset = AssetDatabase.LoadMainAssetAtPath(movedAssets[i]) as Node; + + // If the renamed asset is a node graph, but the v2 AssetDatabase has swapped a sub-asset node to be its + // main asset, reset the node graph to be the main asset and rename the node asset back to its default + // name. + if (nodeAsset != null && AssetDatabase.IsMainAsset(nodeAsset)) { + AssetDatabase.SetMainObject(nodeAsset.graph, movedAssets[i]); + AssetDatabase.ImportAsset(movedAssets[i]); + + nodeAsset.name = NodeEditorUtilities.NodeDefaultName(nodeAsset.GetType()); + EditorUtility.SetDirty(nodeAsset); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/GraphRenameFixAssetProcessor.cs.meta b/Scripts/Editor/GraphRenameFixAssetProcessor.cs.meta new file mode 100644 index 0000000..77e87ee --- /dev/null +++ b/Scripts/Editor/GraphRenameFixAssetProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 65da1ff1c50a9984a9c95fd18799e8dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 871427249bde0a04008d308691c88da134fbc661 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Sun, 8 Mar 2020 14:07:31 +0100 Subject: [PATCH 13/34] Added .editorconfig --- .editorconfig | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..82fb8f5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*.cs] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = false +trim_trailing_whitespace = true From 6a69cdaf9c63890572fd35659d29953771d5b9c2 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Fri, 3 Apr 2020 11:09:57 +0200 Subject: [PATCH 14/34] New component: SceneGraph, lets you create a graph inside the scene which can reference scene objects. --- Examples.meta | 8 +++ Scripts/Editor/SceneGraphEditor.cs | 76 +++++++++++++++++++++++++ Scripts/Editor/SceneGraphEditor.cs.meta | 11 ++++ Scripts/SceneGraph.cs | 23 ++++++++ Scripts/SceneGraph.cs.meta | 11 ++++ 5 files changed, 129 insertions(+) create mode 100644 Examples.meta create mode 100644 Scripts/Editor/SceneGraphEditor.cs create mode 100644 Scripts/Editor/SceneGraphEditor.cs.meta create mode 100644 Scripts/SceneGraph.cs create mode 100644 Scripts/SceneGraph.cs.meta diff --git a/Examples.meta b/Examples.meta new file mode 100644 index 0000000..231b3e2 --- /dev/null +++ b/Examples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3cfe6eabeed0aa44e8d9d54b308a461f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/SceneGraphEditor.cs b/Scripts/Editor/SceneGraphEditor.cs new file mode 100644 index 0000000..911f66c --- /dev/null +++ b/Scripts/Editor/SceneGraphEditor.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using XNode; + +namespace XNodeEditor { + [CustomEditor(typeof(SceneGraph), true)] + public class SceneGraphEditor : Editor { + private SceneGraph sceneGraph; + private bool removeSafely; + private Type graphType; + + public override void OnInspectorGUI() { + if (sceneGraph.graph == null) { + if (GUILayout.Button("New graph", GUILayout.Height(40))) { + if (graphType == null) { + Type[] graphTypes = NodeEditorReflection.GetDerivedTypes(typeof(NodeGraph)); + GenericMenu menu = new GenericMenu(); + for (int i = 0; i < graphTypes.Length; i++) { + Type graphType = graphTypes[i]; + menu.AddItem(new GUIContent(graphType.Name), false, () => CreateGraph(graphType)); + } + menu.ShowAsContext(); + } else { + CreateGraph(graphType); + } + } + } else { + if (GUILayout.Button("Open graph", GUILayout.Height(40))) { + NodeEditorWindow.Open(sceneGraph.graph); + } + if (removeSafely) { + GUILayout.BeginHorizontal(); + GUILayout.Label("Really remove graph?"); + GUI.color = new Color(1, 0.8f, 0.8f); + if (GUILayout.Button("Remove")) { + removeSafely = false; + sceneGraph.graph = null; + } + GUI.color = Color.white; + if (GUILayout.Button("Cancel")) { + removeSafely = false; + } + GUILayout.EndHorizontal(); + } else { + GUI.color = new Color(1, 0.8f, 0.8f); + if (GUILayout.Button("Remove graph")) { + removeSafely = true; + } + GUI.color = Color.white; + } + } + } + + private void OnEnable() { + sceneGraph = target as SceneGraph; + Type sceneGraphType = sceneGraph.GetType(); + if (sceneGraphType == typeof(SceneGraph)) { + graphType = null; + } else { + Type baseType = sceneGraphType.BaseType; + if (baseType.IsGenericType) { + graphType = sceneGraphType = baseType.GetGenericArguments() [0]; + } + } + } + + public void CreateGraph(Type type) { + serializedObject.Update(); + sceneGraph.graph = ScriptableObject.CreateInstance(type) as NodeGraph; + serializedObject.ApplyModifiedProperties(); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/SceneGraphEditor.cs.meta b/Scripts/Editor/SceneGraphEditor.cs.meta new file mode 100644 index 0000000..e1bf0b2 --- /dev/null +++ b/Scripts/Editor/SceneGraphEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aea725adabc311f44b5ea8161360a915 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SceneGraph.cs b/Scripts/SceneGraph.cs new file mode 100644 index 0000000..bb2774f --- /dev/null +++ b/Scripts/SceneGraph.cs @@ -0,0 +1,23 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using XNode; + +namespace XNode { + /// Lets you instantiate a node graph in the scene. This allows you to reference in-scene objects. + public class SceneGraph : MonoBehaviour { + public NodeGraph graph; + } + + /// Derive from this class to create a SceneGraph with a specific graph type. + /// + /// + /// public class MySceneGraph : SceneGraph { + /// + /// } + /// + /// + public class SceneGraph : SceneGraph where T : NodeGraph { + public new T graph { get { return base.graph as T; } set { base.graph = value; } } + } +} \ No newline at end of file diff --git a/Scripts/SceneGraph.cs.meta b/Scripts/SceneGraph.cs.meta new file mode 100644 index 0000000..c7978b6 --- /dev/null +++ b/Scripts/SceneGraph.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7915171fc13472a40a0162003052d2db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From f3b211ed83834fb237c0e40093a0367a027fbba6 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Fri, 3 Apr 2020 11:26:12 +0200 Subject: [PATCH 15/34] Fixed error on adding nodes in scene graphs --- Scripts/Editor/NodeGraphEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 3a0464c..9c25911 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -143,7 +143,7 @@ namespace XNodeEditor { Undo.RegisterCreatedObjectUndo(node, "Create Node"); node.position = position; if (node.name == null || node.name.Trim() == "") node.name = NodeEditorUtilities.NodeDefaultName(type); - AssetDatabase.AddObjectToAsset(node, target); + if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(target))) AssetDatabase.AddObjectToAsset(node, target); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); NodeEditorWindow.RepaintAll(); return node; From f899e48ee08c59443d1cd833ab8f273b0b5a11db Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Fri, 3 Apr 2020 15:01:44 +0200 Subject: [PATCH 16/34] Added undo support and default graph name to SceneGraph --- Scripts/Editor/SceneGraphEditor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/SceneGraphEditor.cs b/Scripts/Editor/SceneGraphEditor.cs index 911f66c..9fb1c67 100644 --- a/Scripts/Editor/SceneGraphEditor.cs +++ b/Scripts/Editor/SceneGraphEditor.cs @@ -37,6 +37,7 @@ namespace XNodeEditor { GUI.color = new Color(1, 0.8f, 0.8f); if (GUILayout.Button("Remove")) { removeSafely = false; + Undo.RecordObject(sceneGraph, "Removed graph"); sceneGraph.graph = null; } GUI.color = Color.white; @@ -68,9 +69,9 @@ namespace XNodeEditor { } public void CreateGraph(Type type) { - serializedObject.Update(); + Undo.RecordObject(sceneGraph, "Create graph"); sceneGraph.graph = ScriptableObject.CreateInstance(type) as NodeGraph; - serializedObject.ApplyModifiedProperties(); + sceneGraph.graph.name = sceneGraph.name + "-graph"; } } } \ No newline at end of file From e2c7934f1e46ca098f0212aa5268eba01a8e3e35 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Sat, 4 Apr 2020 14:01:05 +0200 Subject: [PATCH 17/34] Limit the OnDropObjects warning to only show when a node graph editor is present --- Scripts/Editor/NodeGraphEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 9c25911..6089d29 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -133,7 +133,7 @@ namespace XNodeEditor { /// Deal with objects dropped into the graph through DragAndDrop public virtual void OnDropObjects(UnityEngine.Object[] objects) { - Debug.Log("No OnDropObjects override defined for " + GetType()); + if (GetType() != typeof(NodeGraphEditor)) Debug.Log("No OnDropObjects override defined for " + GetType()); } /// Create a node and save it in the graph asset From 01a5666adedc7049d690a06c7d46ef6d40fee00c Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 6 Apr 2020 22:19:10 +0200 Subject: [PATCH 18/34] Delete Examples.meta --- Examples.meta | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 Examples.meta diff --git a/Examples.meta b/Examples.meta deleted file mode 100644 index 231b3e2..0000000 --- a/Examples.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 3cfe6eabeed0aa44e8d9d54b308a461f -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From 76cf82509098ed2a2a86baea8337644aabb7d578 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Mon, 13 Apr 2020 19:35:52 +0200 Subject: [PATCH 19/34] Suggestion! A new way of drawing lines very similar to Unitys upcoming Shader Node Editor. --- Scripts/Editor/NodeEditorGUI.cs | 35 +++++++++++++++++++++++++ Scripts/Editor/NodeEditorPreferences.cs | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 358b2e5..236091c 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -267,6 +267,41 @@ namespace XNodeEditor { } } break; + case NoodlePath.ShaderLab: + Vector2 start = gridPoints[0]; + Vector2 end = gridPoints[length - 1]; + //Modify first and last point in array so we can loop trough them nicely. + gridPoints[0] = gridPoints[0] + Vector2.right * (20 / zoom); + gridPoints[length - 1] = gridPoints[length - 1] + Vector2.left * (20 / zoom); + //Draw first vertical lines going out from nodes + Handles.color = gradient.Evaluate(0f); + DrawAAPolyLineNonAlloc(thickness, start, gridPoints[0]); + Handles.color = gradient.Evaluate(1f); + DrawAAPolyLineNonAlloc(thickness, end, gridPoints[length - 1]); + for (int i = 0; i < length - 1; i++) { + Vector2 point_a = gridPoints[i]; + Vector2 point_b = gridPoints[i + 1]; + // Draws the line with the coloring. + Vector2 prev_point = point_a; + // Approximately one segment per 5 pixels + int segments = (int) Vector2.Distance(point_a, point_b) / 5; + + int draw = 0; + for (int j = 0; j <= segments; j++) { + draw++; + float t = j / (float) segments; + Vector2 lerp = Vector2.Lerp(point_a, point_b, t); + if (draw > 0) { + if (i == length - 2) Handles.color = gradient.Evaluate(t); + DrawAAPolyLineNonAlloc(thickness, prev_point, lerp); + } + prev_point = lerp; + if (stroke == NoodleStroke.Dashed && draw >= 2) draw = -2; + } + } + gridPoints[0] = start; + gridPoints[length - 1] = end; + break; } } diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 467318a..72eb8aa 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -5,7 +5,7 @@ using UnityEngine; using UnityEngine.Serialization; namespace XNodeEditor { - public enum NoodlePath { Curvy, Straight, Angled } + public enum NoodlePath { Curvy, Straight, Angled, ShaderLab } public enum NoodleStroke { Full, Dashed } public static class NodeEditorPreferences { From a61bb3f87ea87a4851e4524b9c5f72f8835ae28f Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 14 Apr 2020 08:11:21 +0200 Subject: [PATCH 20/34] Implemented OrderBy in Add Node Menu (#247) --- Scripts/Editor/NodeGraphEditor.cs | 15 +++++++++++++-- Scripts/Node.cs | 10 ++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 6089d29..f8e1eda 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -41,11 +41,22 @@ namespace XNodeEditor { return NodeEditorUtilities.NodeDefaultPath(type); } + /// The order by which the menu items are displayed. + 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 + return attrib.order; + else + 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) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); - for (int i = 0; i < NodeEditorReflection.nodeTypes.Length; i++) { - Type type = NodeEditorReflection.nodeTypes[i]; + var nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); + for (int i = 0; i < nodeTypes.Length; i++) { + Type type = nodeTypes[i]; //Get node context menu path string path = GetNodeMenuName(type); diff --git a/Scripts/Node.cs b/Scripts/Node.cs index beb5f77..9d2721b 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -317,10 +317,20 @@ namespace XNode { [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class CreateNodeMenuAttribute : Attribute { public string menuName; + public int order; /// Manually supply node class with a context menu path /// Path to this node in the context menu. Null or empty hides it. public CreateNodeMenuAttribute(string menuName) { this.menuName = menuName; + this.order = 0; + } + + /// Manually supply node class with a context menu path + /// Path to this node in the context menu. Null or empty hides it. + /// The order by which the menu items are displayed. + public CreateNodeMenuAttribute(string menuName, int order) { + this.menuName = menuName; + this.order = order; } } From 049a62465c6af3588fb01fb29c43e9d4fc647cfa Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Sun, 19 Apr 2020 20:41:12 +0200 Subject: [PATCH 21/34] Made nodeEditorWindow.selectedReroutes public --- 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 8021a16..8e840af 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -25,7 +25,7 @@ namespace XNodeEditor { [NonSerialized] private XNode.NodePort autoConnectOutput = null; [NonSerialized] private List draggedOutputReroutes = new List(); private RerouteReference hoveredReroute = new RerouteReference(); - private List selectedReroutes = new List(); + public List selectedReroutes = new List(); private Vector2 dragBoxStart; private UnityEngine.Object[] preBoxSelection; private RerouteReference[] preBoxSelectionReroute; From e704b97c77b4b06c6a2bc01d27bb6297f788de9f Mon Sep 17 00:00:00 2001 From: Dan Erhardt <51964473+DanErhardt@users.noreply.github.com> Date: Mon, 20 Apr 2020 11:21:19 -0300 Subject: [PATCH 22/34] A fix for an ArgumentException error. (#250) This is the fix proposed by nostravaganza on discord. It works 100%. Could not reproduce the error again. --- Scripts/Editor/NodeEditorUtilities.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index 9973145..ac12e33 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -74,8 +74,10 @@ namespace XNodeEditor { Attribute attr; if (!typeTypes.TryGetValue(typeof(T), out attr)) { - if (GetAttrib(classType, fieldName, out attribOut)) typeTypes.Add(typeof(T), attribOut); - else typeTypes.Add(typeof(T), null); + if (GetAttrib(classType, fieldName, out attribOut)) { + typeTypes.Add(typeof(T), attribOut); + return true; + } else typeTypes.Add(typeof(T), null); } if (attr == null) { @@ -261,4 +263,4 @@ namespace XNodeEditor { } } } -} \ No newline at end of file +} From bbdbee90a169778d1a8a4b45f2362ec7c6817479 Mon Sep 17 00:00:00 2001 From: Dan Erhardt <51964473+DanErhardt@users.noreply.github.com> Date: Mon, 20 Apr 2020 11:56:33 -0300 Subject: [PATCH 23/34] Fix to prevent division by 0 (#251) --- Scripts/Editor/NodeEditorGUI.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 236091c..7c82a12 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -202,6 +202,7 @@ namespace XNodeEditor { Vector2 prev_point = point_a; // Approximately one segment per 5 pixels int segments = (int) Vector2.Distance(point_a, point_b) / 5; + segments = Math.Max(segments, 1); int draw = 0; for (int j = 0; j <= segments; j++) { @@ -285,6 +286,7 @@ namespace XNodeEditor { Vector2 prev_point = point_a; // Approximately one segment per 5 pixels int segments = (int) Vector2.Distance(point_a, point_b) / 5; + segments = Math.Max(segments, 1); int draw = 0; for (int j = 0; j <= segments; j++) { From a550a87b9e26064acf14e9ac9441c37d8be2f541 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 21 Apr 2020 15:29:24 +0200 Subject: [PATCH 24/34] Fix .editorconfig. Line endings were set as LF despite all endings being CRLF --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 82fb8f5..03c2cb5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,6 @@ root = true [*.cs] indent_style = space indent_size = 4 -end_of_line = lf +end_of_line = crlf insert_final_newline = false trim_trailing_whitespace = true From 8965d365e62aec3c458fca9585e2a7d6d5c0fa50 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 21 Apr 2020 15:31:07 +0200 Subject: [PATCH 25/34] Add [DisallowMultipleNodes] for limiting how many nodes of a certain type can exist on any graph --- Scripts/Editor/NodeEditorAction.cs | 9 +++++++++ Scripts/Editor/NodeGraphEditor.cs | 12 +++++++++++- Scripts/Node.cs | 10 ++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 8e840af..f9a81fb 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -439,6 +439,15 @@ namespace XNodeEditor { 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)) { + int typeCount = graph.nodes.Count(x => x.GetType() == nodeType); + if (typeCount >= disallowAttrib.max) continue; + } + XNode.Node newNode = graphEditor.CopyNode(srcNode); substitutes.Add(srcNode, newNode); newNode.position = srcNode.position + offset; diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index f8e1eda..3806d39 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -62,7 +62,17 @@ namespace XNodeEditor { string path = GetNodeMenuName(type); if (string.IsNullOrEmpty(path)) continue; - menu.AddItem(new GUIContent(path), false, () => { + // 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)) { + 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, () => { XNode.Node node = CreateNode(type, pos); NodeEditorWindow.current.AutoConnect(node); }); diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 9d2721b..8e1a670 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -334,6 +334,16 @@ namespace XNode { } } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class DisallowMultipleNodesAttribute : Attribute { + public int max; + /// Prevents Node of the same type (or subtype) to be added more than once (configurable) to a NodeGraph + /// How many nodes to allow. Defaults to 1. + public DisallowMultipleNodesAttribute(int max = 1) { + this.max = max; + } + } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class NodeTintAttribute : Attribute { public Color color; From c2a99ba2b5ea226639ec6c53903ce2e69ef4dd78 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 21 Apr 2020 15:52:06 +0200 Subject: [PATCH 26/34] Improved commenting on attributes --- Scripts/Node.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 8e1a670..6744cc5 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -314,6 +314,7 @@ namespace XNode { public OutputAttribute(ShowBackingValue backingValue, ConnectionType connectionType, bool dynamicPortList) : this(backingValue, connectionType, TypeConstraint.None, dynamicPortList) { } } + /// Manually supply node class with a context menu path [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class CreateNodeMenuAttribute : Attribute { public string menuName; @@ -334,16 +335,20 @@ namespace XNode { } } + /// Prevents Node of the same type to be added more than once (configurable) to a NodeGraph [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class DisallowMultipleNodesAttribute : Attribute { + // TODO: Make inheritance work in such a way that applying [DisallowMultipleNodes(1)] to type NodeBar : Node + // while type NodeFoo : NodeBar exists, will let you add *either one* of these nodes, but not both. public int max; - /// Prevents Node of the same type (or subtype) to be added more than once (configurable) to a NodeGraph + /// Prevents Node of the same type to be added more than once (configurable) to a NodeGraph /// How many nodes to allow. Defaults to 1. public DisallowMultipleNodesAttribute(int max = 1) { this.max = max; } } + /// Specify a color for this node type [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class NodeTintAttribute : Attribute { public Color color; @@ -370,6 +375,7 @@ namespace XNode { } } + /// Specify a width for this node type [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class NodeWidthAttribute : Attribute { public int width; From ff2c1e815885f57b842187f730b0f40f95084c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Th=C3=A9ry?= Date: Thu, 23 Apr 2020 11:27:02 +0200 Subject: [PATCH 27/34] Mark CopyNode as virtual (#252) --- Scripts/Editor/NodeGraphEditor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 3806d39..7551e2f 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -171,7 +171,7 @@ namespace XNodeEditor { } /// Creates a copy of the original node in the graph - public 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"); @@ -211,4 +211,4 @@ namespace XNodeEditor { } } } -} \ No newline at end of file +} From c298b5ee537f53add2c5903a04c6984b7acb122e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Th=C3=A9ry?= Date: Thu, 23 Apr 2020 15:08:43 +0200 Subject: [PATCH 28/34] Add virtual OnWindowFocus and OnWindowFocusLost in NodeGraphEditor(#253) --- Scripts/Editor/NodeEditorWindow.cs | 9 ++++++++- Scripts/Editor/NodeGraphEditor.cs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 4fc1136..a751722 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -77,7 +77,14 @@ namespace XNodeEditor { void OnFocus() { current = this; ValidateGraphEditor(); - if (graphEditor != null && NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); + if (graphEditor != null) { + graphEditor.OnWindowFocus(); + if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); + } + } + + void OnLostFocus() { + if (graphEditor != null) graphEditor.OnWindowFocusLost(); } [InitializeOnLoadMethod] diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 7551e2f..12b9ba1 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -17,6 +17,12 @@ 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() { return NodeEditorPreferences.GetSettings().gridTexture; From d9d90f0f7b8f240baaaf2e1d8d0b6d1fd790453f Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Fri, 24 Apr 2020 15:57:50 +0200 Subject: [PATCH 29/34] Add [RequireNode] attribute to graphs (#254) --- Scripts/Editor/NodeEditor.cs | 16 +++++---- Scripts/Editor/NodeGraphEditor.cs | 17 +++++++++ Scripts/Editor/NodeGraphImporter.cs | 45 ++++++++++++++++++++++++ Scripts/Editor/NodeGraphImporter.cs.meta | 11 ++++++ Scripts/NodeGraph.cs | 39 ++++++++++++++++++++ 5 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 Scripts/Editor/NodeGraphImporter.cs create mode 100644 Scripts/Editor/NodeGraphImporter.cs.meta diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index edf66d6..36c3a6e 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -67,7 +67,7 @@ namespace XNodeEditor { serializedObject.ApplyModifiedProperties(); #if ODIN_INSPECTOR - // Call repaint so that the graph window elements respond properly to layout changes coming from Odin + // Call repaint so that the graph window elements respond properly to layout changes coming from Odin if (GUIHelper.RepaintRequested) { GUIHelper.ClearRepaintRequest(); window.Repaint(); @@ -106,17 +106,22 @@ 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) { + bool canRemove = true; // Actions if only one node is selected if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; menu.AddItem(new GUIContent("Move To Top"), false, () => NodeEditorWindow.current.MoveNodeToTop(node)); menu.AddItem(new GUIContent("Rename"), false, NodeEditorWindow.current.RenameSelectedNode); + + canRemove = NodeGraphEditor.GetEditor(node.graph, NodeEditorWindow.current).CanRemove(node); } // Add actions to any number of selected nodes menu.AddItem(new GUIContent("Copy"), false, NodeEditorWindow.current.CopySelectedNodes); menu.AddItem(new GUIContent("Duplicate"), false, NodeEditorWindow.current.DuplicateSelectedNodes); - menu.AddItem(new GUIContent("Remove"), false, NodeEditorWindow.current.RemoveSelectedNodes); + + if (canRemove) menu.AddItem(new GUIContent("Remove"), false, NodeEditorWindow.current.RemoveSelectedNodes); + else menu.AddItem(new GUIContent("Remove"), false, null); // Custom sctions if only one node is selected if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { @@ -132,11 +137,10 @@ namespace XNodeEditor { OnRename(); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); } - + /// Called after this node's name has changed. public virtual void OnRename() { } - - + [AttributeUsage(AttributeTargets.Class)] public class CustomNodeEditorAttribute : Attribute, XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib { @@ -152,4 +156,4 @@ namespace XNodeEditor { } } } -} +} \ No newline at end of file diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 12b9ba1..01de70e 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -187,8 +187,25 @@ namespace XNodeEditor { return node; } + /// Return false for nodes that can't be removed + 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) { + return false; + } + } + return true; + } + /// Safely remove a node and all its connections. public virtual void RemoveNode(XNode.Node node) { + if (!CanRemove(node)) return; + + // Remove the node Undo.RecordObject(node, "Delete Node"); Undo.RecordObject(target, "Delete Node"); foreach (var port in node.Ports) diff --git a/Scripts/Editor/NodeGraphImporter.cs b/Scripts/Editor/NodeGraphImporter.cs new file mode 100644 index 0000000..3faf54f --- /dev/null +++ b/Scripts/Editor/NodeGraphImporter.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEditor.Experimental.AssetImporters; +using UnityEngine; +using XNode; + +namespace XNodeEditor { + /// Deals with modified assets + class NodeGraphImporter : AssetPostprocessor { + private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { + foreach (string path in importedAssets) { + // Skip processing anything without the .asset extension + if (Path.GetExtension(path) != ".asset") continue; + + // Get the object that is requested for deletion + NodeGraph graph = AssetDatabase.LoadAssetAtPath(path); + if (graph == null) continue; + + // Get attributes + Type graphType = graph.GetType(); + NodeGraph.RequireNodeAttribute[] attribs = Array.ConvertAll( + graphType.GetCustomAttributes(typeof(NodeGraph.RequireNodeAttribute), true), x => x as NodeGraph.RequireNodeAttribute); + + Vector2 position = Vector2.zero; + foreach (NodeGraph.RequireNodeAttribute attrib in attribs) { + if (attrib.type0 != null) AddRequired(graph, attrib.type0, ref position); + if (attrib.type1 != null) AddRequired(graph, attrib.type1, ref position); + if (attrib.type2 != null) AddRequired(graph, attrib.type2, ref position); + } + } + } + + private static void AddRequired(NodeGraph graph, Type type, ref Vector2 position) { + if (!graph.nodes.Any(x => x.GetType() == type)) { + XNode.Node node = graph.AddNode(type); + node.position = position; + position.x += 200; + if (node.name == null || node.name.Trim() == "") node.name = NodeEditorUtilities.NodeDefaultName(type); + if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(graph))) AssetDatabase.AddObjectToAsset(node, graph); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/NodeGraphImporter.cs.meta b/Scripts/Editor/NodeGraphImporter.cs.meta new file mode 100644 index 0000000..b3dd1fe --- /dev/null +++ b/Scripts/Editor/NodeGraphImporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a816f2790bf3da48a2d6d0035ebc9a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs index 6a0cead..d928f94 100644 --- a/Scripts/NodeGraph.cs +++ b/Scripts/NodeGraph.cs @@ -81,5 +81,44 @@ namespace XNode { // Remove all nodes prior to graph destruction Clear(); } + +#region Attributes + /// Automatically ensures the existance of a certain node type, and prevents it from being deleted. + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class RequireNodeAttribute : Attribute { + public Type type0; + public Type type1; + public Type type2; + + /// Automatically ensures the existance of a certain node type, and prevents it from being deleted + public RequireNodeAttribute(Type type) { + this.type0 = type; + this.type1 = null; + this.type2 = null; + } + + /// Automatically ensures the existance of a certain node type, and prevents it from being deleted + public RequireNodeAttribute(Type type, Type type2) { + this.type0 = type; + this.type1 = type2; + this.type2 = null; + } + + /// Automatically ensures the existance of a certain node type, and prevents it from being deleted + public RequireNodeAttribute(Type type, Type type2, Type type3) { + this.type0 = type; + this.type1 = type2; + this.type2 = type3; + } + + public bool Requires(Type type) { + if (type == null) return false; + if (type == type0) return true; + else if (type == type1) return true; + else if (type == type2) return true; + return false; + } + } +#endregion } } \ No newline at end of file From a1cf78d3fb9ac379a1ee4acffd0430c3eb35a761 Mon Sep 17 00:00:00 2001 From: Dan Erhardt <51964473+DanErhardt@users.noreply.github.com> Date: Sun, 26 Apr 2020 12:44:46 -0300 Subject: [PATCH 30/34] Fix for AutoConnect method. (#255) Prevents incompatible connections between node ports when creating a new node on connection drag. --- Scripts/Editor/NodeEditorAction.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index f9a81fb..1a02556 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -535,8 +535,8 @@ namespace XNodeEditor { XNode.NodePort inputPort = node.Ports.FirstOrDefault(x => x.IsInput && x.ValueType == autoConnectOutput.ValueType); // Fallback to input port if (inputPort == null) inputPort = node.Ports.FirstOrDefault(x => x.IsInput); - // Autoconnect - if (inputPort != null) autoConnectOutput.Connect(inputPort); + // Autoconnect if connection is compatible + if (inputPort != null && inputPort.CanConnectTo(autoConnectOutput)) autoConnectOutput.Connect(inputPort); // Save changes EditorUtility.SetDirty(graph); @@ -544,4 +544,4 @@ namespace XNodeEditor { autoConnectOutput = null; } } -} \ No newline at end of file +} From 4c6d22a15246bbb72aac724e1cdf29b0847fec88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Th=C3=A9ry?= Date: Thu, 7 May 2020 10:21:39 +0200 Subject: [PATCH 31/34] Improve rename popup (#256) --- Scripts/Editor/RenamePopup.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/RenamePopup.cs b/Scripts/Editor/RenamePopup.cs index 5429270..a43837f 100644 --- a/Scripts/Editor/RenamePopup.cs +++ b/Scripts/Editor/RenamePopup.cs @@ -1,9 +1,11 @@ -using UnityEditor; +using UnityEditor; using UnityEngine; namespace XNodeEditor { /// Utility for renaming assets public class RenamePopup : EditorWindow { + private const string inputControlName = "nameInput"; + public static RenamePopup current { get; private set; } public Object target; public string input; @@ -19,7 +21,6 @@ namespace XNodeEditor { window.input = target.name; window.minSize = new Vector2(100, 44); window.position = new Rect(0, 0, width, 44); - GUI.FocusControl("ClearAllFocus"); window.UpdatePositionToMouse(); return window; } @@ -43,7 +44,9 @@ namespace XNodeEditor { UpdatePositionToMouse(); firstFrame = false; } + GUI.SetNextControlName(inputControlName); input = EditorGUILayout.TextField(input); + EditorGUI.FocusTextInControl(inputControlName); Event e = Event.current; // If input is empty, revert name to default instead if (input == null || input.Trim() == "") { @@ -67,6 +70,14 @@ namespace XNodeEditor { target.TriggerOnValidate(); } } + + if (e.isKey && e.keyCode == KeyCode.Escape) { + Close(); + } + } + + private void OnDestroy() { + EditorGUIUtility.editingTextField = false; } } } \ No newline at end of file From 8046e6e0bfb17f645ad9663e805bde843aa91313 Mon Sep 17 00:00:00 2001 From: fdtdev Date: Sat, 9 May 2020 13:21:35 +0200 Subject: [PATCH 32/34] [FIX] fix a problem when a complex class with optional port drawing is used with NodeEditorGUILayout.DynamicPortList. By having an item that wasn't drawing the port, the method was throwing a KeyNotFoundException when reordering. (#262) The fix detects if there isn't a port when swapping rects. --- Scripts/Editor/NodeEditorGUILayout.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 0b8a0cd..3574ace 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -371,7 +371,10 @@ namespace XNodeEditor { }; list.onReorderCallback = (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) { @@ -380,9 +383,10 @@ namespace XNodeEditor { port.SwapConnections(nextPort); // Swap cached positions to mitigate twitching - Rect rect = NodeEditorWindow.current.portConnectionPoints[port]; - NodeEditorWindow.current.portConnectionPoints[port] = NodeEditorWindow.current.portConnectionPoints[nextPort]; - NodeEditorWindow.current.portConnectionPoints[nextPort] = rect; + 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; } } // Move down @@ -393,9 +397,10 @@ namespace XNodeEditor { port.SwapConnections(nextPort); // Swap cached positions to mitigate twitching - Rect rect = NodeEditorWindow.current.portConnectionPoints[port]; - NodeEditorWindow.current.portConnectionPoints[port] = NodeEditorWindow.current.portConnectionPoints[nextPort]; - NodeEditorWindow.current.portConnectionPoints[nextPort] = rect; + 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; } } // Apply changes From a8daac60b0bfdb913302654730151f838cac1bc9 Mon Sep 17 00:00:00 2001 From: chrisfairc Date: Wed, 27 May 2020 07:20:20 +0100 Subject: [PATCH 33/34] Add a dragthreshold to better distunguish right clicks and right drags (#230) * add a drag threshold so right clicks are better distinguished from right drags especially on large screens * ignore mac files too * dont use less than 1 as a drag threshold Co-authored-by: Chris Fairclough --- .gitignore | 3 +++ Scripts/Editor/NodeEditorAction.cs | 8 ++++++-- Scripts/Editor/NodeEditorWindow.cs | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 64ab4c0..c75a01e 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ sysinfo.txt .git.meta .gitignore.meta .gitattributes.meta + +# OS X only: +.DS_Store \ No newline at end of file diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 1a02556..b112732 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -32,6 +32,7 @@ namespace XNodeEditor { private Rect selectionBox; private bool isDoubleClick = false; private Vector2 lastMousePosition; + private float dragThreshold = 1f; public void Controls() { wantsMouseMove = true; @@ -134,8 +135,11 @@ namespace XNodeEditor { Repaint(); } } else if (e.button == 1 || e.button == 2) { - panOffset += e.delta * zoom; - isPanning = true; + //check drag threshold for larger screens + if (e.delta.magnitude > dragThreshold) { + panOffset += e.delta * zoom; + isPanning = true; + } } break; case EventType.MouseDown: diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index a751722..4f0a102 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -81,6 +81,8 @@ namespace XNodeEditor { graphEditor.OnWindowFocus(); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } + + dragThreshold = Math.Max(1f, Screen.width / 1000f); } void OnLostFocus() { From 6c85abf9bc18998fdfcac6caafec128550e8a99d Mon Sep 17 00:00:00 2001 From: Christiaan Bloemendaal Date: Wed, 27 May 2020 14:20:37 +0200 Subject: [PATCH 34/34] Fix handles color not being reset to original color (#266) Co-authored-by: Christiaan Bloemendaal --- Scripts/Editor/NodeEditorGUI.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 7c82a12..99cdecf 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -142,6 +142,7 @@ namespace XNodeEditor { for (int i = 0; i < gridPoints.Count; ++i) gridPoints[i] = GridToWindowPosition(gridPoints[i]); + Color originalHandlesColor = Handles.color; Handles.color = gradient.Evaluate(0f); int length = gridPoints.Count; switch (path) { @@ -305,6 +306,7 @@ namespace XNodeEditor { gridPoints[length - 1] = end; break; } + Handles.color = originalHandlesColor; } /// Draws all connections