1
0
mirror of https://github.com/Siccity/xNode.git synced 2025-12-20 09:16:01 +08:00

Odin inspector support (#182)

* Added Odin Inspector support
* Added support for ports
This commit is contained in:
Thor Brigsted 2019-09-01 23:49:19 +02:00
parent 6fa3bdf2ad
commit 43bcb54fda
9 changed files with 245 additions and 1 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 327994a52f523b641898a39ff7500a02
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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<T> : OdinAttributeProcessor<T> where T : Node {
public override bool CanProcessSelfAttributes(InspectorProperty property) {
return false;
}
public override bool CanProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member) {
if (!NodeEditor.inNodeEditor)
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<Attribute> attributes) {
switch (member.Name) {
case "graph":
case "position":
case "ports":
attributes.Add(new HideInInspector());
break;
default:
break;
}
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3cf2561fbfea9a041ac81efbbb5b3e0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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<XNode.Node.InputAttribute> {
protected override bool CanDrawAttributeProperty(InspectorProperty property) {
Node node = property.Tree.WeakTargets[0] as Node;
return node != null;
}
protected override void DrawPropertyLayout(GUIContent label) {
Node node = Property.Tree.WeakTargets[0] as Node;
NodePort port = node.GetInputPort(Property.Name);
if (!NodeEditor.inNodeEditor) {
if (Attribute.backingValue == XNode.Node.ShowBackingValue.Always || Attribute.backingValue == XNode.Node.ShowBackingValue.Unconnected && !port.IsConnected)
CallNextDrawer(label);
return;
}
if (Property.Tree.WeakTargets.Count > 1) {
SirenixEditorGUI.WarningMessageBox("Cannot draw ports with multiple nodes selected");
return;
}
if (port != null) {
var portPropoerty = Property.Tree.GetUnityPropertyForPath(Property.UnityPropertyPath);
if (portPropoerty == null) {
SirenixEditorGUI.ErrorMessageBox("Port property missing at: " + Property.UnityPropertyPath);
return;
} else {
var labelWidth = Property.GetAttribute<LabelWidthAttribute>();
if (labelWidth != null)
GUIHelper.PushLabelWidth(labelWidth.Width);
NodeEditorGUILayout.PropertyField(portPropoerty, label == null ? GUIContent.none : label, true, GUILayout.MinWidth(30));
if (labelWidth != null)
GUIHelper.PopLabelWidth();
}
}
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2fd590b2e9ea0bd49b6986a2ca9010ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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<XNode.Node.OutputAttribute> {
protected override bool CanDrawAttributeProperty(InspectorProperty property) {
Node node = property.Tree.WeakTargets[0] as Node;
return node != null;
}
protected override void DrawPropertyLayout(GUIContent label) {
Node node = Property.Tree.WeakTargets[0] as Node;
NodePort port = node.GetOutputPort(Property.Name);
if (!NodeEditor.inNodeEditor) {
if (Attribute.backingValue == XNode.Node.ShowBackingValue.Always || Attribute.backingValue == XNode.Node.ShowBackingValue.Unconnected && !port.IsConnected)
CallNextDrawer(label);
return;
}
if (Property.Tree.WeakTargets.Count > 1) {
SirenixEditorGUI.WarningMessageBox("Cannot draw ports with multiple nodes selected");
return;
}
if (port != null) {
var portPropoerty = Property.Tree.GetUnityPropertyForPath(Property.UnityPropertyPath);
if (portPropoerty == null) {
SirenixEditorGUI.ErrorMessageBox("Port property missing at: " + Property.UnityPropertyPath);
return;
} else {
var labelWidth = Property.GetAttribute<LabelWidthAttribute>();
if (labelWidth != null)
GUIHelper.PushLabelWidth(labelWidth.Width);
NodeEditorGUILayout.PropertyField(portPropoerty, label == null ? GUIContent.none : label, true, GUILayout.MinWidth(30));
if (labelWidth != null)
GUIHelper.PopLabelWidth();
}
}
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e7ebd8f2b42e2384aa109551dc46af88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -3,6 +3,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
#endif
namespace XNodeEditor { namespace XNodeEditor {
/// <summary> Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. </summary> /// <summary> Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. </summary>
@ -15,18 +20,34 @@ namespace XNodeEditor {
public static Action<XNode.Node> onUpdateNode; public static Action<XNode.Node> onUpdateNode;
public readonly static Dictionary<XNode.NodePort, Vector2> portPositions = new Dictionary<XNode.NodePort, Vector2>(); public readonly static Dictionary<XNode.NodePort, Vector2> portPositions = new Dictionary<XNode.NodePort, Vector2>();
#if ODIN_INSPECTOR
internal static bool inNodeEditor = false;
#endif
public virtual void OnHeaderGUI() { public virtual void OnHeaderGUI() {
GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30)); GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
} }
/// <summary> Draws standard field editors for all public fields </summary> /// <summary> Draws standard field editors for all public fields </summary>
public virtual void OnBodyGUI() { public virtual void OnBodyGUI() {
#if ODIN_INSPECTOR
inNodeEditor = true;
#endif
// Unity specifically requires this to save/update any serial object. // Unity specifically requires this to save/update any serial object.
// serializedObject.Update(); must go at the start of an inspector gui, and // serializedObject.Update(); must go at the start of an inspector gui, and
// serializedObject.ApplyModifiedProperties(); goes at the end. // serializedObject.ApplyModifiedProperties(); goes at the end.
serializedObject.Update(); serializedObject.Update();
string[] excludes = { "m_Script", "graph", "position", "ports" }; string[] excludes = { "m_Script", "graph", "position", "ports" };
#if ODIN_INSPECTOR
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) // Iterate through serialized properties and draw them like the Inspector (But with ports)
SerializedProperty iterator = serializedObject.GetIterator(); SerializedProperty iterator = serializedObject.GetIterator();
bool enterChildren = true; bool enterChildren = true;
@ -36,6 +57,7 @@ namespace XNodeEditor {
if (excludes.Contains(iterator.name)) continue; if (excludes.Contains(iterator.name)) continue;
NodeEditorGUILayout.PropertyField(iterator, true); NodeEditorGUILayout.PropertyField(iterator, true);
} }
#endif
// Iterate through dynamic ports and draw them in the order in which they are serialized // Iterate through dynamic ports and draw them in the order in which they are serialized
foreach (XNode.NodePort dynamicPort in target.DynamicPorts) { foreach (XNode.NodePort dynamicPort in target.DynamicPorts) {
@ -44,6 +66,20 @@ namespace XNodeEditor {
} }
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
#if ODIN_INSPECTOR
// Call repaint so that the graph window elements respond properly to layout changes coming from Odin
if (GUIHelper.RepaintRequested) {
GUIHelper.ClearRepaintRequest();
window.Repaint();
}
#else
window.Repaint();
#endif
#if ODIN_INSPECTOR
inNodeEditor = false;
#endif
} }
public virtual int GetWidth() { public virtual int GetWidth() {

View File

@ -4,6 +4,9 @@ using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector.Editor;
#endif
namespace XNodeEditor.Internal { namespace XNodeEditor.Internal {
/// <summary> Handles caching of custom editor classes and their target types. Accessible with GetEditor(Type type) </summary> /// <summary> Handles caching of custom editor classes and their target types. Accessible with GetEditor(Type type) </summary>
@ -17,6 +20,24 @@ namespace XNodeEditor.Internal {
public NodeEditorWindow window; public NodeEditorWindow window;
public K target; public K target;
public SerializedObject serializedObject; public SerializedObject serializedObject;
#if ODIN_INSPECTOR
private PropertyTree _objectTree;
public PropertyTree objectTree {
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);
}
}
return this._objectTree;
}
}
#endif
public static T GetEditor(K target, NodeEditorWindow window) { public static T GetEditor(K target, NodeEditorWindow window) {
if (target == null) return null; if (target == null) return null;