From 984fe730c0c5e0b66f07617261a1fefae53e0843 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Fri, 8 Nov 2019 09:57:54 +0100 Subject: [PATCH 1/2] Added support for [Header] attribute --- Scripts/Editor/NodeEditorGUILayout.cs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index ec93cc1..b6c18e9 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -45,6 +45,11 @@ namespace XNodeEditor { SpaceAttribute spaceAttribute; if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out spaceAttribute)) spacePadding = spaceAttribute.height; + //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs + float headerPadding = 0; + HeaderAttribute headerAttribute; + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out headerAttribute)) headerPadding = EditorGUIUtility.singleLineHeight * 1.5f; + // If property is an input, display a regular property field and put a port handle on the left side if (port.direction == XNode.NodePort.IO.Input) { // Get data from [Input] attribute @@ -64,6 +69,14 @@ namespace XNodeEditor { GUILayout.Space(spacePadding); spacePadding = 0; } + if (headerPadding > 0 && useLayoutSpace) + { + Rect position = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight * 1.5f); + position.yMin += (EditorGUIUtility.singleLineHeight * 0.5f) + EditorGUIUtility.standardVerticalSpacing; + position = EditorGUI.IndentedRect(position); + GUI.Label(position, headerAttribute.header, EditorStyles.boldLabel); + headerPadding = 0; + } if (dynamicPortList) { Type type = GetType(property); @@ -89,7 +102,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position - new Vector2(16, -spacePadding); + rect.position = rect.position - new Vector2(16, -(spacePadding + headerPadding)); // If property is an output, display a text label and put a port handle on the right side } else if (port.direction == XNode.NodePort.IO.Output) { // Get data from [Output] attribute @@ -109,6 +122,14 @@ namespace XNodeEditor { GUILayout.Space(spacePadding); spacePadding = 0; } + if (headerPadding > 0 && useLayoutSpace) + { + Rect position = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight * 1.5f); + position.yMin += (EditorGUIUtility.singleLineHeight * 0.5f) + EditorGUIUtility.standardVerticalSpacing; + position = EditorGUI.IndentedRect(position); + GUI.Label(position, headerAttribute.header, EditorStyles.boldLabel); + headerPadding = 0; + } if (dynamicPortList) { Type type = GetType(property); @@ -134,7 +155,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position + new Vector2(rect.width, spacePadding); + rect.position = rect.position + new Vector2(rect.width, spacePadding + headerPadding); } rect.size = new Vector2(16, 16); From 72ff273b5c3342f3f460f932849b6afb6c9d38dc Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 12 Nov 2019 22:37:29 +0100 Subject: [PATCH 2/2] Rewrite that caches the ordered property attributes so the gui layout drawer can draw them in the correct order. Rewrote how the property attributes are drawn, more compact less messy code. --- Scripts/Editor/NodeEditorGUILayout.cs | 71 +++++++++++++-------------- Scripts/Editor/NodeEditorUtilities.cs | 21 ++++++++ 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index b6c18e9..f9333db 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -41,14 +41,7 @@ namespace XNodeEditor { else { Rect rect = new Rect(); - float spacePadding = 0; - SpaceAttribute spaceAttribute; - if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out spaceAttribute)) spacePadding = spaceAttribute.height; - - //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs - float headerPadding = 0; - HeaderAttribute headerAttribute; - if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out headerAttribute)) headerPadding = EditorGUIUtility.singleLineHeight * 1.5f; + List propertyAttributes = NodeEditorUtilities.GetCachedPropertyAttribs(port.node.GetType(), property.name); // If property is an input, display a regular property field and put a port handle on the left side if (port.direction == XNode.NodePort.IO.Input) { @@ -61,21 +54,24 @@ namespace XNodeEditor { showBacking = inputAttribute.backingValue; } - //Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField - bool useLayoutSpace = dynamicPortList || + bool usePropertyAttributes = dynamicPortList || showBacking == XNode.Node.ShowBackingValue.Never || (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); - if (spacePadding > 0 && useLayoutSpace) { - GUILayout.Space(spacePadding); - spacePadding = 0; - } - if (headerPadding > 0 && useLayoutSpace) - { - Rect position = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight * 1.5f); - position.yMin += (EditorGUIUtility.singleLineHeight * 0.5f) + EditorGUIUtility.standardVerticalSpacing; - position = EditorGUI.IndentedRect(position); - GUI.Label(position, headerAttribute.header, EditorStyles.boldLabel); - headerPadding = 0; + + float spacePadding = 0; + foreach (var attr in propertyAttributes) { + if (attr is SpaceAttribute) { + if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); + else spacePadding += (attr as SpaceAttribute).height; + } else if (attr is HeaderAttribute) { + if (usePropertyAttributes) { + //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs + Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. + position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; + position = EditorGUI.IndentedRect(position); + GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); + } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } } if (dynamicPortList) { @@ -102,7 +98,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position - new Vector2(16, -(spacePadding + headerPadding)); + rect.position = rect.position - new Vector2(16, -spacePadding); // If property is an output, display a text label and put a port handle on the right side } else if (port.direction == XNode.NodePort.IO.Output) { // Get data from [Output] attribute @@ -114,21 +110,24 @@ namespace XNodeEditor { showBacking = outputAttribute.backingValue; } - //Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField - bool useLayoutSpace = dynamicPortList || + bool usePropertyAttributes = dynamicPortList || showBacking == XNode.Node.ShowBackingValue.Never || (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); - if (spacePadding > 0 && useLayoutSpace) { - GUILayout.Space(spacePadding); - spacePadding = 0; - } - if (headerPadding > 0 && useLayoutSpace) - { - Rect position = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight * 1.5f); - position.yMin += (EditorGUIUtility.singleLineHeight * 0.5f) + EditorGUIUtility.standardVerticalSpacing; - position = EditorGUI.IndentedRect(position); - GUI.Label(position, headerAttribute.header, EditorStyles.boldLabel); - headerPadding = 0; + + float spacePadding = 0; + foreach (var attr in propertyAttributes) { + if (attr is SpaceAttribute) { + if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); + else spacePadding += (attr as SpaceAttribute).height; + } else if (attr is HeaderAttribute) { + if (usePropertyAttributes) { + //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs + Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. + position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; + position = EditorGUI.IndentedRect(position); + GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); + } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } } if (dynamicPortList) { @@ -155,7 +154,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position + new Vector2(rect.width, spacePadding + headerPadding); + rect.position = rect.position + new Vector2(rect.width, spacePadding); } rect.size = new Vector2(16, 16); diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index bf92ba8..9973145 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -18,6 +18,9 @@ namespace XNodeEditor { /// Saves Attribute from Type+Field for faster lookup. Resets on recompiles. private static Dictionary>> typeAttributes = new Dictionary>>(); + /// Saves ordered PropertyAttribute from Type+Field for faster lookup. Resets on recompiles. + private static Dictionary>> typeOrderedPropertyAttributes = new Dictionary>>(); + public static bool GetAttrib(Type classType, out T attribOut) where T : Attribute { object[] attribs = classType.GetCustomAttributes(typeof(T), false); return GetAttrib(attribs, out attribOut); @@ -84,6 +87,24 @@ namespace XNodeEditor { return true; } + public static List GetCachedPropertyAttribs(Type classType, string fieldName) { + Dictionary> typeFields; + if (!typeOrderedPropertyAttributes.TryGetValue(classType, out typeFields)) { + typeFields = new Dictionary>(); + typeOrderedPropertyAttributes.Add(classType, typeFields); + } + + List typeAttributes; + if (!typeFields.TryGetValue(fieldName, out typeAttributes)) { + FieldInfo field = classType.GetFieldInfo(fieldName); + object[] attribs = field.GetCustomAttributes(typeof(PropertyAttribute), true); + typeAttributes = attribs.Cast().Reverse().ToList(); //Unity draws them in reverse + typeFields.Add(fieldName, typeAttributes); + } + + return typeAttributes; + } + public static bool IsMac() { #if UNITY_2017_1_OR_NEWER return SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX;