diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 08fcf5e..8383a81 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)) { @@ -327,7 +329,6 @@ namespace XNodeEditor { list.list = dynamicPorts; list.DoLayoutList(); - node.UpdatePorts(); } 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) { diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 9ea7411..e5323c1 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -123,7 +123,7 @@ namespace XNode { Init(); } - /// Update all ports to reflect class fields. This happens automatically on enable or on redrawing the list of ports. + /// 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 c4bbc2e..53e9604 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using UnityEditor; using UnityEngine; namespace XNode { @@ -10,7 +9,7 @@ namespace XNode { private static PortDataCache portDataCache; private static bool Initialized { get { return portDataCache != null; } } - /// Update static and dynamic ports to reflect class fields. + /// Update static and dynamic ports managed by DynamicPortLists to reflect class fields. public static void UpdatePorts(Node node, Dictionary ports) { if (!Initialized) BuildCache(); @@ -18,7 +17,7 @@ namespace XNode { Dictionary> removedPorts = new Dictionary>(); System.Type nodeType = node.GetType(); - List dynamicPorts = new List(); + List dynamicListPorts = new List(); List typePortCache; if (portDataCache.TryGetValue(nodeType, out typePortCache)) { @@ -28,7 +27,7 @@ namespace XNode { } // Cleanup port dict - Remove nonexisting static ports - update static port types - // AND update dynamic ports too! + // 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 @@ -47,9 +46,9 @@ namespace XNode { port.ClearConnections(); ports.Remove(port.fieldName); } - // If the port is dynamic, flag it for reference updates - else { - dynamicPorts.Add(port); + // 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 @@ -69,32 +68,41 @@ namespace XNode { } } - // Finally, make sure dynamic port settings correspond to the settings of their backing field - foreach (NodePort dynamicPort in dynamicPorts) { - string[] parts = dynamicPort.fieldName.Split(' '); - if (parts.Length != 2) { - Debug.LogError("Dynamic port name " + dynamicPort.fieldName + " is invalid."); - continue; - } - string backingPortName = parts[0]; - NodePort backingPort; - if (!staticPorts.TryGetValue(backingPortName, out backingPort)) { - Debug.LogError($"Could not find backing port \"{backingPortName}\" for port {dynamicPort.fieldName}"); - continue; - } + // 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. - dynamicPort.ValueType = backingPort.ValueType; - dynamicPort.direction = backingPort.direction; - dynamicPort.connectionType = backingPort.connectionType; - dynamicPort.typeConstraint = backingPort.typeConstraint; - + 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(); @@ -183,10 +191,5 @@ namespace XNode { this.Add(keys[i], values[i]); } } - - [MenuItem("TOOLS/LOL")] - public static void LOL() { - portDataCache = null; - } } } diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index a41b667..b2f1ad1 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -19,10 +19,6 @@ 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; }