├── .editorconfig ├── Editor.meta ├── Editor ├── Drawers.meta ├── Drawers │ ├── Attributes.meta │ ├── Attributes │ │ ├── Node.meta │ │ ├── Node │ │ │ ├── DisplayDynamicPortsAttributeDrawer.cs │ │ │ └── DisplayDynamicPortsAttributeDrawer.cs.meta │ │ ├── NodePortAttributeDrawer.cs │ │ ├── NodePortAttributeDrawer.cs.meta │ │ ├── Port.meta │ │ └── Port │ │ │ ├── BoxedPortAttributeDrawer.cs │ │ │ ├── BoxedPortAttributeDrawer.cs.meta │ │ │ ├── DrawConnectionNameAttributeDrawer.cs │ │ │ ├── DrawConnectionNameAttributeDrawer.cs.meta │ │ │ ├── FoldoutPortAttributeDrawer.cs │ │ │ ├── FoldoutPortAttributeDrawer.cs.meta │ │ │ ├── HandleOnlyAttributeDrawer.cs │ │ │ ├── HandleOnlyAttributeDrawer.cs.meta │ │ │ ├── HidePortLabelAttributeDrawer.cs │ │ │ └── HidePortLabelAttributeDrawer.cs.meta │ ├── DefaultNodePortDrawer.cs │ ├── DefaultNodePortDrawer.cs.meta │ ├── DefaultNodePortListDrawer.cs │ ├── DefaultNodePortListDrawer.cs.meta │ ├── DynamicNoDataNodePropertyPortDrawer.cs │ ├── DynamicNoDataNodePropertyPortDrawer.cs.meta │ ├── DynamicNodePropertyPortNameDrawer.cs │ ├── DynamicNodePropertyPortNameDrawer.cs.meta │ ├── FoldedNodePropertyDrawer.cs │ ├── FoldedNodePropertyDrawer.cs.meta │ ├── NodePortDrawer.cs │ ├── NodePortDrawer.cs.meta │ ├── NodePortDrawerHelper.cs │ ├── NodePortDrawerHelper.cs.meta │ ├── NodePortListDrawer.cs │ └── NodePortListDrawer.cs.meta ├── Processors.meta ├── Processors │ ├── HideInNodeEditorAttributeProcessor.cs │ ├── HideInNodeEditorAttributeProcessor.cs.meta │ ├── HideOutsideNodeEditorAttributeProcessor.cs │ ├── HideOutsideNodeEditorAttributeProcessor.cs.meta │ ├── NodeGraphPropertyProcessor.cs │ ├── NodeGraphPropertyProcessor.cs.meta │ ├── NodePortAttributeProcessor.cs │ ├── NodePortAttributeProcessor.cs.meta │ ├── NodePropertyProcessor.cs │ └── NodePropertyProcessor.cs.meta ├── Resolvers.meta ├── Resolvers │ ├── DynamicDataNodePropertyPortResolver.cs │ ├── DynamicDataNodePropertyPortResolver.cs.meta │ ├── DynamicNoDataNodePropertyPortResolver.cs │ ├── DynamicNoDataNodePropertyPortResolver.cs.meta │ ├── DynamicPortHelper.cs │ ├── DynamicPortHelper.cs.meta │ ├── NodePropertyPortResolver.cs │ └── NodePropertyPortResolver.cs.meta ├── XNodeEditorOdin.asmdef └── XNodeEditorOdin.asmdef.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── Attributes.meta ├── Attributes │ ├── Node.meta │ ├── Node │ │ ├── DisplayDynamicPortsAttribute.cs │ │ ├── DisplayDynamicPortsAttribute.cs.meta │ │ ├── ShowFoldButtonsAttribute.cs │ │ ├── ShowFoldButtonsAttribute.cs.meta │ │ ├── ShowNameInInspectorAttribute.cs │ │ ├── ShowNameInInspectorAttribute.cs.meta │ │ ├── ShowNameInNodeEditorAttribute.cs │ │ └── ShowNameInNodeEditorAttribute.cs.meta │ ├── Port.meta │ └── Port │ │ ├── BoxedPortAttribute.cs │ │ ├── BoxedPortAttribute.cs.meta │ │ ├── DrawConnectionNameAttribute.cs │ │ ├── DrawConnectionNameAttribute.cs.meta │ │ ├── Dynamic.meta │ │ ├── Dynamic │ │ ├── HideConnectionLabelAttribute.cs │ │ └── HideConnectionLabelAttribute.cs.meta │ │ ├── FoldoutPortAttribute.cs │ │ ├── FoldoutPortAttribute.cs.meta │ │ ├── HandleOnlyAttribute.cs │ │ ├── HandleOnlyAttribute.cs.meta │ │ ├── HideInNodeEditorAttribute.cs │ │ ├── HideInNodeEditorAttribute.cs.meta │ │ ├── HideOutsideNodeEditor.cs │ │ ├── HideOutsideNodeEditor.cs.meta │ │ ├── HidePortLabelAttribute.cs │ │ ├── HidePortLabelAttribute.cs.meta │ │ ├── NodePortAttribute.cs │ │ ├── NodePortAttribute.cs.meta │ │ ├── NodePortConfigurationAttribute.cs │ │ └── NodePortConfigurationAttribute.cs.meta ├── OdinSerializer.meta ├── OdinSerializer │ ├── SerializableNode.cs │ ├── SerializableNode.cs.meta │ ├── XNodeOdinSerializer.asmdef │ └── XNodeOdinSerializer.asmdef.meta ├── XNodeOdin.asmdef └── XNodeOdin.asmdef.meta ├── package.json └── package.json.meta /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.cs] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = crlf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4631662a46a6c404e902e0ee6e055afc 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Drawers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 369bbaeccf4154b42a6c52a254008c2a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 62943cafdd899a74ea063e014f491aa1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Node.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4305b878d2a499143be58b09f46705cf 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Node/DisplayDynamicPortsAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector.Editor; 2 | using Sirenix.Utilities.Editor; 3 | using UnityEditor; 4 | using UnityEngine; 5 | using XNode.Odin; 6 | 7 | namespace XNodeEditor.Odin 8 | { 9 | [NodePortAttributeDrawerPriority] 10 | public class DisplayDynamicPortsAttributeDrawer : NodePortAttributeDrawer 11 | { 12 | protected override bool CanDrawNodePort( INodePortResolver portResolver, NodePortInfo nodePortInfo, InspectorProperty property ) 13 | { 14 | return property.GetAttribute().ShowRemoveButton; 15 | } 16 | 17 | protected override void DrawPort( GUIContent label ) 18 | { 19 | if ( IsVisible ) 20 | { 21 | SirenixEditorGUI.BeginBox(); 22 | if ( NodePortInfo.IsInput ) 23 | { 24 | using ( new EditorGUILayout.HorizontalScope() ) 25 | { 26 | CallNextDrawer( label ); 27 | if ( GUILayout.Button( "Remove" ) ) 28 | PortResolver.ForgetDynamicPort( Property ); 29 | } 30 | } 31 | else 32 | { 33 | using ( new EditorGUILayout.HorizontalScope() ) 34 | { 35 | if ( GUILayout.Button( "Remove" ) ) 36 | PortResolver.ForgetDynamicPort( Property ); 37 | CallNextDrawer( label ); 38 | } 39 | } 40 | SirenixEditorGUI.EndBox(); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Node/DisplayDynamicPortsAttributeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ef8f47ec39550b042a3c0bb437a4d840 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/NodePortAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Sirenix.OdinInspector.Editor; 4 | using Sirenix.Utilities.Editor; 5 | using UnityEngine; 6 | using XNode.Odin; 7 | using static XNode.Node; 8 | 9 | namespace XNodeEditor.Odin 10 | { 11 | public class NodePortAttributeDrawerPriorityAttribute : DrawerPriorityAttribute 12 | { 13 | public NodePortAttributeDrawerPriorityAttribute( int offset = 0 ) : base( 0, 1, 1000 + offset ) 14 | { 15 | } 16 | } 17 | 18 | public abstract class NodePortAttributeDrawer : OdinAttributeDrawer 19 | where TAttribute : NodePortAttribute 20 | { 21 | protected sealed override bool CanDrawAttributeProperty( InspectorProperty property ) 22 | { 23 | if ( !NodeEditor.InNodeEditor ) 24 | return false; 25 | 26 | var parent = property.ParentValueProperty; 27 | #if ODIN_INSPECTOR_3 28 | if ( parent == null ) 29 | parent = property.Tree.RootProperty; 30 | #else 31 | if ( parent == null ) 32 | parent = property.Tree.SecretRootProperty; 33 | #endif 34 | 35 | if ( parent.ChildResolver is INodePortResolver ) 36 | { 37 | var resolver = parent.ChildResolver as INodePortResolver; 38 | NodePortInfo portInfo = resolver.GetNodePortInfo( property.Name ); 39 | if ( portInfo != null ) 40 | return ( portInfo.IsDynamic || !portInfo.IsDynamicPortList ) && CanDrawNodePort( resolver, portInfo, property ); 41 | 42 | return false; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | protected virtual bool CanDrawNodePort( INodePortResolver portResolver, NodePortInfo nodePortInfo, InspectorProperty property ) 49 | { 50 | return true; 51 | } 52 | 53 | protected INodePortResolver PortResolver { get; private set; } 54 | protected NodePortInfo NodePortInfo { get; private set; } 55 | protected bool CanFold { get; private set; } 56 | 57 | protected bool DrawValue { get; private set; } 58 | protected bool IsVisible { get; private set; } 59 | 60 | protected override void Initialize() 61 | { 62 | var parent = Property.ParentValueProperty; 63 | #if ODIN_INSPECTOR_3 64 | if ( parent == null ) 65 | parent = Property.Tree.RootProperty; 66 | #else 67 | if ( parent == null ) 68 | parent = Property.Tree.SecretRootProperty; 69 | #endif 70 | 71 | PortResolver = parent.ChildResolver as INodePortResolver; 72 | NodePortInfo = PortResolver.GetNodePortInfo( Property.Name ); 73 | CanFold = Property.GetAttribute() == null; 74 | DrawValue = true; 75 | } 76 | 77 | protected sealed override void DrawPropertyLayout( GUIContent label ) 78 | { 79 | if ( NodePortDrawerHelper.DisplayMissingPort( Property, PortResolver, NodePortInfo ) ) 80 | return; 81 | 82 | if ( Event.current.type == EventType.Layout && !NodeEditorWindow.current.IsDraggingPort ) 83 | { 84 | switch ( NodePortInfo.ShowBackingValue ) 85 | { 86 | case ShowBackingValue.Always: 87 | DrawValue = true; 88 | break; 89 | 90 | case ShowBackingValue.Never: 91 | DrawValue = false; 92 | break; 93 | 94 | case ShowBackingValue.Unconnected: 95 | DrawValue = !NodePortInfo.Port.IsConnected; 96 | break; 97 | } 98 | 99 | IsVisible = !NodePortInfo.Node.folded; 100 | IsVisible |= NodePortInfo.ShowBackingValue == ShowBackingValue.Always; 101 | IsVisible |= PortResolver is IDynamicDataNodePropertyPortResolver; // Dynamics will be folded somewhere else 102 | IsVisible |= NodePortInfo.Port.IsConnected; 103 | IsVisible |= !CanFold; 104 | 105 | DrawValue &= NodePortInfo.HasValue; 106 | } 107 | 108 | if ( !IsVisible ) 109 | return; 110 | 111 | DrawPort( label ); 112 | } 113 | 114 | protected abstract void DrawPort( GUIContent label ); 115 | } 116 | 117 | public abstract class NodePortAttributeDrawer : OdinAttributeDrawer 118 | where TAttribute : NodePortAttribute 119 | { 120 | protected sealed override bool CanDrawAttributeValueProperty( InspectorProperty property ) 121 | { 122 | if ( !NodeEditor.InNodeEditor ) 123 | return false; 124 | 125 | var parent = property.ParentValueProperty; 126 | #if ODIN_INSPECTOR_3 127 | if ( parent == null ) 128 | parent = property.Tree.RootProperty; 129 | #else 130 | if ( parent == null ) 131 | parent = property.Tree.SecretRootProperty; 132 | #endif 133 | 134 | if ( parent.ChildResolver is INodePortResolver ) 135 | { 136 | var resolver = parent.ChildResolver as INodePortResolver; 137 | NodePortInfo portInfo = resolver.GetNodePortInfo( property.Name ); 138 | if ( portInfo != null ) 139 | return ( portInfo.IsDynamic || !portInfo.IsDynamicPortList ) && CanDrawNodePort( portInfo, property ); 140 | 141 | return false; 142 | } 143 | 144 | return false; 145 | } 146 | protected virtual bool CanDrawNodePort( NodePortInfo nodePortInfo, InspectorProperty property ) 147 | { 148 | return true; 149 | } 150 | 151 | protected INodePortResolver PortResolver { get; private set; } 152 | protected NodePortInfo NodePortInfo { get; private set; } 153 | protected bool CanFold { get; private set; } 154 | 155 | protected bool DrawValue { get; private set; } 156 | protected bool IsVisible { get; private set; } 157 | 158 | protected override void Initialize() 159 | { 160 | var parent = Property.ParentValueProperty; 161 | #if ODIN_INSPECTOR_3 162 | if ( parent == null ) 163 | parent = Property.Tree.RootProperty; 164 | #else 165 | if ( parent == null ) 166 | parent = Property.Tree.SecretRootProperty; 167 | #endif 168 | 169 | PortResolver = parent.ChildResolver as INodePortResolver; 170 | NodePortInfo = PortResolver.GetNodePortInfo( Property.Name ); 171 | CanFold = Property.GetAttribute() == null; 172 | DrawValue = true; 173 | } 174 | 175 | protected sealed override void DrawPropertyLayout( GUIContent label ) 176 | { 177 | if ( NodePortDrawerHelper.DisplayMissingPort( Property, PortResolver, NodePortInfo ) ) 178 | return; 179 | 180 | if ( Event.current.type == EventType.Layout && !NodeEditorWindow.current.IsDraggingPort ) 181 | { 182 | switch ( NodePortInfo.ShowBackingValue ) 183 | { 184 | case ShowBackingValue.Always: 185 | DrawValue = true; 186 | break; 187 | 188 | case ShowBackingValue.Never: 189 | DrawValue = false; 190 | break; 191 | 192 | case ShowBackingValue.Unconnected: 193 | DrawValue = !NodePortInfo.Port.IsConnected; 194 | break; 195 | } 196 | 197 | IsVisible = !NodePortInfo.Node.folded; 198 | IsVisible |= NodePortInfo.ShowBackingValue == ShowBackingValue.Always; 199 | IsVisible |= PortResolver is IDynamicDataNodePropertyPortResolver; // Dynamics will be folded somewhere else 200 | IsVisible |= NodePortInfo.Port.IsConnected; 201 | IsVisible |= !CanFold; 202 | 203 | DrawValue &= NodePortInfo.HasValue; 204 | } 205 | 206 | if ( !IsVisible ) 207 | return; 208 | 209 | DrawPort( label ); 210 | } 211 | 212 | protected abstract void DrawPort( GUIContent label ); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/NodePortAttributeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f837868125ec5644d97f25f2b8f095b4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c31cd8f509e9b7d4ca37364e09e7e13e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/BoxedPortAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.Utilities.Editor; 2 | using UnityEditor; 3 | using UnityEngine; 4 | using XNode.Odin; 5 | 6 | namespace XNodeEditor.Odin 7 | { 8 | [NodePortAttributeDrawerPriority] 9 | public class BoxedPortAttributeDrawer : NodePortAttributeDrawer 10 | { 11 | protected override void Initialize() 12 | { 13 | base.Initialize(); 14 | 15 | NodePortDrawerHelper.DisableDefaultPortDrawer( this ); 16 | } 17 | 18 | protected override void DrawPort( GUIContent label ) 19 | { 20 | SirenixEditorGUI.BeginBox(); 21 | SirenixEditorGUI.BeginBoxHeader(); 22 | if ( label != null ) 23 | EditorGUILayout.LabelField( label ); 24 | NodePortDrawerHelper.DrawPortHandle( NodePortInfo ); 25 | SirenixEditorGUI.EndBoxHeader(); 26 | 27 | if ( DrawValue ) 28 | CallNextDrawer( null ); 29 | else 30 | GUILayout.Space( -3.5f ); 31 | 32 | SirenixEditorGUI.EndBox(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/BoxedPortAttributeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4a10f7f10f179384e9f4436e3482f485 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/DrawConnectionNameAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using Sirenix.OdinInspector.Editor; 3 | using Sirenix.Utilities.Editor; 4 | 5 | using UnityEngine; 6 | using XNode.Odin; 7 | using static XNode.Node; 8 | 9 | namespace XNodeEditor.Odin 10 | { 11 | [DrawerPriority( 0.99, 0, 0 )] 12 | public class DrawConnectionNameAttributeDrawer : NodePortAttributeDrawer 13 | { 14 | protected GUIContent connectionName = GUIContent.none; 15 | 16 | protected override void DrawPort( GUIContent label ) 17 | { 18 | // Extra sanity checks 19 | if ( Event.current.type == EventType.Layout ) 20 | { 21 | if ( ( NodePortInfo.ConnectionType == ConnectionType.Override || NodePortInfo.Port.ConnectionCount == 1 ) && 22 | NodePortInfo.Port.IsConnected && NodePortInfo.Port.Connection != null && NodePortInfo.Port.Connection.node != null ) 23 | connectionName = new GUIContent( NodePortInfo.Port.Connection.node.name ); 24 | else 25 | connectionName = label; 26 | } 27 | 28 | CallNextDrawer( connectionName ); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/DrawConnectionNameAttributeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b9958f98274e68c47af91fbd99fdc7d3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/FoldoutPortAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector.Editor; 2 | using Sirenix.Utilities.Editor; 3 | using UnityEngine; 4 | using XNode.Odin; 5 | 6 | namespace XNodeEditor.Odin 7 | { 8 | [NodePortAttributeDrawerPriority] 9 | public class FoldoutPortAttributeDrawer : NodePortAttributeDrawer 10 | { 11 | protected override void Initialize() 12 | { 13 | base.Initialize(); 14 | 15 | NodePortDrawerHelper.DisableDefaultPortDrawer( this ); 16 | isUnfolded = Property.Context.GetPersistent( this, nameof( isUnfolded ), GeneralDrawerConfig.Instance.ExpandFoldoutByDefault ); 17 | } 18 | 19 | protected LocalPersistentContext isUnfolded; 20 | 21 | protected override void DrawPort( GUIContent label ) 22 | { 23 | SirenixEditorGUI.BeginBox(); 24 | SirenixEditorGUI.BeginBoxHeader(); 25 | isUnfolded.Value = SirenixEditorGUI.Foldout( isUnfolded.Value, label == null ? GUIContent.none : label ); 26 | NodePortDrawerHelper.DrawPortHandle( NodePortInfo ); 27 | SirenixEditorGUI.EndBoxHeader(); 28 | 29 | if ( SirenixEditorGUI.BeginFadeGroup( this, isUnfolded.Value ) ) 30 | CallNextDrawer( null ); 31 | SirenixEditorGUI.EndFadeGroup(); 32 | 33 | SirenixEditorGUI.EndBox(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/FoldoutPortAttributeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19f6f00fd6f30a940a2c0c28bc5b67b5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/HandleOnlyAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using XNode.Odin; 3 | 4 | namespace XNodeEditor.Odin 5 | { 6 | [NodePortDrawerPriority( 1 )] 7 | public class HandleOnlyAttributeDrawer : NodePortAttributeDrawer 8 | { 9 | protected override void DrawPort( GUIContent label ) 10 | { 11 | NodePortDrawerHelper.DrawPortHandle( NodePortInfo, 0 ); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/HandleOnlyAttributeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 817360feb2083714db4e01e011d5ce59 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/HidePortLabelAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using XNode.Odin; 3 | 4 | namespace XNodeEditor.Odin 5 | { 6 | [NodePortDrawerPriority( 2 )] 7 | public class HidePortLabelAttributeDrawer : NodePortAttributeDrawer 8 | { 9 | protected override void DrawPort( GUIContent label ) 10 | { 11 | this.CallNextDrawer( null ); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Editor/Drawers/Attributes/Port/HidePortLabelAttributeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b6a996fe26c7a54da12c118d29f7188 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/DefaultNodePortDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using Sirenix.OdinInspector.Editor; 3 | using Sirenix.Utilities.Editor; 4 | using UnityEditor; 5 | 6 | using UnityEngine; 7 | using XNode; 8 | using XNode.Odin; 9 | 10 | namespace XNodeEditor.Odin 11 | { 12 | [NodePortDrawerPriority] 13 | public class DefaultNodePortDrawer : NodePortDrawer, IDefinesGenericMenuItems 14 | { 15 | public bool hideContents = false; 16 | 17 | protected override bool CanDrawNodePort( INodePortResolver portResolver, NodePortInfo nodePortInfo, InspectorProperty property ) 18 | { 19 | // Don't draw ports for lists that are also ports 20 | if ( property.ChildResolver is ICollectionResolver ) 21 | { 22 | if ( property.ChildResolver is IDynamicDataNodePropertyPortResolver ) 23 | return false; 24 | } 25 | 26 | return true; 27 | } 28 | 29 | void IDefinesGenericMenuItems.PopulateGenericMenu( InspectorProperty property, GenericMenu genericMenu ) 30 | { 31 | if ( NodePortInfo.Port.ConnectionCount > 0 ) 32 | { 33 | // Remove all connections 34 | genericMenu.AddSeparator( string.Empty ); 35 | genericMenu.AddItem( new GUIContent( "Clear Connections" ), false, ClearConnections ); 36 | genericMenu.AddSeparator( string.Empty ); 37 | 38 | for ( int i = 0; i < NodePortInfo.Port.ConnectionCount; ++i ) 39 | { 40 | NodePort connection = NodePortInfo.Port.GetConnection( i ); 41 | if ( connection == null ) // Connection exists but isn't actually connected 42 | { 43 | genericMenu.AddItem( new GUIContent( "Remove blank connections" ), false, RemoveBlankConnections ); 44 | break; 45 | } 46 | } 47 | 48 | for ( int i = 0; i < NodePortInfo.Port.ConnectionCount; ++i ) 49 | { 50 | NodePort connection = NodePortInfo.Port.GetConnection( i ); 51 | if ( connection == null ) // Connection exists but isn't actually connected 52 | continue; 53 | 54 | int connectionIndex = i; 55 | genericMenu.AddItem( new GUIContent( $"Disconnect {connectionIndex} {connection.node.name}:{connection.fieldName}" ), false, () => Disconnect( connectionIndex ) ); 56 | } 57 | } 58 | } 59 | 60 | protected void ClearConnections() 61 | { 62 | EditorApplication.delayCall += () => NodePortInfo.Port.ClearConnections(); 63 | } 64 | 65 | protected void RemoveBlankConnections() 66 | { 67 | EditorApplication.delayCall += () => 68 | { 69 | NodePortInfo.Port.VerifyConnections(); 70 | }; 71 | } 72 | 73 | public void Disconnect( int connectionIndex ) 74 | { 75 | EditorApplication.delayCall += () => 76 | { 77 | NodePortInfo.Port.Disconnect( connectionIndex ); 78 | }; 79 | } 80 | 81 | protected override void Initialize() 82 | { 83 | base.Initialize(); 84 | 85 | var configuration = Property.GetAttribute(); 86 | if ( configuration != null ) 87 | hideContents = configuration.HideContents; 88 | } 89 | 90 | protected override void DrawPort( GUIContent label ) 91 | { 92 | if ( hideContents ) 93 | { 94 | NodePortDrawerHelper.DrawPortHandle( NodePortInfo ); 95 | 96 | // Offset back to make up for the port draw 97 | GUILayout.Space( -18 ); 98 | return; 99 | } 100 | 101 | using ( new EditorGUILayout.HorizontalScope() ) 102 | { 103 | NodePortDrawerHelper.DrawPortHandle( NodePortInfo ); 104 | 105 | // Offset back to make up for the port draw 106 | GUILayout.Space( -4 ); 107 | 108 | // Collections don't have the same kinds of labels 109 | if ( DrawValue && Property.ChildResolver is ICollectionResolver ) 110 | { 111 | CallNextDrawer( label ); 112 | return; 113 | } 114 | 115 | bool drawLabel = label != null && label != GUIContent.none; 116 | if ( NodePortInfo.Port.IsInput ) 117 | { 118 | if ( drawLabel ) 119 | { 120 | if ( DrawValue ) 121 | EditorGUILayout.LabelField( label, GUILayout.Width( GUIHelper.BetterLabelWidth ) ); 122 | else 123 | EditorGUILayout.LabelField( label, GUILayout.MaxWidth( float.MaxValue ), GUILayout.ExpandWidth( true ) ); 124 | } 125 | 126 | if ( DrawValue ) 127 | { 128 | using ( new EditorGUILayout.VerticalScope() ) 129 | CallNextDrawer( null ); 130 | } 131 | 132 | if ( !DrawValue || drawLabel && Property.Parent != null && Property.Parent.ChildResolver is GroupPropertyResolver ) 133 | GUILayout.FlexibleSpace(); 134 | } 135 | else 136 | { 137 | if ( !DrawValue || drawLabel && Property.Parent != null && Property.Parent.ChildResolver is GroupPropertyResolver ) 138 | GUILayout.FlexibleSpace(); 139 | 140 | if ( DrawValue ) 141 | { 142 | using ( new EditorGUILayout.VerticalScope() ) 143 | CallNextDrawer( null ); 144 | } 145 | 146 | if ( drawLabel ) 147 | { 148 | if ( DrawValue ) 149 | EditorGUILayout.LabelField( label, NodeEditorResources.OutputPort, GUILayout.Width( GUIHelper.BetterLabelWidth ) ); 150 | else 151 | EditorGUILayout.LabelField( label, NodeEditorResources.OutputPort, GUILayout.MaxWidth( float.MaxValue ), GUILayout.ExpandWidth( true ) ); 152 | } 153 | } 154 | } 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Editor/Drawers/DefaultNodePortDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad28205c107c10844be83268cdd1c99f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/DefaultNodePortListDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | 4 | using Sirenix.OdinInspector.Editor; 5 | 6 | using UnityEngine; 7 | 8 | using XNode; 9 | 10 | namespace XNodeEditor.Odin 11 | { 12 | [NodePortDrawerPriority] 13 | public class DefaultNodePortListDrawer : NodePortListDrawer 14 | { 15 | protected override void DrawPortList( GUIContent label ) 16 | { 17 | CallNextDrawer( label ); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Editor/Drawers/DefaultNodePortListDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5834e8b2e963506459aa3f3ed321d045 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/DynamicNoDataNodePropertyPortDrawer.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector.Editor; 2 | using Sirenix.Utilities; 3 | 4 | using UnityEngine; 5 | 6 | namespace XNodeEditor.Odin 7 | { 8 | [DrawerPriority( 91, 0, 0 )] 9 | public class DynamicNoDataNodePropertyPortDrawer : OdinValueDrawer 10 | { 11 | protected override bool CanDrawValueProperty( InspectorProperty property ) 12 | { 13 | return property.ChildResolver != null && property.ChildResolver.GetType().ImplementsOpenGenericClass( typeof( DynamicNoDataNodePropertyPortResolver<> ) ); 14 | } 15 | 16 | protected override void DrawPropertyLayout( GUIContent label ) 17 | { 18 | var portListProp = Property.Children[NodePropertyPort.NodePortListPropertyName]; 19 | if ( portListProp != null ) 20 | portListProp.Draw( label ); 21 | else 22 | CallNextDrawer( label ); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Editor/Drawers/DynamicNoDataNodePropertyPortDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 573fbecc533e72a40b478c87fbea4ae8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/DynamicNodePropertyPortNameDrawer.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector.Editor; 2 | using Sirenix.Utilities.Editor; 3 | using UnityEngine; 4 | using XNode.Odin; 5 | 6 | namespace XNodeEditor.Odin 7 | { 8 | [DrawerPriority( 1.1, 0, 0 )] 9 | public class DynamicNodePropertyPortNameDrawer : NodePortDrawer 10 | { 11 | protected override bool CanDrawNodePort( INodePortResolver portResolver, NodePortInfo nodePortInfo, InspectorProperty property ) 12 | { 13 | if ( !base.CanDrawNodePort( portResolver, nodePortInfo, property ) ) 14 | return false; 15 | 16 | if ( property.GetAttribute() != null ) 17 | return false; 18 | 19 | return property.Parent != null && property.Parent.ChildResolver is IDynamicDataNodePropertyPortResolver; 20 | } 21 | 22 | protected override void DrawPort( GUIContent label ) 23 | { 24 | if ( label == null ) 25 | { 26 | // Maybe we don't want labels? 27 | var resolver = Property.Parent.ChildResolver as IDynamicDataNodePropertyPortResolver; 28 | var nodePortInfo = resolver.GetNodePortInfo( Property.Name ); 29 | 30 | label = new GUIContent( $"{nodePortInfo.BaseFieldName}" ); 31 | } 32 | 33 | CallNextDrawer( label ); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Editor/Drawers/DynamicNodePropertyPortNameDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cacdbc3a006a47740bb50bdc5cf4ec3c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/FoldedNodePropertyDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using Sirenix.OdinInspector.Editor; 3 | 4 | using UnityEngine; 5 | 6 | namespace XNodeEditor.Odin 7 | { 8 | [DrawerPriority( 1.5, 0, 0 )] 9 | internal class FoldedNodePropertyDrawer : OdinDrawer 10 | { 11 | public override bool CanDrawProperty( InspectorProperty property ) 12 | { 13 | if ( !NodeEditor.InNodeEditor ) 14 | return false; 15 | 16 | var parent = property.ParentValueProperty; 17 | #if ODIN_INSPECTOR_3 18 | if ( property.IsTreeRoot ) 19 | return false; 20 | 21 | if ( parent == null ) 22 | parent = property.Tree.RootProperty; 23 | #else 24 | if ( parent == null ) 25 | parent = property.Tree.SecretRootProperty; 26 | #endif 27 | 28 | if ( parent.ChildResolver is INodePortResolver ) 29 | { 30 | var resolver = parent.ChildResolver as INodePortResolver; 31 | NodePortInfo portInfo = resolver.GetNodePortInfo( property.Name ); 32 | if ( portInfo != null ) 33 | return false; 34 | } 35 | else 36 | { 37 | return false; 38 | } 39 | 40 | if ( property.ChildResolver is GroupPropertyResolver ) 41 | return false; 42 | 43 | return property.GetAttribute() == null; 44 | } 45 | 46 | protected INodePortResolver PortResolver { get; private set; } 47 | protected NodePortInfo NodePortInfo { get; private set; } 48 | protected bool CanFold { get; private set; } 49 | protected bool DrawValue { get; private set; } 50 | 51 | protected override void Initialize() 52 | { 53 | var parent = Property.ParentValueProperty; 54 | #if ODIN_INSPECTOR_3 55 | if ( parent == null ) 56 | parent = Property.Tree.RootProperty; 57 | #else 58 | if ( parent == null ) 59 | parent = Property.Tree.SecretRootProperty; 60 | #endif 61 | 62 | PortResolver = parent.ChildResolver as INodePortResolver; 63 | NodePortInfo = PortResolver.GetNodePortInfo( Property.Name ); 64 | CanFold = Property.GetAttribute() == null; 65 | DrawValue = true; 66 | } 67 | 68 | private bool isVisible = false; 69 | 70 | protected override void DrawPropertyLayout( GUIContent label ) 71 | { 72 | if ( Event.current.type == EventType.Layout ) 73 | isVisible = !PortResolver.Node.folded; 74 | 75 | if ( !isVisible ) 76 | return; 77 | 78 | CallNextDrawer( label ); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Editor/Drawers/FoldedNodePropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8181f2626ea6fe14094f1334a663481b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/NodePortDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Linq; 3 | using Sirenix.OdinInspector.Editor; 4 | using Sirenix.Utilities; 5 | using Sirenix.Utilities.Editor; 6 | using UnityEditor; 7 | using UnityEngine; 8 | using XNode; 9 | using static XNode.Node; 10 | 11 | namespace XNodeEditor.Odin 12 | { 13 | public class NodePortDrawerPriorityAttribute : DrawerPriorityAttribute 14 | { 15 | public NodePortDrawerPriorityAttribute( int offset = 0 ) : base( 0, 1, offset ) 16 | { 17 | } 18 | } 19 | 20 | public abstract class NodePortDrawer : OdinValueDrawer 21 | { 22 | protected sealed override bool CanDrawValueProperty( InspectorProperty property ) 23 | { 24 | if ( !NodeEditor.InNodeEditor ) 25 | return false; 26 | 27 | var parent = property.ParentValueProperty; 28 | #if ODIN_INSPECTOR_3 29 | if ( parent == null ) 30 | parent = property.Tree.RootProperty; 31 | #else 32 | if ( parent == null ) 33 | parent = property.Tree.SecretRootProperty; 34 | #endif 35 | 36 | if ( parent.ChildResolver is INodePortResolver ) 37 | { 38 | var resolver = parent.ChildResolver as INodePortResolver; 39 | NodePortInfo portInfo = resolver.GetNodePortInfo( property.Name ); 40 | if ( portInfo != null ) 41 | return ( portInfo.IsDynamic || !portInfo.IsDynamicPortList ) && CanDrawNodePort( resolver, portInfo, property ); 42 | 43 | return false; 44 | } 45 | 46 | return false; 47 | } 48 | 49 | protected virtual bool CanDrawNodePort( INodePortResolver portResolver, NodePortInfo nodePortInfo, InspectorProperty property ) 50 | { 51 | return true; 52 | } 53 | 54 | protected INodePortResolver PortResolver { get; private set; } 55 | protected NodePortInfo NodePortInfo { get; private set; } 56 | protected bool CanFold { get; private set; } 57 | 58 | protected bool DrawValue { get; private set; } 59 | protected bool IsVisible { get; private set; } 60 | 61 | protected override void Initialize() 62 | { 63 | var parent = Property.ParentValueProperty; 64 | #if ODIN_INSPECTOR_3 65 | if ( parent == null ) 66 | parent = Property.Tree.RootProperty; 67 | #else 68 | if ( parent == null ) 69 | parent = Property.Tree.SecretRootProperty; 70 | #endif 71 | 72 | PortResolver = parent.ChildResolver as INodePortResolver; 73 | NodePortInfo = PortResolver.GetNodePortInfo( Property.Name ); 74 | CanFold = Property.GetAttribute() == null; 75 | DrawValue = true; 76 | } 77 | 78 | protected sealed override void DrawPropertyLayout( GUIContent label ) 79 | { 80 | if ( NodePortDrawerHelper.DisplayMissingPort( Property, PortResolver, NodePortInfo ) ) 81 | return; 82 | 83 | if ( Event.current.type == EventType.Layout && !NodeEditorWindow.current.IsDraggingPort ) 84 | { 85 | switch ( NodePortInfo.ShowBackingValue ) 86 | { 87 | case ShowBackingValue.Always: 88 | DrawValue = true; 89 | break; 90 | 91 | case ShowBackingValue.Never: 92 | DrawValue = false; 93 | break; 94 | 95 | case ShowBackingValue.Unconnected: 96 | DrawValue = !NodePortInfo.Port.IsConnected; 97 | break; 98 | } 99 | 100 | IsVisible = !NodePortInfo.Node.folded; 101 | //IsVisible |= NodePortInfo.ShowBackingValue == ShowBackingValue.Always; 102 | IsVisible |= PortResolver is IDynamicDataNodePropertyPortResolver; // Dynamics will be folded somewhere else 103 | IsVisible |= NodePortInfo.Port.IsConnected; 104 | IsVisible |= !CanFold; 105 | 106 | DrawValue &= NodePortInfo.HasValue; 107 | } 108 | 109 | if ( !IsVisible ) 110 | return; 111 | 112 | DrawPort( label ); 113 | } 114 | 115 | protected abstract void DrawPort( GUIContent label ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Editor/Drawers/NodePortDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 59f5c735bf1813e41ae150c8d5ee4745 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/NodePortDrawerHelper.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Linq; 3 | using Sirenix.OdinInspector.Editor; 4 | using Sirenix.Utilities; 5 | using Sirenix.Utilities.Editor; 6 | using UnityEditor; 7 | using UnityEngine; 8 | using XNode; 9 | using static XNode.Node; 10 | 11 | namespace XNodeEditor.Odin 12 | { 13 | public static class NodePortDrawerHelper 14 | { 15 | public static void DisableDefaultPortDrawer( OdinDrawer drawer ) 16 | { 17 | var defaultDrawer = drawer.Property.GetActiveDrawerChain().BakedDrawerArray.FirstOrDefault( x => x.GetType().ImplementsOpenGenericClass( typeof( DefaultNodePortDrawer<> ) ) ); 18 | if ( defaultDrawer != null ) 19 | defaultDrawer.SkipWhenDrawing = true; 20 | } 21 | 22 | public static void DisableDefaultPortDrawer( OdinValueDrawer drawer ) 23 | { 24 | var defaultDrawer = drawer.Property.GetActiveDrawerChain().BakedDrawerArray.FirstOrDefault( x => x is DefaultNodePortDrawer ); 25 | if ( defaultDrawer != null ) 26 | defaultDrawer.SkipWhenDrawing = true; 27 | } 28 | 29 | public static void DisableDefaultPortDrawer( OdinAttributeDrawer drawer ) where TAttribute : System.Attribute 30 | { 31 | var defaultDrawer = drawer.Property.GetActiveDrawerChain().BakedDrawerArray.FirstOrDefault( x => x is DefaultNodePortDrawer ); 32 | if ( defaultDrawer != null ) 33 | defaultDrawer.SkipWhenDrawing = true; 34 | } 35 | 36 | public static void DrawPortHandle( NodePortInfo nodePortInfo, int overridePortHeight = -1 ) 37 | { 38 | DrawPortHandle( nodePortInfo.Port, overridePortHeight ); 39 | } 40 | 41 | public static void DrawPortHandle( NodePort port, int overridePortHeight = -1 ) 42 | { 43 | var nodeEditorWindow = NodeEditorWindow.current; 44 | if ( nodeEditorWindow == null ) 45 | return; 46 | 47 | NodeEditor nodeEditor = NodeEditor.GetEditor( port.node, nodeEditorWindow ); 48 | var portPosition = EditorGUILayout.GetControlRect( false, 0, GUILayout.Width( 0 ), GUILayout.Height( overridePortHeight >= 0 ? overridePortHeight : EditorGUIUtility.singleLineHeight ) ); 49 | 50 | // Inputs go on the left, outputs on the right 51 | if ( port.IsInput ) 52 | { 53 | NodeEditorGUILayout.PortField( 54 | new Vector2( 0, portPosition.y ), 55 | port 56 | ); 57 | } 58 | else 59 | { 60 | NodeEditorGUILayout.PortField( 61 | new Vector2( nodeEditor.GetWidth() - 16, portPosition.y ), 62 | port 63 | ); 64 | } 65 | } 66 | 67 | public static bool DisplayMissingPort( InspectorProperty property, INodePortResolver resolver, NodePortInfo nodePortInfo ) 68 | { 69 | if ( nodePortInfo == null ) 70 | { 71 | SirenixEditorGUI.ErrorMessageBox( $"This info went missing. {property.Name}" ); 72 | return true; 73 | } 74 | 75 | if ( nodePortInfo.Port == null ) 76 | { 77 | using ( new EditorGUILayout.VerticalScope() ) 78 | { 79 | SirenixEditorGUI.ErrorMessageBox( "This port went missing." ); 80 | using ( new EditorGUILayout.HorizontalScope() ) 81 | { 82 | if ( nodePortInfo.IsDynamic ) 83 | { 84 | if ( GUILayout.Button( "Restore" ) ) 85 | resolver.RememberDynamicPort( property ); 86 | if ( GUILayout.Button( "Remove" ) ) 87 | resolver.ForgetDynamicPort( property ); 88 | } 89 | else 90 | { 91 | if ( GUILayout.Button( "Restore" ) ) 92 | nodePortInfo.Node.UpdatePorts(); 93 | } 94 | 95 | } 96 | } 97 | return true; 98 | } 99 | 100 | return false; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Editor/Drawers/NodePortDrawerHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3415c0d9366cedc4ab7e064135ba5851 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Drawers/NodePortListDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Sirenix.OdinInspector.Editor; 6 | 7 | using UnityEngine; 8 | 9 | using XNode; 10 | 11 | namespace XNodeEditor.Odin 12 | { 13 | public abstract class NodePortListDrawer : OdinValueDrawer 14 | { 15 | protected sealed override bool CanDrawValueProperty( InspectorProperty property ) 16 | { 17 | if ( !NodeEditor.InNodeEditor ) 18 | return false; 19 | 20 | if ( property.ChildResolver is IDynamicDataNodePropertyPortResolver ) 21 | { 22 | var parent = property.ParentValueProperty; 23 | #if ODIN_INSPECTOR_3 24 | if ( parent == null ) 25 | parent = property.Tree.RootProperty; 26 | #else 27 | if ( parent == null ) 28 | parent = property.Tree.SecretRootProperty; 29 | #endif 30 | 31 | if ( parent.ChildResolver is INodePortResolver ) 32 | { 33 | var resolver = parent.ChildResolver as INodePortResolver; 34 | NodePortInfo portInfo = resolver.GetNodePortInfo( property.Name ); 35 | if ( portInfo != null ) 36 | return true; 37 | } 38 | 39 | return false; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | protected INodePortResolver PortResolver { get; private set; } 46 | protected IDynamicDataNodePropertyPortResolver PortListResolver { get; private set; } 47 | 48 | protected NodePortInfo NodePortInfo { get; private set; } 49 | protected bool CanFold { get; private set; } 50 | protected bool DrawValue { get; private set; } 51 | 52 | protected override void Initialize() 53 | { 54 | var parent = Property.ParentValueProperty; 55 | #if ODIN_INSPECTOR_3 56 | if ( parent == null ) 57 | parent = Property.Tree.RootProperty; 58 | #else 59 | if ( parent == null ) 60 | parent = Property.Tree.SecretRootProperty; 61 | #endif 62 | 63 | PortResolver = parent.ChildResolver as INodePortResolver; 64 | PortListResolver = Property.ChildResolver as IDynamicDataNodePropertyPortResolver; 65 | NodePortInfo = PortResolver.GetNodePortInfo( Property.Name ); 66 | CanFold = Property.GetAttribute() == null; 67 | DrawValue = true; 68 | } 69 | 70 | private bool isVisible = false; 71 | 72 | protected sealed override void DrawPropertyLayout( GUIContent label ) 73 | { 74 | if ( Event.current.type == EventType.Layout && !NodeEditorWindow.current.IsDraggingPort ) 75 | { 76 | isVisible = !NodePortInfo.Node.folded; 77 | isVisible |= PortListResolver.AnyConnected; 78 | isVisible |= !CanFold; 79 | } 80 | 81 | if ( !isVisible ) 82 | return; 83 | 84 | DrawPortList( label ); 85 | } 86 | 87 | protected abstract void DrawPortList( GUIContent label ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Editor/Drawers/NodePortListDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7c460ecdc4dfe464db2a6bc8ed1757e3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Processors.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d4b79233170275244a7e59c784681b03 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Processors/HideInNodeEditorAttributeProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using Sirenix.OdinInspector; 6 | using Sirenix.OdinInspector.Editor; 7 | using UnityEngine; 8 | using XNode.Odin; 9 | 10 | namespace XNodeEditor.Odin 11 | { 12 | public class HideInNodeEditorAttributeProcessor : OdinAttributeProcessor 13 | { 14 | public override bool CanProcessSelfAttributes( InspectorProperty property ) 15 | { 16 | if ( NodeEditor.InNodeEditor ) 17 | { 18 | if ( property.GetAttribute() != null ) 19 | return true; 20 | 21 | if ( typeof( T ).GetCustomAttribute() != null ) 22 | return true; 23 | } 24 | 25 | return false; 26 | } 27 | 28 | public override bool CanProcessChildMemberAttributes( InspectorProperty parentProperty, MemberInfo member ) 29 | { 30 | return false; 31 | } 32 | 33 | public override void ProcessSelfAttributes( InspectorProperty property, List attributes ) 34 | { 35 | attributes.Add( new HideInInspector() ); 36 | } 37 | } 38 | 39 | // I shouldn't need this, but it guarantees things get removed that shouldn't be shown 40 | public class HideInNodeEditorPropertyProcessor : OdinPropertyProcessor 41 | { 42 | public override bool CanProcessForProperty( InspectorProperty property ) 43 | { 44 | return NodeEditor.InNodeEditor; 45 | } 46 | 47 | public override void ProcessMemberProperties( List propertyInfos ) 48 | { 49 | for ( int i = propertyInfos.Count -1; i>=0;--i) 50 | { 51 | InspectorPropertyInfo p = propertyInfos[i]; 52 | if ( p.GetAttribute() != null ) 53 | propertyInfos.RemoveAt( i ); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Editor/Processors/HideInNodeEditorAttributeProcessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c6b6d3a1af50a6f47826c3e3434b5975 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Processors/HideOutsideNodeEditorAttributeProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using Sirenix.OdinInspector.Editor; 6 | using UnityEngine; 7 | using XNode.Odin; 8 | 9 | namespace XNodeEditor.Odin 10 | { 11 | public class HideOutsideNodeEditorAttributeProcessor : OdinAttributeProcessor 12 | { 13 | public override bool CanProcessSelfAttributes( InspectorProperty property ) 14 | { 15 | if ( !NodeEditor.InNodeEditor ) 16 | { 17 | if ( property.GetAttribute() != null ) 18 | return true; 19 | 20 | if ( typeof( T ).GetCustomAttribute() != null ) 21 | return true; 22 | } 23 | 24 | return false; 25 | } 26 | 27 | public override bool CanProcessChildMemberAttributes( InspectorProperty parentProperty, MemberInfo member ) 28 | { 29 | return false; 30 | } 31 | 32 | public override void ProcessSelfAttributes( InspectorProperty property, List attributes ) 33 | { 34 | attributes.Add( new HideInInspector() ); 35 | } 36 | } 37 | 38 | // I shouldn't need this, but it guarantees things get removed that shouldn't be shown 39 | public class HideOutsideNodeEditorPropertyProcessor : OdinPropertyProcessor 40 | { 41 | public override bool CanProcessForProperty( InspectorProperty property ) 42 | { 43 | return !NodeEditor.InNodeEditor; 44 | } 45 | 46 | public override void ProcessMemberProperties( List propertyInfos ) 47 | { 48 | for ( int i = propertyInfos.Count - 1; i >= 0; --i ) 49 | { 50 | InspectorPropertyInfo p = propertyInfos[i]; 51 | if ( p.GetAttribute() != null ) 52 | propertyInfos.RemoveAt( i ); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Editor/Processors/HideOutsideNodeEditorAttributeProcessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b09065f88a0fed46a2ce45ed2658614 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Processors/NodeGraphPropertyProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Sirenix.OdinInspector; 4 | using Sirenix.OdinInspector.Editor; 5 | using Sirenix.Utilities.Editor; 6 | using UnityEditor; 7 | using UnityEngine; 8 | using XNode; 9 | using XNode.Odin; 10 | 11 | namespace XNodeEditor.Odin 12 | { 13 | public class NodeGraphPropertyProcessor : OdinPropertyProcessor 14 | where TNodeGraph : NodeGraph 15 | { 16 | protected const string FOLDBUTTON_GROUP = "xnode:foldall"; 17 | 18 | public System.Action foldAll; 19 | public System.Action unfoldAll; 20 | 21 | protected override void Initialize() 22 | { 23 | base.Initialize(); 24 | 25 | foldAll = FoldAll; 26 | unfoldAll = UnfoldAll; 27 | } 28 | 29 | public override void ProcessMemberProperties( List infos ) 30 | { 31 | if ( NodeEditor.InNodeEditor ) 32 | return; 33 | 34 | if ( Property.GetAttribute() != null ) 35 | { 36 | var unfoldAllProperty = InspectorPropertyInfo.CreateForDelegate( 37 | "unfoldAllNodes", 38 | 0, 39 | typeof( TNodeGraph ), 40 | unfoldAll, 41 | new ButtonGroupAttribute( FOLDBUTTON_GROUP ) 42 | ); 43 | infos.Insert( 0, unfoldAllProperty ); 44 | var foldAllProperty = InspectorPropertyInfo.CreateForDelegate( 45 | "foldAllNodes", 46 | 0, 47 | typeof( TNodeGraph ), 48 | foldAll, 49 | new ButtonGroupAttribute( FOLDBUTTON_GROUP ) 50 | ); 51 | infos.Insert( 0, foldAllProperty ); 52 | } 53 | } 54 | 55 | protected void FoldAll() 56 | { 57 | foreach ( var nodeGraph in Property.ValueEntry.WeakValues.OfType() ) 58 | { 59 | Undo.RegisterFullObjectHierarchyUndo( nodeGraph, "Fold Nodes" ); 60 | foreach ( var node in nodeGraph.nodes ) 61 | { 62 | Undo.RegisterFullObjectHierarchyUndo( node, "Fold Nodes" ); 63 | 64 | bool foldable; 65 | node.GetType().TryGetAttributeFoldable( out foldable ); 66 | 67 | if ( foldable ) 68 | node.folded = true; 69 | } 70 | 71 | Undo.CollapseUndoOperations( Undo.GetCurrentGroup() ); 72 | } 73 | 74 | // Find node editor windows and repaint them 75 | foreach ( var nodeEditorWindow in Resources.FindObjectsOfTypeAll() ) 76 | { 77 | // Twice because of port caching 78 | nodeEditorWindow.Repaint(); 79 | nodeEditorWindow.Repaint(); 80 | } 81 | } 82 | 83 | protected void UnfoldAll() 84 | { 85 | foreach ( var nodeGraph in Property.ValueEntry.WeakValues.OfType() ) 86 | { 87 | Undo.RegisterFullObjectHierarchyUndo( nodeGraph, "Unfold Nodes" ); 88 | foreach ( var node in nodeGraph.nodes ) 89 | { 90 | Undo.RegisterFullObjectHierarchyUndo( node, "Unfold Nodes" ); 91 | node.folded = false; 92 | } 93 | 94 | Undo.CollapseUndoOperations( Undo.GetCurrentGroup() ); 95 | } 96 | 97 | // Find node editor windows and repaint them 98 | foreach ( var nodeEditorWindow in Resources.FindObjectsOfTypeAll() ) 99 | { 100 | // Twice because of port caching 101 | nodeEditorWindow.Repaint(); 102 | nodeEditorWindow.Repaint(); 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Editor/Processors/NodeGraphPropertyProcessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 03ac29e0184aa6a40a6e2a13f77e323f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Processors/NodePortAttributeProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | using Sirenix.OdinInspector; 6 | using Sirenix.OdinInspector.Editor; 7 | 8 | using XNode; 9 | 10 | namespace XNodeEditor.Odin 11 | { 12 | public class NodePortAttributeProcessor : OdinAttributeProcessor 13 | { 14 | public override bool CanProcessSelfAttributes( InspectorProperty property ) 15 | { 16 | return true; 17 | } 18 | 19 | public override bool CanProcessChildMemberAttributes( InspectorProperty parentProperty, MemberInfo member ) 20 | { 21 | return false; 22 | } 23 | 24 | public override void ProcessSelfAttributes( InspectorProperty property, List attributes ) 25 | { 26 | attributes.Add( new HideReferenceObjectPickerAttribute() ); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Editor/Processors/NodePortAttributeProcessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4b1500e2b36e7ec4cb3fc5e71f7aa781 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Processors/NodePropertyProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Sirenix.OdinInspector.Editor; 3 | using UnityEditor; 4 | using XNode; 5 | using XNode.Odin; 6 | 7 | namespace XNodeEditor.Odin 8 | { 9 | public static class NodePropertyPort 10 | { 11 | public const string NodePortPropertyName = "xnode:port"; 12 | public const string NodePortListPropertyName = "xnode:portlist"; 13 | } 14 | 15 | public class NodePropertyProcessor : OdinPropertyProcessor 16 | where TNode : Node 17 | { 18 | public override void ProcessMemberProperties( List infos ) 19 | { 20 | if ( !NodeEditor.InNodeEditor ) 21 | return; 22 | 23 | // Remove excluded properties 24 | string[] excludes = { "m_Script", "graph", "position", "folded", "ports" }; 25 | foreach ( var exclude in excludes ) 26 | infos.Remove( infos.Find( exclude ) ); 27 | 28 | if ( Property.GetAttribute() != null ) 29 | { 30 | var nameProperty = InspectorPropertyInfo.CreateValue( 31 | "name", 32 | 0, 33 | Property.ValueEntry.SerializationBackend, 34 | new GetterSetter( 35 | ( ref TNode node ) => node.name, 36 | ( ref TNode node, string value ) => 37 | { 38 | Undo.RegisterFullObjectHierarchyUndo( node, "Set node name" ); 39 | node.name = value; 40 | } 41 | ), 42 | new Sirenix.OdinInspector.DelayedPropertyAttribute() 43 | ); 44 | infos.Insert( 0, nameProperty ); 45 | } 46 | } 47 | } 48 | 49 | public class InspectorNodePropertyProcessor : OdinPropertyProcessor 50 | where TNode : Node 51 | { 52 | public override void ProcessMemberProperties( List infos ) 53 | { 54 | if ( NodeEditor.InNodeEditor ) 55 | return; 56 | 57 | if ( Property.GetAttribute() != null ) 58 | { 59 | var nameProperty = InspectorPropertyInfo.CreateValue( 60 | "name", 61 | 0, 62 | Property.ValueEntry.SerializationBackend, 63 | new GetterSetter( 64 | ( ref TNode node ) => node.name, 65 | ( ref TNode node, string value ) => 66 | { 67 | Undo.RegisterFullObjectHierarchyUndo( node, "Set node name" ); 68 | node.name = value; 69 | } 70 | ), 71 | new Sirenix.OdinInspector.DelayedPropertyAttribute() 72 | ); 73 | infos.Insert( 0, nameProperty ); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Editor/Processors/NodePropertyProcessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e1ed16984e855124f8b7fdaf39b9cf38 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Resolvers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f6d9028e84e5dae4dac9dfef07a63258 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resolvers/DynamicDataNodePropertyPortResolver.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Sirenix.OdinInspector.Editor; 6 | using Sirenix.Utilities.Editor; 7 | using UnityEditor; 8 | using UnityEngine; 9 | 10 | using XNode; 11 | 12 | namespace XNodeEditor.Odin 13 | { 14 | public interface IDynamicDataNodePropertyPortResolver : INodePortResolver 15 | { 16 | //DynamicPortInfo DynamicPortInfo { get; } 17 | bool AnyConnected { get; } 18 | } 19 | 20 | [ResolverPriority( 20 )] // No data at 30 21 | // I want this to pass through sometimes 22 | public class DynamicDataNodePropertyPortResolver : StrongListPropertyResolver, IDynamicDataNodePropertyPortResolver 23 | where TList : IList 24 | { 25 | public override bool CanResolveForPropertyFilter( InspectorProperty property ) 26 | { 27 | if ( !NodeEditor.InNodeEditor ) 28 | return false; 29 | 30 | var parent = property.ParentValueProperty; 31 | if ( parent != null ) // Parent value property *should* only be valid for something living under the NoData dynamic list 32 | { 33 | if ( parent.ChildResolver is IDynamicNoDataNodePropertyPortResolver ) 34 | return true; 35 | } 36 | 37 | #if ODIN_INSPECTOR_3 38 | if ( parent == null ) 39 | parent = property.Tree.RootProperty; 40 | #else 41 | if ( parent == null ) 42 | parent = property.Tree.SecretRootProperty; 43 | #endif 44 | 45 | var resolver = parent.ChildResolver as INodePortResolver; 46 | if ( resolver == null ) 47 | return false; 48 | 49 | NodePortInfo portInfo = resolver.GetNodePortInfo( property.Name ); 50 | return portInfo != null && portInfo.IsDynamicPortList; 51 | } 52 | 53 | protected override bool AllowNullValues => false; 54 | 55 | public Node Node => nodePortInfo.Node; 56 | 57 | protected INodePortResolver portResolver; 58 | protected NodePortInfo nodePortInfo; 59 | 60 | protected IDynamicNoDataNodePropertyPortResolver noDataResolver; 61 | 62 | //public DynamicPortInfo DynamicPortInfo { get; private set; } 63 | protected Dictionary childPortInfos = new Dictionary(); 64 | 65 | protected Dictionary nameToNodePropertyInfo = new Dictionary(); 66 | protected Dictionary propertyToNodeProperty = new Dictionary(); 67 | 68 | public NodePortInfo GetNodePortInfo( string propertyName ) 69 | { 70 | // Ensure the properties exist 71 | var index = CollectionResolverUtilities.DefaultChildNameToIndex( propertyName ); 72 | var portInfo = GetInfoForPortAtIndex( index ); 73 | if ( portInfo == null ) 74 | return null; 75 | 76 | if ( propertyToNodeProperty.TryGetValue( propertyName, out var portPropertyName ) ) 77 | { 78 | if ( nameToNodePropertyInfo.TryGetValue( portPropertyName, out var nodePortInfo ) ) 79 | { 80 | return nodePortInfo; 81 | } 82 | } 83 | 84 | return null; 85 | } 86 | 87 | protected override void Initialize() 88 | { 89 | Property.ValueEntry.Update(); 90 | 91 | // Port is already resolved for the base 92 | var parent = Property.ParentValueProperty; 93 | #if ODIN_INSPECTOR_3 94 | if ( parent == null ) 95 | parent = Property.Tree.RootProperty; 96 | #else 97 | if ( parent == null ) 98 | parent = Property.Tree.SecretRootProperty; 99 | #endif 100 | 101 | portResolver = parent.ChildResolver as INodePortResolver; 102 | nodePortInfo = portResolver.GetNodePortInfo( Property.Name ); 103 | 104 | noDataResolver = Property.ParentValueProperty == null ? null : parent.ChildResolver as IDynamicNoDataNodePropertyPortResolver; 105 | 106 | base.Initialize(); 107 | 108 | for ( int i = 0; i < ChildCount; ++i ) 109 | { 110 | var info = GetInfoForPortAtIndex( i ); 111 | } 112 | } 113 | 114 | public bool AnyConnected 115 | { 116 | get 117 | { 118 | return nameToNodePropertyInfo.Select( x => x.Value ).Any( x => x.Port == null || x.Port.IsConnected ); 119 | } 120 | } 121 | 122 | public override int ChildNameToIndex( string name ) 123 | { 124 | if ( name.EndsWith( ":port" ) ) 125 | return CollectionResolverUtilities.DefaultChildNameToIndex( name ) + base.ChildCount; 126 | 127 | return base.ChildNameToIndex( name ); 128 | } 129 | 130 | protected InspectorPropertyInfo GetInfoForPortAtIndex( int index ) 131 | { 132 | InspectorPropertyInfo childPortInfo; 133 | if ( !childPortInfos.TryGetValue( index, out childPortInfo ) ) 134 | { 135 | InspectorPropertyInfo sourceChildInfo = base.GetChildInfo( index ); 136 | 137 | string portName = $"{nodePortInfo.BaseFieldName} {index}"; 138 | Node node = nodePortInfo.Node; 139 | NodePort port = node.GetPort( portName ); 140 | 141 | childPortInfo = InspectorPropertyInfo.CreateValue( 142 | $"{CollectionResolverUtilities.DefaultIndexToChildName( index )}:port", 143 | 0, 144 | Property.ValueEntry.SerializationBackend, 145 | new GetterSetter( 146 | ( ref TList owner ) => port, 147 | ( ref TList owner, NodePort value ) => { } 148 | ) 149 | , new HideInInspector() 150 | ); 151 | 152 | var childNodePortInfo = new NodePortInfo( 153 | childPortInfo, 154 | sourceChildInfo, 155 | portName, 156 | typeof( TElement ), 157 | node, // Needed? 158 | nodePortInfo.ShowBackingValue, 159 | nodePortInfo.ConnectionType, 160 | nodePortInfo.TypeConstraint, 161 | nodePortInfo.IsDynamicPortList, 162 | true, 163 | nodePortInfo.IsInput, 164 | noDataResolver == null 165 | ); 166 | 167 | propertyToNodeProperty[sourceChildInfo.PropertyName] = childPortInfo.PropertyName; 168 | nameToNodePropertyInfo[childPortInfo.PropertyName] = childNodePortInfo; 169 | 170 | childPortInfos[index] = childPortInfo; 171 | } 172 | return childPortInfo; 173 | } 174 | 175 | public override InspectorPropertyInfo GetChildInfo( int childIndex ) 176 | { 177 | if ( childIndex >= base.ChildCount ) 178 | return GetInfoForPortAtIndex( childIndex - base.ChildCount ); 179 | 180 | return base.GetChildInfo( childIndex ); 181 | } 182 | 183 | protected override int GetChildCount( TList value ) 184 | { 185 | if ( value == null ) 186 | return 0; 187 | return base.GetChildCount( value ); 188 | } 189 | 190 | public void RememberDynamicPort( InspectorProperty property ) 191 | { 192 | var nodePortInfo = GetNodePortInfo( property.Name ); 193 | if ( nodePortInfo.IsInput ) 194 | nodePortInfo.Node.AddDynamicInput( nodePortInfo.Type, nodePortInfo.ConnectionType, nodePortInfo.TypeConstraint, nodePortInfo.BaseFieldName ); 195 | else 196 | nodePortInfo.Node.AddDynamicOutput( nodePortInfo.Type, nodePortInfo.ConnectionType, nodePortInfo.TypeConstraint, nodePortInfo.BaseFieldName ); 197 | } 198 | 199 | public void ForgetDynamicPort( InspectorProperty property ) 200 | { 201 | var nodePortInfo = GetNodePortInfo( property.Name ); 202 | 203 | // What index does he think he is? 204 | int index = CollectionResolverUtilities.DefaultChildNameToIndex( property.Name ); 205 | if ( index >= 0 && index < ChildCount ) 206 | { 207 | var currentNodeWindow = NodeEditorWindow.current; 208 | EditorApplication.delayCall += () => 209 | { 210 | RemoveAt( (TList)Property.ValueEntry.WeakValues[0], index ); // Note: This really shouldn't be index 0 but it's impossible to multi edit 211 | currentNodeWindow.Repaint(); 212 | }; 213 | } 214 | } 215 | 216 | #region Collection Handlers 217 | protected override void Add( TList collection, object value ) 218 | { 219 | int nextId = this.ChildCount; 220 | 221 | if ( nodePortInfo.Port.IsInput ) 222 | nodePortInfo.Node.AddDynamicInput( typeof( TElement ), nodePortInfo.ConnectionType, nodePortInfo.TypeConstraint, string.Format( "{0} {1}", nodePortInfo.BaseFieldName, nextId ) ); 223 | else 224 | nodePortInfo.Node.AddDynamicOutput( typeof( TElement ), nodePortInfo.ConnectionType, nodePortInfo.TypeConstraint, string.Format( "{0} {1}", nodePortInfo.BaseFieldName, nextId ) ); 225 | 226 | lastRemovedConnections.Clear(); 227 | 228 | base.Add( collection, value ); 229 | } 230 | 231 | protected NodePort GetNodePort( int index ) 232 | { 233 | NodePortInfo nodePortInfo = GetNodePortInfo( index ); 234 | if ( nodePortInfo != null ) 235 | return nodePortInfo.Port; 236 | 237 | return null; 238 | } 239 | 240 | protected NodePortInfo GetNodePortInfo( int index ) 241 | { 242 | if ( childPortInfos.TryGetValue( index, out var kInfo ) ) 243 | return nameToNodePropertyInfo[kInfo.PropertyName]; 244 | 245 | return null; 246 | } 247 | 248 | protected override void InsertAt( TList collection, int index, object value ) 249 | { 250 | int newChildCount = this.ChildCount + 1; 251 | int nextId = this.ChildCount; 252 | 253 | // Remove happens before insert and we lose all the connections 254 | // Add a new port at the end 255 | if ( nodePortInfo.Port.IsInput ) 256 | nodePortInfo.Node.AddDynamicInput( typeof( TElement ), nodePortInfo.ConnectionType, nodePortInfo.TypeConstraint, string.Format( "{0} {1}", nodePortInfo.BaseFieldName, nextId ) ); 257 | else 258 | nodePortInfo.Node.AddDynamicOutput( typeof( TElement ), nodePortInfo.ConnectionType, nodePortInfo.TypeConstraint, string.Format( "{0} {1}", nodePortInfo.BaseFieldName, nextId ) ); 259 | 260 | // Move everything down to make space - if something is missing just pretend we moved it? 261 | for ( int k = newChildCount - 1; k > index; --k ) 262 | { 263 | NodePort k1Port = GetNodePort( k - 1 ); 264 | if ( k1Port == null ) // It is missing, I have nothing to move 265 | continue; 266 | 267 | for ( int j = 0; j < k1Port.ConnectionCount; j++ ) 268 | { 269 | NodePort other = k1Port.GetConnection( j ); 270 | k1Port.Disconnect( other ); 271 | 272 | NodePort kPort = GetNodePort( k ); 273 | if ( kPort == null ) 274 | continue; 275 | 276 | kPort.Connect( other ); 277 | } 278 | } 279 | 280 | // Let's just re-add connections to this node that were probably his 281 | foreach ( var c in lastRemovedConnections ) 282 | { 283 | NodePort indexPort = GetNodePort( index ); 284 | if ( indexPort != null ) 285 | indexPort.Connect( c ); 286 | } 287 | 288 | lastRemovedConnections.Clear(); 289 | 290 | //if ( noDataResolver == null ) 291 | base.InsertAt( collection, index, value ); 292 | 293 | this.ForceUpdateChildCount(); 294 | } 295 | 296 | protected override void Remove( TList collection, object value ) 297 | { 298 | int index = collection.IndexOf( (TElement)value ); 299 | RemoveAt( collection, index ); 300 | } 301 | 302 | protected NodePort ReplaceNodeForRemove( int index ) 303 | { 304 | var childPortInfo = GetNodePortInfo( index ); // Info still exists when this happens (probably) 305 | if ( childPortInfo.IsInput ) 306 | return childPortInfo.Node.AddDynamicInput( childPortInfo.Type, childPortInfo.ConnectionType, childPortInfo.TypeConstraint, childPortInfo.BaseFieldName ); 307 | else 308 | return childPortInfo.Node.AddDynamicOutput( childPortInfo.Type, childPortInfo.ConnectionType, childPortInfo.TypeConstraint, childPortInfo.BaseFieldName ); 309 | } 310 | 311 | protected List lastRemovedConnections = new List(); 312 | 313 | protected override void RemoveAt( TList collection, int index ) 314 | { 315 | NodePort indexPort = GetNodePort( index ); 316 | 317 | if ( indexPort == null ) 318 | { 319 | Debug.LogWarning( $"No port found at index {index}" ); 320 | } 321 | 322 | lastRemovedConnections.Clear(); 323 | if ( indexPort != null ) 324 | { 325 | lastRemovedConnections.AddRange( indexPort.GetConnections() ); 326 | 327 | // Clear the removed ports connections 328 | indexPort.ClearConnections(); 329 | } 330 | 331 | // Cache the last port because I'm about to remove it 332 | NodePort lastPort = GetNodePort( ChildCount - 1 ); 333 | 334 | // Move following connections one step up to replace the missing connection 335 | for ( int k = index + 1; k < ChildCount; k++ ) 336 | { 337 | NodePort kPort = GetNodePort( k ); 338 | if ( kPort == null ) 339 | continue; 340 | 341 | NodePort k1Port = GetNodePort( k - 1 ); 342 | // Port k - 1 missing means I need to actually rename a port instead 343 | // Create k -1, add all the correct connections ... leave K alone because he existed 344 | if ( k1Port == null ) 345 | k1Port = ReplaceNodeForRemove( k - 1 ); 346 | 347 | for ( int j = 0; j < kPort.ConnectionCount; j++ ) 348 | { 349 | NodePort other = kPort.GetConnection( j ); 350 | kPort.Disconnect( other ); 351 | 352 | k1Port.Connect( other ); 353 | } 354 | } 355 | 356 | // Remove the last dynamic port, to avoid messing up the indexing 357 | if ( lastPort != null ) 358 | lastPort.node.RemoveDynamicPort( lastPort ); 359 | 360 | base.RemoveAt( collection, index ); 361 | 362 | this.ForceUpdateChildCount(); 363 | } 364 | 365 | protected override void Clear( TList collection ) 366 | { 367 | for ( int i = 0; i < ChildCount; ++i ) 368 | { 369 | NodePort port = GetNodePort( i ); 370 | if ( port != null ) 371 | nodePortInfo.Node.RemoveDynamicPort( port ); 372 | } 373 | 374 | lastRemovedConnections.Clear(); 375 | 376 | base.Clear( collection ); 377 | } 378 | #endregion 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /Editor/Resolvers/DynamicDataNodePropertyPortResolver.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b9935d0934fce914897d8ff869cf14b9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Resolvers/DynamicNoDataNodePropertyPortResolver.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Sirenix.OdinInspector; 6 | using Sirenix.OdinInspector.Editor; 7 | using Sirenix.Utilities; 8 | 9 | using UnityEngine; 10 | 11 | using XNode; 12 | using XNode.Odin; 13 | 14 | namespace XNodeEditor.Odin 15 | { 16 | public interface IDynamicNoDataNodePropertyPortResolver : INodePortResolver 17 | { 18 | void UpdateDynamicPorts(); 19 | } 20 | 21 | [ResolverPriority( 30 )] 22 | public class DynamicNoDataNodePropertyPortResolver : OdinPropertyResolver, IDynamicNoDataNodePropertyPortResolver 23 | { 24 | public override bool CanResolveForPropertyFilter( InspectorProperty property ) 25 | { 26 | if ( !NodeEditor.InNodeEditor ) 27 | return false; 28 | 29 | 30 | var parent = property.ParentValueProperty; 31 | #if ODIN_INSPECTOR_3 32 | if ( parent == null || parent == property.Tree.RootProperty ) // Root only 33 | { 34 | parent = property.Tree.RootProperty; 35 | #else 36 | if ( parent == null ) // Root only 37 | { 38 | parent = property.Tree.SecretRootProperty; 39 | #endif 40 | var resolver = parent.ChildResolver as INodePortResolver; 41 | if ( resolver == null ) 42 | return false; 43 | 44 | NodePortInfo portInfo = resolver.GetNodePortInfo( property.Name ); 45 | return portInfo != null && portInfo.IsDynamicPortList && !typeof( TValue ).ImplementsOrInherits( typeof( System.Collections.IList ) ); 46 | } 47 | 48 | return false; 49 | } 50 | 51 | protected override bool AllowNullValues => true; 52 | 53 | public Node Node => nodePortInfo.Node; 54 | 55 | protected INodePortResolver portResolver; 56 | protected NodePortInfo nodePortInfo; 57 | 58 | protected InspectorPropertyInfo fakeListInfo; 59 | protected List dynamicPorts; 60 | 61 | protected List GetDynamicPorts( ref TValue owner ) 62 | { 63 | return dynamicPorts; 64 | } 65 | 66 | protected override void Initialize() 67 | { 68 | // Port is already resolved for the base 69 | var parent = Property.ParentValueProperty; 70 | #if ODIN_INSPECTOR_3 71 | if ( parent == null ) 72 | parent = Property.Tree.RootProperty; 73 | #else 74 | if ( parent == null ) 75 | parent = Property.Tree.SecretRootProperty; 76 | #endif 77 | 78 | portResolver = parent.ChildResolver as INodePortResolver; 79 | nodePortInfo = portResolver.GetNodePortInfo( Property.Name ); 80 | 81 | UpdateDynamicPorts(); 82 | 83 | fakeListInfo = InspectorPropertyInfo.CreateValue( 84 | NodePropertyPort.NodePortListPropertyName, 85 | 0, 86 | Property.ValueEntry.SerializationBackend, 87 | new GetterSetter>( 88 | GetDynamicPorts, 89 | ( ref TValue owner, List value ) => { } 90 | ), 91 | Property.Attributes 92 | .Where( x => 93 | x is ListDrawerSettingsAttribute || 94 | x is NodePortAttribute || 95 | x is ShowDrawerChainAttribute || 96 | x is ShowPropertyResolverAttribute 97 | ) 98 | .ToArray() 99 | ); 100 | } 101 | 102 | protected virtual TValue GenerateDefaultValue() 103 | { 104 | return default( TValue ); 105 | } 106 | 107 | public void UpdateDynamicPorts() 108 | { 109 | if ( dynamicPorts == null ) 110 | dynamicPorts = new List(); 111 | dynamicPorts.Clear(); 112 | 113 | DynamicPortInfo dynamicPortInfo = DynamicPortHelper.GetDynamicPortData( nodePortInfo.Node, nodePortInfo.Port.fieldName ); 114 | for ( int i = 0; i <= dynamicPortInfo.max; ++i ) 115 | dynamicPorts.Add( GenerateDefaultValue() ); 116 | } 117 | 118 | public NodePortInfo GetNodePortInfo( NodePort port ) 119 | { 120 | Debug.Assert( nodePortInfo.Port == port, "Ports are not equal, how?" ); 121 | return nodePortInfo; 122 | } 123 | 124 | public NodePortInfo GetNodePortInfo( string propertyName ) 125 | { 126 | return nodePortInfo; 127 | } 128 | 129 | public void RememberDynamicPort( InspectorProperty property ) 130 | { 131 | throw new System.NotImplementedException(); 132 | } 133 | 134 | public void ForgetDynamicPort( InspectorProperty property ) 135 | { 136 | throw new System.NotImplementedException(); 137 | } 138 | 139 | public override int ChildNameToIndex( string name ) 140 | { 141 | return 0; 142 | } 143 | 144 | public override InspectorPropertyInfo GetChildInfo( int childIndex ) 145 | { 146 | return fakeListInfo; 147 | } 148 | 149 | protected override int GetChildCount( TValue value ) 150 | { 151 | return 1; 152 | } 153 | } 154 | 155 | [ResolverPriority( 31 )] 156 | public class DynamicNoDataNodePropertyPortResolverForNullabel : DynamicNoDataNodePropertyPortResolver where TValue : new() 157 | { 158 | protected override TValue GenerateDefaultValue() 159 | { 160 | return new TValue(); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /Editor/Resolvers/DynamicNoDataNodePropertyPortResolver.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a6f0f524476c0d34ea12924d61ca4de6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Resolvers/DynamicPortHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Sirenix.OdinInspector.Editor; 4 | 5 | using UnityEngine; 6 | using XNode; 7 | 8 | namespace XNodeEditor.Odin 9 | { 10 | public class DynamicPortInfo 11 | { 12 | public List ports; 13 | 14 | public int min; 15 | public int max; 16 | 17 | //public int ExpectedCount => max - min; 18 | } 19 | 20 | public class DynamicPortHelper : MonoBehaviour 21 | { 22 | // Taken from 'public static void DynamicPortList' in NodeEditorGUILayout 23 | public static DynamicPortInfo GetDynamicPortData( Node node, string baseFieldName ) 24 | { 25 | DynamicPortInfo info = new DynamicPortInfo() { min = int.MaxValue, max = int.MinValue }; 26 | 27 | var indexedPorts = node.DynamicPorts.Select( x => 28 | { 29 | string[] split = x.fieldName.Split( ' ' ); 30 | if ( split != null && split.Length == 2 && split[0] == baseFieldName ) 31 | { 32 | int i = -1; 33 | if ( int.TryParse( split[1], out i ) ) 34 | { 35 | info.min = i < info.min ? i : info.min; 36 | info.max = i > info.max ? i : info.max; 37 | return new { index = i, port = x }; 38 | } 39 | } 40 | return new { index = -1, port = (XNode.NodePort)null }; 41 | } ).Where( x => x.port != null ); 42 | 43 | info.ports = indexedPorts.OrderBy( x => x.index ).Select( x => x.port ).ToList(); 44 | return info; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Editor/Resolvers/DynamicPortHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eb4f4462d3123d945bb47677dd1103cc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Resolvers/NodePropertyPortResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using Sirenix.OdinInspector; 6 | using Sirenix.OdinInspector.Editor; 7 | using Sirenix.Utilities; 8 | using UnityEditor; 9 | using UnityEngine; 10 | 11 | using XNode; 12 | using XNode.Odin; 13 | using static XNode.Node; 14 | 15 | namespace XNodeEditor.Odin 16 | { 17 | public interface INodePortResolver 18 | { 19 | Node Node { get; } 20 | 21 | NodePortInfo GetNodePortInfo( string propertyName ); 22 | 23 | void RememberDynamicPort( InspectorProperty property ); 24 | void ForgetDynamicPort( InspectorProperty property ); 25 | } 26 | 27 | public class NodePortInfo 28 | { 29 | private ConnectionType _connectionType; 30 | private TypeConstraint _typeConstraint; 31 | 32 | public InspectorPropertyInfo PortPropertyInfo { get; private set; } 33 | public InspectorPropertyInfo SourcePropertyInfo { get; private set; } 34 | 35 | public string BaseFieldName { get; private set; } 36 | 37 | public Type Type { get; private set; } 38 | 39 | public Node Node { get; private set; } 40 | public NodePort Port => Node.GetPort( BaseFieldName ); 41 | 42 | public ShowBackingValue ShowBackingValue { get; private set; } 43 | public ConnectionType ConnectionType { get => Port == null ? _connectionType : Port.connectionType; private set => _connectionType = value; } 44 | public TypeConstraint TypeConstraint { get => Port == null ? _typeConstraint : Port.typeConstraint; private set => _typeConstraint = value; } 45 | 46 | public bool IsDynamicPortList { get; private set; } 47 | public bool IsDynamic { get; private set; } 48 | 49 | public bool HasValue { get; } 50 | 51 | public bool IsInput { get; private set; } 52 | 53 | public NodePortInfo( 54 | InspectorPropertyInfo portPropertyInfo, 55 | InspectorPropertyInfo sourcePropertyInfo, 56 | string baseFieldName, 57 | Type type, 58 | Node node, // Needed? 59 | ShowBackingValue showBackingValue, 60 | ConnectionType connectionType, 61 | TypeConstraint typeConstraint, 62 | bool isDynamicPortList, 63 | bool isDynamic, 64 | bool isInput, 65 | bool hasValue 66 | ) 67 | { 68 | PortPropertyInfo = portPropertyInfo; 69 | SourcePropertyInfo = sourcePropertyInfo; 70 | BaseFieldName = baseFieldName; 71 | Type = type; 72 | Node = node; 73 | ShowBackingValue = showBackingValue; 74 | ConnectionType = connectionType; 75 | TypeConstraint = typeConstraint; 76 | IsDynamicPortList = isDynamicPortList; 77 | IsDynamic = isDynamic; 78 | IsInput = isInput; 79 | HasValue = hasValue; 80 | } 81 | } 82 | 83 | // Invert the pattern 84 | // Inject this property into a node port holder 85 | [ResolverPriority( 10 )] 86 | public abstract class NodePropertyPortResolver : OdinPropertyResolver, IRefreshableResolver, IDisposable, INodePortResolver 87 | { 88 | #region Taken from BaseMemberPropertyResolver 89 | private List infos; 90 | private Dictionary namesToIndex; 91 | 92 | public sealed override InspectorPropertyInfo GetChildInfo( int childIndex ) 93 | { 94 | if ( object.ReferenceEquals( this.infos, null ) ) 95 | { 96 | this.LazyInitialize(); 97 | } 98 | 99 | return this.infos[childIndex]; 100 | } 101 | 102 | public sealed override int ChildNameToIndex( string name ) 103 | { 104 | if ( object.ReferenceEquals( this.infos, null ) ) 105 | { 106 | this.LazyInitialize(); 107 | } 108 | 109 | int result; 110 | if ( this.namesToIndex.TryGetValue( name, out result ) ) return result; 111 | return -1; 112 | } 113 | 114 | protected sealed override int GetChildCount( TValue value ) 115 | { 116 | if ( object.ReferenceEquals( this.infos, null ) ) 117 | { 118 | this.LazyInitialize(); 119 | } 120 | 121 | return this.infos.Count; 122 | } 123 | 124 | private bool initializing; 125 | 126 | private void LazyInitialize() 127 | { 128 | if ( this.initializing ) 129 | throw new Exception( "Illegal API call was made: cannot query members of a property that are dependent on children being initialized, during the initialization of the property's children." ); 130 | 131 | this.initializing = true; 132 | this.infos = this.GetPropertyInfos().ToList(); 133 | this.initializing = false; 134 | 135 | this.namesToIndex = new Dictionary(); 136 | 137 | for ( int i = 0; i < infos.Count; i++ ) 138 | { 139 | var info = infos[i]; 140 | this.namesToIndex[info.PropertyName] = i; 141 | } 142 | } 143 | #endregion 144 | 145 | protected static Regex s_DynamicPortRegex = new Regex( @"^(.+) (\d)$" ); 146 | 147 | public override bool CanResolveForPropertyFilter( InspectorProperty property ) 148 | { 149 | if ( !NodeEditor.InNodeEditor ) 150 | return false; 151 | 152 | return base.CanResolveForPropertyFilter( property ); 153 | } 154 | 155 | private List processors; 156 | 157 | public virtual void Dispose() 158 | { 159 | if ( this.processors != null ) 160 | { 161 | for ( int i = 0; i < this.processors.Count; i++ ) 162 | { 163 | var disposable = this.processors[i] as IDisposable; 164 | 165 | if ( disposable != null ) 166 | { 167 | disposable.Dispose(); 168 | } 169 | } 170 | } 171 | 172 | EditorApplication.update -= Update; 173 | } 174 | 175 | public Node Node { get; private set; } 176 | 177 | protected Dictionary nameToNodePropertyInfo = new Dictionary(); 178 | protected Dictionary propertyToNodeProperty = new Dictionary(); 179 | 180 | protected DisplayDynamicPortsAttribute displayDynamicPortsAttribute; 181 | 182 | public NodePortInfo GetNodePortInfo( string propertyName ) 183 | { 184 | if ( propertyToNodeProperty.TryGetValue( propertyName, out var portPropertyName ) ) 185 | { 186 | if ( nameToNodePropertyInfo.TryGetValue( portPropertyName, out var nodePortInfo ) ) 187 | return nodePortInfo; 188 | } 189 | 190 | return null; 191 | } 192 | 193 | protected override void Initialize() 194 | { 195 | base.Initialize(); 196 | 197 | Node = Property.Tree.WeakTargets.FirstOrDefault() as Node; 198 | } 199 | 200 | protected InspectorPropertyInfo[] GetPropertyInfos() 201 | { 202 | if ( this.processors == null ) 203 | { 204 | this.processors = OdinPropertyProcessorLocator.GetMemberProcessors( this.Property ); 205 | } 206 | 207 | var includeSpeciallySerializedMembers = this.Property.ValueEntry.SerializationBackend != SerializationBackend.Unity; 208 | var infos = InspectorPropertyInfoUtility.CreateMemberProperties( this.Property, typeof( TValue ), includeSpeciallySerializedMembers ); 209 | 210 | // If we resolve the ports from the port dictionary i might be able to communicate between properties 211 | // in order to make dynamic port adding cleaner 212 | 213 | // Resolve my own members so I can see them 214 | #if DEBUG_RESOLVER 215 | infos.AddValue( 216 | $"resolver:{nameof(infos)}", 217 | () => this.infos, 218 | value => { } 219 | ); 220 | 221 | infos.AddValue( 222 | $"resolver:{nameof( namesToIndex )}", 223 | () => this.namesToIndex, 224 | value => { } 225 | ); 226 | 227 | infos.AddValue( 228 | $"resolver:{nameof( nameToNodePropertyInfo )}", 229 | () => this.nameToNodePropertyInfo, 230 | value => { } 231 | ); 232 | 233 | infos.AddValue( 234 | $"resolver:{nameof( propertyToNodeProperty )}", 235 | () => this.propertyToNodeProperty, 236 | value => { } 237 | ); 238 | #endif 239 | 240 | LabelWidthAttribute labelWidthAttribute = Property.GetAttribute(); 241 | displayDynamicPortsAttribute = Property.GetAttribute(); 242 | 243 | // Port makers 244 | { 245 | for ( int i = 0; i < infos.Count; ++i ) 246 | { 247 | var info = infos[i]; 248 | if ( labelWidthAttribute != null ) 249 | { 250 | if ( info.GetAttribute() == null ) 251 | info.GetEditableAttributesList().Add( labelWidthAttribute ); 252 | } 253 | 254 | var inputAttribute = info.GetMemberInfo().GetAttribute(); 255 | var outputAttribute = info.GetMemberInfo().GetAttribute(); 256 | if ( inputAttribute != null || outputAttribute != null ) // Make a port.... we'll deal with dynamic later 257 | { 258 | string baseFieldName = info.PropertyName; 259 | NodePort port = Node.GetPort( info.PropertyName ); 260 | ShowBackingValue showBackingValue = ShowBackingValue.Always; 261 | ConnectionType connectionType = ConnectionType.Multiple; 262 | TypeConstraint typeConstraint = TypeConstraint.None; 263 | bool isDynamicPortList = false; 264 | bool isInput = false; 265 | 266 | if ( inputAttribute != null ) 267 | { 268 | showBackingValue = inputAttribute.backingValue; 269 | connectionType = inputAttribute.connectionType; 270 | typeConstraint = inputAttribute.typeConstraint; 271 | isDynamicPortList = inputAttribute.dynamicPortList; 272 | isInput = true; 273 | } 274 | else if ( outputAttribute != null ) 275 | { 276 | showBackingValue = outputAttribute.backingValue; 277 | connectionType = outputAttribute.connectionType; 278 | typeConstraint = outputAttribute.typeConstraint; 279 | isDynamicPortList = outputAttribute.dynamicPortList; 280 | isInput = false; 281 | } 282 | 283 | // The port didn't exist... let's just make it exist again? 284 | if ( port == null ) 285 | { 286 | Node.UpdatePorts(); 287 | port = Node.GetPort( info.PropertyName ); 288 | } 289 | 290 | var portInfo = InspectorPropertyInfo.CreateValue( 291 | $"{info.PropertyName}:port", 292 | 0, 293 | Property.ValueEntry.SerializationBackend, 294 | new GetterSetter( 295 | ( ref TValue owner ) => port, 296 | ( ref TValue owner, NodePort value ) => { } 297 | ) 298 | , new HideInInspector() 299 | ); 300 | 301 | var nodePortInfo = new NodePortInfo( 302 | portInfo, 303 | info, 304 | baseFieldName, 305 | info.TypeOfValue, 306 | Property.Tree.WeakTargets.FirstOrDefault() as Node, // Needed? 307 | showBackingValue, 308 | connectionType, 309 | typeConstraint, 310 | isDynamicPortList, 311 | false, 312 | isInput, 313 | true 314 | ); 315 | 316 | propertyToNodeProperty[info.PropertyName] = portInfo.PropertyName; 317 | nameToNodePropertyInfo[portInfo.PropertyName] = nodePortInfo; 318 | 319 | if ( isDynamicPortList ) 320 | { 321 | var listDrawerAttributes = info.GetAttribute(); 322 | if ( listDrawerAttributes == null ) 323 | { 324 | listDrawerAttributes = new ListDrawerSettingsAttribute(); 325 | info.GetEditableAttributesList().Add( listDrawerAttributes ); 326 | } 327 | 328 | #if ODIN_INSPECTOR_3_1 329 | listDrawerAttributes.ShowFoldout = true; 330 | #else 331 | listDrawerAttributes.Expanded = true; 332 | #endif 333 | listDrawerAttributes.ShowPaging = false; 334 | } 335 | 336 | infos.Insert( i, portInfo ); 337 | ++i; // Skip the next entry 338 | } 339 | } 340 | 341 | if ( displayDynamicPortsAttribute != null ) 342 | { 343 | // If I find any dynamic ports that were not covered here then add them as well 344 | // This should include anything that wouldn't be directly related to the ports I *did* find 345 | foreach ( var port in Node.Ports ) 346 | { 347 | if ( port.IsDynamic ) 348 | { 349 | // If is likely to be an automatically added port? 350 | if ( IsManagedPort( port.fieldName ) ) 351 | continue; 352 | 353 | // No one claimed it? 354 | var nodePortInfo = CreateLooseDynamicPortInfo( port, out var info, out var portInfo, displayDynamicPortsAttribute ); 355 | 356 | propertyToNodeProperty[info.PropertyName] = portInfo.PropertyName; 357 | nameToNodePropertyInfo[portInfo.PropertyName] = nodePortInfo; 358 | 359 | infos.Add( info ); 360 | infos.Add( portInfo ); 361 | } 362 | } 363 | } 364 | } 365 | 366 | for ( int i = 0; i < this.processors.Count; i++ ) 367 | { 368 | ProcessedMemberPropertyResolverExtensions.ProcessingOwnerType = typeof( TValue ); 369 | this.processors[i].ProcessMemberProperties( infos ); 370 | } 371 | 372 | EditorApplication.update -= Update; 373 | if ( displayDynamicPortsAttribute != null ) 374 | EditorApplication.update += Update; 375 | 376 | knownPortKeys.Clear(); 377 | knownPortKeys.AddRange( Node.Ports.Select( x => x.fieldName ) ); 378 | return InspectorPropertyInfoUtility.BuildPropertyGroupsAndFinalize( this.Property, typeof( TValue ), infos, includeSpeciallySerializedMembers ); 379 | } 380 | 381 | protected void RemoveProperty( int index ) 382 | { 383 | var info = infos[index]; 384 | infos.RemoveAt( index ); 385 | namesToIndex.Remove( info.PropertyName ); 386 | 387 | var keys = new List( namesToIndex.Keys ); // Pool? 388 | foreach ( var key in keys ) 389 | { 390 | int value = namesToIndex[key]; 391 | if ( value > index ) 392 | namesToIndex[key] = value - 1; 393 | } 394 | } 395 | 396 | public virtual bool ChildPropertyRequiresRefresh( int index, InspectorPropertyInfo info ) 397 | { 398 | return this.GetChildInfo( index ) != info; 399 | } 400 | 401 | public void RememberDynamicPort( InspectorProperty property ) 402 | { 403 | var nodePortInfo = GetNodePortInfo( property.Name ); 404 | if ( nodePortInfo.IsInput ) 405 | nodePortInfo.Node.AddDynamicInput( nodePortInfo.Type, nodePortInfo.ConnectionType, nodePortInfo.TypeConstraint, nodePortInfo.BaseFieldName ); 406 | else 407 | nodePortInfo.Node.AddDynamicOutput( nodePortInfo.Type, nodePortInfo.ConnectionType, nodePortInfo.TypeConstraint, nodePortInfo.BaseFieldName ); 408 | } 409 | 410 | public void ForgetDynamicPort( InspectorProperty property ) 411 | { 412 | var nodePortInfo = GetNodePortInfo( property.Name ); 413 | 414 | propertyToNodeProperty.Remove( nodePortInfo.SourcePropertyInfo.PropertyName ); 415 | nameToNodePropertyInfo.Remove( nodePortInfo.BaseFieldName ); 416 | 417 | // Also remove it from the arrays - a bit of work since it could be nested? 418 | int infoIndex = namesToIndex[nodePortInfo.SourcePropertyInfo.PropertyName]; 419 | RemoveProperty( infoIndex ); 420 | 421 | int portInfoIndex = namesToIndex[nodePortInfo.PortPropertyInfo.PropertyName]; 422 | RemoveProperty( portInfoIndex ); 423 | 424 | // If the port still exists then kill it 425 | if ( nodePortInfo.Port != null && nodePortInfo.Node != null ) 426 | nodePortInfo.Node.RemoveDynamicPort( nodePortInfo.Port ); 427 | } 428 | 429 | public NodePortInfo CreateLooseDynamicPortInfo( NodePort port, out InspectorPropertyInfo info, out InspectorPropertyInfo portInfo, params Attribute[] attributes ) 430 | { 431 | info = InspectorPropertyInfo.CreateValue( 432 | port.fieldName, 433 | 1, 434 | Property.ValueEntry.SerializationBackend, 435 | new GetterSetter( 436 | ( ref TValue owner ) => 0, 437 | ( ref TValue owner, int value ) => { } 438 | ), 439 | attributes 440 | ); 441 | 442 | portInfo = InspectorPropertyInfo.CreateValue( 443 | $"{info.PropertyName}:port", 444 | 0, 445 | Property.ValueEntry.SerializationBackend, 446 | new GetterSetter( 447 | ( ref TValue owner ) => port, 448 | ( ref TValue owner, NodePort value ) => { } 449 | ) 450 | , new HideInInspector() 451 | ); 452 | 453 | // Create a fake property and a fake node 454 | NodePortInfo nodePortInfo = new NodePortInfo( 455 | portInfo, 456 | info, 457 | port.fieldName, 458 | port.ValueType, 459 | Property.Tree.WeakTargets.FirstOrDefault() as Node, // Needed? 460 | ShowBackingValue.Never, 461 | port.connectionType, 462 | port.typeConstraint, 463 | false, 464 | true, 465 | port.IsInput, 466 | false 467 | ); 468 | 469 | return nodePortInfo; 470 | } 471 | 472 | protected List knownPortKeys = new List(); 473 | protected List currentPortKeys = new List(); 474 | 475 | protected bool IsManagedPort( string portName ) 476 | { 477 | if ( GetNodePortInfo( portName ) != null ) 478 | return true; 479 | 480 | // If is likely to be an automatically added port? 481 | var match = s_DynamicPortRegex.Match( portName ); 482 | if ( match != null ) // Matched numbered ports 483 | { 484 | var basePortName = match.Groups[1].Value; 485 | var foundPort = Node.GetPort( basePortName ); 486 | if ( foundPort != null && foundPort.IsStatic ) 487 | return true; 488 | } 489 | 490 | return false; 491 | } 492 | 493 | // Listening for port changes 494 | protected void Update() 495 | { 496 | currentPortKeys.Clear(); 497 | currentPortKeys.AddRange( Node.Ports.Select( x => x.fieldName ) ); 498 | 499 | var lostPorts = knownPortKeys.Except( currentPortKeys ); 500 | var gainedPorts = currentPortKeys.Except( knownPortKeys ); 501 | 502 | foreach ( var lost in lostPorts ) 503 | { 504 | if ( IsManagedPort( lost ) ) 505 | continue; 506 | } 507 | foreach ( var gain in gainedPorts ) 508 | { 509 | if ( IsManagedPort( gain ) ) 510 | continue; 511 | 512 | // Check if we already know about it 513 | if ( nameToNodePropertyInfo.ContainsKey( gain ) ) 514 | continue; 515 | 516 | var port = Node.GetPort( gain ); 517 | 518 | // No one claimed it? 519 | var nodePortInfo = CreateLooseDynamicPortInfo( port, out var info, out var portInfo, displayDynamicPortsAttribute ); 520 | 521 | propertyToNodeProperty[info.PropertyName] = portInfo.PropertyName; 522 | nameToNodePropertyInfo[portInfo.PropertyName] = nodePortInfo; 523 | 524 | namesToIndex[info.PropertyName] = this.infos.Count; 525 | this.infos.Add( info ); 526 | namesToIndex[portInfo.PropertyName] = this.infos.Count; 527 | this.infos.Add( portInfo ); 528 | } 529 | 530 | knownPortKeys.Clear(); 531 | knownPortKeys.AddRange( currentPortKeys ); 532 | } 533 | } 534 | 535 | public class DefaultNodePropertyPortResolver : NodePropertyPortResolver 536 | where T : Node 537 | { 538 | } 539 | } 540 | -------------------------------------------------------------------------------- /Editor/Resolvers/NodePropertyPortResolver.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a2fb26fe98dfc534c938a14de007ec7d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/XNodeEditorOdin.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "XNodeEditorOdin", 3 | "references": [ 4 | "XNode", 5 | "XNodeEditor", 6 | "XNodeOdin", 7 | "Sirenix.OdinInspector.Editor", 8 | "Sirenix.Utilities.Editor", 9 | "Sirenix.OdinInspector.Attributes", 10 | "Sirenix.Utilities" 11 | ], 12 | "includePlatforms": [ 13 | "Editor" 14 | ], 15 | "excludePlatforms": [], 16 | "allowUnsafeCode": false, 17 | "overrideReferences": false, 18 | "precompiledReferences": [], 19 | "autoReferenced": true, 20 | "defineConstraints": [ 21 | "ODIN_INSPECTOR" 22 | ], 23 | "versionDefines": [] 24 | } -------------------------------------------------------------------------------- /Editor/XNodeEditorOdin.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fcee8d91a7038ed48a24d527da492450 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 KAJed82 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b999d13ef7e39d4682cebd2dd22b79b 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xNode-OdinExtensions 2 | Full Odin integration on top of the Odin support provided by my fork. 3 | 4 | [KAJed82/xNode](https://github.com/KAJed82/xNode) 5 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8690d98ffcf87f144b5a4183446b5314 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1ebf0e348877c7a4db0cee28e5bed8c6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Attributes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 43d85f200cc84a44fb4c767e5fd56cb9 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38b85b9f5c6a51d4dba81bb571cee4b4 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node/DisplayDynamicPortsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | /// 6 | /// Automatically draws extra 'loose' dynamic ports to the end of the node. 7 | /// 8 | [AttributeUsage( AttributeTargets.Class, AllowMultiple = false, Inherited = false )] 9 | public class DisplayDynamicPortsAttribute : NodePortAttribute 10 | { 11 | public bool ShowRemoveButton { get; private set; } 12 | 13 | public DisplayDynamicPortsAttribute() 14 | { 15 | } 16 | 17 | /// 18 | /// 19 | /// 20 | /// Set this to true to had a button automatically added to 'loose' dynamic ports to remove them. 21 | public DisplayDynamicPortsAttribute( bool showRemoveButton ) 22 | { 23 | ShowRemoveButton = showRemoveButton; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node/DisplayDynamicPortsAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ecfa4823d7a58c7409e677b58bd18c79 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node/ShowFoldButtonsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | [AttributeUsage( AttributeTargets.Class, AllowMultiple = false, Inherited = false )] 6 | public class ShowFoldButtonsAttribute : Attribute { } 7 | } 8 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node/ShowFoldButtonsAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f8bd29e43ed0ecd46909d17feb9f834a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node/ShowNameInInspectorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | [AttributeUsage( AttributeTargets.Class, AllowMultiple = false, Inherited = false )] 6 | public class ShowNameInInspectorAttribute : Attribute { } 7 | } 8 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node/ShowNameInInspectorAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e018abeef176ac4b87e0981b224798f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node/ShowNameInNodeEditorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | [AttributeUsage( AttributeTargets.Class, AllowMultiple = false, Inherited = false )] 6 | public class ShowNameInNodeEditorAttribute : Attribute { } 7 | } 8 | -------------------------------------------------------------------------------- /Runtime/Attributes/Node/ShowNameInNodeEditorAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 385f5631b1b62194d8a837f65e92ccd5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b4f77e14fccb61347895574fdc5daf61 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/BoxedPortAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | [AttributeUsage( AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface, 6 | AllowMultiple = false, 7 | Inherited = false )] 8 | public class BoxedPortAttribute : NodePortAttribute { } 9 | } 10 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/BoxedPortAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cbd537457ca878941a1936ab6e2220c7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/DrawConnectionNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | public class DrawConnectionNameAttribute : NodePortAttribute { } 6 | } 7 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/DrawConnectionNameAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dd280b94a7e7e0c45ba4feb9c7c6c8b8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/Dynamic.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 70cb5b0f0ab04e749ab635b5161b5865 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/Dynamic/HideConnectionLabelAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | /// 6 | /// Apply to a dynamic port list in order to hide the connection names in the list 7 | /// 8 | public class HideConnectionLabelAttribute : NodePortAttribute { } 9 | } 10 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/Dynamic/HideConnectionLabelAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 57a8419805c35c34baadfa6b0f360d28 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/FoldoutPortAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | [AttributeUsage( AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface, 6 | AllowMultiple = false, 7 | Inherited = false )] 8 | public class FoldoutPortAttribute : NodePortAttribute { } 9 | } 10 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/FoldoutPortAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 02315e1a158e6754fa39a4769a75ae10 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/HandleOnlyAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace XNode.Odin 2 | { 3 | public class HandleOnlyAttribute : NodePortAttribute 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/HandleOnlyAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 47a431a70986f7a4ebdbd820b0912825 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/HideInNodeEditorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | public class HideInNodeEditorAttribute : Attribute { } 6 | } 7 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/HideInNodeEditorAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d2175fb0695be3a4e8376dd0fae31d6d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/HideOutsideNodeEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | public class HideOutsideNodeEditorAttribute : Attribute { } 6 | } 7 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/HideOutsideNodeEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cba0d5ff9ff0da046970edc6130c6d85 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/HidePortLabelAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace XNode.Odin 2 | { 3 | public class HidePortLabelAttribute : NodePortAttribute 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/HidePortLabelAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 453b5b380c9389a4e91a4b0dfef01b50 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/NodePortAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XNode.Odin 4 | { 5 | [AttributeUsage( AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false )] 6 | public abstract class NodePortAttribute : Attribute { } 7 | } 8 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/NodePortAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cf7f14565e64b8d4ebd71dff42b453ad 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/NodePortConfigurationAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace XNode.Odin 2 | { 3 | public class NodePortConfigurationAttribute : NodePortAttribute 4 | { 5 | /// 6 | /// Useful for ports that only want to be connections and not hold data in any way. 7 | /// 8 | public bool HideContents { get; protected set; } 9 | 10 | public NodePortConfigurationAttribute( bool hideContents ) 11 | { 12 | HideContents = hideContents; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Runtime/Attributes/Port/NodePortConfigurationAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1326c4319a681e344a04ebe322d63ab3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/OdinSerializer.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad069b76743300f4b90a9953c3bd0beb 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/OdinSerializer/SerializableNode.cs: -------------------------------------------------------------------------------- 1 |  2 | using Sirenix.OdinInspector; 3 | using Sirenix.Serialization; 4 | using UnityEngine; 5 | 6 | namespace XNode.Odin 7 | { 8 | [ShowOdinSerializedPropertiesInInspector] 9 | public abstract class SerializableNode : Node, ISerializationCallbackReceiver 10 | { 11 | #region Odin serialized data 12 | [SerializeField, HideInInspector] 13 | private SerializationData serializationData; 14 | 15 | void ISerializationCallbackReceiver.OnAfterDeserialize() 16 | { 17 | UnitySerializationUtility.DeserializeUnityObject( this, ref this.serializationData ); 18 | this.OnAfterDeserialize(); 19 | } 20 | 21 | void ISerializationCallbackReceiver.OnBeforeSerialize() 22 | { 23 | this.OnBeforeSerialize(); 24 | UnitySerializationUtility.SerializeUnityObject( this, ref this.serializationData ); 25 | } 26 | #endregion 27 | 28 | public virtual void OnAfterDeserialize() 29 | { 30 | } 31 | 32 | public virtual void OnBeforeSerialize() 33 | { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Runtime/OdinSerializer/SerializableNode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c569da9c0b60884187707f26e4419ee 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/OdinSerializer/XNodeOdinSerializer.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "XNodeOdinSerializer", 3 | "references": [ 4 | "XNode", 5 | "Sirenix.Serialization" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [ 14 | "ODIN_INSPECTOR", 15 | "ODIN_SERIALIZER" 16 | ], 17 | "versionDefines": [] 18 | } -------------------------------------------------------------------------------- /Runtime/OdinSerializer/XNodeOdinSerializer.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8f9792b05cc529a47a8c7a32ce3e94f8 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/XNodeOdin.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "XNodeOdin", 3 | "references": [], 4 | "includePlatforms": [], 5 | "excludePlatforms": [], 6 | "allowUnsafeCode": false, 7 | "overrideReferences": false, 8 | "precompiledReferences": [], 9 | "autoReferenced": true, 10 | "defineConstraints": [ 11 | "ODIN_INSPECTOR" 12 | ], 13 | "versionDefines": [] 14 | } -------------------------------------------------------------------------------- /Runtime/XNodeOdin.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 939c29fbb20335b4fad2b1ef312ff79f 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.github.siccity.xnode.odin", 3 | "description": "Provides a full Odin wrapper on xNode (requires the odin-support fork).", 4 | "version": "1.0.0", 5 | "unity": "2018.1", 6 | "displayName": "xNode-OdinExtensions" 7 | } 8 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1f92477d67cc84a438b7b2e8e1327eaa 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------