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

520 lines
20 KiB
C#

using System.Collections.Generic;
using System.Reflection;
using System;
using UnityEngine;
using UnityEditor;
namespace AlicizaX.Editor
{
public static class EditorUtils
{
public static class Styles
{
public static GUIStyle IconButton => GUI.skin.FindStyle("IconButton");
public static readonly GUIContent PlusIcon = EditorGUIUtility.TrIconContent("Toolbar Plus", "Add Item");
public static readonly GUIContent MinusIcon = EditorGUIUtility.TrIconContent("Toolbar Minus", "Remove Item");
public static readonly GUIContent TrashIcon = EditorGUIUtility.TrIconContent("TreeEditor.Trash", "Remove Item");
public static readonly GUIContent RefreshIcon = EditorGUIUtility.TrIconContent("Refresh", "Refresh");
public static readonly GUIContent Linked = EditorGUIUtility.TrIconContent("Linked");
public static readonly GUIContent UnLinked = EditorGUIUtility.TrIconContent("Unlinked");
public static readonly GUIContent Database = EditorGUIUtility.TrIconContent("Package Manager");
public static readonly GUIContent GreenLight = EditorGUIUtility.TrIconContent("greenLight");
public static readonly GUIContent OrangeLight = EditorGUIUtility.TrIconContent("orangeLight");
public static readonly GUIContent RedLight = EditorGUIUtility.TrIconContent("redLight");
public static GUIStyle RichLabel => new GUIStyle(EditorStyles.label)
{
richText = true
};
}
public class BoxGroupScope : GUI.Scope
{
public BoxGroupScope(string icon, string title, float height = 22)
{
GUIContent iconTitle = EditorGUIUtility.TrTextContentWithIcon(" " + title, icon);
EditorGUILayout.BeginVertical(GUI.skin.box);
Rect headerRect = GUILayoutUtility.GetRect(1, height);
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
headerRect.x += EditorGUIUtility.standardVerticalSpacing;
EditorGUI.LabelField(headerRect, iconTitle, EditorStyles.boldLabel);
EditorGUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
}
public BoxGroupScope(string title, float height = 22)
{
EditorGUILayout.BeginVertical(GUI.skin.box);
Rect headerRect = GUILayoutUtility.GetRect(1, height);
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
headerRect.x += EditorGUIUtility.standardVerticalSpacing;
EditorGUI.LabelField(headerRect, title, EditorStyles.boldLabel);
EditorGUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
}
protected override void CloseScope()
{
EditorGUILayout.EndVertical();
}
}
public struct PopupArray
{
public List<string> contents;
public List<string> selected;
public PopupArray(string[] contents, string[] selected)
{
this.contents = new List<string>(contents);
this.selected = new List<string>(selected);
}
}
public sealed class PopupElement
{
public Action<string[]> onSelect;
public PopupArray popupArray;
public string name;
public PopupElement(PopupArray popupArray, string name, Action<string[]> onSelect)
{
this.popupArray = popupArray;
this.name = name;
this.onSelect = onSelect;
}
}
public static void DrawOutline(Rect rect, RectOffset border)
{
Color color = new Color(0.6f, 0.6f, 0.6f, 1.333f);
if (EditorGUIUtility.isProSkin)
{
color.r = 0.12f;
color.g = 0.12f;
color.b = 0.12f;
}
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 + 1, border.left, rect.height - 2 * border.left), EditorGUIUtility.whiteTexture); //left
GUI.DrawTexture(new Rect(rect.xMax - border.right, rect.y + 1, border.right, rect.height - 2 * border.right), EditorGUIUtility.whiteTexture); //right
GUI.color = orgColor;
}
public static void DrawOutline(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 + 1, border.left, rect.height - 2 * border.left), EditorGUIUtility.whiteTexture); //left
GUI.DrawTexture(new Rect(rect.xMax - border.right, rect.y + 1, border.right, rect.height - 2 * border.right), EditorGUIUtility.whiteTexture); //right
GUI.color = orgColor;
}
public static Rect DrawHeader(float height, string title)
{
Rect rect = GUILayoutUtility.GetRect(0, height);
EditorGUI.DrawRect(rect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
var labelRect = rect;
labelRect.x += 3f;
EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel);
return rect;
}
public static void DrawHeader(Rect rect, string title, float labelX, float labelY, GUIStyle labelStyle)
{
EditorGUI.DrawRect(rect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
var labelRect = rect;
labelRect.x += labelX;
labelRect.y += labelY;
EditorGUI.LabelField(labelRect, title, labelStyle);
}
public static Rect DrawHeaderWithBorder(GUIContent title, float height, ref Rect rect, bool rounded)
{
GUI.Box(rect, GUIContent.none, new GUIStyle(rounded ? "HelpBox" : "Tooltip"));
Rect headerRect = rect;
headerRect.height = height;
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
Rect labelRect = headerRect;
labelRect.y += height / 2;
EditorGUI.LabelField(labelRect, title, EditorStyles.miniBoldLabel);
rect.x += 1;
rect.y += 1;
rect.height -= 1;
rect.width -= 2;
rect.y += height;
rect.height -= height;
return rect;
}
public static Rect DrawHeaderWithBorder(GUIContent title, float height, ref Rect rect, GUIStyle boxStyle)
{
GUI.Box(rect, GUIContent.none, boxStyle);
rect.x += 1;
rect.y += 1;
rect.height -= 1;
rect.width -= 2;
var headerRect = rect;
headerRect.height = height + EditorGUIUtility.standardVerticalSpacing;
rect.y += headerRect.height;
rect.height -= headerRect.height;
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
var labelRect = headerRect;
labelRect.y += EditorGUIUtility.standardVerticalSpacing;
labelRect.x += 2f;
EditorGUI.LabelField(labelRect, title, EditorStyles.miniBoldLabel);
return headerRect;
}
public static Rect DrawHeaderWithBorder(GUIContent title, float height, ref Rect rect, RectOffset border)
{
DrawOutline(rect, border);
rect.x += 1;
rect.y += 1;
rect.height -= 1;
rect.width -= 2;
var headerRect = rect;
headerRect.height = height + EditorGUIUtility.standardVerticalSpacing;
rect.y += headerRect.height;
rect.height -= headerRect.height;
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
var labelRect = headerRect;
labelRect.y += EditorGUIUtility.standardVerticalSpacing;
labelRect.x += 2f;
EditorGUI.LabelField(labelRect, title, EditorStyles.miniBoldLabel);
return headerRect;
}
public static bool DrawBoxFoldoutHeader(string title, bool state, float height = 22)
{
Rect rect = GUILayoutUtility.GetRect(1, height);
EditorGUI.DrawRect(rect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
rect.x += EditorGUIUtility.standardVerticalSpacing;
Rect foldoutRect = EditorGUI.IndentedRect(rect);
state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout);
rect.x += EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing * 2;
EditorGUI.LabelField(rect, new GUIContent(title), EditorStyles.boldLabel);
return state;
}
public static bool DrawFoldoutHeader(float height, string title, bool state)
{
Rect rect = GUILayoutUtility.GetRect(1, height);
rect.x += EditorGUIUtility.standardVerticalSpacing;
Rect foldoutRect = EditorGUI.IndentedRect(rect);
state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout);
rect.x += EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing * 2;
EditorGUI.LabelField(rect, new GUIContent(title), EditorStyles.boldLabel);
return state;
}
public static void DrawFoldoutToggleHeader(Rect rect, string title, ref bool isExpanded, ref bool isEnabled, bool showFoldout = true, bool toggleDisable = false)
{
Color headerColor = new Color(0.1f, 0.1f, 0.1f, 0f);
var expandRect = rect;
expandRect.xMin += EditorGUIUtility.singleLineHeight * 2;
var foldoutRect = rect;
foldoutRect.width = EditorGUIUtility.singleLineHeight;
var toggleRect = rect;
toggleRect.width = EditorGUIUtility.singleLineHeight;
toggleRect.x += EditorGUIUtility.singleLineHeight;
var labelRect = rect;
labelRect.xMin += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 2;
if (showFoldout)
{
// events
var e = Event.current;
if (expandRect.Contains(e.mousePosition))
{
if (e.type == EventType.MouseDown && e.button == 0)
{
isExpanded = !isExpanded;
e.Use();
}
}
// foldout
isExpanded = GUI.Toggle(foldoutRect, isExpanded, GUIContent.none, EditorStyles.foldout);
}
// background
EditorGUI.DrawRect(rect, headerColor);
// toggle
using (new EditorGUI.DisabledGroupScope(toggleDisable))
{
isEnabled = GUI.Toggle(toggleRect, isEnabled, new GUIContent("", "Extension Enabled State"), EditorStyles.toggle);
}
// title
EditorGUI.LabelField(labelRect, new GUIContent(title), EditorStyles.boldLabel);
}
private static void PopupSelect(object data)
{
PopupElement element = (PopupElement)data;
PopupArray array = element.popupArray;
string name = element.name;
if (array.selected.Contains(name))
array.selected.Remove(name);
else array.selected.Add(name);
element.onSelect?.Invoke(array.selected.ToArray());
}
public static void DrawMultiSelectionPopup(Rect rect, string title, PopupArray popupArray, Action<string[]> onSelect)
{
GenericMenu menu = new GenericMenu();
for (int i = 0; i < popupArray.contents.Count; i++)
{
string name = popupArray.contents[i];
bool on = popupArray.selected.Contains(name);
PopupElement element = new PopupElement(popupArray, name, onSelect);
menu.AddItem(new GUIContent(name), on, PopupSelect, element);
}
if(GUI.Button(rect, title, EditorStyles.popup))
{
menu.ShowAsContext();
}
}
public static void DrawRelativeProperties(SerializedProperty root, float width)
{
var childrens = root.GetVisibleChildrens();
foreach (var childProperty in childrens)
{
float height = EditorGUI.GetPropertyHeight(childProperty, true);
Rect rect = GUILayoutUtility.GetRect(1f, height);
rect.xMin += width;
EditorGUI.PropertyField(rect, childProperty, true);
EditorGUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
}
}
public static IEnumerable<SerializedProperty> GetVisibleChildrens(this SerializedProperty serializedProperty)
{
SerializedProperty currentProperty = serializedProperty.Copy();
SerializedProperty nextSiblingProperty = serializedProperty.Copy();
{
nextSiblingProperty.NextVisible(false);
}
if (currentProperty.NextVisible(true))
{
do
{
if (SerializedProperty.EqualContents(currentProperty, nextSiblingProperty))
break;
yield return currentProperty;
}
while (currentProperty.NextVisible(false));
}
}
public static void TrHelpIconText(string message, string icon, bool rich = false)
{
GUIStyle style = new GUIStyle(EditorStyles.helpBox)
{
richText = rich
};
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(" " + message, icon), style, new GUILayoutOption[0]);
}
public static void TrHelpIconText(Rect rect, string message, string icon, bool rich = false)
{
GUIStyle style = new GUIStyle(EditorStyles.helpBox)
{
richText = rich
};
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(" " + message, icon), style);
}
public static void TrHelpIconText(string message, MessageType messageType, bool rich = false, bool space = true)
{
string icon = string.Empty;
GUIStyle style = new GUIStyle(EditorStyles.helpBox)
{
richText = rich
};
switch (messageType)
{
case MessageType.Info:
icon = "console.infoicon.sml";
break;
case MessageType.Warning:
icon = "console.warnicon.sml";
break;
case MessageType.Error:
icon = "console.erroricon.sml";
break;
}
if (!string.IsNullOrEmpty(icon))
{
string text = space ? " " + message : message;
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style, new GUILayoutOption[0]);
}
else
{
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContent(message), style, new GUILayoutOption[0]);
}
}
public static void TrHelpIconText(Rect rect, string message, MessageType messageType, bool rich = false, bool space = true)
{
string icon = string.Empty;
GUIStyle style = new GUIStyle(EditorStyles.helpBox)
{
richText = rich
};
switch (messageType)
{
case MessageType.Info:
icon = "console.infoicon.sml";
break;
case MessageType.Warning:
icon = "console.warnicon.sml";
break;
case MessageType.Error:
icon = "console.erroricon.sml";
break;
}
if (!string.IsNullOrEmpty(icon))
{
string text = space ? " " + message : message;
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style);
}
else
{
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContent(message), style);
}
}
public static void TrIconText(string message, string icon, GUIStyle style, bool rich = false, bool space = true)
{
style.richText = rich;
string text = space ? " " + message : message;
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style, new GUILayoutOption[0]);
}
public static void TrIconText(string message, MessageType messageType, GUIStyle style, bool rich = false, bool space = true)
{
string icon = string.Empty;
style.richText = rich;
switch (messageType)
{
case MessageType.Info:
icon = "console.infoicon.sml";
break;
case MessageType.Warning:
icon = "console.warnicon.sml";
break;
case MessageType.Error:
icon = "console.erroricon.sml";
break;
}
if (!string.IsNullOrEmpty(icon))
{
string text = space ? " " + message : message;
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style, new GUILayoutOption[0]);
}
else
{
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContent(message), style, new GUILayoutOption[0]);
}
}
public static void TrIconText(Rect rect, string message, MessageType messageType, GUIStyle style, bool rich = false, bool space = true)
{
string icon = string.Empty;
style.richText = rich;
switch (messageType)
{
case MessageType.Info:
icon = "console.infoicon.sml";
break;
case MessageType.Warning:
icon = "console.warnicon.sml";
break;
case MessageType.Error:
icon = "console.erroricon.sml";
break;
}
if (!string.IsNullOrEmpty(icon))
{
string text = space ? " " + message : message;
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style);
}
else
{
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContent(message), style);
}
}
}
}