added more utilty packges

This commit is contained in:
Sebastian Bularca
2026-03-29 18:59:24 +02:00
parent ee97b2fec3
commit 71b432e253
131 changed files with 7674 additions and 54 deletions

View File

@@ -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 InspectorToolkit.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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6be3865b984cad4488a515d4047a1e6c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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 InspectorToolkit.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

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1647a9f1d48b4cdcbc9d8c0ed8ec8c5d
timeCreated: 1550229037