From 489767b313232729c5599daa48d33985f1ec0d92 Mon Sep 17 00:00:00 2001 From: Kailey Joanette Date: Tue, 24 Sep 2019 19:22:49 -0400 Subject: [PATCH] Refactored OdinInspector integration. --- .../Drawers/Odin/AsDynamicPortAttributes.cs | 98 +++++++ ...s.meta => AsDynamicPortAttributes.cs.meta} | 2 +- .../Drawers/Odin/AsStaticPortAttributes.cs | 83 ++++++ ...cs.meta => AsStaticPortAttributes.cs.meta} | 2 +- .../Odin/DynamicPortListBackedResolver.cs | 219 +++++++++++++++ .../DynamicPortListBackedResolver.cs.meta | 11 + .../Odin/DynamicPortListNoDataResolver.cs | 250 ++++++++++++++++++ .../DynamicPortListNoDataResolver.cs.meta | 11 + .../Odin/InNodeEditorAttributeProcessor.cs | 7 +- .../Drawers/Odin/InputAttributeDrawer.cs | 49 ---- .../Drawers/Odin/OutputAttributeDrawer.cs | 49 ---- .../Drawers/Odin/SimpleNodePortDrawer.cs | 26 ++ .../Drawers/Odin/SimpleNodePortDrawer.cs.meta | 11 + Scripts/Editor/NodeEditor.cs | 8 +- Scripts/Editor/NodeEditorBase.cs | 6 +- 15 files changed, 722 insertions(+), 110 deletions(-) create mode 100644 Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs rename Scripts/Editor/Drawers/Odin/{InputAttributeDrawer.cs.meta => AsDynamicPortAttributes.cs.meta} (83%) create mode 100644 Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs rename Scripts/Editor/Drawers/Odin/{OutputAttributeDrawer.cs.meta => AsStaticPortAttributes.cs.meta} (83%) create mode 100644 Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs create mode 100644 Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs.meta create mode 100644 Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs create mode 100644 Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs.meta delete mode 100644 Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs delete mode 100644 Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs create mode 100644 Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs create mode 100644 Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs.meta diff --git a/Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs b/Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs new file mode 100644 index 0000000..5f9de23 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs @@ -0,0 +1,98 @@ +#if UNITY_EDITOR && ODIN_INSPECTOR +using Sirenix.OdinInspector.Editor; +using System; +using UnityEditor; +using UnityEngine; +using XNode; +using static XNode.Node; + +namespace XNodeEditor.Odin +{ + internal abstract class AsDynamicPortAtribute : System.Attribute + { + internal string fieldName { get; set; } + internal int index { get; set; } + internal Node Node { get; set; } + + internal ConnectionType connectionType { get; set; } + internal ShowBackingValue backingValue { get; set; } + + internal NodePort Port + { + get + { + return Node.GetPort( $"{fieldName} {index}" ); + } + } + } + + internal class AsDynamicPortNoDataAtribute : AsDynamicPortAtribute { } + internal class AsDynamicPortWithDataAtribute : AsDynamicPortAtribute { } + + internal struct AsDynamicPortScope : IDisposable + { + public AsDynamicPortScope( NodePort port ) + { + EditorGUILayout.BeginVertical(); + var rect = GUILayoutUtility.GetRect( 0f, float.MaxValue, 0f, 0f, GUI.skin.label, GUILayout.ExpandWidth( true ) ); + if ( NodeEditor.isNodeEditor ) + { + if ( port.IsInput ) + { + NodeEditorGUILayout.PortField( new Vector2( rect.xMin - 42, rect.center.y ), port ); + } + else + { + NodeEditorGUILayout.PortField( new Vector2( rect.xMax + 21, rect.center.y ), port ); + } + } + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.BeginVertical(); + } + + public void Dispose() + { + EditorGUILayout.EndVertical(); + EditorGUILayout.EndHorizontal(); + EditorGUILayout.EndVertical(); + } + } + + [DrawerPriority( 0.4, 0, 0 )] + internal class AsDynamicPortNoDataAttributeDrawer : OdinAttributeDrawer + { + protected override void DrawPropertyLayout( GUIContent label ) + { + if ( Attribute.Port == null ) + return; + + using ( new AsDynamicPortScope( Attribute.Port ) ) + CallNextDrawer( label ); + } + } + + [DrawerPriority( 0.4, 0, 0 )] + internal class AsDynamicPortWithDataAtributeDrawer : OdinAttributeDrawer + { + protected bool drawData = false; + + protected override void DrawPropertyLayout( GUIContent label ) + { + if ( Attribute.Port == null ) + return; + + if ( Event.current.type == EventType.Layout ) + drawData = Attribute.backingValue == ShowBackingValue.Always || Attribute.backingValue == ShowBackingValue.Unconnected && !Attribute.Port.IsConnected; + + using ( new AsDynamicPortScope( Attribute.Port ) ) + { + if ( drawData ) + CallNextDrawer( label ); + else + EditorGUILayout.LabelField( label ); + } + } + } +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta b/Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs.meta similarity index 83% rename from Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta rename to Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs.meta index 12b7615..243edf4 100644 --- a/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta +++ b/Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2fd590b2e9ea0bd49b6986a2ca9010ab +guid: 0ef93b42a7d5fe8459b6755d72583900 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs b/Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs new file mode 100644 index 0000000..d6fce9b --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs @@ -0,0 +1,83 @@ +#if UNITY_EDITOR && ODIN_INSPECTOR +using Sirenix.OdinInspector.Editor; +using System; +using UnityEditor; +using UnityEngine; +using XNode; +using static XNode.Node; + +namespace XNodeEditor.Odin +{ + internal struct AsStaticPortScope : IDisposable + { + public AsStaticPortScope( NodePort port ) + { + EditorGUILayout.BeginVertical(); + var rect = GUILayoutUtility.GetRect( 0f, float.MaxValue, 0f, 0f, GUI.skin.label, GUILayout.ExpandWidth( true ) ); + if ( NodeEditor.isNodeEditor ) + { + if ( port.IsInput ) + { + NodeEditorGUILayout.PortField( new Vector2( rect.xMin - 18, rect.center.y + 2 ), port ); + } + else + { + NodeEditorGUILayout.PortField( new Vector2( rect.xMax + 2, rect.center.y + 2 ), port ); + } + } + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.BeginVertical(); + } + + public void Dispose() + { + EditorGUILayout.EndVertical(); + EditorGUILayout.EndHorizontal(); + EditorGUILayout.EndVertical(); + } + } + + [DrawerPriority( 0.4, 0, 0 )] + internal class InputAttributeDrawer : OdinAttributeDrawer + { + protected bool drawData = false; + + protected override void DrawPropertyLayout( GUIContent label ) + { + NodePort port = ( Property.Tree.UnitySerializedObject.targetObject as Node ).GetInputPort( Property.Name ); + if ( Event.current.type == EventType.Layout ) + drawData = Attribute.backingValue == ShowBackingValue.Always || Attribute.backingValue == ShowBackingValue.Unconnected && !port.IsConnected; + + using ( new AsStaticPortScope( port ) ) + { + if ( drawData ) + CallNextDrawer( label ); + else + EditorGUILayout.LabelField( label ); + } + } + } + + [DrawerPriority( 0.4, 0, 0 )] + internal class OutputAttributeDrawer : OdinAttributeDrawer + { + protected bool drawData = false; + + protected override void DrawPropertyLayout( GUIContent label ) + { + NodePort port = ( Property.Tree.UnitySerializedObject.targetObject as Node ).GetOutputPort( Property.Name ); + if ( Event.current.type == EventType.Layout ) + drawData = Attribute.backingValue == ShowBackingValue.Always || Attribute.backingValue == ShowBackingValue.Unconnected && !port.IsConnected; + + using ( new AsStaticPortScope( port ) ) + { + if ( drawData ) + CallNextDrawer( label ); + else + EditorGUILayout.LabelField( label ); + } + } + } +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta b/Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs.meta similarity index 83% rename from Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta rename to Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs.meta index aa22218..4bc6891 100644 --- a/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta +++ b/Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e7ebd8f2b42e2384aa109551dc46af88 +guid: 36e90d7590dbcad418bc8ca94192d5e2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs b/Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs new file mode 100644 index 0000000..44607e2 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs @@ -0,0 +1,219 @@ +#if UNITY_EDITOR && ODIN_INSPECTOR +using Sirenix.OdinInspector; +using Sirenix.OdinInspector.Editor; +using Sirenix.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using XNode; +using static XNode.Node; + +namespace XNodeEditor.Odin +{ + [ResolverPriority( 10 )] + public class DynamicPortListBackedResolver : StrongListPropertyResolver + where TList : IList + { + public override bool CanResolveForPropertyFilter( InspectorProperty property ) + { + var input = property.GetAttribute(); + if ( input != null ) + return input.dynamicPortList; + + var output = property.GetAttribute(); + if ( output != null ) + return output.dynamicPortList; + + return false; + } + + private Dictionary childInfos = new Dictionary(); + + public override InspectorPropertyInfo GetChildInfo( int childIndex ) + { + if ( childIndex < 0 || childIndex >= this.ChildCount ) + { + throw new IndexOutOfRangeException(); + } + + InspectorPropertyInfo result; + + if ( !this.childInfos.TryGetValue( childIndex, out result ) ) + { + var attributes = this.Property.Attributes.Where( attr => !attr.GetType().IsDefined( typeof( DontApplyToListElementsAttribute ), true ) ); + var labelTextAttribute = attributes.OfType().SingleOrDefault(); + var hideLabelAttribute = attributes.OfType().SingleOrDefault(); + + var listDrawerSettingsAttribute = attributes.OfType().SingleOrDefault(); + if ( listDrawerSettingsAttribute == null ) + { + listDrawerSettingsAttribute = new ListDrawerSettingsAttribute() { Expanded = true, ShowPaging = false }; + } + else + { + listDrawerSettingsAttribute.Expanded = true; + listDrawerSettingsAttribute.ShowPaging = false; + } + + attributes = attributes + .Append( GetPortAttribute( Property.Name, childIndex ) ) + .AppendIf( labelTextAttribute == null && hideLabelAttribute == null, new LabelTextAttribute( $"{Property.Name} {childIndex}" ) ); + + result = InspectorPropertyInfo.CreateValue( + name: CollectionResolverUtilities.DefaultIndexToChildName( childIndex ), + order: childIndex, + serializationBackend: this.Property.BaseValueEntry.SerializationBackend, + new GetterSetter( + getter: ( ref TList list ) => list[childIndex], + setter: ( ref TList list, TElement element ) => list[childIndex] = element ), + attributes: attributes.ToArray() ); + + this.childInfos[childIndex] = result; + } + + return result; + } + + internal AsDynamicPortWithDataAtribute GetPortAttribute( string fieldName, int index ) + { + return new AsDynamicPortWithDataAtribute() + { + fieldName = fieldName, + index = index, + Node = node, + + connectionType = connectionType, + backingValue = backingValue + }; + } + + protected override void Add( TList collection, object value ) + { + int nextId = this.ChildCount; + + if ( IsInput ) + this.node.AddDynamicInput( typeof( TElement ), connectionType, typeConstraint, $"{Property.Name} {nextId}" ); + else + this.node.AddDynamicOutput( typeof( TElement ), connectionType, typeConstraint, $"{Property.Name} {nextId}" ); + + lastRemovedConnections.Clear(); + + base.Add( collection, value ); + } + + protected override void InsertAt( TList collection, int index, object value ) + { + int nextId = this.ChildCount; + + // Remove happens before insert and we lose all the connections + // Add a new port at the end + if ( IsInput ) + this.node.AddDynamicInput( typeof( TElement ), connectionType, typeConstraint, $"{Property.Name} {nextId}" ); + else + this.node.AddDynamicOutput( typeof( TElement ), connectionType, typeConstraint, $"{Property.Name} {nextId}" ); + + var dynamicPorts = this.ports; + + // Move everything down to make space + for ( int k = dynamicPorts.Count - 1; k > index; --k ) + { + for ( int j = 0; j < dynamicPorts[k - 1].ConnectionCount; j++ ) + { + XNode.NodePort other = dynamicPorts[k - 1].GetConnection( j ); + dynamicPorts[k - 1].Disconnect( other ); + dynamicPorts[k].Connect( other ); + } + } + + // Let's just re-add connections to this node that were probably his + foreach ( var c in lastRemovedConnections ) + dynamicPorts[index].Connect( c ); + + lastRemovedConnections.Clear(); + + base.InsertAt( collection, index, value ); + } + + protected override void Remove( TList collection, object value ) + { + int index = collection.IndexOf( (TElement)value ); + RemoveAt( collection, index ); + } + + protected List lastRemovedConnections = new List(); + + protected override void RemoveAt( TList collection, int index ) + { + var dynamicPorts = this.ports; + + if ( dynamicPorts[index] == null ) + { + Debug.LogWarning( "No port found at index " + index + " - Skipped" ); + } + else if ( dynamicPorts.Count <= index ) + { + Debug.LogWarning( "DynamicPorts[" + index + "] out of range. Length was " + dynamicPorts.Count + " - Skipped" ); + } + else + { + lastRemovedConnections.Clear(); + lastRemovedConnections.AddRange( dynamicPorts[index].GetConnections() ); + + // 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++ ) + { + XNode.NodePort other = dynamicPorts[k].GetConnection( j ); + dynamicPorts[k].Disconnect( other ); + dynamicPorts[k - 1].Connect( other ); + } + } + + // Remove the last dynamic port, to avoid messing up the indexing + node.RemoveDynamicPort( dynamicPorts[dynamicPorts.Count() - 1].fieldName ); + } + + base.RemoveAt( collection, index ); + } + + protected override void Clear( TList collection ) + { + foreach ( var port in ports ) + node.RemoveDynamicPort( port ); + + lastRemovedConnections.Clear(); + + base.Clear( collection ); + } + + protected Node node => ( Property.Tree.UnitySerializedObject.targetObject as Node ); + protected List ports + { + get + { + // This created a lot of garbage + List dynamicPorts = new List(); + for ( int i = 0; i < int.MaxValue; ++i ) + { + var nodePort = node.GetPort( $"{Property.Name} {i}" ); + if ( nodePort == null ) + break; + + dynamicPorts.Add( nodePort ); + } + return dynamicPorts; + } + } + + protected bool IsInput => Property.GetAttribute() != null; + + public ConnectionType connectionType => IsInput ? Property.GetAttribute().connectionType : Property.GetAttribute().connectionType; + public TypeConstraint typeConstraint => IsInput ? Property.GetAttribute().typeConstraint : Property.GetAttribute().typeConstraint; + public ShowBackingValue backingValue => IsInput ? Property.GetAttribute().backingValue : Property.GetAttribute().backingValue; + } +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs.meta b/Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs.meta new file mode 100644 index 0000000..4f887a2 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f8fa0ef545c9f5049bbc8da047874fd1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs b/Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs new file mode 100644 index 0000000..0d8b27b --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs @@ -0,0 +1,250 @@ +#if UNITY_EDITOR && ODIN_INSPECTOR +using Sirenix.OdinInspector; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Editor.Drawers; +using Sirenix.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using XNode; +using XNodeEditor.Odin; +using static XNode.Node; + +namespace XNodeEditor +{ + [DrawerPriority( 0.4, 0, 0 )] + public class SillyCollectionDrawer : CollectionDrawer + { + } + + public class DynamicPortListNoDataResolver : DynamicPortListNoDataResolver { } + + public class DynamicPortListNoDataResolver : BaseOrderedCollectionResolver + where TElement : NodePort + { + public override bool CanResolveForPropertyFilter( InspectorProperty property ) + { + var input = property.GetAttribute(); + if ( input != null ) + return input.dynamicPortList; + + var output = property.GetAttribute(); + if ( output != null ) + return output.dynamicPortList; + + return false; + } + + private Dictionary childInfos = new Dictionary(); + + public override InspectorPropertyInfo GetChildInfo( int childIndex ) + { + if ( childIndex < 0 || childIndex >= this.ChildCount ) + { + throw new IndexOutOfRangeException(); + } + + InspectorPropertyInfo result; + + if ( !this.childInfos.TryGetValue( childIndex, out result ) ) + { + var attributes = this.Property.Attributes.Where( attr => !attr.GetType().IsDefined( typeof( DontApplyToListElementsAttribute ), true ) ); + var labelTextAttribute = attributes.OfType().SingleOrDefault(); + var hideLabelAttribute = attributes.OfType().SingleOrDefault(); + + var listDrawerSettingsAttribute = attributes.OfType().SingleOrDefault(); + if ( listDrawerSettingsAttribute == null ) + { + listDrawerSettingsAttribute = new ListDrawerSettingsAttribute() { Expanded = true, ShowPaging = false }; + } + else + { + listDrawerSettingsAttribute.Expanded = true; + listDrawerSettingsAttribute.ShowPaging = false; + } + + attributes = attributes + .Append( GetPortAttribute( Property.Name, childIndex ) ) + .AppendIf( labelTextAttribute == null && hideLabelAttribute == null, new LabelTextAttribute( $"{Property.Name} {childIndex}" ) ); + + result = InspectorPropertyInfo.CreateValue( + name: CollectionResolverUtilities.DefaultIndexToChildName( childIndex ), + order: childIndex, + serializationBackend: this.Property.BaseValueEntry.SerializationBackend, + new GetterSetter( + getter: ( ref TNotAList list ) => ports[childIndex], // Return absolutely nothing? Return a port? + setter: ( ref TNotAList list, TElement element ) => ports[childIndex] = element ), + attributes: attributes.ToArray() ); + + this.childInfos[childIndex] = result; + } + + return result; + } + + internal AsDynamicPortNoDataAtribute GetPortAttribute( string fieldName, int index ) + { + return new AsDynamicPortNoDataAtribute() + { + fieldName = fieldName, + index = index, + Node = node, + + connectionType = connectionType, + backingValue = backingValue + }; + } + + protected override void Add( TNotAList collection, object value ) + { + int nextId = this.ChildCount; + + if ( IsInput ) + this.node.AddDynamicInput( typeof( TNotAList ), connectionType, typeConstraint, $"{Property.Name} {nextId}" ); + else + this.node.AddDynamicOutput( typeof( TNotAList ), connectionType, typeConstraint, $"{Property.Name} {nextId}" ); + + lastRemovedConnections.Clear(); + + //base.Add( collection, value ); + } + + protected override void InsertAt( TNotAList collection, int index, object value ) + { + int nextId = this.ChildCount; + + // Remove happens before insert and we lose all the connections + // Add a new port at the end + if ( IsInput ) + this.node.AddDynamicInput( typeof( TNotAList ), connectionType, typeConstraint, $"{Property.Name} {nextId}" ); + else + this.node.AddDynamicOutput( typeof( TNotAList ), connectionType, typeConstraint, $"{Property.Name} {nextId}" ); + + var dynamicPorts = this.ports; + + // Move everything down to make space + for ( int k = dynamicPorts.Count - 1; k > index; --k ) + { + for ( int j = 0; j < dynamicPorts[k - 1].ConnectionCount; j++ ) + { + XNode.NodePort other = dynamicPorts[k - 1].GetConnection( j ); + dynamicPorts[k - 1].Disconnect( other ); + dynamicPorts[k].Connect( other ); + } + } + + // Let's just re-add connections to this node that were probably his + foreach ( var c in lastRemovedConnections ) + dynamicPorts[index].Connect( c ); + + lastRemovedConnections.Clear(); + + //base.InsertAt( collection, index, value ); + } + + protected override void Remove( TNotAList collection, object value ) + { + //int index = collection.IndexOf( (TElement)value ); + //RemoveAt( collection, index ); + throw new NotImplementedException(); + } + + protected List lastRemovedConnections = new List(); + + protected override void RemoveAt( TNotAList collection, int index ) + { + var dynamicPorts = this.ports; + + if ( dynamicPorts[index] == null ) + { + Debug.LogWarning( "No port found at index " + index + " - Skipped" ); + } + else if ( dynamicPorts.Count <= index ) + { + Debug.LogWarning( "DynamicPorts[" + index + "] out of range. Length was " + dynamicPorts.Count + " - Skipped" ); + } + else + { + lastRemovedConnections.Clear(); + lastRemovedConnections.AddRange( dynamicPorts[index].GetConnections() ); + + // 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++ ) + { + XNode.NodePort other = dynamicPorts[k].GetConnection( j ); + dynamicPorts[k].Disconnect( other ); + dynamicPorts[k - 1].Connect( other ); + } + } + + // Remove the last dynamic port, to avoid messing up the indexing + node.RemoveDynamicPort( dynamicPorts[dynamicPorts.Count() - 1].fieldName ); + } + + //base.RemoveAt( collection, index ); + } + + protected override void Clear( TNotAList collection ) + { + foreach ( var port in ports ) + node.RemoveDynamicPort( port ); + + lastRemovedConnections.Clear(); + + //base.Clear( collection ); + } + + public override bool ChildPropertyRequiresRefresh( int index, InspectorPropertyInfo info ) + { + return false; + } + + protected override bool CollectionIsReadOnly( TNotAList collection ) + { + return false; + } + + protected override int GetChildCount( TNotAList value ) + { + return ports.Count; + } + + public override int ChildNameToIndex( string name ) + { + return CollectionResolverUtilities.DefaultChildNameToIndex( name ); + } + + public override Type ElementType => typeof( TElement ); + + protected Node node => ( Property.Tree.UnitySerializedObject.targetObject as Node ); + protected List ports + { + get + { + // This created a lot of garbage + List dynamicPorts = new List(); + for ( int i = 0; i < int.MaxValue; ++i ) + { + var nodePort = node.GetPort( $"{Property.Name} {i}" ); + if ( nodePort == null ) + break; + + dynamicPorts.Add( nodePort as TElement ); + } + return dynamicPorts; + } + } + + protected bool IsInput => Property.GetAttribute() != null; + + public ConnectionType connectionType => IsInput ? Property.GetAttribute().connectionType : Property.GetAttribute().connectionType; + public TypeConstraint typeConstraint => IsInput ? Property.GetAttribute().typeConstraint : Property.GetAttribute().typeConstraint; + public ShowBackingValue backingValue => IsInput ? Property.GetAttribute().backingValue : Property.GetAttribute().backingValue; + } +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs.meta b/Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs.meta new file mode 100644 index 0000000..971da19 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16bd57b189b3213449470c2dce67f5ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs b/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs index 84c6d8e..5794eda 100644 --- a/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs +++ b/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs @@ -1,19 +1,20 @@ #if UNITY_EDITOR && ODIN_INSPECTOR +using Sirenix.OdinInspector.Editor; using System; using System.Collections.Generic; using System.Reflection; -using Sirenix.OdinInspector.Editor; using UnityEngine; using XNode; -namespace XNodeEditor { +namespace XNodeEditor +{ internal class OdinNodeInGraphAttributeProcessor : OdinAttributeProcessor where T : Node { public override bool CanProcessSelfAttributes(InspectorProperty property) { return false; } public override bool CanProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member) { - if (!NodeEditor.inNodeEditor) + if (!NodeEditor.isNodeEditor) return false; if (member.MemberType == MemberTypes.Field) { diff --git a/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs b/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs deleted file mode 100644 index a384bdc..0000000 --- a/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs +++ /dev/null @@ -1,49 +0,0 @@ -#if UNITY_EDITOR && ODIN_INSPECTOR -using Sirenix.OdinInspector; -using Sirenix.OdinInspector.Editor; -using Sirenix.Utilities.Editor; -using UnityEngine; -using XNode; - -namespace XNodeEditor { - public class InputAttributeDrawer : OdinAttributeDrawer { - protected override bool CanDrawAttributeProperty(InspectorProperty property) { - Node node = property.Tree.WeakTargets[0] as Node; - return node != null; - } - - protected override void DrawPropertyLayout(GUIContent label) { - Node node = Property.Tree.WeakTargets[0] as Node; - NodePort port = node.GetInputPort(Property.Name); - - if (!NodeEditor.inNodeEditor) { - if (Attribute.backingValue == XNode.Node.ShowBackingValue.Always || Attribute.backingValue == XNode.Node.ShowBackingValue.Unconnected && !port.IsConnected) - CallNextDrawer(label); - return; - } - - if (Property.Tree.WeakTargets.Count > 1) { - SirenixEditorGUI.WarningMessageBox("Cannot draw ports with multiple nodes selected"); - return; - } - - if (port != null) { - var portPropoerty = Property.Tree.GetUnityPropertyForPath(Property.UnityPropertyPath); - if (portPropoerty == null) { - SirenixEditorGUI.ErrorMessageBox("Port property missing at: " + Property.UnityPropertyPath); - return; - } else { - var labelWidth = Property.GetAttribute(); - if (labelWidth != null) - GUIHelper.PushLabelWidth(labelWidth.Width); - - NodeEditorGUILayout.PropertyField(portPropoerty, label == null ? GUIContent.none : label, true, GUILayout.MinWidth(30)); - - if (labelWidth != null) - GUIHelper.PopLabelWidth(); - } - } - } - } -} -#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs b/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs deleted file mode 100644 index ff59615..0000000 --- a/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs +++ /dev/null @@ -1,49 +0,0 @@ -#if UNITY_EDITOR && ODIN_INSPECTOR -using Sirenix.OdinInspector; -using Sirenix.OdinInspector.Editor; -using Sirenix.Utilities.Editor; -using UnityEngine; -using XNode; - -namespace XNodeEditor { - public class OutputAttributeDrawer : OdinAttributeDrawer { - protected override bool CanDrawAttributeProperty(InspectorProperty property) { - Node node = property.Tree.WeakTargets[0] as Node; - return node != null; - } - - protected override void DrawPropertyLayout(GUIContent label) { - Node node = Property.Tree.WeakTargets[0] as Node; - NodePort port = node.GetOutputPort(Property.Name); - - if (!NodeEditor.inNodeEditor) { - if (Attribute.backingValue == XNode.Node.ShowBackingValue.Always || Attribute.backingValue == XNode.Node.ShowBackingValue.Unconnected && !port.IsConnected) - CallNextDrawer(label); - return; - } - - if (Property.Tree.WeakTargets.Count > 1) { - SirenixEditorGUI.WarningMessageBox("Cannot draw ports with multiple nodes selected"); - return; - } - - if (port != null) { - var portPropoerty = Property.Tree.GetUnityPropertyForPath(Property.UnityPropertyPath); - if (portPropoerty == null) { - SirenixEditorGUI.ErrorMessageBox("Port property missing at: " + Property.UnityPropertyPath); - return; - } else { - var labelWidth = Property.GetAttribute(); - if (labelWidth != null) - GUIHelper.PushLabelWidth(labelWidth.Width); - - NodeEditorGUILayout.PropertyField(portPropoerty, label == null ? GUIContent.none : label, true, GUILayout.MinWidth(30)); - - if (labelWidth != null) - GUIHelper.PopLabelWidth(); - } - } - } - } -} -#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs b/Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs new file mode 100644 index 0000000..c2dd36c --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs @@ -0,0 +1,26 @@ +#if UNITY_EDITOR && ODIN_INSPECTOR +using Sirenix.OdinInspector.Editor; +using UnityEditor; +using UnityEngine; +using XNode; + +namespace XNodeEditor.Odin +{ + public class SimpleNodePortDrawer : OdinValueDrawer + where T : NodePort + { + protected override void Initialize() + { + base.Initialize(); + + this.SkipWhenDrawing = !NodeEditor.isNodeEditor; + } + + protected override void DrawPropertyLayout( GUIContent label ) + { + if ( label != null ) + EditorGUILayout.LabelField( label ); + } + } +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs.meta b/Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs.meta new file mode 100644 index 0000000..5229044 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e30f21284cedd4945ab30934cd164eee +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 8d293ab..7df07cb 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; + public static bool isNodeEditor { get; internal set; } #endif public virtual void OnHeaderGUI() { @@ -31,7 +31,7 @@ namespace XNodeEditor { /// Draws standard field editors for all public fields public virtual void OnBodyGUI() { #if ODIN_INSPECTOR - inNodeEditor = true; + isNodeEditor = true; #endif // Unity specifically requires this to save/update any serial object. @@ -57,13 +57,13 @@ namespace XNodeEditor { if (excludes.Contains(iterator.name)) continue; NodeEditorGUILayout.PropertyField(iterator, true); } -#endif // Iterate through dynamic ports and draw them in the order in which they are serialized foreach (XNode.NodePort dynamicPort in target.DynamicPorts) { if (NodeEditorGUILayout.IsDynamicPortListPort(dynamicPort)) continue; NodeEditorGUILayout.PortField(dynamicPort); } +#endif serializedObject.ApplyModifiedProperties(); @@ -78,7 +78,7 @@ namespace XNodeEditor { #endif #if ODIN_INSPECTOR - inNodeEditor = false; + isNodeEditor = false; #endif } diff --git a/Scripts/Editor/NodeEditorBase.cs b/Scripts/Editor/NodeEditorBase.cs index 1fc28c7..3201079 100644 --- a/Scripts/Editor/NodeEditorBase.cs +++ b/Scripts/Editor/NodeEditorBase.cs @@ -26,10 +26,10 @@ namespace XNodeEditor.Internal { get { if (this._objectTree == null) { try { - bool wasInEditor = NodeEditor.inNodeEditor; - NodeEditor.inNodeEditor = true; + bool wasInEditor = NodeEditor.isNodeEditor; + NodeEditor.isNodeEditor = true; this._objectTree = PropertyTree.Create(this.serializedObject); - NodeEditor.inNodeEditor = wasInEditor; + NodeEditor.isNodeEditor = wasInEditor; } catch (ArgumentException ex) { Debug.Log(ex); }