mirror of
https://github.com/Siccity/xNode.git
synced 2026-03-26 22:49:02 +08:00
Refactored OdinInspector integration.
This commit is contained in:
parent
979bd5f7cf
commit
489767b313
98
Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs
Normal file
98
Scripts/Editor/Drawers/Odin/AsDynamicPortAttributes.cs
Normal file
@ -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<T> : OdinAttributeDrawer<AsDynamicPortNoDataAtribute, T>
|
||||
{
|
||||
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<T> : OdinAttributeDrawer<AsDynamicPortWithDataAtribute, T>
|
||||
{
|
||||
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
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fd590b2e9ea0bd49b6986a2ca9010ab
|
||||
guid: 0ef93b42a7d5fe8459b6755d72583900
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
83
Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs
Normal file
83
Scripts/Editor/Drawers/Odin/AsStaticPortAttributes.cs
Normal file
@ -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<T> : OdinAttributeDrawer<InputAttribute, T>
|
||||
{
|
||||
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<T> : OdinAttributeDrawer<OutputAttribute, T>
|
||||
{
|
||||
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
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7ebd8f2b42e2384aa109551dc46af88
|
||||
guid: 36e90d7590dbcad418bc8ca94192d5e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
219
Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs
Normal file
219
Scripts/Editor/Drawers/Odin/DynamicPortListBackedResolver.cs
Normal file
@ -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<TList, TElement> : StrongListPropertyResolver<TList, TElement>
|
||||
where TList : IList<TElement>
|
||||
{
|
||||
public override bool CanResolveForPropertyFilter( InspectorProperty property )
|
||||
{
|
||||
var input = property.GetAttribute<InputAttribute>();
|
||||
if ( input != null )
|
||||
return input.dynamicPortList;
|
||||
|
||||
var output = property.GetAttribute<OutputAttribute>();
|
||||
if ( output != null )
|
||||
return output.dynamicPortList;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Dictionary<int, InspectorPropertyInfo> childInfos = new Dictionary<int, InspectorPropertyInfo>();
|
||||
|
||||
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<LabelTextAttribute>().SingleOrDefault();
|
||||
var hideLabelAttribute = attributes.OfType<HideLabelAttribute>().SingleOrDefault();
|
||||
|
||||
var listDrawerSettingsAttribute = attributes.OfType<ListDrawerSettingsAttribute>().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<TList, TElement>(
|
||||
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<NodePort> lastRemovedConnections = new List<NodePort>();
|
||||
|
||||
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<NodePort> ports
|
||||
{
|
||||
get
|
||||
{
|
||||
// This created a lot of garbage
|
||||
List<NodePort> dynamicPorts = new List<NodePort>();
|
||||
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<InputAttribute>() != null;
|
||||
|
||||
public ConnectionType connectionType => IsInput ? Property.GetAttribute<InputAttribute>().connectionType : Property.GetAttribute<OutputAttribute>().connectionType;
|
||||
public TypeConstraint typeConstraint => IsInput ? Property.GetAttribute<InputAttribute>().typeConstraint : Property.GetAttribute<OutputAttribute>().typeConstraint;
|
||||
public ShowBackingValue backingValue => IsInput ? Property.GetAttribute<InputAttribute>().backingValue : Property.GetAttribute<OutputAttribute>().backingValue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8fa0ef545c9f5049bbc8da047874fd1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
250
Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs
Normal file
250
Scripts/Editor/Drawers/Odin/DynamicPortListNoDataResolver.cs
Normal file
@ -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<int>
|
||||
{
|
||||
}
|
||||
|
||||
public class DynamicPortListNoDataResolver<T> : DynamicPortListNoDataResolver<T, NodePort> { }
|
||||
|
||||
public class DynamicPortListNoDataResolver<TNotAList, TElement> : BaseOrderedCollectionResolver<TNotAList>
|
||||
where TElement : NodePort
|
||||
{
|
||||
public override bool CanResolveForPropertyFilter( InspectorProperty property )
|
||||
{
|
||||
var input = property.GetAttribute<InputAttribute>();
|
||||
if ( input != null )
|
||||
return input.dynamicPortList;
|
||||
|
||||
var output = property.GetAttribute<OutputAttribute>();
|
||||
if ( output != null )
|
||||
return output.dynamicPortList;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Dictionary<int, InspectorPropertyInfo> childInfos = new Dictionary<int, InspectorPropertyInfo>();
|
||||
|
||||
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<LabelTextAttribute>().SingleOrDefault();
|
||||
var hideLabelAttribute = attributes.OfType<HideLabelAttribute>().SingleOrDefault();
|
||||
|
||||
var listDrawerSettingsAttribute = attributes.OfType<ListDrawerSettingsAttribute>().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<TNotAList, TElement>(
|
||||
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<NodePort> lastRemovedConnections = new List<NodePort>();
|
||||
|
||||
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<TElement> ports
|
||||
{
|
||||
get
|
||||
{
|
||||
// This created a lot of garbage
|
||||
List<TElement> dynamicPorts = new List<TElement>();
|
||||
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<InputAttribute>() != null;
|
||||
|
||||
public ConnectionType connectionType => IsInput ? Property.GetAttribute<InputAttribute>().connectionType : Property.GetAttribute<OutputAttribute>().connectionType;
|
||||
public TypeConstraint typeConstraint => IsInput ? Property.GetAttribute<InputAttribute>().typeConstraint : Property.GetAttribute<OutputAttribute>().typeConstraint;
|
||||
public ShowBackingValue backingValue => IsInput ? Property.GetAttribute<InputAttribute>().backingValue : Property.GetAttribute<OutputAttribute>().backingValue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16bd57b189b3213449470c2dce67f5ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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<T> : OdinAttributeProcessor<T> 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) {
|
||||
|
||||
@ -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<XNode.Node.InputAttribute> {
|
||||
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<LabelWidthAttribute>();
|
||||
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
|
||||
@ -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<XNode.Node.OutputAttribute> {
|
||||
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<LabelWidthAttribute>();
|
||||
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
|
||||
26
Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs
Normal file
26
Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs
Normal file
@ -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<T> : OdinValueDrawer<T>
|
||||
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
|
||||
11
Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs.meta
Normal file
11
Scripts/Editor/Drawers/Odin/SimpleNodePortDrawer.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e30f21284cedd4945ab30934cd164eee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -21,7 +21,7 @@ namespace XNodeEditor {
|
||||
public readonly static Dictionary<XNode.NodePort, Vector2> portPositions = new Dictionary<XNode.NodePort, Vector2>();
|
||||
|
||||
#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 {
|
||||
/// <summary> Draws standard field editors for all public fields </summary>
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user