com.alicizax.unity.framework/Editor/Utility/EditorDrawing.cs
2025-10-11 15:18:09 +08:00

1867 lines
71 KiB
C#

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;
}
}
/// <summary>
/// Set custom icon size.
/// </summary>
public static Vector2 SetIconSize(float iconSize)
{
Vector2 prevIconSize = EditorGUIUtility.GetIconSize();
EditorGUIUtility.SetIconSize(new Vector2(iconSize, iconSize));
return prevIconSize;
}
/// <summary>
/// Reset custom icon size.
/// </summary>
public static void ResetIconSize()
{
EditorGUIUtility.SetIconSize(Vector2.zero);
}
/// <summary>
/// Set the header label text color.
/// </summary>
public static void SetLabelColor(Color color)
{
Styles.labelColor = color;
}
/// <summary>
/// Set the header label text color.
/// </summary>
public static void SetLabelColor(string htmlColor)
{
if (ColorUtility.TryParseHtmlString(htmlColor, out Color color))
Styles.labelColor = color;
}
/// <summary>
/// Reset the header label text color.
/// </summary>
public static void ResetLabelColor()
{
Styles.labelColor = null;
}
/// <summary>
/// Get GUIContent with specified icon.
/// </summary>
public static GUIContent IconContent(string iconName, float iconSize = 16f)
{
SetIconSize(iconSize);
return EditorGUIUtility.TrIconContent(iconName);
}
/// <summary>
/// Get GUIContent with specified icon and text.
/// </summary>
public static GUIContent IconTextContent(string text, string iconName, float iconSize = 16f)
{
SetIconSize(iconSize);
return EditorGUIUtility.TrTextContentWithIcon(" " + text, iconName);
}
/// <summary>
/// Get GUIContent with specified icon and text.
/// </summary>
public static GUIContent IconTextContent(string text, Texture2D texture, float iconSize = 16f)
{
SetIconSize(iconSize);
return EditorGUIUtility.TrTextContentWithIcon(" " + text, texture);
}
/// <summary>
/// Classic HelpBox but with Rich Text.
/// </summary>
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;
}
/// <summary>
/// Draw custom border box.
/// </summary>
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;
}
/// <summary>
/// Draw the heading at the top of the inspector.
/// </summary>
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<Texture2D>("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
}
/// <summary>
/// Draw classic prefix label.
/// </summary>
public static void DrawPrefixLabel(string title, string text, GUIStyle style)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(title);
EditorGUILayout.LabelField(text, style);
EditorGUILayout.EndHorizontal();
}
/// <summary>
/// Draw min-max slider from MinMax property.
/// </summary>
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;
}
/// <summary>
/// Draw a property field with a label after the field.
/// </summary>
public static void DrawPropertyAndLabel(SerializedProperty property, GUIContent content)
{
DrawPropertyAndLabel(property, content, EditorStyles.boldLabel);
}
/// <summary>
/// Draw a property field with a label after the field.
/// </summary>
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);
}
/// <summary>
/// Draw horizontal separator.
/// </summary>
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));
}
/// <summary>
/// Draw horizontal (spaced) separator.
/// </summary>
public static void SeparatorSpaced(float space, int height = 1)
{
EditorGUILayout.Space(space);
Separator(height);
EditorGUILayout.Space(space);
}
/// <summary>
/// Draw a large sprite selection similar to the EditorGUI.ObjectField sprite selection.
/// <br>But with the correct, not stretched image.</br>
/// </summary>
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<Sprite>(obj, false, "", GUIUtility.GetControlID(FocusType.Passive));
}
else if (Event.current.commandName == "ObjectSelectorUpdated")
{
return EditorGUIUtility.GetObjectPickerObject();
}
return obj;
}
/// <summary>
/// Draw classic object field with picker.
/// </summary>
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);
}
}
/// <summary>
/// Draw a multi selection toolbar.
/// </summary>
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;
}
}
}
/// <summary>
/// Draw multi selection popup menu.
/// </summary>
public static void DrawMultiSelectPopup(Rect rect, string[] content, string[] selected, Action<string[]> onItemSelect, int maxDisplay = 3)
{
GenericMenu menu = new GenericMenu();
List<string> selectedArr = new List<string>(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);
}
}
/// <summary>
/// Draw multi selection popup menu.
/// </summary>
public static void DrawMultiSelectPopup(Rect rect, GUIContent title, string[] content, string[] selected, Action<string[]> onItemSelect, int maxDisplay = 3)
{
GenericMenu menu = new GenericMenu();
GUIContent guiTitle = new GUIContent(title);
List<string> selectedArr = new List<string>(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);
}
}
/// <summary>
/// Draw string selection popup menu.
/// </summary>
public static void DrawStringSelectPopup(Rect rect, string[] content, string selected, Action<string> 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);
}
}
/// <summary>
/// Draw string selection popup menu.
/// </summary>
public static void DrawStringSelectPopup(Rect rect, GUIContent title, string[] content, string selected, Action<string> 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);
}
}
/// <summary>
/// Draw string selection popup menu.
/// </summary>
public static void DrawStringSelectPopup(GUIContent label, GUIContent title, string[] content, string selected, Action<string> onItemSelect)
{
Rect rect = EditorGUILayout.GetControlRect();
rect = EditorGUI.PrefixLabel(rect, label);
DrawStringSelectPopup(rect, title, content, selected, onItemSelect);
}
/// <summary>
/// Draw string selection popup menu.
/// </summary>
public static void DrawStringSelectPopup(GUIContent title, string[] content, string selected, Action<string> onItemSelect)
{
Rect rect = EditorGUILayout.GetControlRect();
DrawStringSelectPopup(rect, title, content, selected, onItemSelect);
}
/// <summary>
/// Draw custom class property list.
/// </summary>
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();
}
/// <summary>
/// Setup reorderable list for easy drawing.
/// </summary>
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--;
}
};
}
/// <summary>
/// Get count of properties.
/// </summary>
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;
}
/// <summary>
/// Draw header by specified rect.
/// </summary>
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);
}
/// <summary>
/// Draw toggle header by specified rect.
/// </summary>
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;
}
/// <summary>
/// Draw foldout header by specified rect.
/// </summary>
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;
}
/// <summary>
/// Draw foldout header by specified rect.
/// </summary>
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;
}
/// <summary>
/// Draw foldout toggle header by specified rect.
/// </summary>
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();
}
}
/// <summary>
/// Draw header with border.
/// </summary>
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;
}
/// <summary>
/// Draw header with border.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical group;
/// </summary>
public static void BeginBorderLayout(bool roundedBox = true)
{
Rect drawingRect = EditorGUILayout.BeginVertical(Styles.borderBoxStyle);
GUI.Box(drawingRect, GUIContent.none, new GUIStyle(roundedBox ? "HelpBox" : "Tooltip"));
}
/// <summary>
/// Begin a bordered vertical header group;
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout group.
/// </summary>
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);
}
/// <summary>
/// Begin a bordered vertical foldout group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical toggle group.
/// </summary>
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);
}
/// <summary>
/// Begin a bordered vertical toggle group.
/// </summary>
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);
}
/// <summary>
/// Begin a bordered vertical foldout toggle group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout toggle group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout toggle group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout toggle group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout toggle group.
/// </summary>
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;
}
/// <summary>
/// Begin a bordered vertical foldout toggle group.
/// </summary>
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;
}
/// <summary>
/// End a bordered vertical group.
/// </summary>
public static void EndBorderHeaderLayout()
{
EditorGUILayout.EndVertical();
}
/// <summary>
/// Draw all class property childs inside bordered foldout.
/// </summary>
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;
}
/// <summary>
/// Draw all class property childs inside bordered foldout.
/// </summary>
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;
}
/// <summary>
/// Draw scriptable property childs inside bordered foldout.
/// </summary>
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;
}
/// <summary>
/// Draw scriptable property childs inside bordered foldout toggle.
/// </summary>
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;
}
/// <summary>
/// Draw scriptable property childs inside bordered foldout toggle.
/// </summary>
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;
}
/// <summary>
/// Create a new texture with the given dimensions and color.
/// </summary>
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;
}
/// <summary>
/// Draw texture with a transprent background.
/// </summary>
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;
}
/// <summary>
/// Draw texture with a transprent background.
/// </summary>
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);
}
}
}