forked from Shardstone/trail-into-darkness
changed directory structure
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd2b08b6aac506f4eabedf9dff5a11cb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,580 @@
|
||||
#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
|
||||
|
||||
/// <summary>
|
||||
/// HelpBox with centered text alignment
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ToolbarButtonStyle is not resizable by default
|
||||
/// </summary>
|
||||
public static GUIStyle ResizableToolbarButtonStyle {
|
||||
get {
|
||||
if(_resizableToolbarButtonStyle != null) return _resizableToolbarButtonStyle;
|
||||
_resizableToolbarButtonStyle = new GUIStyle(EditorStyles.toolbarButton);
|
||||
_resizableToolbarButtonStyle.fixedHeight = 0;
|
||||
return _resizableToolbarButtonStyle;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIStyle _resizableToolbarButtonStyle;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ToolbarButton with Border, Margin and Padding set to 0
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Style for a toggle button
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// MiniButtonLeft/Middle/Right style based on array index
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <returns></returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Draw Separator within GuiLayout
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw Line within GUILayout
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw line within Rect and get Rect back with offset
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw Rect filled with Color
|
||||
/// </summary>
|
||||
public static void DrawColouredRect(Rect rect, Color color) {
|
||||
var defaultBackgroundColor = GUI.backgroundColor;
|
||||
GUI.backgroundColor = color;
|
||||
GUI.Box(rect, "");
|
||||
GUI.backgroundColor = defaultBackgroundColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw background Line within GUILayout
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw background Line of height
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Make a field for SerializedProperty and check if changed
|
||||
/// </summary>
|
||||
public static bool PropertyField(SerializedProperty property, params GUILayoutOption[] options) {
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(property, options);
|
||||
return EditorGUI.EndChangeCheck();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a field for SerializedProperty and check if changed
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Move array element at index on index-1 position
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Move array element at index on index+1 position
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add new array element to property and get it as SerializedProperty
|
||||
/// </summary>
|
||||
public static SerializedProperty NewArrayElementButton(this SerializedProperty property) {
|
||||
if(PlusButton()) {
|
||||
return property.NewElement();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove array element at index
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Drag-and-Drop Area to catch objects of specific type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Asset type to catch</typeparam>
|
||||
/// <param name="areaText">Label to display</param>
|
||||
/// <param name="height">Height of the Drop Area</param>
|
||||
/// <param name="allowExternal">Allow to drag external files and import as unity assets</param>
|
||||
/// <param name="externalImportFolder">Path relative to Assets folder</param>
|
||||
/// <returns>Received objects. Null if none received</returns>
|
||||
public static T[] DropArea<T>(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<T> result = new List<T>();
|
||||
|
||||
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<string> importedFiles = new List<string>();
|
||||
|
||||
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<T>(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<T>(AssetDatabase.GetAssetPath(dragged));
|
||||
|
||||
if(validObject != null) result.Add(validObject);
|
||||
}
|
||||
}
|
||||
|
||||
return result.Count > 0 ? result.OrderBy(o => o.name).ToArray() : null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Drag-and-Drop Area to get paths of received objects
|
||||
/// </summary>
|
||||
/// <param name="areaText">Label to display</param>
|
||||
/// <param name="height">Height of the Drop Area</param>
|
||||
/// <returns>Received paths</returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Creates a filepath textfield with a browse button. Opens the open file panel.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a folder path textfield with a browse button. Opens the save folder panel.
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Display Button with ArrowUI
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toolbar that is filled in from an Enum. Useful for setting tool modes.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates an array foldout like in inspectors
|
||||
/// </summary>
|
||||
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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6be3865b984cad4488a515d4047a1e6c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,254 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Jovian.InspectorTools.EditorTools {
|
||||
public static class MySerializedProperty {
|
||||
#region Collections Handling
|
||||
|
||||
/// <summary>
|
||||
/// Get array of property childs, if parent property is array
|
||||
/// </summary>
|
||||
public static SerializedProperty[] AsArray(this SerializedProperty property) {
|
||||
List<SerializedProperty> items = new List<SerializedProperty>();
|
||||
for(int i = 0; i < property.arraySize; i++)
|
||||
items.Add(property.GetArrayElementAtIndex(i));
|
||||
return items.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get array of property childs casted to specific type
|
||||
/// </summary>
|
||||
public static T[] AsArray<T>(this SerializedProperty property) {
|
||||
var propertiesArray = property.AsArray();
|
||||
return propertiesArray.Select(s => s.objectReferenceValue).OfType<T>().ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get array of property childs, if parent property is array
|
||||
/// </summary>
|
||||
public static IEnumerable<SerializedProperty> AsIEnumerable(this SerializedProperty property) {
|
||||
for(int i = 0; i < property.arraySize; i++)
|
||||
yield return property.GetArrayElementAtIndex(i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace array contents of SerializedProperty with another array
|
||||
/// </summary>
|
||||
public static void ReplaceArray(this SerializedProperty property, Object[] newElements) {
|
||||
property.arraySize = 0;
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
property.arraySize = newElements.Length;
|
||||
for(var i = 0; i < newElements.Length; i++) {
|
||||
property.GetArrayElementAtIndex(i).objectReferenceValue = newElements[i];
|
||||
}
|
||||
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If property is array, insert new element at the end and get it as a property
|
||||
/// </summary>
|
||||
public static SerializedProperty NewElement(this SerializedProperty property) {
|
||||
int newElementIndex = property.arraySize;
|
||||
property.InsertArrayElementAtIndex(newElementIndex);
|
||||
return property.GetArrayElementAtIndex(newElementIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Property is float, int, vector or int vector
|
||||
/// </summary>
|
||||
public static bool IsNumerical(this SerializedProperty property) {
|
||||
var propertyType = property.propertyType;
|
||||
switch(propertyType) {
|
||||
case SerializedPropertyType.Float:
|
||||
case SerializedPropertyType.Integer:
|
||||
case SerializedPropertyType.Vector2:
|
||||
case SerializedPropertyType.Vector3:
|
||||
case SerializedPropertyType.Vector4:
|
||||
case SerializedPropertyType.Vector2Int:
|
||||
case SerializedPropertyType.Vector3Int:
|
||||
return true;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get string representation of serialized property
|
||||
/// </summary>
|
||||
public static string AsStringValue(this SerializedProperty property) {
|
||||
switch(property.propertyType) {
|
||||
case SerializedPropertyType.String:
|
||||
return property.stringValue;
|
||||
|
||||
case SerializedPropertyType.Character:
|
||||
case SerializedPropertyType.Integer:
|
||||
if(property.type == "char") return Convert.ToChar(property.intValue).ToString();
|
||||
return property.intValue.ToString();
|
||||
|
||||
case SerializedPropertyType.ObjectReference:
|
||||
return property.objectReferenceValue != null ? property.objectReferenceValue.ToString() : "null";
|
||||
|
||||
case SerializedPropertyType.Boolean:
|
||||
return property.boolValue.ToString();
|
||||
|
||||
case SerializedPropertyType.Enum:
|
||||
return property.GetValue().ToString();
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property path for collection without ".Array.data[x]" in it
|
||||
/// </summary>
|
||||
public static string GetFixedPropertyPath(this SerializedProperty property) => property.propertyPath.Replace(".Array.data[", "[");
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get FieldInfo out of SerializedProperty
|
||||
/// </summary>
|
||||
public static FieldInfo GetFieldInfo(this SerializedProperty property) {
|
||||
var targetObject = property.serializedObject.targetObject;
|
||||
var targetType = targetObject.GetType();
|
||||
return targetType.GetField(property.propertyPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get raw object value out of the SerializedProperty
|
||||
/// </summary>
|
||||
public static object GetValue(this SerializedProperty property) {
|
||||
if(property == null) return null;
|
||||
|
||||
object obj = property.serializedObject.targetObject;
|
||||
var elements = property.GetFixedPropertyPath().Split('.');
|
||||
foreach(var element in elements) {
|
||||
if(element.Contains("[")) {
|
||||
var elementName = element.Substring(0, element.IndexOf("[", StringComparison.Ordinal));
|
||||
var index = Convert.ToInt32(element.Substring(element.IndexOf("[", StringComparison.Ordinal)).Replace("[", "").Replace("]", ""));
|
||||
obj = GetValueByArrayFieldName(obj, elementName, index);
|
||||
}
|
||||
else obj = GetValueByFieldName(obj, element);
|
||||
}
|
||||
return obj;
|
||||
|
||||
|
||||
object GetValueByArrayFieldName(object source, string name, int index) {
|
||||
if(!(GetValueByFieldName(source, name) is IEnumerable enumerable)) return null;
|
||||
var enumerator = enumerable.GetEnumerator();
|
||||
|
||||
for(var i = 0; i <= index; i++) if(!enumerator.MoveNext()) return null;
|
||||
return enumerator.Current;
|
||||
}
|
||||
|
||||
// Search "source" object for a field with "name" and get it's value
|
||||
object GetValueByFieldName(object source, string name) {
|
||||
if(source == null) return null;
|
||||
var type = source.GetType();
|
||||
|
||||
while(type != null) {
|
||||
var fieldInfo = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
if(fieldInfo != null) return fieldInfo.GetValue(source);
|
||||
|
||||
var propertyInfo = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
if(propertyInfo != null) return propertyInfo.GetValue(source, null);
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set raw object value to the SerializedProperty
|
||||
/// </summary>
|
||||
public static void SetValue(this SerializedProperty property, object value) {
|
||||
GetFieldInfo(property).SetValue(property.serializedObject.targetObject, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is specific attribute defined on SerializedProperty
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static bool IsAttributeDefined<T>(this SerializedProperty property) where T : Attribute {
|
||||
var fieldInfo = property.GetFieldInfo();
|
||||
if(fieldInfo == null) return false;
|
||||
return Attribute.IsDefined(fieldInfo, typeof(T));
|
||||
}
|
||||
|
||||
#region SerializedProperty Get Parent
|
||||
|
||||
// Found here http://answers.unity.com/answers/425602/view.html
|
||||
// Update here https://gist.github.com/AdrienVR/1548a145c039d2fddf030ebc22f915de to support inherited private members.
|
||||
/// <summary>
|
||||
/// Get parent object of SerializedProperty
|
||||
/// </summary>
|
||||
public static object GetParent(this SerializedProperty prop) {
|
||||
var path = prop.propertyPath.Replace(".Array.data[", "[");
|
||||
object obj = prop.serializedObject.targetObject;
|
||||
var elements = path.Split('.');
|
||||
foreach(var element in elements.Take(elements.Length - 1)) {
|
||||
if(element.Contains("[")) {
|
||||
var elementName = element.Substring(0, element.IndexOf("[", StringComparison.Ordinal));
|
||||
var index = Convert.ToInt32(element.Substring(element.IndexOf("[", StringComparison.Ordinal)).Replace("[", "").Replace("]", ""));
|
||||
obj = GetValueAt(obj, elementName, index);
|
||||
}
|
||||
else {
|
||||
obj = GetValue(obj, element);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static object GetValue(object source, string name) {
|
||||
if(source == null)
|
||||
return null;
|
||||
|
||||
foreach(var type in GetHierarchyTypes(source.GetType())) {
|
||||
var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
if(f != null)
|
||||
return f.GetValue(source);
|
||||
|
||||
var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
if(p != null)
|
||||
return p.GetValue(source, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetHierarchyTypes(Type sourceType) {
|
||||
yield return sourceType;
|
||||
while(sourceType.BaseType != null) {
|
||||
yield return sourceType.BaseType;
|
||||
sourceType = sourceType.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
private static object GetValueAt(object source, string name, int index) {
|
||||
var enumerable = GetValue(source, name) as IEnumerable;
|
||||
if(enumerable == null) return null;
|
||||
|
||||
var enm = enumerable.GetEnumerator();
|
||||
while(index-- >= 0)
|
||||
enm.MoveNext();
|
||||
return enm.Current;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1647a9f1d48b4cdcbc9d8c0ed8ec8c5d
|
||||
timeCreated: 1550229037
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Jovian.InspectorTools {
|
||||
public static class MyCollections {
|
||||
/// <summary>
|
||||
/// Returns new array with inserted empty element at index
|
||||
/// </summary>
|
||||
public static T[] InsertAt<T>(this T[] array, int index) {
|
||||
if(index < 0) {
|
||||
Debug.LogError("Index is less than zero. Array is not modified");
|
||||
return array;
|
||||
}
|
||||
|
||||
if(index > array.Length) {
|
||||
Debug.LogError("Index exceeds array length. Array is not modified");
|
||||
return array;
|
||||
}
|
||||
|
||||
T[] newArray = new T[array.Length + 1];
|
||||
int index1 = 0;
|
||||
for(int index2 = 0; index2 < newArray.Length; ++index2) {
|
||||
if(index2 == index) continue;
|
||||
|
||||
newArray[index2] = array[index1];
|
||||
++index1;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
#region IsNullOrEmpty and NotNullOrEmpty
|
||||
|
||||
/// <summary>
|
||||
/// Is array null or empty
|
||||
/// </summary>
|
||||
public static bool IsNullOrEmpty<T>(this T[] collection) => collection == null || collection.Length == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Is list null or empty
|
||||
/// </summary>
|
||||
public static bool IsNullOrEmpty<T>(this IList<T> collection) => collection == null || collection.Count == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Is collection null or empty. IEnumerable is relatively slow. Use Array or List implementation if possible
|
||||
/// </summary>
|
||||
public static bool IsNullOrEmpty<T>(this IEnumerable<T> collection) => collection == null || !collection.Any();
|
||||
|
||||
public static bool NotNullOrEmpty<T>(this IEnumerable<T> collection) => !collection.IsNullOrEmpty();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Performs a function on each element of a collection.
|
||||
/// </summary>
|
||||
public static IEnumerable<T> ForEach<T, R>(this IEnumerable<T> source, Func<T, R> func) {
|
||||
foreach(T element in source) func(element);
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6de18df9fa1208947a40cdd99eaf83f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jovian.InspectorTools
|
||||
{
|
||||
public static class MyRegex
|
||||
{
|
||||
public const string WholeNumber = @"^-?\d+$";
|
||||
public const string FloatingNumber = @"^-?\d*(\.\d+)?$";
|
||||
|
||||
public const string AlphanumericWithoutSpace = @"^[a-zA-Z0-9]*$";
|
||||
public const string AlphanumericWithSpace = @"^[a-zA-Z0-9 ]*$";
|
||||
|
||||
public const string Email = @"^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$";
|
||||
public const string URL = @"(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Replace all but matching parts of the input string
|
||||
/// </summary>
|
||||
public static string KeepMatching(this Regex regex, string input) => regex.Matches(input).Cast<Match>()
|
||||
.Aggregate(string.Empty, (a, m) => a + m.Value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1403a06229e54d928e7366105c447cb7
|
||||
timeCreated: 1623083222
|
||||
Reference in New Issue
Block a user