#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Jovian.InspectorTools.EditorTools {
public static class MyGUI {
#region Colors
public static class Colors {
public static readonly Color Red = new Color(.8f, .6f, .6f);
public static readonly Color Green = new Color(.4f, .6f, .4f);
public static readonly Color Blue = new Color(.6f, .6f, .8f);
public static readonly Color Gray = new Color(.3f, .3f, .3f);
public static readonly Color Yellow = new Color(.8f, .8f, .2f, .6f);
public static readonly Color Brown = new Color(.7f, .5f, .2f, .6f);
}
#endregion
#region Characters
public static class Characters {
public const string ArrowUp = "▲";
public const string ArrowDown = "▼";
public const string ArrowLeft = "◀";
public const string ArrowRight = "▶";
public const string ArrowLeftLight = "←";
public const string ArrowRightLight = "→";
public const string ArrowTopRightLight = "↘";
public const string Check = "✓";
public const string Cross = "×";
}
#endregion
#region Editor Icons
public static class EditorIcons {
public static GUIContent Plus => EditorGUIUtility.IconContent("Toolbar Plus");
public static GUIContent Minus => EditorGUIUtility.IconContent("Toolbar Minus");
public static GUIContent Refresh => EditorGUIUtility.IconContent("Refresh");
public static GUIContent ConsoleInfo => EditorGUIUtility.IconContent("console.infoicon.sml");
public static GUIContent ConsoleWarning => EditorGUIUtility.IconContent("console.warnicon.sml");
public static GUIContent ConsoleError => EditorGUIUtility.IconContent("console.erroricon.sml");
public static GUIContent Check => EditorGUIUtility.IconContent("FilterSelectedOnly");
public static GUIContent Cross => EditorGUIUtility.IconContent("d_winbtn_win_close");
public static GUIContent Dropdown => EditorGUIUtility.IconContent("icon dropdown");
public static GUIContent EyeOn => EditorGUIUtility.IconContent("d_VisibilityOn");
public static GUIContent EyeOff => EditorGUIUtility.IconContent("d_VisibilityOff");
public static GUIContent Zoom => EditorGUIUtility.IconContent("d_ViewToolZoom");
public static GUIContent Help => EditorGUIUtility.IconContent("_Help");
public static GUIContent Favourite => EditorGUIUtility.IconContent("Favorite");
public static GUIContent Label => EditorGUIUtility.IconContent("FilterByLabel");
public static GUIContent Settings => EditorGUIUtility.IconContent("d_Settings");
public static GUIContent SettingsPopup => EditorGUIUtility.IconContent("_Popup");
public static GUIContent SettingsMixer => EditorGUIUtility.IconContent("Audio Mixer");
public static GUIContent Circle => EditorGUIUtility.IconContent("TestNormal");
public static GUIContent CircleYellow => EditorGUIUtility.IconContent("TestInconclusive");
public static GUIContent CircleDotted => EditorGUIUtility.IconContent("TestIgnored");
public static GUIContent CircleRed => EditorGUIUtility.IconContent("TestFailed");
}
#endregion
#region Editor Styles
///
/// HelpBox with centered text alignment
///
public static GUIStyle HelpBoxStyle {
get {
if(_helpBoxStyle != null) return _helpBoxStyle;
_helpBoxStyle = new GUIStyle(GUI.skin.GetStyle("HelpBox"));
_helpBoxStyle.alignment = TextAnchor.MiddleCenter;
return _helpBoxStyle;
}
}
private static GUIStyle _helpBoxStyle;
///
/// ToolbarButtonStyle is not resizable by default
///
public static GUIStyle ResizableToolbarButtonStyle {
get {
if(_resizableToolbarButtonStyle != null) return _resizableToolbarButtonStyle;
_resizableToolbarButtonStyle = new GUIStyle(EditorStyles.toolbarButton);
_resizableToolbarButtonStyle.fixedHeight = 0;
return _resizableToolbarButtonStyle;
}
}
private static GUIStyle _resizableToolbarButtonStyle;
///
/// ToolbarButton with Border, Margin and Padding set to 0
///
public static GUIStyle BorderlessToolbarButtonStyle {
get {
if(_borderlessToolbarButton != null) return _borderlessToolbarButton;
_borderlessToolbarButton = new GUIStyle(ResizableToolbarButtonStyle);
var emptyOffset = new RectOffset();
_borderlessToolbarButton.border = emptyOffset;
_borderlessToolbarButton.margin = emptyOffset;
_borderlessToolbarButton.padding = emptyOffset;
return _borderlessToolbarButton;
}
}
private static GUIStyle _borderlessToolbarButton;
///
/// Style for a toggle button
///
public static GUIStyle ButtonToggledStyle(bool toggled) {
if(!toggled) return EditorStyles.miniButton;
if(_buttonToggledStyle != null) return _buttonToggledStyle;
_buttonToggledStyle = new GUIStyle(EditorStyles.miniButton);
_buttonToggledStyle.normal.background = _buttonToggledStyle.active.background;
return _buttonToggledStyle;
}
private static GUIStyle _buttonToggledStyle;
///
/// MiniButtonLeft/Middle/Right style based on array index
///
///
///
///
public static GUIStyle MiniButtonStyle(int index, Array collection) {
if(collection.Length == 1) return EditorStyles.miniButton;
if(index == 0) return EditorStyles.miniButtonLeft;
if(index == collection.Length - 1) return EditorStyles.miniButtonRight;
return EditorStyles.miniButtonMid;
}
#endregion
#region Draw Coloured lines and boxes
///
/// Draw Separator within GuiLayout
///
public static void Separator() {
var color = GUI.color;
EditorGUILayout.Space();
var spaceRect = EditorGUILayout.GetControlRect();
var separatorRectPosition = new Vector2(spaceRect.position.x, spaceRect.position.y + spaceRect.height / 2);
var separatorRect = new Rect(separatorRectPosition, new Vector2(spaceRect.width, 1));
GUI.color = Color.white;
GUI.Box(separatorRect, GUIContent.none);
GUI.color = color;
}
///
/// Draw Line within GUILayout
///
public static void DrawLine(Color color, bool withSpace = false) {
if(withSpace) EditorGUILayout.Space();
var defaultBackgroundColor = GUI.backgroundColor;
GUI.backgroundColor = color;
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
GUI.backgroundColor = defaultBackgroundColor;
if(withSpace) EditorGUILayout.Space();
}
///
/// Draw line within Rect and get Rect back with offset
///
public static Rect DrawLine(Color color, Rect rect) {
var h = rect.height;
var defaultBackgroundColor = GUI.backgroundColor;
GUI.backgroundColor = color;
rect.y += 5;
rect.height = 1;
GUI.Box(rect, "");
rect.y += 5;
GUI.backgroundColor = defaultBackgroundColor;
rect.height = h;
return rect;
}
///
/// Draw Rect filled with Color
///
public static void DrawColouredRect(Rect rect, Color color) {
var defaultBackgroundColor = GUI.backgroundColor;
GUI.backgroundColor = color;
GUI.Box(rect, "");
GUI.backgroundColor = defaultBackgroundColor;
}
///
/// Draw background Line within GUILayout
///
public static void DrawBackgroundLine(Color color, int yOffset = 0) {
var defColor = GUI.color;
GUI.color = color;
var rect = GUILayoutUtility.GetLastRect();
rect.center = new Vector2(rect.center.x, rect.center.y + 6 + yOffset);
rect.height = 17;
GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
GUI.color = defColor;
}
///
/// Draw background Line of height
///
public static void DrawBackgroundBox(Color color, int height, int yOffset = 0) {
var defColor = GUI.color;
GUI.color = color;
var rect = GUILayoutUtility.GetLastRect();
rect.center = new Vector2(rect.center.x, rect.center.y + 6 + yOffset);
rect.height = height;
GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
GUI.color = defColor;
}
#endregion
#region Property Field
///
/// Make a field for SerializedProperty and check if changed
///
public static bool PropertyField(SerializedProperty property, params GUILayoutOption[] options) {
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(property, options);
return EditorGUI.EndChangeCheck();
}
///
/// Make a field for SerializedProperty and check if changed
///
public static bool PropertyField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) {
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(property, label, options);
return EditorGUI.EndChangeCheck();
}
#endregion
#region SerializedProperty Manipulation Buttons
///
/// Move array element at index on index-1 position
///
public static void MoveArrayElementUpButton(this SerializedProperty property, int elementAt) {
var guiState = GUI.enabled;
if(elementAt <= 0) GUI.enabled = false;
if(UpButton()) {
EditorApplication.delayCall += () => {
property.MoveArrayElement(elementAt, elementAt - 1);
property.serializedObject.ApplyModifiedProperties();
};
}
GUI.enabled = guiState;
}
///
/// Move array element at index on index+1 position
///
public static void MoveArrayElementDownButton(this SerializedProperty property, int elementAt) {
var guiState = GUI.enabled;
if(elementAt >= property.arraySize - 1) GUI.enabled = false;
if(DownButton()) {
EditorApplication.delayCall += () => {
property.MoveArrayElement(elementAt, elementAt + 1);
property.serializedObject.ApplyModifiedProperties();
};
}
GUI.enabled = guiState;
}
///
/// Add new array element to property and get it as SerializedProperty
///
public static SerializedProperty NewArrayElementButton(this SerializedProperty property) {
if(PlusButton()) {
return property.NewElement();
}
return null;
}
///
/// Remove array element at index
///
public static void RemoveElementButton(this SerializedProperty property, int elementAt) {
var guiState = GUI.enabled;
if(elementAt < 0 || elementAt >= property.arraySize - 1) GUI.enabled = false;
if(CrossButton()) {
EditorApplication.delayCall += () => {
property.DeleteArrayElementAtIndex(elementAt);
property.serializedObject.ApplyModifiedProperties();
};
}
GUI.enabled = guiState;
}
#endregion
#region Drop Area
///
/// Drag-and-Drop Area to catch objects of specific type
///
/// Asset type to catch
/// Label to display
/// Height of the Drop Area
/// Allow to drag external files and import as unity assets
/// Path relative to Assets folder
/// Received objects. Null if none received
public static T[] DropArea(string areaText, float height, bool allowExternal = false,
string externalImportFolder = null) where T : Object {
Event currentEvent = Event.current;
Rect dropArea = GUILayoutUtility.GetRect(0.0f, height, GUILayout.ExpandWidth(true));
var style = new GUIStyle(GUI.skin.box);
style.alignment = TextAnchor.MiddleCenter;
GUI.Box(dropArea, areaText, style);
bool dragEvent = currentEvent.type == EventType.DragUpdated || currentEvent.type == EventType.DragPerform;
if(!dragEvent) return null;
bool overDropArea = dropArea.Contains(currentEvent.mousePosition);
if(!overDropArea) return null;
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if(currentEvent.type != EventType.DragPerform) return null;
DragAndDrop.AcceptDrag();
Event.current.Use();
List result = new List();
bool anyExternal = DragAndDrop.paths.Length > 0 &&
DragAndDrop.paths.Length > DragAndDrop.objectReferences.Length;
if(allowExternal && anyExternal) {
var folderToLoad = "/";
if(!string.IsNullOrEmpty(externalImportFolder)) {
folderToLoad = "/" + externalImportFolder.Replace("Assets/", "").Trim('/', '\\') + "/";
}
List importedFiles = new List();
foreach(string externalPath in DragAndDrop.paths) {
if(externalPath.Length == 0) continue;
try {
var filename = Path.GetFileName(externalPath);
var relativePath = folderToLoad + filename;
Directory.CreateDirectory(Application.dataPath + folderToLoad);
FileUtil.CopyFileOrDirectory(externalPath, Application.dataPath + relativePath);
importedFiles.Add("Assets" + relativePath);
}
catch(Exception ex) {
Debug.LogException(ex);
}
}
AssetDatabase.Refresh();
foreach(var importedFile in importedFiles) {
var asset = AssetDatabase.LoadAssetAtPath(importedFile);
if(asset != null) {
result.Add(asset);
Debug.Log("Asset imported at path: " + importedFile);
}
else AssetDatabase.DeleteAsset(importedFile);
}
}
else {
foreach(Object dragged in DragAndDrop.objectReferences) {
var validObject = dragged as T ?? AssetDatabase.LoadAssetAtPath(AssetDatabase.GetAssetPath(dragged));
if(validObject != null) result.Add(validObject);
}
}
return result.Count > 0 ? result.OrderBy(o => o.name).ToArray() : null;
}
///
/// Drag-and-Drop Area to get paths of received objects
///
/// Label to display
/// Height of the Drop Area
/// Received paths
public static string[] DropAreaPaths(string areaText, float height) {
Event currentEvent = Event.current;
Rect dropArea = GUILayoutUtility.GetRect(0.0f, height, GUILayout.ExpandWidth(true));
var style = new GUIStyle(GUI.skin.box);
style.alignment = TextAnchor.MiddleCenter;
GUI.Box(dropArea, areaText, style);
bool dragEvent = currentEvent.type == EventType.DragUpdated || currentEvent.type == EventType.DragPerform;
if(!dragEvent) return null;
bool overDropArea = dropArea.Contains(currentEvent.mousePosition);
if(!overDropArea) return null;
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if(currentEvent.type != EventType.DragPerform) return null;
DragAndDrop.AcceptDrag();
Event.current.Use();
return DragAndDrop.paths;
}
#endregion
#region Browse Buttons
///
/// Creates a filepath textfield with a browse button. Opens the open file panel.
///
public static string BrowsFileLabel(string name, float labelWidth, string path, string extension) {
EditorGUILayout.BeginHorizontal();
GUILayout.Label(name, GUILayout.MaxWidth(labelWidth));
string filepath = EditorGUILayout.TextField(path);
if(GUILayout.Button("Browse")) {
filepath = EditorUtility.OpenFilePanel(name, path, extension);
}
EditorGUILayout.EndHorizontal();
return filepath;
}
///
/// Creates a folder path textfield with a browse button. Opens the save folder panel.
///
public static string BrowseFolderLabel(string name, float labelWidth, string path) {
EditorGUILayout.BeginHorizontal();
string filepath = EditorGUILayout.TextField(name, path, GUILayout.MaxWidth(labelWidth));
if(GUILayout.Button("Browse", GUILayout.MaxWidth(60))) {
filepath = EditorUtility.SaveFolderPanel(name, path, "Folder");
}
EditorGUILayout.EndHorizontal();
return filepath;
}
#endregion
#region Predefined Buttons
///
/// Display Button with ArrowUI
///
public static bool UpButton() {
return GUILayout.Button(Characters.ArrowUp, EditorStyles.toolbarButton, GUILayout.Width(18));
}
public static bool DownButton() {
return GUILayout.Button(Characters.ArrowDown, EditorStyles.toolbarButton, GUILayout.Width(18));
}
public static bool PlusButton() {
return GUILayout.Button("+", EditorStyles.toolbarButton, GUILayout.Width(18));
}
public static bool CrossButton() {
return GUILayout.Button(Characters.Cross, EditorStyles.toolbarButton, GUILayout.Width(18));
}
#endregion
///
/// Creates a toolbar that is filled in from an Enum. Useful for setting tool modes.
///
public static Enum EnumToolbar(Enum selected) {
string[] toolbar = Enum.GetNames(selected.GetType());
Array values = Enum.GetValues(selected.GetType());
for(int i = 0; i < toolbar.Length; i++) {
string toolName = toolbar[i];
toolName = toolName.Replace("_", " ");
toolbar[i] = toolName;
}
int selectedIndex = 0;
while(selectedIndex < values.Length) {
if(selected.ToString() == values.GetValue(selectedIndex).ToString()) {
break;
}
selectedIndex++;
}
selectedIndex = GUILayout.Toolbar(selectedIndex, toolbar);
return (Enum)values.GetValue(selectedIndex);
}
///
/// Creates an array foldout like in inspectors
///
public static string[] ArrayFoldout(string label, string[] array, ref bool foldout) {
EditorGUILayout.BeginVertical();
EditorGUIUtility.labelWidth = 0;
EditorGUIUtility.fieldWidth = 0;
foldout = EditorGUILayout.Foldout(foldout, label);
string[] newArray = array;
if(foldout) {
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
EditorGUILayout.BeginVertical();
int arraySize = EditorGUILayout.IntField("Size", array.Length);
if(arraySize != array.Length)
newArray = new string[arraySize];
for(int i = 0; i < arraySize; i++) {
string entry = "";
if(i < array.Length)
entry = array[i];
newArray[i] = EditorGUILayout.TextField("Element " + i, entry);
}
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
return newArray;
}
}
}
#endif