diff --git a/Scripts/Editor/Drawers/Odin.meta b/Scripts/Editor/Drawers/Odin.meta new file mode 100644 index 0000000..c2b0ac9 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 327994a52f523b641898a39ff7500a02 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs b/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs new file mode 100644 index 0000000..84c6d8e --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs @@ -0,0 +1,48 @@ +#if UNITY_EDITOR && ODIN_INSPECTOR +using System; +using System.Collections.Generic; +using System.Reflection; +using Sirenix.OdinInspector.Editor; +using UnityEngine; +using XNode; + +namespace XNodeEditor { + internal class OdinNodeInGraphAttributeProcessor : OdinAttributeProcessor where T : Node { + public override bool CanProcessSelfAttributes(InspectorProperty property) { + return false; + } + + public override bool CanProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member) { + if (!NodeEditor.inNodeEditor) + return false; + + if (member.MemberType == MemberTypes.Field) { + switch (member.Name) { + case "graph": + case "position": + case "ports": + return true; + + default: + break; + } + } + + return false; + } + + public override void ProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member, List attributes) { + switch (member.Name) { + case "graph": + case "position": + case "ports": + attributes.Add(new HideInInspector()); + break; + + default: + break; + } + } + } +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs.meta b/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs.meta new file mode 100644 index 0000000..15f6990 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3cf2561fbfea9a041ac81efbbb5b3e0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs b/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs new file mode 100644 index 0000000..a384bdc --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs @@ -0,0 +1,49 @@ +#if UNITY_EDITOR && ODIN_INSPECTOR +using Sirenix.OdinInspector; +using Sirenix.OdinInspector.Editor; +using Sirenix.Utilities.Editor; +using UnityEngine; +using XNode; + +namespace XNodeEditor { + public class InputAttributeDrawer : OdinAttributeDrawer { + protected override bool CanDrawAttributeProperty(InspectorProperty property) { + Node node = property.Tree.WeakTargets[0] as Node; + return node != null; + } + + protected override void DrawPropertyLayout(GUIContent label) { + Node node = Property.Tree.WeakTargets[0] as Node; + NodePort port = node.GetInputPort(Property.Name); + + if (!NodeEditor.inNodeEditor) { + if (Attribute.backingValue == XNode.Node.ShowBackingValue.Always || Attribute.backingValue == XNode.Node.ShowBackingValue.Unconnected && !port.IsConnected) + CallNextDrawer(label); + return; + } + + if (Property.Tree.WeakTargets.Count > 1) { + SirenixEditorGUI.WarningMessageBox("Cannot draw ports with multiple nodes selected"); + return; + } + + if (port != null) { + var portPropoerty = Property.Tree.GetUnityPropertyForPath(Property.UnityPropertyPath); + if (portPropoerty == null) { + SirenixEditorGUI.ErrorMessageBox("Port property missing at: " + Property.UnityPropertyPath); + return; + } else { + var labelWidth = Property.GetAttribute(); + if (labelWidth != null) + GUIHelper.PushLabelWidth(labelWidth.Width); + + NodeEditorGUILayout.PropertyField(portPropoerty, label == null ? GUIContent.none : label, true, GUILayout.MinWidth(30)); + + if (labelWidth != null) + GUIHelper.PopLabelWidth(); + } + } + } + } +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta b/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta new file mode 100644 index 0000000..12b7615 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2fd590b2e9ea0bd49b6986a2ca9010ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs b/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs new file mode 100644 index 0000000..ff59615 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs @@ -0,0 +1,49 @@ +#if UNITY_EDITOR && ODIN_INSPECTOR +using Sirenix.OdinInspector; +using Sirenix.OdinInspector.Editor; +using Sirenix.Utilities.Editor; +using UnityEngine; +using XNode; + +namespace XNodeEditor { + public class OutputAttributeDrawer : OdinAttributeDrawer { + protected override bool CanDrawAttributeProperty(InspectorProperty property) { + Node node = property.Tree.WeakTargets[0] as Node; + return node != null; + } + + protected override void DrawPropertyLayout(GUIContent label) { + Node node = Property.Tree.WeakTargets[0] as Node; + NodePort port = node.GetOutputPort(Property.Name); + + if (!NodeEditor.inNodeEditor) { + if (Attribute.backingValue == XNode.Node.ShowBackingValue.Always || Attribute.backingValue == XNode.Node.ShowBackingValue.Unconnected && !port.IsConnected) + CallNextDrawer(label); + return; + } + + if (Property.Tree.WeakTargets.Count > 1) { + SirenixEditorGUI.WarningMessageBox("Cannot draw ports with multiple nodes selected"); + return; + } + + if (port != null) { + var portPropoerty = Property.Tree.GetUnityPropertyForPath(Property.UnityPropertyPath); + if (portPropoerty == null) { + SirenixEditorGUI.ErrorMessageBox("Port property missing at: " + Property.UnityPropertyPath); + return; + } else { + var labelWidth = Property.GetAttribute(); + if (labelWidth != null) + GUIHelper.PushLabelWidth(labelWidth.Width); + + NodeEditorGUILayout.PropertyField(portPropoerty, label == null ? GUIContent.none : label, true, GUILayout.MinWidth(30)); + + if (labelWidth != null) + GUIHelper.PopLabelWidth(); + } + } + } + } +} +#endif \ No newline at end of file diff --git a/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta b/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta new file mode 100644 index 0000000..aa22218 --- /dev/null +++ b/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e7ebd8f2b42e2384aa109551dc46af88 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 358f438..6effeee 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -20,12 +20,18 @@ namespace XNodeEditor { public static Action onUpdateNode; public readonly static Dictionary portPositions = new Dictionary(); +#if ODIN_INSPECTOR + internal static bool inNodeEditor = false; +#endif + public virtual void OnHeaderGUI() { GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30)); } /// Draws standard field editors for all public fields public virtual void OnBodyGUI() { + inNodeEditor = true; + // Unity specifically requires this to save/update any serial object. // serializedObject.Update(); must go at the start of an inspector gui, and // serializedObject.ApplyModifiedProperties(); goes at the end. @@ -33,9 +39,12 @@ namespace XNodeEditor { string[] excludes = { "m_Script", "graph", "position", "ports" }; #if ODIN_INSPECTOR - // let xNode handle these - string[] drawnbyXNode = target.Ports.Select(x => x.fieldName).ToArray(); -#endif + InspectorUtilities.BeginDrawPropertyTree(objectTree, true); + GUIHelper.PushLabelWidth(84); + objectTree.Draw(true); + InspectorUtilities.EndDrawPropertyTree(objectTree); + GUIHelper.PopLabelWidth(); +#else // Iterate through serialized properties and draw them like the Inspector (But with ports) SerializedProperty iterator = serializedObject.GetIterator(); @@ -43,22 +52,9 @@ namespace XNodeEditor { EditorGUIUtility.labelWidth = 84; while (iterator.NextVisible(enterChildren)) { enterChildren = false; -#if ODIN_INSPECTOR - if (drawnbyXNode.Contains(iterator.name)) NodeEditorGUILayout.PropertyField(iterator, true); -#else if (excludes.Contains(iterator.name)) continue; NodeEditorGUILayout.PropertyField(iterator, true); -#endif } - -#if ODIN_INSPECTOR - InspectorUtilities.BeginDrawPropertyTree(objectTree, true); - objectTree.EnumerateTree(includeChildren: false).ForEach(p => { - if (!drawnbyXNode.Contains(p.Name) && !excludes.Contains(p.Name)) { - p.Draw(); - } - }); - InspectorUtilities.EndDrawPropertyTree(objectTree); #endif // Iterate through dynamic ports and draw them in the order in which they are serialized @@ -78,6 +74,8 @@ namespace XNodeEditor { #else window.Repaint(); #endif + + inNodeEditor = false; } public virtual int GetWidth() { @@ -144,4 +142,4 @@ namespace XNodeEditor { } } } -} +} \ No newline at end of file diff --git a/Scripts/Editor/NodeEditorBase.cs b/Scripts/Editor/NodeEditorBase.cs index e380a25..1fc28c7 100644 --- a/Scripts/Editor/NodeEditorBase.cs +++ b/Scripts/Editor/NodeEditorBase.cs @@ -26,7 +26,10 @@ namespace XNodeEditor.Internal { get { if (this._objectTree == null) { try { + bool wasInEditor = NodeEditor.inNodeEditor; + NodeEditor.inNodeEditor = true; this._objectTree = PropertyTree.Create(this.serializedObject); + NodeEditor.inNodeEditor = wasInEditor; } catch (ArgumentException ex) { Debug.Log(ex); }