Skip to content

Instantly share code, notes, and snippets.

@LotteMakesStuff
Last active April 28, 2023 09:50
Show Gist options
  • Save LotteMakesStuff/e354cf6e8a4a8194fced3323b15fc1ba to your computer and use it in GitHub Desktop.
Save LotteMakesStuff/e354cf6e8a4a8194fced3323b15fc1ba to your computer and use it in GitHub Desktop.
A reimplementation of the logic in the Unity Editor that draws an inspector for a component. We can use this to learn how unity actually draws the inspector. for more info see https://www.patreon.com/posts/16724128
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;
public static class EditorExtensionMethods
{
// Public reimplmentation of extention methods and helpers that are marked internal in UnityEditor.dll
public static bool IsArrayOrList(this Type listType)
{
return listType.IsArray || (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(List<>));
}
public static Type GetArrayOrListElementType(this Type listType)
{
if (listType.IsArray)
{
return listType.GetElementType();
}
if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(List<>))
{
return listType.GetGenericArguments()[0];
}
return null;
}
}
public class AssemblyHelper
{
internal static Type[] GetTypesFromAssembly(Assembly assembly)
{
if (assembly == null)
{
return new Type[0];
}
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException)
{
return new Type[0];
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
// a custom inspector that forces unity to use our reimplementation of property drawing logic, rather than their own
// this attribute will make this match for anythig based on MononBahviour.
[CustomEditor(typeof(MonoBehaviour), true)]
public class GenericInspector : Editor
{
public override void OnInspectorGUI()
{
#if false // use unitys default inspector logic
DrawDefaultInspector();
#else // use our reimplementation
ReflectedDrawDefaultInspector();
#endif
}
// implemented in Editor.DoDrawDefaultInspector(...)
private void ReflectedDrawDefaultInspector()
{
var obj = this.serializedObject;
EditorGUI.BeginChangeCheck(); //
obj.Update();
SerializedProperty iterator = obj.GetIterator();
bool enterChildren = true; // We *have* to enter children on the first loop. This returns all
// the properties on the component. After that, we do not want children, because the property
// drawer will handle drawing thoes
while (iterator.NextVisible(enterChildren)) // go over each property
{
// inspectors start with a object link to the script file that implements the MonoBehaviour,
// this makes sure that link is drawn disabled
using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath))
{
// actually draw the property!
// this method is actually implemented in EditorGUILayout.PropertyField(...)
PropertyField(iterator, true);
}
enterChildren = false;
}
obj.ApplyModifiedProperties();
EditorGUI.EndChangeCheck();
// to make it clear that this is being drawn by our reimplementation, lets flag it with a lil watermark
using (new EditorGUI.DisabledScope(true))
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.Label("LotteMakesStuff");
GUILayout.EndHorizontal();
}
}
public static bool PropertyField(SerializedProperty property, bool includeChildren, params GUILayoutOption[] options)
{
return PropertyField(property, null, includeChildren, options);
}
public static bool PropertyField(SerializedProperty property, GUIContent label, bool includeChildren, params GUILayoutOption[] options)
{
// get the property handler and then ask it to draw.
return ScriptAttributeUtility.GetHandler(property).OnGUILayout(property, label, includeChildren, options);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEditor;
using ReflectedExtentions;
public class PropertyHandler
{
// handles drawing a single property to the inspector - keeps track of property and decorator drawers
private PropertyDrawer m_PropertyDrawer = null;
private List<DecoratorDrawer> m_DecoratorDrawers = null;
public string tooltip = null;
public List<ContextMenuItemAttribute> contextMenuItems = null;
public bool hasPropertyDrawer
{
get
{
return this.propertyDrawer != null;
}
}
private PropertyDrawer propertyDrawer
{
get
{
return (!this.isCurrentlyNested) ? this.m_PropertyDrawer : null;
}
}
private bool isCurrentlyNested
{
get
{
return this.m_PropertyDrawer != null && ScriptAttributeUtility.s_DrawerStack.Any() && this.m_PropertyDrawer == ScriptAttributeUtility.s_DrawerStack.Peek();
}
}
public bool empty
{
get
{
return this.m_DecoratorDrawers == null && this.tooltip == null && this.propertyDrawer == null && this.contextMenuItems == null;
}
}
public void HandleAttribute(PropertyAttribute attribute, FieldInfo field, Type propertyType)
{
if (attribute is TooltipAttribute)
{
this.tooltip = (attribute as TooltipAttribute).tooltip;
}
else if (attribute is ContextMenuItemAttribute)
{
if (!propertyType.IsArrayOrList())
{
if (this.contextMenuItems == null)
{
this.contextMenuItems = new List<ContextMenuItemAttribute>();
}
this.contextMenuItems.Add(attribute as ContextMenuItemAttribute);
}
}
else
{
this.HandleDrawnType(attribute.GetType(), propertyType, field, attribute);
}
}
public void HandleDrawnType(Type drawnType, Type propertyType, FieldInfo field, PropertyAttribute attribute)
{
Type drawerTypeForType = ScriptAttributeUtility.GetDrawerTypeForType(drawnType);
if (drawerTypeForType != null)
{
if (typeof(PropertyDrawer).IsAssignableFrom(drawerTypeForType))
{
if (propertyType != null && propertyType.IsArrayOrList())
{
return;
}
this.m_PropertyDrawer = (PropertyDrawer)Activator.CreateInstance(drawerTypeForType);
this.m_PropertyDrawer.SetFieldInfo(field);
this.m_PropertyDrawer.SetAttribute(attribute);
}
else if (typeof(DecoratorDrawer).IsAssignableFrom(drawerTypeForType))
{
if (field != null && field.FieldType.IsArrayOrList() && !propertyType.IsArrayOrList())
{
return;
}
DecoratorDrawer decoratorDrawer = (DecoratorDrawer)Activator.CreateInstance(drawerTypeForType);
decoratorDrawer.SetAttribute(attribute);
if (this.m_DecoratorDrawers == null)
{
this.m_DecoratorDrawers = new List<DecoratorDrawer>();
}
this.m_DecoratorDrawers.Add(decoratorDrawer);
}
}
}
public bool OnGUI(Rect position, SerializedProperty property, GUIContent label, bool includeChildren)
{
// save off the ammount of height were supposed to use
float heighRemaining = position.height;
position.height = 0f;
// Do we have any DecoratorDrawers? if so do em
if (this.m_DecoratorDrawers != null && !this.isCurrentlyNested)
{
foreach (DecoratorDrawer decoratorDrawer in this.m_DecoratorDrawers)
{
position.height = decoratorDrawer.GetHeight();
// cache widths incase they change
float labelWidth = EditorGUIUtility.labelWidth;
float fieldWidth = EditorGUIUtility.fieldWidth;
// draw the decorator
decoratorDrawer.OnGUI(position);
// reset widths
EditorGUIUtility.labelWidth = labelWidth;
EditorGUIUtility.fieldWidth = fieldWidth;
// add update position with the space we just took up
position.y += position.height;
heighRemaining -= position.height;
}
}
// plug in the ammount of height left post drawing Decorators
position.height = heighRemaining;
// Do we have a property drawer? if so, use it, otherwise falldown to default drawers
if (this.propertyDrawer != null)
{
float labelWidth = EditorGUIUtility.labelWidth;
float fieldWidth = EditorGUIUtility.fieldWidth;
this.propertyDrawer.OnGUISafe(position, property.Copy(),
label ?? Reflected.EditorGUIUtility.TempContent(property.displayName));
EditorGUIUtility.labelWidth = labelWidth;
EditorGUIUtility.fieldWidth = fieldWidth;
return false;
}
if (!includeChildren)
{
// if were not drawing nested children, fall down into the default drawer and return
return Reflected.EditorGUI.DefaultPropertyField(position, property, label);
}
// save off things that might change
Vector2 iconSize = EditorGUIUtility.GetIconSize();
bool enabled = GUI.enabled;
int indentLevel = EditorGUI.indentLevel;
indentLevel = indentLevel - property.depth;
SerializedProperty serializedProperty = property.Copy(); // copy the itterator so we can advance it without screwing up logic elsewhere
SerializedProperty endProperty = serializedProperty.GetEndProperty();
// draw the root property and remove its height from position, so we can draw the children in the right position
position.height = Reflected.EditorGUI.GetSinglePropertyHeight(serializedProperty, label);
EditorGUI.indentLevel = serializedProperty.depth + indentLevel;
bool enterChildren = Reflected.EditorGUI.DefaultPropertyField(position, serializedProperty, label) && Reflected.EditorGUI.HasVisibleChildFields(serializedProperty);
position.y += position.height + 2f;
// while we have children to jump down in to, get them to draw
while (serializedProperty.NextVisible(enterChildren) && !SerializedProperty.EqualContents(serializedProperty, endProperty))
{
EditorGUI.indentLevel = serializedProperty.depth + indentLevel;
position.height = EditorGUI.GetPropertyHeight(serializedProperty, null, false);
EditorGUI.BeginChangeCheck();
enterChildren = (ScriptAttributeUtility.GetHandler(serializedProperty).OnGUI(position, serializedProperty, null, false) && Reflected.EditorGUI.HasVisibleChildFields(serializedProperty));
if (EditorGUI.EndChangeCheck())
{
break;
}
position.y += position.height + 2f;
}
// reset things that might have changed whilst drawing nested children
GUI.enabled = enabled;
EditorGUIUtility.SetIconSize(iconSize);
EditorGUI.indentLevel = indentLevel;
return false;
}
public bool OnGUILayout(SerializedProperty property, GUIContent label, bool includeChildren, params GUILayoutOption[] options)
{
return this.OnGUI(Reflected.EditorGUILayout.s_LastRect = ((property.propertyType != SerializedPropertyType.Boolean || this.propertyDrawer != null || (this.m_DecoratorDrawers != null && this.m_DecoratorDrawers.Count != 0))
? EditorGUILayout.GetControlRect(Reflected.EditorGUI.LabelHasContent(label), this.GetHeight(property, label, includeChildren), options)
: Reflected.EditorGUILayout.GetToggleRect(true, options)), property, label, includeChildren); // reflected options param pass might not work?
}
// Measure this properties height
public float GetHeight(SerializedProperty property, GUIContent label, bool includeChildren)
{
float height = 0f;
if (this.m_DecoratorDrawers != null && !this.isCurrentlyNested)
{
// add up the heigh of all attached decorators
foreach (DecoratorDrawer decoratorDrawer in this.m_DecoratorDrawers)
{
height += decoratorDrawer.GetHeight();
}
}
if (this.propertyDrawer != null)
{
// if this is drawn by a Property drawer, ask that for its height
height += this.propertyDrawer.GetPropertyHeightSafe(property.Copy(), label ?? Reflected.EditorGUIUtility.TempContent(property.displayName));
}
else if (!includeChildren)
{
// otherwise, it must use the default drawing logic. If it dosnt have children, lets ask it for its single height
height += Reflected.EditorGUI.GetSinglePropertyHeight(property, label);
}
else
{
// otherwise we need to drop down and ask each child how big it is and add that to height
property = property.Copy(); // copy the itterator so we can advance it without screwing up logic elsewhere
SerializedProperty endProperty = property.GetEndProperty();
height += Reflected.EditorGUI.GetSinglePropertyHeight(property, label);
bool enterChildren = property.isExpanded && Reflected.EditorGUI.HasVisibleChildFields(property);
while (property.NextVisible(enterChildren) && !SerializedProperty.EqualContents(property, endProperty))
{
height += ScriptAttributeUtility.GetHandler(property).GetHeight(property, Reflected.EditorGUIUtility.TempContent(property.displayName), true);
enterChildren = false;
height += 2f;
}
}
return height;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using ReflectedExtentions;
// clone of an internal Unity class that does stuff like keep track of PropertyHandlers and finding Attributes the inspector uses for drawing
internal class ScriptAttributeUtility
{
private struct DrawerKeySet
{
public Type drawer;
public Type type;
}
internal static Stack<PropertyDrawer> s_DrawerStack = new Stack<PropertyDrawer>();
private static Dictionary<Type, DrawerKeySet> s_DrawerTypeForType = null;
private static Dictionary<string, List<PropertyAttribute>> s_BuiltinAttributes = null;
private static PropertyHandler s_SharedNullHandler = new PropertyHandler();
private static PropertyHandler s_NextHandler = new PropertyHandler();
private static PropertyHandlerCache s_GlobalCache = new PropertyHandlerCache();
private static PropertyHandlerCache s_CurrentCache = null;
internal static PropertyHandlerCache propertyHandlerCache
{
get
{
return s_CurrentCache ?? s_GlobalCache;
}
set
{
s_CurrentCache = value;
}
}
internal static void ClearGlobalCache() // TODO in Unity this is called by InspectorWindow.OnSelectionChange(), need to trigger this outselves from some selection change event
{
s_GlobalCache.Clear();
}
private static void PopulateBuiltinAttributes()
{
s_BuiltinAttributes = new Dictionary<string, List<PropertyAttribute>>();
AddBuiltinAttribute("GUIText", "m_Text", new MultilineAttribute());
AddBuiltinAttribute("TextMesh", "m_Text", new MultilineAttribute());
}
private static void AddBuiltinAttribute(string componentTypeName, string propertyPath, PropertyAttribute attr)
{
string key = componentTypeName + "_" + propertyPath;
if (!s_BuiltinAttributes.ContainsKey(key))
{
s_BuiltinAttributes.Add(key, new List<PropertyAttribute>());
}
s_BuiltinAttributes[key].Add(attr);
}
private static List<PropertyAttribute> GetBuiltinAttributes(SerializedProperty property)
{
if (property.serializedObject.targetObject == null)
{
return null;
}
Type type = property.serializedObject.targetObject.GetType();
if (type == null)
{
return null;
}
string key = type.Name + "_" + property.propertyPath;
List<PropertyAttribute> result = null;
s_BuiltinAttributes.TryGetValue(key, out result);
return result;
}
private static void BuildDrawerTypeForTypeDictionary()
{
ScriptAttributeUtility.s_DrawerTypeForType = new Dictionary<Type, DrawerKeySet>();
Type[] source = AppDomain.CurrentDomain.GetAssemblies().SelectMany((Assembly x) => AssemblyHelper.GetTypesFromAssembly(x)).ToArray();
foreach (Type item in Reflected.EditorAssemblies.SubclassesOf(typeof(GUIDrawer)))
{
object[] customAttributes = item.GetCustomAttributes(typeof(CustomPropertyDrawer), true);
object[] array = customAttributes;
for (int i = 0; i < array.Length; i++)
{
CustomPropertyDrawer editor = (CustomPropertyDrawer)array[i];
//ScriptAttributeUtility.s_DrawerTypeForType[editor.m_Type] = new DrawerKeySet
s_DrawerTypeForType[editor.GetHiddenType()] = new DrawerKeySet
{
drawer = item,
type = editor.GetHiddenType() /*editor.m_Type*/
};
if (editor.GetUseForChildren() /*editor.m_UseForChildren*/)
{
IEnumerable<Type> enumerable = from x in source
where x.IsSubclassOf(editor.GetHiddenType() /*editor.m_Type*/)
select x;
foreach (Type item2 in enumerable)
{
if (ScriptAttributeUtility.s_DrawerTypeForType.ContainsKey(item2))
{
Type type = editor.GetHiddenType(); //editor.m_Type;
DrawerKeySet drawerKeySet = ScriptAttributeUtility.s_DrawerTypeForType[item2];
if (!type.IsAssignableFrom(drawerKeySet.type))
{
goto IL_0158;
}
continue;
}
goto IL_0158;
IL_0158:
ScriptAttributeUtility.s_DrawerTypeForType[item2] = new DrawerKeySet
{
drawer = item,
type = editor.GetHiddenType() //editor.m_Type;
};
}
}
}
}
}
internal static Type GetDrawerTypeForType(Type type)
{
if (ScriptAttributeUtility.s_DrawerTypeForType == null)
{
ScriptAttributeUtility.BuildDrawerTypeForTypeDictionary();
}
DrawerKeySet drawerKeySet = default(DrawerKeySet);
ScriptAttributeUtility.s_DrawerTypeForType.TryGetValue(type, out drawerKeySet);
if (drawerKeySet.drawer != null)
{
return drawerKeySet.drawer;
}
if (type.IsGenericType)
{
ScriptAttributeUtility.s_DrawerTypeForType.TryGetValue(type.GetGenericTypeDefinition(), out drawerKeySet);
}
return drawerKeySet.drawer;
}
private static List<PropertyAttribute> GetFieldAttributes(FieldInfo field)
{
if (field == null)
{
return null;
}
object[] customAttributes = field.GetCustomAttributes(typeof(PropertyAttribute), true);
if (customAttributes != null && customAttributes.Length > 0)
{
return new List<PropertyAttribute>(from e in customAttributes
select e as PropertyAttribute into e
orderby -e.order
select e);
}
return null;
}
private static FieldInfo GetFieldInfoFromProperty(SerializedProperty property, out Type type)
{
Type scriptTypeFromProperty = ScriptAttributeUtility.GetScriptTypeFromProperty(property);
if (scriptTypeFromProperty == null)
{
type = null;
return null;
}
return ScriptAttributeUtility.GetFieldInfoFromPropertyPath(scriptTypeFromProperty, property.propertyPath, out type);
}
private static Type GetScriptTypeFromProperty(SerializedProperty property)
{
SerializedProperty serializedProperty = property.serializedObject.FindProperty("m_Script");
if (serializedProperty == null)
{
return null;
}
MonoScript monoScript = serializedProperty.objectReferenceValue as MonoScript;
if ((UnityEngine.Object)monoScript == (UnityEngine.Object)null)
{
return null;
}
return monoScript.GetClass();
}
private static FieldInfo GetFieldInfoFromPropertyPath(Type host, string path, out Type type)
{
FieldInfo fieldInfo = null;
type = host;
string[] array = path.Split('.');
for (int i = 0; i < array.Length; i++)
{
string text = array[i];
if (i < array.Length - 1 && text == "Array" && array[i + 1].StartsWith("data["))
{
if (type.IsArrayOrList())
{
type = type.GetArrayOrListElementType();
}
i++;
}
else
{
FieldInfo fieldInfo2 = null;
Type type2 = type;
while (fieldInfo2 == null && type2 != null)
{
fieldInfo2 = type2.GetField(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
type2 = type2.BaseType;
}
if (fieldInfo2 == null)
{
type = null;
return null;
}
fieldInfo = fieldInfo2;
type = fieldInfo.FieldType;
}
}
return fieldInfo;
}
internal static PropertyHandler GetHandler(SerializedProperty property)
{
if (property == null)
{
return ScriptAttributeUtility.s_SharedNullHandler;
}
if (property.serializedObject.GetInspectorMode() != 0)
{
return ScriptAttributeUtility.s_SharedNullHandler;
}
PropertyHandler handler = ScriptAttributeUtility.propertyHandlerCache.GetHandler(property);
if (handler != null)
{
return handler;
}
Type type = null;
List<PropertyAttribute> list = null;
FieldInfo field = null;
UnityEngine.Object targetObject = property.serializedObject.targetObject;
if (targetObject is MonoBehaviour || targetObject is ScriptableObject)
{
field = ScriptAttributeUtility.GetFieldInfoFromProperty(property, out type);
list = ScriptAttributeUtility.GetFieldAttributes(field);
}
else
{
if (ScriptAttributeUtility.s_BuiltinAttributes == null)
{
ScriptAttributeUtility.PopulateBuiltinAttributes();
}
if (list == null)
{
list = ScriptAttributeUtility.GetBuiltinAttributes(property);
}
}
handler = ScriptAttributeUtility.s_NextHandler;
if (list != null)
{
for (int num = list.Count - 1; num >= 0; num--)
{
handler.HandleAttribute(list[num], field, type);
}
}
if (!handler.hasPropertyDrawer && type != null)
{
handler.HandleDrawnType(type, type, field, null);
}
if (handler.empty)
{
ScriptAttributeUtility.propertyHandlerCache.SetHandler(property, ScriptAttributeUtility.s_SharedNullHandler);
handler = ScriptAttributeUtility.s_SharedNullHandler;
}
else
{
ScriptAttributeUtility.propertyHandlerCache.SetHandler(property, handler);
ScriptAttributeUtility.s_NextHandler = new PropertyHandler();
}
return handler;
}
}
// stores PropertyHandlers for drawing properties in a dictionary against property hashes
internal class PropertyHandlerCache
{
protected Dictionary<int, PropertyHandler> m_PropertyHandlers = new Dictionary<int, PropertyHandler>();
internal PropertyHandler GetHandler(SerializedProperty property)
{
int propertyHash = GetPropertyHash(property);
PropertyHandler result = null;
if (this.m_PropertyHandlers.TryGetValue(propertyHash, out result))
{
return result;
}
return null;
}
internal void SetHandler(SerializedProperty property, PropertyHandler handler)
{
int propertyHash = GetPropertyHash(property);
m_PropertyHandlers[propertyHash] = handler;
}
private static int GetPropertyHash(SerializedProperty property)
{
if (property.serializedObject.targetObject == null)
{
return 0;
}
int num = property.serializedObject.targetObject.GetInstanceID() ^ property.GetHashCodeForPropertyPathWithoutArrayIndex(); /*property.hashCodeForPropertyPathWithoutArrayIndex;*/
if (property.propertyType == SerializedPropertyType.ObjectReference)
{
num ^= property.objectReferenceInstanceIDValue;
}
return num;
}
public void Clear()
{
this.m_PropertyHandlers.Clear();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEditor;
using Reflected;
namespace Reflected
{
// provides easy access to a bunch of APIs that are marked as internal in the UnityEditor. we wimply copy the method signiture
// and use reflection to get access to stuff were not supposed to
public static class EditorGUIUtility
{
internal static GUIContent TempContent(string t)
{
var bindflags = BindingFlags.NonPublic | BindingFlags.Static;
var method = typeof(UnityEditor.EditorGUIUtility).GetMethod("TempContent", bindflags, null, new[] { typeof(string) }, null );
return (GUIContent)method.Invoke(null, new [] { t });
}
}
public static class EditorGUILayout
{
public static Rect s_LastRect
{
get
{
return ReflectionHelper.GetField<Rect>("s_LastRect", typeof(UnityEditor.EditorGUILayout));
}
set
{
ReflectionHelper.SetField<Rect>("s_LastRect", typeof(UnityEditor.EditorGUILayout), value);
}
}
internal static Rect GetToggleRect(bool hasLabel, params GUILayoutOption[] options)
{
return (Rect)ReflectionHelper.GetReflectedMethod("GetToggleRect", typeof(UnityEditor.EditorGUILayout)).Invoke(null, new object[] { hasLabel, options });
}
}
public static class EditorGUI
{
public static bool LabelHasContent(GUIContent label)
{
if (label == null)
{
return true;
}
return label.text != string.Empty || label.image != null;
}
public static float GetSinglePropertyHeight(SerializedProperty property, GUIContent label)
{
return (float)ReflectionHelper.GetReflectedMethod("GetSinglePropertyHeight", typeof(UnityEditor.EditorGUI)).Invoke(null, new object[] { property, label });
}
internal static bool HasVisibleChildFields(SerializedProperty property)
{
return (bool)ReflectionHelper.GetReflectedMethod("HasVisibleChildFields", typeof(UnityEditor.EditorGUI)).Invoke(null, new object[] { property });
}
internal static bool DefaultPropertyField(Rect position, SerializedProperty property, GUIContent label)
{
return (bool)ReflectionHelper.GetReflectedMethod("DefaultPropertyField", typeof(UnityEditor.EditorGUI)).Invoke(null, new object[] { position, property, label });
}
}
public class EditorAssemblies
{
internal static IEnumerable<Type> SubclassesOf(Type parent)
{
Type hiddenType = ReflectionHelper.GetPrivateType("UnityEditor.EditorAssemblies", typeof(CustomEditor));
return (IEnumerable<Type>)ReflectionHelper.GetReflectedMethod("SubclassesOf", hiddenType).Invoke(null, new object[] { parent });
}
}
public static class ReflectionHelper
{
public static Type GetPrivateType(string name, Type source)
{
var assembly = source.Assembly;
return assembly.GetType(name);
}
public static Type GetPrivateType(string fqName)
{
return Type.GetType(fqName);
}
public static T GetField<T>(string name, Type type, bool isStatic = true, object instance = null)
{
var bindflags = isStatic ? (BindingFlags.NonPublic | BindingFlags.Static) : (BindingFlags.NonPublic | BindingFlags.Instance);
var field = type.GetField(name, bindflags);
return (T)field.GetValue(instance);
}
public static void SetField<T>(string name, Type type, T value, bool isStatic = true, object instantce = null)
{
var bindflags = isStatic ? (BindingFlags.NonPublic | BindingFlags.Static) : (BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
var field = type.GetField(name, bindflags);
if (instantce != null)
{
field = instantce.GetType().GetField(name, bindflags);
}
field.SetValue(instantce, value);
}
public static T GetProperty<T>(string name, Type type, object instance)
{
var bindflags = BindingFlags.NonPublic | BindingFlags.Instance;
var propInfo = type.GetProperty(name, bindflags);
MethodInfo getAccessor = propInfo.GetGetMethod(true);
return (T)getAccessor.Invoke(instance, null);
}
public static MethodInfo GetReflectedMethod(string name, Type type, bool isStatic = true, object instantce = null)
{
var bindflags = isStatic ? (BindingFlags.NonPublic | BindingFlags.Static) : (BindingFlags.NonPublic | BindingFlags.Instance);
var method = type.GetMethod(name, bindflags);
return method;
}
}
}
namespace ReflectedExtentions
{
public static class PropertyDrawerExtention
{
public static float GetPropertyHeightSafe(this PropertyDrawer drawer, SerializedProperty property, GUIContent label)
{
return (float)ReflectionHelper.GetReflectedMethod("GetPropertyHeightSafe", typeof(UnityEditor.PropertyDrawer), false, drawer).Invoke(drawer, new object[] { property, label });
}
public static void OnGUISafe(this PropertyDrawer drawer, Rect position, SerializedProperty property, GUIContent label)
{
ReflectionHelper.GetReflectedMethod("OnGUISafe", typeof(UnityEditor.PropertyDrawer), false, drawer).Invoke(drawer, new object[] { position, property, label });
}
public static void SetFieldInfo(this PropertyDrawer drawer, FieldInfo info)
{
ReflectionHelper.SetField("m_FieldInfo", typeof(PropertyDrawer), info, false, drawer);
}
public static void SetAttribute(this PropertyDrawer drawer, PropertyAttribute attrib)
{
ReflectionHelper.SetField("m_Attribute", typeof(PropertyDrawer), attrib, false, drawer);
}
}
public static class DecoratorDrawerExtention
{
public static void SetAttribute(this DecoratorDrawer drawer, PropertyAttribute attrib)
{
ReflectionHelper.SetField("m_Attribute", typeof(DecoratorDrawer), attrib, false, drawer);
}
}
public static class CustomPropertyDrawerExtentions
{
public static Type GetHiddenType(this CustomPropertyDrawer prop)
{
return ReflectionHelper.GetField<Type>("m_Type", typeof(CustomPropertyDrawer), false, prop);
}
public static bool GetUseForChildren(this CustomPropertyDrawer prop)
{
return ReflectionHelper.GetField<bool>("m_UseForChildren", typeof(CustomPropertyDrawer), false, prop);
}
}
public static class SerializedPropertyExtentions
{
public static int GetHashCodeForPropertyPathWithoutArrayIndex(this SerializedProperty prop)
{
return ReflectionHelper.GetProperty<int>("hashCodeForPropertyPathWithoutArrayIndex", typeof(SerializedProperty), prop);
}
}
public static class SerializedObjectExtentions
{
public static int GetInspectorMode(this SerializedObject prop)
{
//return ReflectionHelper.GetField<int>("inspectorMode", typeof(SerializedObject), false, prop);
return ReflectionHelper.GetProperty<int>("inspectorMode", typeof(SerializedObject), prop);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment