using System; using System.Linq; using System.Reflection; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditor.IMGUI.Controls; using Object = UnityEngine.Object; using UnityEditorInternal; namespace AlicizaX.Editor { public class MultiToolbarItem { public GUIContent content; public SerializedProperty property; public MultiToolbarItem(GUIContent content, SerializedProperty toggleProperty) { this.content = content; property = toggleProperty; } } public static class EditorDrawing { public static class Styles { public static Color? labelColor = null; public static GUIStyle borderBoxHeaderStyle { get { GUIStyle style = new(); style.margin = new RectOffset(3, 3, 2, 2); style.padding = new RectOffset(5, 5, 2, 5); return style; } } public static GUIStyle borderBoxStyle { get { GUIStyle style = new(); style.margin = new RectOffset(3, 3, 2, 2); style.padding = new RectOffset(5, 5, 5, 5); return style; } } public static GUIStyle miniBoldLabelMiddleLeft { get { GUIStyle style = new(EditorStyles.miniBoldLabel); style.alignment = TextAnchor.MiddleLeft; if (labelColor.HasValue) style.normal.textColor = labelColor.Value; return style; } } public static GUIStyle boldLabelMiddleLeft { get { GUIStyle style = new(EditorStyles.boldLabel); style.alignment = TextAnchor.MiddleLeft; if (labelColor.HasValue) style.normal.textColor = labelColor.Value; return style; } } public static GUIStyle miniBoldLabelCenter { get { GUIStyle style = new(EditorStyles.miniBoldLabel); style.alignment = TextAnchor.MiddleLeft; if (labelColor.HasValue) style.normal.textColor = labelColor.Value; return style; } } public static GUIStyle miniBoldLabelFoldout { get { GUIStyle style = new(EditorStyles.foldout); style.font = EditorStyles.miniBoldLabel.font; style.fontStyle = EditorStyles.miniBoldLabel.fontStyle; style.fontSize = EditorStyles.miniBoldLabel.fontSize; return style; } } public static GUIStyle miniBoldButton { get { GUIStyle style = new(GUI.skin.button); style.font = EditorStyles.miniBoldLabel.font; style.fontStyle = EditorStyles.miniBoldLabel.fontStyle; style.fontSize = EditorStyles.miniBoldLabel.fontSize; return style; } } public static GUIStyle RichLabel { get => new(EditorStyles.label) { richText = true }; } public static GUIStyle RichHelpBox { get => new(EditorStyles.helpBox) { richText = true }; } public static Texture2D TransparentCheckerTexture { get { if (EditorGUIUtility.isProSkin) { return EditorGUIUtility.LoadRequired("Previews/Textures/textureCheckerDark.png") as Texture2D; } return EditorGUIUtility.LoadRequired("Previews/Textures/textureChecker.png") as Texture2D; } } } public static GUIStyle CenterStyle(GUIStyle style) { return new GUIStyle(style) { alignment = TextAnchor.MiddleCenter }; } public static GUIStyle CenterStyleLeft(GUIStyle style) { return new GUIStyle(style) { alignment = TextAnchor.MiddleLeft }; } public class BorderBoxScope : GUI.Scope { public BorderBoxScope(bool roundedBox = true) { BeginBorderLayout(roundedBox); } public BorderBoxScope(GUIContent title, float headerHeight = 18f, bool roundedBox = true) { BeginHeaderBorderLayout(title, headerHeight, roundedBox); } protected override void CloseScope() { EndBorderHeaderLayout(); } } public class ToggleBorderBoxScope : GUI.Scope { private readonly bool disableContent; public ToggleBorderBoxScope(GUIContent title, SerializedProperty toggle, float headerHeight = 18f, bool roundedBox = true, bool disableContent = true) { this.disableContent = disableContent; toggle.boolValue = BeginToggleBorderLayout(title, toggle.boolValue, headerHeight, roundedBox); if (disableContent) GUI.enabled = toggle.boolValue; } protected override void CloseScope() { if (disableContent) GUI.enabled = true; EndBorderHeaderLayout(); } } public class IconSizeScope : GUI.Scope { private Vector2 prevIconSize; public IconSizeScope(Vector2 iconSize) { prevIconSize = EditorGUIUtility.GetIconSize(); EditorGUIUtility.SetIconSize(iconSize); } public IconSizeScope(float iconSize) { prevIconSize = EditorGUIUtility.GetIconSize(); EditorGUIUtility.SetIconSize(new Vector2(iconSize, iconSize)); } public IconSizeScope(float x, float y) { prevIconSize = EditorGUIUtility.GetIconSize(); EditorGUIUtility.SetIconSize(new Vector2(x, y)); } protected override void CloseScope() { EditorGUIUtility.SetIconSize(prevIconSize); } } public class BackgroundColorScope : GUI.Scope { private Color prevColor; public BackgroundColorScope(Color backgroundColor) { prevColor = GUI.backgroundColor; GUI.backgroundColor = backgroundColor; } public BackgroundColorScope(string htmlColor) { prevColor = GUI.backgroundColor; if (ColorUtility.TryParseHtmlString(htmlColor, out Color bgColor)) GUI.backgroundColor = bgColor; } protected override void CloseScope() { GUI.backgroundColor = prevColor; } } /// /// Set custom icon size. /// public static Vector2 SetIconSize(float iconSize) { Vector2 prevIconSize = EditorGUIUtility.GetIconSize(); EditorGUIUtility.SetIconSize(new Vector2(iconSize, iconSize)); return prevIconSize; } /// /// Reset custom icon size. /// public static void ResetIconSize() { EditorGUIUtility.SetIconSize(Vector2.zero); } /// /// Set the header label text color. /// public static void SetLabelColor(Color color) { Styles.labelColor = color; } /// /// Set the header label text color. /// public static void SetLabelColor(string htmlColor) { if (ColorUtility.TryParseHtmlString(htmlColor, out Color color)) Styles.labelColor = color; } /// /// Reset the header label text color. /// public static void ResetLabelColor() { Styles.labelColor = null; } /// /// Get GUIContent with specified icon. /// public static GUIContent IconContent(string iconName, float iconSize = 16f) { SetIconSize(iconSize); return EditorGUIUtility.TrIconContent(iconName); } /// /// Get GUIContent with specified icon and text. /// public static GUIContent IconTextContent(string text, string iconName, float iconSize = 16f) { SetIconSize(iconSize); return EditorGUIUtility.TrTextContentWithIcon(" " + text, iconName); } /// /// Get GUIContent with specified icon and text. /// public static GUIContent IconTextContent(string text, Texture2D texture, float iconSize = 16f) { SetIconSize(iconSize); return EditorGUIUtility.TrTextContentWithIcon(" " + text, texture); } /// /// Classic HelpBox but with Rich Text. /// public static void RichHelpBox(string message, MessageType type) { GUIContent content = new(message); content.image = GetHelpIcon(type); EditorGUILayout.LabelField(GUIContent.none, content, Styles.RichHelpBox); } private static Texture2D GetHelpIcon(MessageType type) { Type editorGUIUtilityType = typeof(EditorGUIUtility); MethodInfo method = editorGUIUtilityType.GetMethod("GetHelpIcon", BindingFlags.NonPublic | BindingFlags.Static ); if (method != null) { return method.Invoke(null, new object[] { type }) as Texture2D; } Debug.LogError("GetHelpIcon method not found!"); return null; } /// /// Draw custom border box. /// public static void DrawBorderBox(Rect rect, RectOffset border, Color color) { if (Event.current.type != EventType.Repaint) return; Color orgColor = GUI.color; GUI.color *= color; GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, border.top), EditorGUIUtility.whiteTexture); //top GUI.DrawTexture(new Rect(rect.x, rect.yMax - border.bottom, rect.width, border.bottom), EditorGUIUtility.whiteTexture); //bottom GUI.DrawTexture(new Rect(rect.x, rect.y + border.left, border.left, rect.height - 2 * border.left), EditorGUIUtility.whiteTexture); //left GUI.DrawTexture(new Rect(rect.xMax - border.right, rect.y + border.right, border.right, rect.height - 2 * border.right), EditorGUIUtility.whiteTexture); //right GUI.color = orgColor; } /// /// Draw the heading at the top of the inspector. /// public static void DrawInspectorHeader(GUIContent title, Object script = null) { GUIStyle headerStyle = new(EditorStyles.boldLabel) { fontSize = 13, alignment = TextAnchor.MiddleCenter }; headerStyle.normal.textColor = Color.white; title.text = title.text.ToUpper(); using (new IconSizeScope(13)) { Rect rect = GUILayoutUtility.GetRect(1, 30); ColorUtility.TryParseHtmlString("#181818", out Color color); EditorGUI.DrawRect(rect, color); //EditorGUI.DrawRect(new Rect(rect.x, rect.y, rect.width, 1), Color.white.Alpha(0.4f)); //EditorGUI.DrawRect(new Rect(rect.x, rect.yMax - 1, rect.width, 1), Color.white.Alpha(0.4f)); //DrawCorners(rect, Vector2.one * 5, 1, Color.white.Alpha(0.75f)); Texture2D mask = AssetDatabase.LoadAssetAtPath("Packages/com.alicizax.unity.framework/Editor/Localization/EditorIcons/mask.png"); GUI.DrawTexture(new Rect(rect.x, rect.yMax - 1, rect.width, 1), mask); if (script != null) { MonoScript monoScript = null; if (script is MonoBehaviour monoBehaviour) monoScript = MonoScript.FromMonoBehaviour(monoBehaviour); else if (script is ScriptableObject scriptableObject) monoScript = MonoScript.FromScriptableObject(scriptableObject); Event e = Event.current; Rect pingRect = rect; Rect docsIconRect = rect; Rect saveableIconRect = rect; docsIconRect.xMin = docsIconRect.xMax - EditorGUIUtility.singleLineHeight - 3f; docsIconRect.y += 7f; docsIconRect.width = EditorGUIUtility.singleLineHeight; docsIconRect.height = EditorGUIUtility.singleLineHeight; saveableIconRect.xMin = saveableIconRect.xMax - (EditorGUIUtility.singleLineHeight * 2) - 3f; saveableIconRect.y += 6f; saveableIconRect.width = EditorGUIUtility.singleLineHeight; saveableIconRect.height = EditorGUIUtility.singleLineHeight; if (pingRect.Contains(e.mousePosition)) { if (e.type == EventType.MouseDown && e.button == 0) { EditorGUIUtility.PingObject(monoScript); } } saveableIconRect = docsIconRect; saveableIconRect.y -= 1f; // draw saveable icon // bool flag1 = typeof(UHFPS.Runtime.ISaveable).IsAssignableFrom(script.GetType()); // bool flag2 = typeof(UHFPS.Runtime.IRuntimeSaveable).IsAssignableFrom(script.GetType()); // bool flag3 = typeof(UHFPS.Runtime.ISaveableCustom).IsAssignableFrom(script.GetType()); // // if (flag1 || flag2 || flag3) // { // GUIContent saveableIcon = EditorGUIUtility.TrIconContent("CacheServerConnected", "Script is Saveable"); // EditorGUI.LabelField(saveableIconRect, saveableIcon); // } } EditorGUI.LabelField(rect, title, headerStyle); } } private static void DrawCorners(Rect rect, Vector2 cornerSize, Color cornerColor) { EditorGUI.DrawRect(new Rect(rect.x, rect.y, cornerSize.x, cornerSize.y), cornerColor); // Draw top-right corner EditorGUI.DrawRect(new Rect(rect.xMax - cornerSize.x, rect.y, cornerSize.x, cornerSize.y), cornerColor); // Draw bottom-left corner EditorGUI.DrawRect(new Rect(rect.x, rect.yMax - cornerSize.y, cornerSize.x, cornerSize.y), cornerColor); // Draw bottom-right corner EditorGUI.DrawRect(new Rect(rect.xMax - cornerSize.x, rect.yMax - cornerSize.y, cornerSize.x, cornerSize.y), cornerColor); } private static void DrawCorners(Rect rect, Vector2 cornerSize, float thickness, Color cornerColor) { // Draw top-left corner EditorGUI.DrawRect(new Rect(rect.x, rect.y, cornerSize.x, thickness), cornerColor); // Horizontal part of L EditorGUI.DrawRect(new Rect(rect.x, rect.y, thickness, cornerSize.y), cornerColor); // Vertical part of L // Draw top-right corner EditorGUI.DrawRect(new Rect(rect.xMax - cornerSize.x, rect.y, cornerSize.x, thickness), cornerColor); // Horizontal part of L EditorGUI.DrawRect(new Rect(rect.xMax - thickness, rect.y, thickness, cornerSize.y), cornerColor); // Vertical part of L // Draw bottom-left corner EditorGUI.DrawRect(new Rect(rect.x, rect.yMax - thickness, cornerSize.x, thickness), cornerColor); // Horizontal part of L EditorGUI.DrawRect(new Rect(rect.x, rect.yMax - cornerSize.y, thickness, cornerSize.y), cornerColor); // Vertical part of L // Draw bottom-right corner EditorGUI.DrawRect(new Rect(rect.xMax - cornerSize.x, rect.yMax - thickness, cornerSize.x, thickness), cornerColor); // Horizontal part of L EditorGUI.DrawRect(new Rect(rect.xMax - thickness, rect.yMax - cornerSize.y, thickness, cornerSize.y), cornerColor); // Vertical part of L } /// /// Draw classic prefix label. /// public static void DrawPrefixLabel(string title, string text, GUIStyle style) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel(title); EditorGUILayout.LabelField(text, style); EditorGUILayout.EndHorizontal(); } /// /// Draw min-max slider from MinMax property. /// public static void DrawMinMaxSlider(SerializedProperty minMaxProperty, float minLimit, float maxLimit) { SerializedProperty minProp = minMaxProperty.FindPropertyRelative("min"); SerializedProperty maxProp = minMaxProperty.FindPropertyRelative("max"); float minValue = minProp.floatValue; float maxValue = maxProp.floatValue; EditorGUILayout.MinMaxSlider(minMaxProperty.displayName, ref minValue, ref maxValue, minLimit, maxLimit); minProp.floatValue = minValue; maxProp.floatValue = maxValue; } /// /// Draw a property field with a label after the field. /// public static void DrawPropertyAndLabel(SerializedProperty property, GUIContent content) { DrawPropertyAndLabel(property, content, EditorStyles.boldLabel); } /// /// Draw a property field with a label after the field. /// public static void DrawPropertyAndLabel(SerializedProperty property, GUIContent content, GUIStyle labelStyle) { Rect rect = EditorGUILayout.GetControlRect(); float labelWidth = labelStyle.CalcSize(content).x; rect.xMax -= labelWidth + 2f; EditorGUI.PropertyField(rect, property); rect.xMin = rect.xMax + 2f; rect.xMax += labelWidth + 2f; EditorGUI.LabelField(rect, content, labelStyle); } /// /// Draw horizontal separator. /// public static void Separator(int height = 1) { Rect rect = EditorGUILayout.GetControlRect(false, height); rect.height = height; EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 1)); } /// /// Draw horizontal (spaced) separator. /// public static void SeparatorSpaced(float space, int height = 1) { EditorGUILayout.Space(space); Separator(height); EditorGUILayout.Space(space); } /// /// Draw a large sprite selection similar to the EditorGUI.ObjectField sprite selection. ///
But with the correct, not stretched image.
///
public static Object DrawLargeSpriteSelector(Rect rect, Object obj) { Rect borderRect = rect; borderRect.height -= 1; borderRect.width -= 1; GUI.Box(borderRect, GUIContent.none, new GUIStyle("HelpBox")); Rect iconRect = rect; iconRect.height -= 10; iconRect.width -= 10; iconRect.x += 5f; iconRect.y += 5f; DrawTransparentTextureWithChecker(iconRect, obj); Rect mageSelectRect = rect; mageSelectRect.yMax -= 15f; Event e = Event.current; if (mageSelectRect.Contains(e.mousePosition)) { if (e.type == EventType.MouseDown && e.button == 0 && obj != null) { EditorGUIUtility.PingObject(obj); e.Use(); } } Rect buttonRect = rect; buttonRect.height = 15f; buttonRect.xMin = rect.width - rect.width * 0.7f; buttonRect.y = rect.height - 16f; buttonRect.x -= 1f; if (GUI.Button(buttonRect, "Select", EditorStyles.objectFieldThumb)) { EditorGUIUtility.ShowObjectPicker(obj, false, "", GUIUtility.GetControlID(FocusType.Passive)); } else if (Event.current.commandName == "ObjectSelectorUpdated") { return EditorGUIUtility.GetObjectPickerObject(); } return obj; } /// /// Draw classic object field with picker. /// public static bool ObjectField(Rect rect, GUIContent text, GUIContent tooltip = null) { using (new IconSizeScope(12f)) { GUI.Box(rect, text, EditorStyles.objectField); GUIStyle buttonStyle = new GUIStyle("ObjectFieldButton") { richText = true }; Rect buttonRect = buttonStyle.margin.Remove(new Rect(rect.xMax - 19, rect.y, 19, rect.height)); return GUI.Button(buttonRect, tooltip ?? new GUIContent(), buttonStyle); } } /// /// Draw a multi selection toolbar. /// public static void DrawMultiToolbar(Rect rect, MultiToolbarItem[] contents, float buttonWidth = 100) { GUIStyle style = new GUIStyle(GUI.skin.button); string name = style.name; GUIStyle midStyle = GUI.skin.FindStyle(name + "mid") ?? style; GUIStyle firstStyle = GUI.skin.FindStyle(name + "left") ?? midStyle; GUIStyle lastStyle = GUI.skin.FindStyle(name + "right") ?? midStyle; Rect toolbarRect = rect; toolbarRect.width = buttonWidth; for (int i = 0; i < contents.Length; i++) { if (i == 0) { // first contents[i].property.boolValue = GUI.Toggle(toolbarRect, contents[i].property.boolValue, contents[i].content, firstStyle); toolbarRect.x += buttonWidth; } else if (i == contents.Length - 1) { // last contents[i].property.boolValue = GUI.Toggle(toolbarRect, contents[i].property.boolValue, contents[i].content, lastStyle); toolbarRect.x += buttonWidth; } else { // mid contents[i].property.boolValue = GUI.Toggle(toolbarRect, contents[i].property.boolValue, contents[i].content, midStyle); toolbarRect.x += buttonWidth; } } } /// /// Draw multi selection popup menu. /// public static void DrawMultiSelectPopup(Rect rect, string[] content, string[] selected, Action onItemSelect, int maxDisplay = 3) { GenericMenu menu = new GenericMenu(); List selectedArr = new List(selected); for (int i = 0; i < content.Length; i++) { string name = content[i]; bool on = selected.Any(x => x.Equals(name)); menu.AddItem(new GUIContent(name), on, data => { if (selectedArr.Contains(name)) selectedArr.Remove(name); else selectedArr.Add(name); onItemSelect?.Invoke(selectedArr.ToArray()); }, name); } string popupTitle = "Nothing"; if (selected.Length > 0) { popupTitle = string.Join(", ", selected.Take(maxDisplay)); if (selected.Length > maxDisplay) popupTitle += " ..."; } if (GUI.Button(rect, popupTitle, EditorStyles.popup)) { menu.DropDown(rect); } } /// /// Draw multi selection popup menu. /// public static void DrawMultiSelectPopup(Rect rect, GUIContent title, string[] content, string[] selected, Action onItemSelect, int maxDisplay = 3) { GenericMenu menu = new GenericMenu(); GUIContent guiTitle = new GUIContent(title); List selectedArr = new List(selected); for (int i = 0; i < content.Length; i++) { string name = content[i]; bool on = selected.Any(x => x.Equals(name)); menu.AddItem(new GUIContent(name), on, data => { if (selectedArr.Contains(name)) selectedArr.Remove(name); else selectedArr.Add(name); onItemSelect?.Invoke(selectedArr.ToArray()); }, name); } if (selected.Length > 0) { guiTitle.text = string.Join(", ", selected.Take(maxDisplay)); if (selected.Length > maxDisplay) guiTitle.text += " ..."; } if (GUI.Button(rect, guiTitle, EditorStyles.popup)) { menu.DropDown(rect); } } /// /// Draw string selection popup menu. /// public static void DrawStringSelectPopup(Rect rect, string[] content, string selected, Action onItemSelect) { GenericMenu menu = new GenericMenu(); for (int i = 0; i < content.Length; i++) { string name = content[i]; bool on = name.Equals(selected); menu.AddItem(new GUIContent(name), on, data => { string selection = (string)data; string result = selected; if (selected != selection) result = selection; onItemSelect?.Invoke(result); }, name); } string popupTitle = string.IsNullOrEmpty(selected) ? "Nothing" : selected; if (GUI.Button(rect, popupTitle, EditorStyles.popup)) { menu.DropDown(rect); } } /// /// Draw string selection popup menu. /// public static void DrawStringSelectPopup(Rect rect, GUIContent title, string[] content, string selected, Action onItemSelect) { GenericMenu menu = new GenericMenu(); GUIContent guiTitle = new GUIContent(title); for (int i = 0; i < content.Length; i++) { string name = content[i]; bool on = name.Equals(selected); menu.AddItem(new GUIContent(name), on, data => { string selection = (string)data; string result = selected; if (selected != selection) result = selection; onItemSelect?.Invoke(result); }, name); } if (!string.IsNullOrEmpty(selected)) guiTitle.text = selected; if (GUI.Button(rect, guiTitle, EditorStyles.popup)) { menu.DropDown(rect); } } /// /// Draw string selection popup menu. /// public static void DrawStringSelectPopup(GUIContent label, GUIContent title, string[] content, string selected, Action onItemSelect) { Rect rect = EditorGUILayout.GetControlRect(); rect = EditorGUI.PrefixLabel(rect, label); DrawStringSelectPopup(rect, title, content, selected, onItemSelect); } /// /// Draw string selection popup menu. /// public static void DrawStringSelectPopup(GUIContent title, string[] content, string selected, Action onItemSelect) { Rect rect = EditorGUILayout.GetControlRect(); DrawStringSelectPopup(rect, title, content, selected, onItemSelect); } /// /// Draw custom class property list. /// public static void DrawList(SerializedProperty listProperty, GUIContent title, float headerHeight = 18f, bool roundedBox = true) { if (BeginFoldoutBorderLayout(listProperty, title, headerHeight, roundedBox)) { for (int i = 0; i < listProperty.arraySize; i++) { SerializedProperty listItem = listProperty.GetArrayElementAtIndex(i); var properties = GetAllProperties(listItem); string elementName = "Element " + i; var firstProperty = properties.First(); if (firstProperty.Value.propertyType == SerializedPropertyType.String) { string stringValue = firstProperty.Value.stringValue; if (!string.IsNullOrEmpty(stringValue)) elementName = stringValue; } if (BeginFoldoutBorderLayout(listItem, new GUIContent(elementName), out Rect itemHeaderRect)) { properties.DrawAll(true); EndBorderHeaderLayout(); } GUIContent minus = EditorGUIUtility.TrIconContent("Toolbar Minus"); Rect minusRect = itemHeaderRect; minusRect.xMin = minusRect.xMax - EditorGUIUtility.singleLineHeight; minusRect.x -= 3f; minusRect.y += 3f; if (GUI.Button(minusRect, minus, EditorStyles.iconButton)) { listProperty.DeleteArrayElementAtIndex(i); } } if (listProperty.arraySize > 0) EditorGUILayout.Space(2f); if (GUILayout.Button("Add")) { listProperty.arraySize++; } EndBorderHeaderLayout(); } } public static int BeginDrawCustomList(SerializedProperty listProperty, GUIContent title) { int arraySize = listProperty.arraySize; EditorGUILayout.BeginVertical(GUI.skin.box); Rect labelRect = EditorGUILayout.GetControlRect(); EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel); Rect countLabelRect = labelRect; countLabelRect.xMin = countLabelRect.xMax - 25f; GUI.enabled = false; EditorGUI.IntField(countLabelRect, arraySize); GUI.enabled = true; EditorGUILayout.Space(2f); return arraySize; } public static void EndDrawCustomList(GUIContent buttonTitle, bool buttonEnabled, Action onClick) { EditorGUILayout.Space(2f); Separator(); EditorGUILayout.Space(2f); EditorGUILayout.BeginHorizontal(); { GUILayout.FlexibleSpace(); float width = GUI.skin.button.CalcSize(buttonTitle).x + 10f; Rect moduleButtonRect = EditorGUILayout.GetControlRect(GUILayout.Width(width), GUILayout.Height(20f)); using (new EditorGUI.DisabledGroupScope(Application.isPlaying || !buttonEnabled)) { if (GUI.Button(moduleButtonRect, buttonTitle)) { onClick?.Invoke(); } } GUILayout.FlexibleSpace(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } /// /// Setup reorderable list for easy drawing. /// public static void SetupReorderableList(ReorderableList reorderableList) { SerializedProperty properties = reorderableList.serializedProperty; reorderableList.elementHeightCallback += (int index) => { SerializedProperty element = properties.GetArrayElementAtIndex(index); int fieldCount = element.CountProperty(); if (element.isExpanded) return (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * (fieldCount + 1); return EditorGUIUtility.singleLineHeight; }; reorderableList.drawElementCallback += (Rect rect, int index, bool isActive, bool isFocused) => { SerializedProperty element = properties.GetArrayElementAtIndex(index); rect.y += 1f; rect.height = EditorGUIUtility.singleLineHeight; Rect propertyRect = new(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight); propertyRect.xMin += 12f; EditorGUI.PropertyField(propertyRect, element); if (element.isExpanded) { EditorGUI.indentLevel++; SerializedProperty childProperty = element.Copy(); SerializedProperty endProperty = element.GetEndProperty(); childProperty.NextVisible(true); while (!SerializedProperty.EqualContents(childProperty, endProperty)) { rect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), childProperty); childProperty.NextVisible(false); } EditorGUI.indentLevel--; } }; } /// /// Get count of properties. /// public static int CountProperty(this SerializedProperty property) { SerializedProperty copy = property.Copy(); SerializedProperty endProperty = property.GetEndProperty(); int count = 0; copy.NextVisible(true); while (!SerializedProperty.EqualContents(copy, endProperty)) { count++; copy.NextVisible(false); } return count; } /// /// Draw header by specified rect. /// public static void DrawHeader(Rect headerRect, GUIContent title, GUIStyle labelStyle = null) { Color headerColor = new Color(0.1f, 0.1f, 0.1f, 0.4f); EditorGUI.DrawRect(headerRect, headerColor); Rect labelRect = new Rect(headerRect.x + 4f, headerRect.y - 1f, headerRect.width - 4f, headerRect.height); EditorGUI.LabelField(labelRect, title, labelStyle ?? Styles.miniBoldLabelCenter); } /// /// Draw toggle header by specified rect. /// public static bool DrawToggleHeader(Rect headerRect, GUIContent title, bool toggle) { Color headerColor = new Color(0.1f, 0.1f, 0.1f, 0.4f); EditorGUI.DrawRect(headerRect, headerColor); Rect toggleRect = new Rect(headerRect.x + 4f, headerRect.y, EditorGUIUtility.singleLineHeight, headerRect.height); toggle = GUI.Toggle(toggleRect, toggle, new GUIContent("", "Enabled"), EditorStyles.toggle); Rect labelRect = new Rect(toggleRect.xMax, headerRect.y - 1f, headerRect.width - toggleRect.xMax, headerRect.height); EditorGUI.LabelField(labelRect, title, Styles.miniBoldLabelCenter); return toggle; } /// /// Draw foldout header by specified rect. /// public static bool DrawFoldoutHeader(Rect headerRect, GUIContent title, bool expanded) { // Constants Color headerColor = new Color(0.1f, 0.1f, 0.1f, 0.4f); float singleLineHeight = EditorGUIUtility.singleLineHeight; // Draw header background EditorGUI.DrawRect(headerRect, headerColor); // Define and draw foldout toggle Rect foldoutRect = new Rect(headerRect.x + 4f, headerRect.y, singleLineHeight, headerRect.height); GUI.Toggle(foldoutRect, expanded, GUIContent.none, EditorStyles.foldout); // Define and draw title label Rect labelRect = new Rect(foldoutRect.xMax, headerRect.y - 1f, headerRect.width - foldoutRect.xMax + 4f, headerRect.height); EditorGUI.LabelField(labelRect, title, Styles.miniBoldLabelCenter); // Handle mouse events for foldout interaction headerRect.xMax -= singleLineHeight + EditorGUIUtility.standardVerticalSpacing; Event e = Event.current; if (headerRect.Contains(e.mousePosition) && e.type == EventType.MouseDown && e.button == 0) { expanded = !expanded; e.Use(); } return expanded; } /// /// Draw foldout header by specified rect. /// public static bool DrawFoldoutHeader(Rect headerRect, GUIContent title, float minusWidth, bool expanded) { // Constants Color headerColor = new Color(0.1f, 0.1f, 0.1f, 0.4f); float singleLineHeight = EditorGUIUtility.singleLineHeight; // Draw header background EditorGUI.DrawRect(headerRect, headerColor); // Draw foldout Rect foldoutRect = new Rect(headerRect.x + 4f, headerRect.y, singleLineHeight, headerRect.height); GUI.Toggle(foldoutRect, expanded, GUIContent.none, EditorStyles.foldout); // Draw title Rect labelRect = new Rect(foldoutRect.xMax, headerRect.y - 1f, headerRect.width - foldoutRect.xMax, headerRect.height); EditorGUI.LabelField(labelRect, title, Styles.miniBoldLabelCenter); // Handle mouse events headerRect.xMax -= singleLineHeight + EditorGUIUtility.standardVerticalSpacing + minusWidth; Event e = Event.current; if (headerRect.Contains(e.mousePosition) && e.type == EventType.MouseDown && e.button == 0) { expanded = !expanded; e.Use(); } return expanded; } /// /// Draw foldout toggle header by specified rect. /// public static void DrawFoldoutToggleHeader(Rect headerRect, GUIContent title, ref bool expanded, ref bool toggle, bool canExpand = true) { // Constants float singleLineHeight = EditorGUIUtility.singleLineHeight; Color headerColor = new Color(0.1f, 0.1f, 0.1f, 0.4f); // Draw header background EditorGUI.DrawRect(headerRect, headerColor); // Set up initial positions Rect foldoutRect = headerRect; foldoutRect.width = singleLineHeight; foldoutRect.x += 4f; // If expandable, draw foldout and adjust for toggle position if (canExpand) { GUI.Toggle(foldoutRect, expanded, GUIContent.none, EditorStyles.foldout); foldoutRect.x += singleLineHeight; } // Draw toggle Rect toggleRect = new Rect(foldoutRect.x, headerRect.y, singleLineHeight, headerRect.height); toggle = GUI.Toggle(toggleRect, toggle, new GUIContent("", "Enabled"), EditorStyles.toggle); // Draw title Rect labelRect = new Rect(toggleRect.xMax, headerRect.y - 1f, headerRect.width - toggleRect.xMax, headerRect.height); EditorGUI.LabelField(labelRect, title, Styles.miniBoldLabelCenter); // Handle events headerRect.xMax -= singleLineHeight + 2f; Event e = Event.current; if (canExpand && headerRect.Contains(e.mousePosition) && e.type == EventType.MouseDown && e.button == 0) { expanded = !expanded; e.Use(); } } /// /// Draw header with border. /// public static Rect DrawHeaderWithBorder(ref Rect rect, GUIContent title, float headerHeight = 18f, bool roundedBox = true) { GUI.Box(rect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); rect.x += 1; rect.y += 1; rect.height -= 1; rect.width -= 2; Rect headerRect = rect; headerRect.height = headerHeight + EditorGUIUtility.standardVerticalSpacing; rect.y += headerRect.height; rect.height -= headerRect.height; Rect titleRect = headerRect; titleRect.x += 2f; using (new IconSizeScope(14)) { EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f)); EditorGUI.LabelField(titleRect, title, EditorStyles.miniBoldLabel); } return headerRect; } /// /// Draw header with border. /// public static Rect DrawHeaderWithBorder(GUIContent title, float headerHeight = 18f, bool roundedBox = true) { // Calculate the rectangle for the header based on the control rect Rect rect = EditorGUILayout.GetControlRect(false, headerHeight); // Adjust the rect for the border drawing Rect borderRect = rect; borderRect.height = headerHeight + EditorGUIUtility.standardVerticalSpacing; // Draw the border box GUI.Box(borderRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); // Adjust the rect for the title inside the header Rect titleRect = rect; titleRect.x += 4f; titleRect.y += 2f; titleRect.height = headerHeight; // Draw the title background EditorGUI.DrawRect(borderRect, new Color(0.1f, 0.1f, 0.1f, 0.4f)); // Draw the title text using (new IconSizeScope(14)) { EditorGUI.LabelField(titleRect, title, EditorStyles.miniBoldLabel); } // Return the rect for further custom drawing if needed return rect; } /// /// Begin a bordered vertical group; /// public static void BeginBorderLayout(bool roundedBox = true) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxStyle); GUI.Box(drawingRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); } /// /// Begin a bordered vertical header group; /// public static Rect BeginHeaderBorderLayout(GUIContent title, float headerHeight = 18f, bool roundedBox = true) { Rect headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); drawingRect.yMin -= headerHeight + 6f; GUI.Box(drawingRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); DrawHeader(headerRect, title); return headerRect; } /// /// Begin a bordered vertical foldout group. /// public static bool BeginFoldoutBorderLayout(GUIContent title, ref bool expanded, float headerHeight = 18f, bool roundedBox = true) { Rect headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool foldoutResult = expanded; if (expanded) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); expanded = DrawFoldoutHeader(headerRect, title, expanded); return foldoutResult; } /// /// Begin a bordered vertical foldout group. /// public static bool BeginFoldoutBorderLayout(GUIContent title, bool expanded, float headerHeight = 18f, bool roundedBox = true) { Rect headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; if (expanded) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); return DrawFoldoutHeader(headerRect, title, expanded); } /// /// Begin a bordered vertical foldout group. /// public static bool BeginFoldoutBorderLayout(GUIContent title, ref bool expanded, out Rect headerRect, float headerHeight = 18f, bool roundedBox = true) { headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool foldoutResult = expanded; if (expanded) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); expanded = DrawFoldoutHeader(headerRect, title, expanded); return foldoutResult; } /// /// Begin a bordered vertical foldout group. /// public static bool BeginFoldoutBorderLayout(SerializedProperty foldoutProperty, GUIContent title, float headerHeight = 18f, bool roundedBox = true) { Rect headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool foldoutResult = foldoutProperty.isExpanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); foldoutProperty.isExpanded = DrawFoldoutHeader(headerRect, title, foldoutProperty.isExpanded); return foldoutResult; } /// /// Begin a bordered vertical foldout group. /// public static bool BeginFoldoutBorderLayout(SerializedProperty foldoutProperty, GUIContent title, out Rect headerRect, float headerHeight = 18f, bool roundedBox = true) { headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool foldoutResult = foldoutProperty.isExpanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); foldoutProperty.isExpanded = DrawFoldoutHeader(headerRect, title, foldoutProperty.isExpanded); return foldoutResult; } /// /// Begin a bordered vertical foldout group. /// public static bool BeginFoldoutBorderLayout(SerializedProperty foldoutProperty, GUIContent title, out Rect headerRect, float minusWidth, float headerHeight = 18f, bool roundedBox = true) { headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool foldoutResult = foldoutProperty.isExpanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); foldoutProperty.isExpanded = DrawFoldoutHeader(headerRect, title, minusWidth, foldoutProperty.isExpanded); return foldoutResult; } /// /// Begin a bordered vertical toggle group. /// public static bool BeginToggleBorderLayout(GUIContent title, bool toggle, float headerHeight = 18f, bool roundedBox = true) { Rect headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); drawingRect.yMin -= headerHeight + 6f; GUI.Box(drawingRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); return DrawToggleHeader(headerRect, title, toggle); } /// /// Begin a bordered vertical toggle group. /// public static bool BeginToggleBorderLayout(GUIContent title, bool toggle, out Rect headerRect, float headerHeight = 18f, bool roundedBox = true) { headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); drawingRect.yMin -= headerHeight + 6f; GUI.Box(drawingRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); return DrawToggleHeader(headerRect, title, toggle); } /// /// Begin a bordered vertical foldout toggle group. /// public static bool BeginFoldoutToggleBorderLayout(GUIContent title, ref bool expanded, ref bool toggle, float headerHeight = 18f, bool roundedBox = true, bool canExpand = true) { Rect headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool foldoutResult = canExpand && expanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); DrawFoldoutToggleHeader(headerRect, title, ref expanded, ref toggle, canExpand); return foldoutResult; } /// /// Begin a bordered vertical foldout toggle group. /// public static bool BeginFoldoutToggleBorderLayout(GUIContent title, ref bool expanded, ref bool toggle, out Rect headerRect, float headerHeight = 18f, bool roundedBox = true, bool canExpand = true) { headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool foldoutResult = canExpand && expanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); DrawFoldoutToggleHeader(headerRect, title, ref expanded, ref toggle, canExpand); return foldoutResult; } /// /// Begin a bordered vertical foldout toggle group. /// public static bool BeginFoldoutToggleBorderLayout(SerializedProperty foldoutProperty, GUIContent title, ref bool toggle, float headerHeight = 18f, bool roundedBox = true, bool canExpand = true) { Rect headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool expanded = canExpand && foldoutProperty.isExpanded; bool foldoutResult = expanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); DrawFoldoutToggleHeader(headerRect, title, ref expanded, ref toggle, canExpand); foldoutProperty.isExpanded = expanded; return foldoutResult; } /// /// Begin a bordered vertical foldout toggle group. /// public static bool BeginFoldoutToggleBorderLayout(GUIContent title, SerializedProperty toggleProperty, float headerHeight = 18f, bool roundedBox = true, bool canExpand = true) { Rect headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool expanded = canExpand && toggleProperty.isExpanded; bool toggle = toggleProperty.boolValue; bool foldoutResult = expanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); DrawFoldoutToggleHeader(headerRect, title, ref expanded, ref toggle, canExpand); toggleProperty.boolValue = toggle; toggleProperty.isExpanded = expanded; return foldoutResult; } /// /// Begin a bordered vertical foldout toggle group. /// public static bool BeginFoldoutToggleBorderLayout(GUIContent title, SerializedProperty toggleProperty, out Rect headerRect, float headerHeight = 18f, bool roundedBox = true, bool canExpand = true) { headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool expanded = canExpand && toggleProperty.isExpanded; bool toggle = toggleProperty.boolValue; bool foldoutResult = expanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); DrawFoldoutToggleHeader(headerRect, title, ref expanded, ref toggle, canExpand); toggleProperty.boolValue = toggle; toggleProperty.isExpanded = expanded; return foldoutResult; } /// /// Begin a bordered vertical foldout toggle group. /// public static bool BeginFoldoutToggleBorderLayout(SerializedProperty foldoutProperty, GUIContent title, ref bool toggle, out Rect headerRect, float headerHeight = 18f, bool roundedBox = true, bool canExpand = true) { headerRect = EditorGUILayout.GetControlRect(false, headerHeight + 4f); Rect boxRect = headerRect; bool expanded = canExpand && foldoutProperty.isExpanded; bool foldoutResult = expanded; if (foldoutResult) { Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxHeaderStyle); boxRect.yMax = drawingRect.yMax; } GUI.Box(boxRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip")); DrawFoldoutToggleHeader(headerRect, title, ref expanded, ref toggle, canExpand); foldoutProperty.isExpanded = expanded; return foldoutResult; } /// /// End a bordered vertical group. /// public static void EndBorderHeaderLayout() { EditorGUILayout.EndVertical(); } /// /// Draw all class property childs inside bordered foldout. /// public static Rect DrawClassBorderFoldout(SerializedProperty classProperty, GUIContent title, float headerHeight = 18f, bool roundedBox = true) { var classChildrens = classProperty.GetVisibleChildrens(); if (BeginFoldoutBorderLayout(classProperty, title, out Rect headerRect, headerHeight, roundedBox)) { foreach (var child in classChildrens) { EditorGUI.BeginChangeCheck(); { bool isArray = IsArray(child); if (isArray) EditorGUI.indentLevel++; { EditorGUILayout.PropertyField(child, true); } if (isArray) EditorGUI.indentLevel--; } if (EditorGUI.EndChangeCheck()) { classProperty.serializedObject.ApplyModifiedProperties(); } } EndBorderHeaderLayout(); } return headerRect; } /// /// Draw all class property childs inside bordered foldout. /// public static Rect DrawClassBorderFoldout(SerializedProperty classProperty, GUIContent title, ref bool expanded, float headerHeight = 18f, bool roundedBox = true) { var classChildrens = classProperty.GetVisibleChildrens(); if (BeginFoldoutBorderLayout(title, ref expanded, out Rect headerRect, headerHeight, roundedBox)) { foreach (var child in classChildrens) { EditorGUI.BeginChangeCheck(); { bool isArray = IsArray(child); if (isArray) EditorGUI.indentLevel++; { EditorGUILayout.PropertyField(child, true); } if (isArray) EditorGUI.indentLevel--; } if (EditorGUI.EndChangeCheck()) { classProperty.serializedObject.ApplyModifiedProperties(); } } EndBorderHeaderLayout(); } return headerRect; } /// /// Draw scriptable property childs inside bordered foldout. /// public static Rect DrawScriptableBorderFoldout(SerializedProperty scriptableProperty, GUIContent title, float headerHeight = 18f, bool roundedBox = true) { SerializedObject serializedObject = scriptableProperty.propertyType == SerializedPropertyType.ObjectReference ? new SerializedObject(scriptableProperty.objectReferenceValue) : scriptableProperty.serializedObject; SerializedProperty iterator = serializedObject.GetIterator(); HasIteratorChilds(iterator); if (BeginFoldoutBorderLayout(scriptableProperty, title, out Rect headerRect, headerHeight, roundedBox)) { do { EditorGUI.BeginChangeCheck(); { SerializedProperty child = serializedObject.FindProperty(iterator.name); bool isArray = IsArray(child); if (isArray) EditorGUI.indentLevel++; { EditorGUILayout.PropertyField(child, true); } if (isArray) EditorGUI.indentLevel--; } if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } } while (iterator.NextVisible(false)); EndBorderHeaderLayout(); } return headerRect; } /// /// Draw scriptable property childs inside bordered foldout toggle. /// public static Rect DrawScriptableBorderFoldoutToggle(SerializedProperty scriptableProperty, GUIContent title, ref bool toggle, float headerHeight = 18f, bool roundedBox = true) { SerializedObject serializedObject = scriptableProperty.propertyType == SerializedPropertyType.ObjectReference ? new SerializedObject(scriptableProperty.objectReferenceValue) : scriptableProperty.serializedObject; SerializedProperty iterator = serializedObject.GetIterator(); bool hasChilds = HasIteratorChilds(iterator); if (BeginFoldoutToggleBorderLayout(scriptableProperty, title, ref toggle, out Rect headerRect, headerHeight, roundedBox, canExpand: hasChilds)) { if (hasChilds) { do { EditorGUI.BeginChangeCheck(); { SerializedProperty child = serializedObject.FindProperty(iterator.name); bool isArray = IsArray(child); if (isArray) EditorGUI.indentLevel++; { EditorGUILayout.PropertyField(child, true); } if (isArray) EditorGUI.indentLevel--; } if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } } while (iterator.NextVisible(false)); } EndBorderHeaderLayout(); } return headerRect; } /// /// Draw scriptable property childs inside bordered foldout toggle. /// public static Rect DrawScriptableBorderFoldoutToggle(SerializedProperty scriptableProperty, GUIContent title, ref bool expanded, ref bool toggle, float headerHeight = 18f, bool roundedBox = true) { SerializedObject serializedObject = scriptableProperty.propertyType == SerializedPropertyType.ObjectReference ? new SerializedObject(scriptableProperty.objectReferenceValue) : scriptableProperty.serializedObject; SerializedProperty iterator = serializedObject.GetIterator(); bool hasChilds = HasIteratorChilds(iterator); if (BeginFoldoutToggleBorderLayout(title, ref expanded, ref toggle, out Rect headerRect, headerHeight, roundedBox, canExpand: hasChilds)) { if (hasChilds) { do { EditorGUI.BeginChangeCheck(); { SerializedProperty child = serializedObject.FindProperty(iterator.name); bool isArray = IsArray(child); if (isArray) EditorGUI.indentLevel++; { EditorGUILayout.PropertyField(child, true); } if (isArray) EditorGUI.indentLevel--; } if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } } while (iterator.NextVisible(false)); } EndBorderHeaderLayout(); } return headerRect; } /// /// Create a new texture with the given dimensions and color. /// public static Texture2D MakeTexture(int width, int height, Color color) { Color[] pix = new Color[width * height]; for (int i = 0; i < pix.Length; i++) pix[i] = color; Texture2D result = new Texture2D(width, height); result.SetPixels(pix); result.Apply(); return result; } /// /// Draw texture with a transprent background. /// public static void DrawTransparentTexture(Rect rect, Texture image) { Color guiColor = GUI.color; GUI.color = Color.clear; EditorGUI.DrawTextureTransparent(rect, image, ScaleMode.ScaleToFit); GUI.color = guiColor; } /// /// Draw texture with a transprent background. /// public static void DrawTransparentTextureWithChecker(Rect rect, Object spriteObj) { GUI.DrawTexture(rect, Styles.TransparentCheckerTexture, ScaleMode.ScaleToFit, alphaBlend: false); if (spriteObj != null) GUI.DrawTexture(rect, ((Sprite)spriteObj).texture, ScaleMode.ScaleToFit, alphaBlend: true); } public static PropertyCollection GetAllProperties(SerializedProperty classProperty) { PropertyCollection properties = new(); SerializedProperty currentProperty = classProperty.Copy(); SerializedProperty nextSiblingProperty = classProperty.Copy(); nextSiblingProperty.NextVisible(false); if (currentProperty.NextVisible(true)) { do { if (SerializedProperty.EqualContents(currentProperty, nextSiblingProperty)) break; properties.Add(currentProperty.name, currentProperty.Copy()); } while (currentProperty.NextVisible(false)); } return properties; } public static PropertyCollection GetAllProperties(SerializedObject serializedObject) { PropertyCollection properties = new(); SerializedProperty property = serializedObject.GetIterator(); SerializedProperty currentProperty = property.Copy(); SerializedProperty nextSiblingProperty = property.Copy(); if (currentProperty.NextVisible(true)) { do { if (SerializedProperty.EqualContents(currentProperty, nextSiblingProperty)) break; properties.Add(currentProperty.name, currentProperty.Copy()); } while (currentProperty.NextVisible(false)); } return properties; } public static bool HasIteratorChilds(SerializedProperty iterator) { return iterator != null && iterator.NextVisible(true) && iterator.NextVisible(false); } public static bool IsArray(SerializedProperty property) { return property.isArray && property.propertyType != SerializedPropertyType.String; } public static float GetPropertyChildHeight(SerializedProperty iterator) { SerializedProperty ite = iterator.Copy(); float totalHeight = EditorGUI.GetPropertyHeight(ite, true); while (ite.NextVisible(false)) { totalHeight += EditorGUIUtility.standardVerticalSpacing; totalHeight += EditorGUI.GetPropertyHeight(ite, true); } return totalHeight; } } public static class SerializedPropertyE { public static void DrawRelative(this SerializedProperty property, string propertyName) { SerializedProperty relative = property.FindPropertyRelative(propertyName); EditorGUILayout.PropertyField(relative); } public static void DrawRelative(this SerializedProperty property, string propertyName, GUIContent label) { SerializedProperty relative = property.FindPropertyRelative(propertyName); EditorGUILayout.PropertyField(relative, label); } public static SerializedProperty Relative(this SerializedProperty property, string propertyName) { return property.FindPropertyRelative(propertyName); } public static SerializedProperty Find(this SerializedProperty property, string path) { if (property == null) throw new ArgumentNullException(nameof(property)); if (string.IsNullOrEmpty(path)) throw new ArgumentException("Path cannot be null or empty", nameof(path)); string[] elements = path.Split('.'); SerializedProperty currentProperty = property; foreach (var element in elements) { currentProperty = currentProperty.FindPropertyRelative(element); if (currentProperty == null) throw new ArgumentException($"Property not found in path: {path}", nameof(path)); } return currentProperty; } } public static class AdvancedDropdownExtensions { public static void Show(this AdvancedDropdown dropdown, Rect buttonRect, float maxHeight) { dropdown.Show(buttonRect); SetMaxHeightForOpenedPopup(buttonRect, maxHeight); } private static void SetMaxHeightForOpenedPopup(Rect buttonRect, float maxHeight) { var window = EditorWindow.focusedWindow; if (window == null) { Debug.LogWarning("EditorWindow.focusedWindow was null."); return; } if (!string.Equals(window.GetType().Namespace, typeof(AdvancedDropdown).Namespace)) { Debug.LogWarning("EditorWindow.focusedWindow " + EditorWindow.focusedWindow.GetType().FullName + " was not in expected namespace."); return; } var position = window.position; if (position.height <= maxHeight) { return; } position.height = maxHeight; window.minSize = position.size; window.maxSize = position.size; window.position = position; window.ShowAsDropDown(GUIUtility.GUIToScreenRect(buttonRect), position.size); } } }