module-inspector/Editor/Drawers/SerializableReferenceToAttributeDrawer.cs

132 lines
4.9 KiB
C#

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();
}
}
}