1.3.0: Added SerializeReferenceTo as a helper for assigning managed type to fields with SerializeReference
This commit is contained in:
parent
ea849a715d
commit
e1d0e0e90b
132
Editor/Drawers/SerializableReferenceToAttributeDrawer.cs
Normal file
132
Editor/Drawers/SerializableReferenceToAttributeDrawer.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Module.Inspector.Editor.Utilities;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Module.Inspector.Editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(SerializableReferenceToAttribute))]
|
||||
internal sealed class SerializableReferenceToAttributeDrawer : DrawerPropertyDrawer
|
||||
{
|
||||
private enum ESupportType
|
||||
{
|
||||
Supported,
|
||||
SpecializeUnityEngineObject,
|
||||
NonSerialized,
|
||||
GenericType,
|
||||
MissingSerializeReference,
|
||||
NotManagedReference
|
||||
}
|
||||
|
||||
public override bool Draw(Rect position, DrawerPropertyAttribute attribute, SerializedProperty property, GUIContent label, EditorPropertyUtility.Result result)
|
||||
{
|
||||
if (IsSupported(property) != ESupportType.Supported)
|
||||
return false;
|
||||
|
||||
var att = (SerializableReferenceToAttribute)attribute;
|
||||
Type type = att.useType ? att.type : property.GetValueType();
|
||||
string[] arrStrings = EditorTypeUtility.GetAssignableFromAsStrings(type);
|
||||
GUIContent[] arrGui = EditorTypeUtility.GetAssignableFromAsGUI(type);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
{
|
||||
var rect = new Rect(position)
|
||||
{
|
||||
height = EditorGUIUtility.singleLineHeight
|
||||
};
|
||||
|
||||
int index = Array.IndexOf(arrStrings, ConvertUnityToCSharp(property.managedReferenceFullTypename));
|
||||
int newIndex = EditorGUI.Popup(rect, label, index, arrGui);
|
||||
|
||||
if (newIndex != -1 && index != newIndex)
|
||||
{
|
||||
Type newType = EditorTypeUtility.GetType(arrStrings[newIndex]);
|
||||
|
||||
if (newType != null && IsSupported(newType))
|
||||
property.managedReferenceValue = FormatterServices.GetUninitializedObject(newType);
|
||||
else
|
||||
EditorUtility.DisplayDialog("Error", "Failed to set managed reference to selected type.\n\nType must be:\nSerializable\nNot abstract\nNon-generic\nNot a specialization of UnityEngine.Object", "Ok");
|
||||
}
|
||||
|
||||
EditorGUI.PropertyField(position, property, true);
|
||||
}
|
||||
EditorGUI.EndProperty();
|
||||
bool hasChanged = EditorGUI.EndChangeCheck();
|
||||
|
||||
if (hasChanged)
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string GetErrorMessage(SerializedProperty property)
|
||||
{
|
||||
ESupportType supportType = IsSupported(property);
|
||||
|
||||
switch (supportType)
|
||||
{
|
||||
case ESupportType.SpecializeUnityEngineObject:
|
||||
return "Must not be a specialization of UnityEngine.Object";
|
||||
case ESupportType.NonSerialized:
|
||||
return "Must be serializable";
|
||||
case ESupportType.GenericType:
|
||||
return "Must not be a generic type";
|
||||
case ESupportType.MissingSerializeReference:
|
||||
return "Missing [SerializeReference]";
|
||||
case ESupportType.NotManagedReference:
|
||||
return "Must be a managed reference type";
|
||||
}
|
||||
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
private static ESupportType IsSupported(SerializedProperty property)
|
||||
{
|
||||
if (property.propertyType != SerializedPropertyType.ManagedReference)
|
||||
return ESupportType.NotManagedReference;
|
||||
|
||||
Type type = property.GetValueType();
|
||||
|
||||
if (!type.IsSerializable && !(type.IsInterface || type.IsAbstract))
|
||||
return ESupportType.NonSerialized;
|
||||
if (typeof(Object).IsAssignableFrom(type))
|
||||
return ESupportType.SpecializeUnityEngineObject;
|
||||
if (type.IsGenericType)
|
||||
return ESupportType.GenericType;
|
||||
|
||||
FieldInfo fi = property.GetFieldInfo();
|
||||
|
||||
if (fi != null && fi.GetCustomAttribute<SerializeReference>() == null)
|
||||
return ESupportType.MissingSerializeReference;
|
||||
|
||||
return ESupportType.Supported;
|
||||
}
|
||||
|
||||
private static bool IsSupported(Type type)
|
||||
{
|
||||
if (!type.IsSerializable)
|
||||
return false;
|
||||
if (typeof(Object).IsAssignableFrom(type))
|
||||
return false;
|
||||
if (type.IsGenericType)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string ConvertUnityToCSharp(string unityNaming)
|
||||
{
|
||||
int index = unityNaming.LastIndexOf(' ');
|
||||
|
||||
if (index != -1)
|
||||
unityNaming = unityNaming.Substring(index);
|
||||
|
||||
unityNaming = unityNaming.Replace('/', '+');
|
||||
return unityNaming.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3d6666b713d84a83b89c84fb2e4fb8ec
|
||||
timeCreated: 1638700710
|
||||
|
|
@ -421,12 +421,18 @@ namespace Module.Inspector.Editor
|
|||
|
||||
foreach (string path in property.propertyPath.Split('.'))
|
||||
{
|
||||
type = obj.GetType();
|
||||
FieldInfo field = type.GetField(path);
|
||||
FieldInfo field = obj.GetType().GetField(path);
|
||||
|
||||
if (field == null)
|
||||
continue;
|
||||
|
||||
obj = field.GetValue(obj);
|
||||
type = field.FieldType;
|
||||
}
|
||||
|
||||
if (type != null && type.IsArray)
|
||||
type = type.GetElementType();
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
|
@ -500,14 +506,14 @@ namespace Module.Inspector.Editor
|
|||
return "Unable to draw value as string";
|
||||
}
|
||||
|
||||
public static int IndexOfProperty(this SerializedProperty sp, SerializedProperty element)
|
||||
public static int IndexOfProperty(this SerializedProperty property, SerializedProperty element)
|
||||
{
|
||||
if (!sp.isArray)
|
||||
if (!property.isArray)
|
||||
return -1;
|
||||
|
||||
for (var i = 0; i < sp.arraySize; i++)
|
||||
for (var i = 0; i < property.arraySize; i++)
|
||||
{
|
||||
SerializedProperty e = sp.GetArrayElementAtIndex(i);
|
||||
SerializedProperty e = property.GetArrayElementAtIndex(i);
|
||||
|
||||
if (e.propertyPath.Equals(element.propertyPath))
|
||||
return i;
|
||||
|
|
@ -515,5 +521,21 @@ namespace Module.Inspector.Editor
|
|||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static FieldInfo GetFieldInfo(this SerializedProperty property)
|
||||
{
|
||||
object obj = property.serializedObject.targetObject;
|
||||
FieldInfo field = null;
|
||||
|
||||
foreach (string path in property.propertyPath.Split('.'))
|
||||
{
|
||||
field = obj.GetType().GetField(path);
|
||||
|
||||
if (field != null)
|
||||
obj = field.GetValue(obj);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ namespace Module.Inspector.Editor
|
|||
private static readonly Dictionary<Type, Type[]> DICT_AS_TYPES = new Dictionary<Type, Type[]>();
|
||||
private static readonly Dictionary<Type, string[]> DICT_AS_STRS = new Dictionary<Type, string[]>();
|
||||
private static readonly Dictionary<Type, string[]> DICT_AS_DESCS = new Dictionary<Type, string[]>();
|
||||
private static readonly Dictionary<Type, GUIContent[]> DICT_AS_GUI = new Dictionary<Type, GUIContent[]>();
|
||||
private static readonly Dictionary<Type, GUIContent[]> DICT_AS_GUI_DESC = new Dictionary<Type, GUIContent[]>();
|
||||
|
||||
private static readonly Dictionary<Type, FieldInfo[]> DICT_AS_FIELDS = new Dictionary<Type, FieldInfo[]>();
|
||||
private static readonly Dictionary<Type, string[]> DICT_FIELDS_AS_STRS = new Dictionary<Type, string[]>();
|
||||
|
|
@ -97,6 +99,14 @@ namespace Module.Inspector.Editor
|
|||
return DICT_AS_STRS[type];
|
||||
}
|
||||
|
||||
internal static GUIContent[] GetAssignableFromAsGUI(Type type)
|
||||
{
|
||||
if (!DICT_AS_GUI.ContainsKey(type))
|
||||
InternalFetch(type);
|
||||
|
||||
return DICT_AS_GUI[type];
|
||||
}
|
||||
|
||||
internal static string[] GetAssignableFromAsDescriptions(Type type)
|
||||
{
|
||||
if (!DICT_AS_DESCS.ContainsKey(type))
|
||||
|
|
@ -105,6 +115,14 @@ namespace Module.Inspector.Editor
|
|||
return DICT_AS_DESCS[type];
|
||||
}
|
||||
|
||||
internal static GUIContent[] GetAssignableFromAsGUIDescriptions(Type type)
|
||||
{
|
||||
if (!DICT_AS_GUI_DESC.ContainsKey(type))
|
||||
InternalFetch(type);
|
||||
|
||||
return DICT_AS_GUI_DESC[type];
|
||||
}
|
||||
|
||||
internal static FieldInfo[] GetFields(Type type)
|
||||
{
|
||||
if (!DICT_AS_FIELDS.ContainsKey(type))
|
||||
|
|
@ -164,16 +182,22 @@ namespace Module.Inspector.Editor
|
|||
listTypes.Sort((t0, t1) => string.Compare(t0.Name, t1.Name, StringComparison.Ordinal));
|
||||
var fullnames = new string[listTypes.Count];
|
||||
var descs = new string[listTypes.Count];
|
||||
var guiFullNames = new GUIContent[listTypes.Count];
|
||||
var guiDescs = new GUIContent[listTypes.Count];
|
||||
|
||||
for (var i = 0; i < fullnames.Length; i++)
|
||||
{
|
||||
fullnames[i] = listTypes[i].FullName;
|
||||
descs[i] = $"{listTypes[i].Name} (<i>{fullnames[i]}</i>)";
|
||||
guiFullNames[i] = new GUIContent(fullnames[i]);
|
||||
guiDescs[i] = new GUIContent(descs[i]);
|
||||
}
|
||||
|
||||
DICT_AS_TYPES.Add(assignableFrom, listTypes.ToArray());
|
||||
DICT_AS_STRS.Add(assignableFrom, fullnames);
|
||||
DICT_AS_DESCS.Add(assignableFrom, descs);
|
||||
DICT_AS_GUI.Add(assignableFrom, guiFullNames);
|
||||
DICT_AS_GUI_DESC.Add(assignableFrom, guiDescs);
|
||||
}
|
||||
|
||||
private static void InternalFetchFields(Type type)
|
||||
|
|
|
|||
|
|
@ -105,6 +105,8 @@ List of all available drawer attributes:
|
|||
* Adds popup with all tag values for field of type string
|
||||
* `UrlGoTo`
|
||||
* Adds a button to the field that calls Application.OpenUrl with string value
|
||||
* `SerializeReferenceTo`
|
||||
* Adds a popup for `SerializeReference` fields with types inheriting from assigned type or field type
|
||||
|
||||
|
||||
## Value
|
||||
|
|
|
|||
22
Runtime/Drawers/SerializableReferenceToAttribute.cs
Normal file
22
Runtime/Drawers/SerializableReferenceToAttribute.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
|
||||
namespace Module.Inspector
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class SerializableReferenceToAttribute : DrawerPropertyAttribute
|
||||
{
|
||||
public readonly bool useType;
|
||||
public readonly Type type;
|
||||
|
||||
public SerializableReferenceToAttribute()
|
||||
{
|
||||
useType = false;
|
||||
}
|
||||
|
||||
public SerializableReferenceToAttribute(Type type)
|
||||
{
|
||||
this.type = type;
|
||||
useType = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Runtime/Drawers/SerializableReferenceToAttribute.cs.meta
Normal file
3
Runtime/Drawers/SerializableReferenceToAttribute.cs.meta
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9480dc4440ae45a0bd31ccbd9ae23977
|
||||
timeCreated: 1638700753
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "com.module.inspector",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0",
|
||||
"displayName": "Module.Inspector",
|
||||
"description": "Custom inspector with various useful property drawers",
|
||||
"unity": "2019.2",
|
||||
|
|
|
|||
Loading…
Reference in a new issue