2025-05-09 15:40:34 +08:00

157 lines
5.6 KiB
C#

// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only]
/// A <see cref="PropertyDrawer"/> for <see cref="IPolymorphic"/> and <see cref="PolymorphicAttribute"/>.
/// </summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/PolymorphicDrawer
[CustomPropertyDrawer(typeof(IPolymorphic), true)]
[CustomPropertyDrawer(typeof(PolymorphicAttribute), true)]
public sealed class PolymorphicDrawer : PropertyDrawer
{
/************************************************************************************************************************/
private bool _DrawerThrewException;
/************************************************************************************************************************/
/// <inheritdoc/>
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var height = 0f;
if (property.propertyType == SerializedPropertyType.ManagedReference)
{
GetDetails(property, out var value, out var drawer, out var details);
if (value == null)
return AnimancerGUI.LineHeight;
if (drawer != null)
{
if (details.SeparateHeader)
{
if (!property.isExpanded)
return AnimancerGUI.LineHeight;
height += AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing;
}
try
{
height += drawer.GetPropertyHeight(property, label);
return height;
}
catch (Exception exception)
{
_DrawerThrewException = true;
Debug.LogException(exception, property.serializedObject.targetObject);
// Continue to the regular calculation.
}
}
}
height += EditorGUI.GetPropertyHeight(property, label, true);
return height;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnGUI(Rect area, SerializedProperty property, GUIContent label)
{
if (property.propertyType != SerializedPropertyType.ManagedReference)
{
EditorGUI.PropertyField(area, property, label, true);
return;
}
GetDetails(property, out _, out var drawer, out var details);
var drawTypeSelectionButton = drawer == null || drawer is not IPolymorphic;
var button = drawTypeSelectionButton
? new TypeSelectionButton(area, property, true)
: default;
if (drawer != null)
{
if (details.SeparateHeader)
{
var foldoutArea = area;
foldoutArea.width = EditorGUIUtility.labelWidth;
foldoutArea.height = AnimancerGUI.LineHeight;
label = EditorGUI.BeginProperty(foldoutArea, label, property);
property.isExpanded = EditorGUI.Foldout(foldoutArea, property.isExpanded, label, true);
EditorGUI.EndProperty();
area.yMin += AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing;
}
// If drawing a separate header, don't draw the body if it's collapsed.
if (!details.SeparateHeader || property.isExpanded)
{
try
{
#pragma warning disable UNT0027 // Do not call PropertyDrawer.OnGUI(). Should only apply to calling base.OnGUI.
drawer.OnGUI(area, property, label);
#pragma warning restore UNT0027 // Do not call PropertyDrawer.OnGUI().
}
catch (Exception exception)
{
_DrawerThrewException = true;
Debug.LogException(exception, property.serializedObject.targetObject);
// Continue to PropertyField.
}
}
}
else
{
EditorGUI.PropertyField(area, property, label, true);
}
if (drawTypeSelectionButton)
button.DoGUI();
}
/************************************************************************************************************************/
private void GetDetails(
SerializedProperty property,
out object value,
out PropertyDrawer drawer,
out PolymorphicDrawerDetails details)
{
value = property.managedReferenceValue;
if (_DrawerThrewException || value == null)
{
drawer = null;
details = PolymorphicDrawerDetails.Default;
return;
}
if (PropertyDrawers.TryGetDrawer(value.GetType(), fieldInfo, attribute, out drawer) &&
drawer is PolymorphicDrawer)
drawer = null;
details = PolymorphicDrawerDetails.Get(value);
}
/************************************************************************************************************************/
}
}
#endif