diff --git a/Example/Nodes/Editor/DisplayValueEditor.cs b/Example/Nodes/Editor/DisplayValueEditor.cs index 0cc8af5..4d37bc2 100644 --- a/Example/Nodes/Editor/DisplayValueEditor.cs +++ b/Example/Nodes/Editor/DisplayValueEditor.cs @@ -7,8 +7,8 @@ namespace BasicNodes { [CustomNodeEditor(typeof(DisplayValue), "BasicNodes/DisplayValue")] public class DisplayValueEditor : NodeEditor { - protected override void OnBodyGUI(out Dictionary portPositions) { - base.OnBodyGUI(out portPositions); + protected override void OnBodyGUI() { + base.OnBodyGUI(); object obj = target.GetValue(null); if (obj != null) EditorGUILayout.LabelField(target.GetValue(null).ToString()); } diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 5352061..6a5a5e4 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -10,38 +10,35 @@ public class NodeEditor { /// Fires every whenever a node was modified through the editor public static Action onUpdateNode; public Node target; + public SerializedObject serializedObject; + public static Dictionary portPositions; /// Draws the node GUI. /// Port handle positions need to be returned to the NodeEditorWindow - public void OnNodeGUI(out Dictionary portPositions) { + public void OnNodeGUI() { OnHeaderGUI(); - OnBodyGUI(out portPositions); + OnBodyGUI(); } - protected virtual void OnHeaderGUI() { + protected void OnHeaderGUI() { GUI.color = Color.white; string title = NodeEditorUtilities.PrettifyCamelCase(target.name); GUILayout.Label(title, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30)); } /// Draws standard field editors for all public fields - protected virtual void OnBodyGUI(out Dictionary portPositions) { + protected virtual void OnBodyGUI() { + string[] excludes = { "m_Script", "graph", "position", "inputs", "outputs" }; portPositions = new Dictionary(); - EditorGUI.BeginChangeCheck(); - FieldInfo[] fields = GetInspectorFields(target); - for (int i = 0; i < fields.Length; i++) { - switch (fields[i].Name) { case "graph": case "position": case "inputs": case "outputs": continue; } - NodeEditorGUILayout.PropertyField(target, fields[i], portPositions); + SerializedProperty iterator = serializedObject.GetIterator(); + bool enterChildren = true; + EditorGUIUtility.labelWidth = 84; + while (iterator.NextVisible(enterChildren)) { + enterChildren = false; + if (excludes.Contains(iterator.name)) continue; + NodeEditorGUILayout.PropertyField(iterator, true); } - //If user changed a value, notify other scripts through onUpdateNode - if (EditorGUI.EndChangeCheck()) { - if (onUpdateNode != null) onUpdateNode(target); - } - } - - private static FieldInfo[] GetInspectorFields(Node node) { - return node.GetType().GetFields().Where(f => f.IsPublic || f.GetCustomAttributes(typeof(SerializeField), false) != null).ToArray(); } public virtual int GetWidth() { diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index a5ee942..2faeddf 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -164,23 +164,31 @@ public partial class NodeEditorWindow { NodeEditor nodeEditor = GetNodeEditor(node.GetType()); nodeEditor.target = node; + nodeEditor.serializedObject = new SerializedObject(node); + NodeEditor.portPositions = new Dictionary(); //Get node position Vector2 nodePos = GridToWindowPositionNoClipped(node.position); - //GUIStyle style = (node == selectedNode) ? (GUIStyle)"flow node 0 on" : (GUIStyle)"flow node 0"; - GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000))); GUIStyle style = NodeEditorResources.styles.nodeBody; GUILayout.BeginVertical(new GUIStyle(style)); + EditorGUI.BeginChangeCheck(); //Draw node contents - Dictionary portHandlePoints; - nodeEditor.OnNodeGUI(out portHandlePoints); + nodeEditor.OnNodeGUI(); + + //Apply + nodeEditor.serializedObject.ApplyModifiedProperties(); + + //If user changed a value, notify other scripts through onUpdateNode + if (EditorGUI.EndChangeCheck()) { + if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node); + } if (e.type == EventType.Repaint) { - foreach (var kvp in portHandlePoints) { + foreach (var kvp in NodeEditor.portPositions) { Vector2 portHandlePos = kvp.Value; portHandlePos += node.position; Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16); diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 936e6b9..76bef8b 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -6,232 +6,53 @@ using System.Reflection; using UnityEditor; using UnityEngine; -/// Provides unec-specific field editors -public class NodeEditorGUILayout { +/// UNEC-specific version of +public static class NodeEditorGUILayout { - private static double tempValue; + public static void PropertyField(SerializedProperty property, bool includeChildren) { + Node node = property.serializedObject.targetObject as Node; + NodePort port = node.GetPortByFieldName(property.name); - public static object PortField(string label, object value, System.Type type, NodePort port, bool fallback, out Vector2 portPosition) { - if (fallback) value = PropertyField(label, value, type); - else EditorGUILayout.LabelField(label); + float temp_labelWidth = EditorGUIUtility.labelWidth; - Rect rect = GUILayoutUtility.GetLastRect(); - if (port == null) { portPosition = rect.position; return null; } - if (port.direction == NodePort.IO.Input) rect.position = rect.position - new Vector2(16, 0); - if (port.direction == NodePort.IO.Output) rect.position = rect.position + new Vector2(rect.width, 0); - rect.size = new Vector2(16, 16); + // If property is not a port, display a regular property field + if (port == null) EditorGUILayout.PropertyField(property, includeChildren); + else { + Rect rect = new Rect(); + // If property is an input, display a regular property field and put a port handle on the left side + if (port.direction == NodePort.IO.Input) { + // Display a label if port is connected + if (port.IsConnected) EditorGUILayout.LabelField(property.displayName); + // Display an editable property field if port is not connected + else EditorGUILayout.PropertyField(property, includeChildren); + rect = GUILayoutUtility.GetLastRect(); + rect.position = rect.position - new Vector2(16, 0); + // If property is an output, display a text label and put a port handle on the right side + } else if (port.direction == NodePort.IO.Output) { + EditorGUILayout.LabelField(property.displayName, NodeEditorResources.styles.outputPort); + rect = GUILayoutUtility.GetLastRect(); + rect.position = rect.position + new Vector2(rect.width, 0); + } + + rect.size = new Vector2(16, 16); + + DrawPortHandle(rect, port.type); + + // Register the handle position + Vector2 portPos = rect.center; + if (NodeEditor.portPositions.ContainsKey(port)) NodeEditor.portPositions[port] = portPos; + else NodeEditor.portPositions.Add(port, portPos); + } + EditorGUIUtility.labelWidth = temp_labelWidth; + } + + private static void DrawPortHandle(Rect rect, Type type) { Color col = GUI.color; GUI.color = new Color32(90, 97, 105, 255); GUI.DrawTexture(rect, NodeEditorResources.dotOuter); - GUI.color = NodeEditorPreferences.GetTypeColor(port.type); + GUI.color = NodeEditorPreferences.GetTypeColor(type); GUI.DrawTexture(rect, NodeEditorResources.dot); GUI.color = col; - portPosition = rect.center; - - return value; - } - - public static object PropertyField(Node target, FieldInfo fieldInfo, Dictionary portPositions) { - Type fieldType = fieldInfo.FieldType; - string fieldName = fieldInfo.Name; - string fieldPrettyName = fieldName.PrettifyCamelCase(); - object fieldValue = fieldInfo.GetValue(target); - object[] fieldAttribs = fieldInfo.GetCustomAttributes(false); - - HeaderAttribute headerAttrib; - if (NodeEditorUtilities.GetAttrib(fieldAttribs, out headerAttrib)) { - EditorGUILayout.LabelField(headerAttrib.header); - } - - Node.OutputAttribute outputAttrib; - Node.InputAttribute inputAttrib; - - EditorGUI.BeginChangeCheck(); - if (NodeEditorUtilities.GetAttrib(fieldAttribs, out inputAttrib)) { - NodePort port = target.GetPortByFieldName(fieldName); - Vector2 portPos; - bool fallback = inputAttrib.backingValue == Node.ShowBackingValue.Always || (inputAttrib.backingValue == Node.ShowBackingValue.Unconnected && !port.IsConnected); - fieldValue = PortField(fieldPrettyName, fieldValue, fieldType, port, fallback, out portPos); - portPositions.Add(port, portPos); - } else if (NodeEditorUtilities.GetAttrib(fieldAttribs, out outputAttrib)) { - NodePort port = target.GetPortByFieldName(fieldName); - Vector2 portPos; - fieldValue = PortField(fieldPrettyName, fieldValue, fieldType, port, false, out portPos); - portPositions.Add(port, portPos); - } else { - fieldValue = PropertyField(fieldPrettyName, fieldValue, fieldType); - } - - if (EditorGUI.EndChangeCheck()) { - fieldInfo.SetValue(target, fieldValue); - EditorUtility.SetDirty(target); - } - return fieldValue; - } - - public static object PropertyField(string label, object value, System.Type type) { - if (type == typeof(int)) return IntField(label, value is int ? (int)value : default(int)); - else if (type == typeof(float)) return FloatField(label, value is float ? (float)value : default(float)); - else if (type == typeof(double)) return DoubleField(label, value is double ? (double)value : default(double)); - else if (type == typeof(long)) return LongField(label, value is long ? (long)value : default(long)); - else if (type == typeof(bool)) return Toggle(label, value is bool ? (bool)value : default(bool)); - else if (type == typeof(string)) return TextField(label, value is string ? (string)value : default(string)); - else if (type == typeof(Rect)) return RectField(label, value is Rect ? (Rect)value : default(Rect)); - else if (type == typeof(Vector2)) return Vector2Field(label, value is Vector2 ? (Vector2)value : default(Vector2)); - else if (type == typeof(Vector3)) return Vector3Field(label, value is Vector3 ? (Vector3)value : default(Vector3)); - else if (type == typeof(Vector4)) return Vector4Field(label, value is Vector4 ? (Vector4)value : default(Vector4)); - else if (type == typeof(Color)) return ColorField(label, value is Color ? (Color)value : default(Color)); - else if (type == typeof(AnimationCurve)) return CurveField(label, value is AnimationCurve ? (AnimationCurve)value : default(AnimationCurve)); - else if (type.IsSubclassOf(typeof(Enum)) || type == typeof(Enum)) return EnumField(label, (Enum)value); - else if (type.IsSubclassOf(typeof(UnityEngine.Object)) || type == typeof(UnityEngine.Object)) return ObjectField(label, (UnityEngine.Object)value, type); - else { GUILayout.Label(label); return value; } - } - public static Rect GetRect(string label) { - Rect rect = EditorGUILayout.GetControlRect(); - rect.width *= 0.5f; - EditorGUI.LabelField(rect, label); - rect.x += rect.width; - return rect; - } - public static UnityEngine.Object ObjectField(string label, UnityEngine.Object value, Type type) { - return EditorGUI.ObjectField(GetRect(label), value, type, true); - } - public static AnimationCurve CurveField(string label, AnimationCurve value) { - if (value == null) value = new AnimationCurve(); - return EditorGUI.CurveField(GetRect(label), value); - } - public static Color ColorField(string label, Color value) { - return EditorGUI.ColorField(GetRect(label), value); - } - public static Vector4 Vector4Field(string label, Vector4 value) { - return EditorGUILayout.Vector4Field(label, value); - } - public static Vector3 Vector3Field(string label, Vector3 value) { - return EditorGUILayout.Vector3Field(label, value); - } - public static Vector2 Vector2Field(string label, Vector2 value) { - return EditorGUILayout.Vector2Field(label, value); - } - public static Rect RectField(string label, Rect value) { - return EditorGUILayout.RectField(label, value); - } - public static string TextField(string label, string value) { - return EditorGUI.TextField(GetRect(label), value); - } - public static bool Toggle(string label, bool value) { - return EditorGUI.Toggle(GetRect(label), value); - } - public static int IntField(string label, int value) { - GUIUtility.GetControlID(FocusType.Passive); - Rect rect = EditorGUILayout.GetControlRect(); - rect.width *= 0.5f; - if (NodeEditorWindow.current != null) { - double v = (double) value; - DragNumber(rect, ref v); - value = (int) v; - if (GUI.changed) NodeEditorWindow.current.Repaint(); - } - EditorGUI.LabelField(rect, label); - rect.x += rect.width; - if (!GUI.changed) value = EditorGUI.IntField(rect, value); - else { - EditorGUI.IntField(rect, value); - } - return value; - } - public static float FloatField(string label, float value) { - GUIUtility.GetControlID(FocusType.Passive); - Rect rect = EditorGUILayout.GetControlRect(); - rect.width *= 0.5f; - if (NodeEditorWindow.current != null) { - double v = (double) value; - DragNumber(rect, ref v); - value = (float) v; - if (GUI.changed) NodeEditorWindow.current.Repaint(); - } - EditorGUI.LabelField(rect, label); - rect.x += rect.width; - if (!GUI.changed) value = EditorGUI.FloatField(rect, value); - else { - EditorGUI.FloatField(rect, value); - } - return value; - } - public static double DoubleField(string label, double value) { - GUIUtility.GetControlID(FocusType.Passive); - Rect rect = EditorGUILayout.GetControlRect(); - rect.width *= 0.5f; - if (NodeEditorWindow.current != null) { - double v = (double) value; - DragNumber(rect, ref v); - value = (double) v; - if (GUI.changed) NodeEditorWindow.current.Repaint(); - } - EditorGUI.LabelField(rect, label); - rect.x += rect.width; - if (!GUI.changed) value = EditorGUI.DoubleField(rect, value); - else { - EditorGUI.DoubleField(rect, value); - } - return value; - } - public static long LongField(string label, long value) { - GUIUtility.GetControlID(FocusType.Passive); - Rect rect = EditorGUILayout.GetControlRect(); - rect.width *= 0.5f; - if (NodeEditorWindow.current != null) { - double v = (double) value; - DragNumber(rect, ref v); - value = (long) v; - if (GUI.changed) NodeEditorWindow.current.Repaint(); - } - EditorGUI.LabelField(rect, label); - rect.x += rect.width; - if (!GUI.changed) value = EditorGUI.LongField(rect, value); - else { - EditorGUI.LongField(rect, value); - } - return value; - } - public static void DragNumber(Rect rect, ref double value) { - double sensitivity = Math.Max(0.09432981473891d, Math.Pow(Math.Abs(value), 0.5d) * 0.03d); - - int id = GUIUtility.GetControlID(FocusType.Passive); - Event e = Event.current; - switch (e.type) { - case EventType.MouseDown: - if (rect.Contains(e.mousePosition) && e.button == 0) { - tempValue = value; - GUIUtility.hotControl = id; - e.Use(); - GUIUtility.keyboardControl = id; - EditorGUIUtility.SetWantsMouseJumping(1); - } - break; - case EventType.MouseUp: - tempValue = 0; - if (GUIUtility.hotControl == id) { - GUIUtility.hotControl = 0; - e.Use(); - EditorGUIUtility.SetWantsMouseJumping(0); - } - break; - case EventType.MouseDrag: - if (GUIUtility.hotControl == id) { - tempValue += HandleUtility.niceMouseDelta * sensitivity; - value = tempValue; - GUI.changed = true; - } - break; - case EventType.Repaint: - if (NodeEditorWindow.current != null && Mathf.Approximately(NodeEditorWindow.current.zoom, 1)) { - EditorGUIUtility.AddCursorRect(rect, MouseCursor.SlideArrow); - } - break; - } - } - public static Enum EnumField(string label, Enum value) { - return EditorGUI.EnumPopup(GetRect(label), value); } } \ No newline at end of file