PropertyDrawers.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. using UnityEditor;
  7. using UnityEngine;
  8. #if UNITY_6000_0_OR_NEWER
  9. using GetDrawerTypeForTypeDelegate = System.Func<System.Type, System.Type[], bool, System.Type>;
  10. #else
  11. using GetDrawerTypeForTypeDelegate = System.Func<System.Type, System.Type>;
  12. #endif
  13. namespace Animancer.Editor
  14. {
  15. /// <summary>[Editor-Only] A cache of <see cref="PropertyDrawer"/>s mapped to their target type.</summary>
  16. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/PropertyDrawers
  17. public static class PropertyDrawers
  18. {
  19. /************************************************************************************************************************/
  20. private const string
  21. ScriptAttributeUtility = "UnityEditor.ScriptAttributeUtility",
  22. FieldFieldName = "m_FieldInfo",
  23. AttributeFieldName = "m_Attribute";
  24. private static readonly GetDrawerTypeForTypeDelegate
  25. GetDrawerTypeForType;
  26. private static readonly FieldInfo
  27. FieldField,
  28. AttributeField;
  29. /************************************************************************************************************************/
  30. static PropertyDrawers()
  31. {
  32. var editorAssembly = typeof(CustomPropertyDrawer).Assembly;
  33. var scriptAttributeUtility = editorAssembly.GetType(ScriptAttributeUtility);
  34. if (scriptAttributeUtility == null)
  35. return;
  36. var getDrawerTypeForType = scriptAttributeUtility.GetMethod(
  37. nameof(GetDrawerTypeForType),
  38. AnimancerReflection.StaticBindings,
  39. null,
  40. #if UNITY_6000_0_OR_NEWER
  41. new Type[] { typeof(Type), typeof(Type[]), typeof(bool) },
  42. #else
  43. new Type[] { typeof(Type) },
  44. #endif
  45. null);
  46. if (getDrawerTypeForType == null)
  47. return;
  48. GetDrawerTypeForType = (GetDrawerTypeForTypeDelegate)Delegate.CreateDelegate(
  49. typeof(GetDrawerTypeForTypeDelegate),
  50. getDrawerTypeForType);
  51. var propertyDrawer = typeof(PropertyDrawer);
  52. FieldField = propertyDrawer.GetField(FieldFieldName, AnimancerReflection.InstanceBindings);
  53. AttributeField = propertyDrawer.GetField(AttributeFieldName, AnimancerReflection.InstanceBindings);
  54. Selection.selectionChanged += OnSelectionChanged;
  55. }
  56. /************************************************************************************************************************/
  57. private static readonly Dictionary<Type, PropertyDrawer>
  58. ObjectTypeToDrawer = new();
  59. private static readonly Dictionary<Type, PropertyDrawer>
  60. DrawerTypeToInstance = new();
  61. /// <summary>Tries to get a <see cref="PropertyDrawer"/> for the given `objectType`.</summary>
  62. public static bool TryGetDrawer(
  63. Type objectType,
  64. FieldInfo field,
  65. Attribute attribute,
  66. out PropertyDrawer drawer)
  67. {
  68. if (GetDrawerTypeForType == null)
  69. {
  70. drawer = null;
  71. return false;
  72. }
  73. if (ObjectTypeToDrawer.TryGetValue(objectType, out drawer))
  74. return true;
  75. Type drawerType;
  76. try
  77. {
  78. #if UNITY_6000_0_OR_NEWER
  79. drawerType = GetDrawerTypeForType(objectType, Type.EmptyTypes, true);
  80. #else
  81. drawerType = GetDrawerTypeForType(objectType);
  82. #endif
  83. }
  84. catch (Exception exception)
  85. {
  86. Debug.LogException(exception);
  87. ObjectTypeToDrawer.Add(objectType, null);
  88. return false;
  89. }
  90. if (DrawerTypeToInstance.TryGetValue(drawerType, out drawer))
  91. {
  92. ObjectTypeToDrawer.Add(objectType, drawer);
  93. return true;
  94. }
  95. try
  96. {
  97. drawer = (PropertyDrawer)Activator.CreateInstance(drawerType);
  98. FieldField?.SetValue(drawer, field);
  99. AttributeField?.SetValue(drawer, attribute);
  100. }
  101. catch (Exception exception)
  102. {
  103. Debug.LogException(exception);
  104. }
  105. ObjectTypeToDrawer.Add(objectType, drawer);
  106. DrawerTypeToInstance.Add(drawerType, drawer);
  107. return drawer != null;
  108. }
  109. /************************************************************************************************************************/
  110. /// <summary>[Editor-Only]
  111. /// Indicates that a cached <see cref="PropertyDrawer"/>
  112. /// should not be kept in the cache after the selection changes.
  113. /// </summary>
  114. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/IDiscardOnSelectionChange
  115. public interface IDiscardOnSelectionChange { }
  116. /************************************************************************************************************************/
  117. /// <summary>Discards any cached <see cref="IDiscardOnSelectionChange"/> drawers.</summary>
  118. private static void OnSelectionChanged()
  119. {
  120. DiscardOnSelectionChanged(ObjectTypeToDrawer);
  121. DiscardOnSelectionChanged(DrawerTypeToInstance);
  122. }
  123. /************************************************************************************************************************/
  124. /// <summary>Discards any cached <see cref="IDiscardOnSelectionChange"/> drawers.</summary>
  125. private static void DiscardOnSelectionChanged(Dictionary<Type, PropertyDrawer> drawers)
  126. {
  127. var discard = ListPool<Type>.Instance.Acquire();
  128. foreach (var drawer in drawers)
  129. if (drawer.Value is IDiscardOnSelectionChange)
  130. discard.Add(drawer.Key);
  131. for (int i = discard.Count - 1; i >= 0; i--)
  132. drawers.Remove(discard[i]);
  133. ListPool<Type>.Instance.Release(discard);
  134. }
  135. /************************************************************************************************************************/
  136. }
  137. }
  138. #endif