PolymorphicDrawer.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using UnityEditor;
  5. using UnityEngine;
  6. namespace Animancer.Editor
  7. {
  8. /// <summary>[Editor-Only]
  9. /// A <see cref="PropertyDrawer"/> for <see cref="IPolymorphic"/> and <see cref="PolymorphicAttribute"/>.
  10. /// </summary>
  11. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/PolymorphicDrawer
  12. [CustomPropertyDrawer(typeof(IPolymorphic), true)]
  13. [CustomPropertyDrawer(typeof(PolymorphicAttribute), true)]
  14. public sealed class PolymorphicDrawer : PropertyDrawer
  15. {
  16. /************************************************************************************************************************/
  17. private bool _DrawerThrewException;
  18. /************************************************************************************************************************/
  19. /// <inheritdoc/>
  20. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  21. {
  22. var height = 0f;
  23. if (property.propertyType == SerializedPropertyType.ManagedReference)
  24. {
  25. GetDetails(property, out var value, out var drawer, out var details);
  26. if (value == null)
  27. return AnimancerGUI.LineHeight;
  28. if (drawer != null)
  29. {
  30. if (details.SeparateHeader)
  31. {
  32. if (!property.isExpanded)
  33. return AnimancerGUI.LineHeight;
  34. height += AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing;
  35. }
  36. try
  37. {
  38. height += drawer.GetPropertyHeight(property, label);
  39. return height;
  40. }
  41. catch (Exception exception)
  42. {
  43. _DrawerThrewException = true;
  44. Debug.LogException(exception, property.serializedObject.targetObject);
  45. // Continue to the regular calculation.
  46. }
  47. }
  48. }
  49. height += EditorGUI.GetPropertyHeight(property, label, true);
  50. return height;
  51. }
  52. /************************************************************************************************************************/
  53. /// <inheritdoc/>
  54. public override void OnGUI(Rect area, SerializedProperty property, GUIContent label)
  55. {
  56. if (property.propertyType != SerializedPropertyType.ManagedReference)
  57. {
  58. EditorGUI.PropertyField(area, property, label, true);
  59. return;
  60. }
  61. GetDetails(property, out _, out var drawer, out var details);
  62. var drawTypeSelectionButton = drawer == null || drawer is not IPolymorphic;
  63. var button = drawTypeSelectionButton
  64. ? new TypeSelectionButton(area, property, true)
  65. : default;
  66. if (drawer != null)
  67. {
  68. if (details.SeparateHeader)
  69. {
  70. var foldoutArea = area;
  71. foldoutArea.width = EditorGUIUtility.labelWidth;
  72. foldoutArea.height = AnimancerGUI.LineHeight;
  73. label = EditorGUI.BeginProperty(foldoutArea, label, property);
  74. property.isExpanded = EditorGUI.Foldout(foldoutArea, property.isExpanded, label, true);
  75. EditorGUI.EndProperty();
  76. area.yMin += AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing;
  77. }
  78. // If drawing a separate header, don't draw the body if it's collapsed.
  79. if (!details.SeparateHeader || property.isExpanded)
  80. {
  81. try
  82. {
  83. #pragma warning disable UNT0027 // Do not call PropertyDrawer.OnGUI(). Should only apply to calling base.OnGUI.
  84. drawer.OnGUI(area, property, label);
  85. #pragma warning restore UNT0027 // Do not call PropertyDrawer.OnGUI().
  86. }
  87. catch (Exception exception)
  88. {
  89. _DrawerThrewException = true;
  90. Debug.LogException(exception, property.serializedObject.targetObject);
  91. // Continue to PropertyField.
  92. }
  93. }
  94. }
  95. else
  96. {
  97. EditorGUI.PropertyField(area, property, label, true);
  98. }
  99. if (drawTypeSelectionButton)
  100. button.DoGUI();
  101. }
  102. /************************************************************************************************************************/
  103. private void GetDetails(
  104. SerializedProperty property,
  105. out object value,
  106. out PropertyDrawer drawer,
  107. out PolymorphicDrawerDetails details)
  108. {
  109. value = property.managedReferenceValue;
  110. if (_DrawerThrewException || value == null)
  111. {
  112. drawer = null;
  113. details = PolymorphicDrawerDetails.Default;
  114. return;
  115. }
  116. if (PropertyDrawers.TryGetDrawer(value.GetType(), fieldInfo, attribute, out drawer) &&
  117. drawer is PolymorphicDrawer)
  118. drawer = null;
  119. details = PolymorphicDrawerDetails.Get(value);
  120. }
  121. /************************************************************************************************************************/
  122. }
  123. }
  124. #endif