alelievr / NodeGraphProcessor

Node graph editor framework focused on data processing using Unity UIElements and C# 4.6
https://github.com/alelievr/NodeGraphProcessor/projects/2
MIT License
2.34k stars 387 forks source link

Changing Port Order With OverrideFieldOrder Loses Port Edge Type #199

Open dannymate opened 2 years ago

dannymate commented 2 years ago

So I have a couple layers of inheritance first. It looks like this: BaseNode > DynamicNode > SequenceNode

In Dynamic node I'm creating ports on the fly with CustomPortBehavior and CustomPortInput. Everything works how you would expect except I'd prefer if the ports were reordered so I did by implementing the OverrideFieldOrder method. Essentially all I'm doing is reversing the original order. i.e. return base.OverrideFieldOrder(fields).Reverse();.

image Also for some reason the property drawer for a float in DynamicNode is in the wrong place by default.

The order is reversed correctly but when creating a connection node in the menu it doesn't pickup up the correct type. Before (Dragging&Dropping an edge): image After (Dragging&Dropping an edge): image

The float itself does work before and after so I think it could be some kind of generic inheritance thing.

Code For DynamicNode:

DynamicNode Code ```c# using System.Collections; using System.Collections.Generic; using UnityEngine; using GraphProcessor; using System.Linq; using System.Reflection; using System; [System.Serializable] public abstract class DynamicNode : BaseNode { [Input("Action Data")] public Dictionary actionData = new Dictionary(); [Input("Float Val"), SerializeField] public float floatVal; [SerializeField, ExpandableSO, ValueChangedCallback(nameof(OnDataChanged))] public T data; protected override void Process() { UpdateActionWithCustomPortData(); } protected virtual void UpdateActionWithCustomPortData() { // We clone due to reference issues Dictionary actionDataClone = new Dictionary(actionData); foreach (var field in GetInputFieldsOfType()) { if (!actionDataClone.ContainsKey(field.fieldInfo.Name)) { field.fieldInfo.SetValue(data, default); } else { field.fieldInfo.SetValue(data, actionDataClone[field.fieldInfo.Name]); } } actionData.Clear(); } protected virtual void OnDataChanged(UnityEditor.SerializedProperty serializedProperty) { UpdatePortsForFieldLocal(nameof(actionData)); } #region Reflection Generation Of Ports private List GetInputFieldsOfType() { List foundInputFields = new List(); Type dataType = actionData != null ? data.GetType() : typeof(T); foreach (var field in dataType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { foreach (var attribute in field.GetCustomAttributes(typeof(InputAttribute), true)) { if (attribute.GetType() != typeof(InputAttribute)) continue; foundInputFields.Add(new FieldPortInfo(field, attribute as InputAttribute)); break; } } return foundInputFields; } [CustomPortInput(nameof(actionData), typeof(object))] protected void PullInputs(List connectedEdges) { if (actionData == null) actionData = new Dictionary(); foreach (SerializableEdge t in connectedEdges) { actionData.Add(t.inputPortIdentifier, t.passThroughBuffer); } } [CustomPortBehavior(nameof(actionData))] protected IEnumerable ActionDataBehaviour(List edges) { foreach (var field in GetInputFieldsOfType()) { yield return new PortData { displayName = field.inputAttribute.name, displayType = field.fieldInfo.FieldType, identifier = field.fieldInfo.Name, acceptMultipleEdges = false, }; } } public override IEnumerable OverrideFieldOrder(IEnumerable fields) { return base.OverrideFieldOrder(fields).Reverse(); // static long GetFieldInheritanceLevel(FieldInfo f) // { // int level = 0; // var t = f.DeclaringType; // while (t != null) // { // t = t.BaseType; // level++; // } // return level; // } // // Order by MetadataToken and inheritance level to sync the order with the port order (make sure FieldDrawers are next to the correct port) // return fields.OrderByDescending(f => (GetFieldInheritanceLevel(f) << 32) | (long)f.MetadataToken); } #endregion } public struct FieldPortInfo { public FieldInfo fieldInfo; public InputAttribute inputAttribute; public FieldPortInfo(FieldInfo fieldInfo, InputAttribute inputAttribute) { this.fieldInfo = fieldInfo; this.inputAttribute = inputAttribute; } } ```