#if UNITY_2019_3_OR_NEWER 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(SerializeReferenceToAttribute))] internal sealed class SerializeReferenceToAttributeDrawer : 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 = (SerializeReferenceToAttribute)attribute; Type type = att.useType ? att.type : property.GetValueType(); string[] arrStrings = EditorTypeUtility.GetAssignableFromAsStrings(type); GUIContent[] arrGui = EditorTypeUtility.GetAssignableFromAsGUI(type, att.useFullname); EditorGUI.BeginChangeCheck(); EditorGUI.BeginProperty(position, label, property); { var rect = new Rect(position) { height = EditorGUIUtility.singleLineHeight }; int index = Array.IndexOf(arrStrings, ConvertUnityTypeFullnameToCSharpName(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() == 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; return !type.IsGenericType; } private static string ConvertUnityTypeFullnameToCSharpName(string unityNaming) { if (string.IsNullOrEmpty(unityNaming)) return string.Empty; int index = unityNaming.LastIndexOf(' '); string str = index != -1 ? unityNaming.Substring(index).Trim() : unityNaming; return str.Replace('/', '+'); } } } #endif