module-inspector/Editor/Drawers/SerializeReferenceToAttributeDrawer.cs
Anders Ejlersen 7d99d62e11 1.5.0: Added IntToLayerMask, marked QuaternionToEuler as obsolete as of 2021 and fixed a couple of bugs
- Drawer: Added IntToLayerMaskAttribute, if a raw int is wanted instead of LayerMask
- Drawer: Added option to either use full or short type name, when using SerializeReferenceTo
- Extension: Fixed issue, where GetValueType and GetValue for SerializedProperty would throw exceptions, if field is private
2022-06-26 15:06:52 +02:00

131 lines
5 KiB
C#

#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<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;
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