From ab74f8fd36cfd09569e47214b85543ef9608ce4c Mon Sep 17 00:00:00 2001 From: Thor Kramer Brigsted Date: Tue, 31 Oct 2017 14:52:10 +0100 Subject: [PATCH] OnBodyGUI() remake Default OnBodyGUI now uses default PropertyFields as suppied by UnityEditor. This means fields are drawn on nodes the same way as they are drawn in the inspector. out portPosition has also been moved to a static field. It is set automatically using the NodeEditorGuiLayout methods. Alternatively, you can alter it manually during your node GUI drawing. --- Example/Nodes/Editor/DisplayValueEditor.cs | 4 +- Scripts/Editor/NodeEditor.cs | 31 ++- Scripts/Editor/NodeEditorGUI.cs | 18 +- Scripts/Editor/NodeEditorGUILayout.cs | 257 ++++----------------- 4 files changed, 68 insertions(+), 242 deletions(-) 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