EventSequenceDrawer.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using System.Runtime.CompilerServices;
  5. using UnityEditor;
  6. using UnityEngine;
  7. using Sequence = Animancer.AnimancerEvent.Sequence;
  8. using Object = UnityEngine.Object;
  9. namespace Animancer.Editor
  10. {
  11. /// <summary>[Editor-Only] Draws the Inspector GUI for a <see cref="Sequence"/>.</summary>
  12. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/EventSequenceDrawer
  13. ///
  14. public class EventSequenceDrawer
  15. {
  16. /************************************************************************************************************************/
  17. private static readonly ConditionalWeakTable<Sequence, EventSequenceDrawer>
  18. SequenceToDrawer = new ConditionalWeakTable<Sequence, EventSequenceDrawer>();
  19. /// <summary>Returns a cached <see cref="EventSequenceDrawer"/> for the `events`.</summary>
  20. /// <remarks>
  21. /// The cache uses a <see cref="ConditionalWeakTable{TKey, TValue}"/> so it doesn't prevent the `events`
  22. /// from being garbage collected.
  23. /// </remarks>
  24. public static EventSequenceDrawer Get(Sequence events)
  25. {
  26. if (events == null)
  27. return null;
  28. if (!SequenceToDrawer.TryGetValue(events, out var drawer))
  29. SequenceToDrawer.Add(events, drawer = new EventSequenceDrawer());
  30. return drawer;
  31. }
  32. /************************************************************************************************************************/
  33. /// <summary>
  34. /// Calculates the number of vertical pixels required to draw the specified `lineCount` using the
  35. /// <see cref="AnimancerGUI.LineHeight"/> and <see cref="AnimancerGUI.StandardSpacing"/>.
  36. /// </summary>
  37. public static float CalculateHeight(int lineCount)
  38. => lineCount == 0 ? 0 :
  39. AnimancerGUI.LineHeight * lineCount +
  40. AnimancerGUI.StandardSpacing * (lineCount - 1);
  41. /************************************************************************************************************************/
  42. /// <summary>Calculates the number of vertical pixels required to draw the contents of the `events`.</summary>
  43. public float CalculateHeight(Sequence events)
  44. => CalculateHeight(CalculateLineCount(events));
  45. /// <summary>Calculates the number of lines required to draw the contents of the `events`.</summary>
  46. public int CalculateLineCount(Sequence events)
  47. {
  48. if (events == null)
  49. return 0;
  50. if (!_IsExpanded)
  51. return 1;
  52. var count = 1;
  53. for (int i = 0; i < events.Count; i++)
  54. {
  55. count++;
  56. count += CalculateLineCount(events[i].callback);
  57. }
  58. count++;
  59. count += CalculateLineCount(events.EndEvent.callback);
  60. return count;
  61. }
  62. /************************************************************************************************************************/
  63. private bool _IsExpanded;
  64. private static ConversionCache<int, string> _EventNumberCache;
  65. private static float _LogButtonWidth = float.NaN;
  66. /************************************************************************************************************************/
  67. /// <summary>Draws the GUI for the `events`.</summary>
  68. public void Draw(ref Rect area, Sequence events, GUIContent label)
  69. {
  70. if (events == null)
  71. return;
  72. area.height = AnimancerGUI.LineHeight;
  73. var headerArea = area;
  74. const string LogLabel = "Log";
  75. if (float.IsNaN(_LogButtonWidth))
  76. _LogButtonWidth = EditorStyles.miniButton.CalculateWidth(LogLabel);
  77. var logArea = AnimancerGUI.StealFromRight(ref headerArea, _LogButtonWidth);
  78. if (GUI.Button(logArea, LogLabel, EditorStyles.miniButton))
  79. Debug.Log(events.DeepToString());
  80. _IsExpanded = EditorGUI.Foldout(headerArea, _IsExpanded, GUIContent.none, true);
  81. using (ObjectPool.Disposable.AcquireContent(out var summary, GetSummary(events)))
  82. EditorGUI.LabelField(headerArea, label, summary);
  83. AnimancerGUI.NextVerticalArea(ref area);
  84. if (!_IsExpanded)
  85. return;
  86. var enabled = GUI.enabled;
  87. GUI.enabled = false;
  88. EditorGUI.indentLevel++;
  89. for (int i = 0; i < events.Count; i++)
  90. {
  91. var name = events.GetName(i);
  92. if (string.IsNullOrEmpty(name))
  93. {
  94. if (_EventNumberCache == null)
  95. _EventNumberCache = new ConversionCache<int, string>((index) => $"Event {index}");
  96. name = _EventNumberCache.Convert(i);
  97. }
  98. Draw(ref area, name, events[i]);
  99. }
  100. Draw(ref area, "End Event", events.EndEvent);
  101. EditorGUI.indentLevel--;
  102. GUI.enabled = enabled;
  103. }
  104. /************************************************************************************************************************/
  105. private static readonly ConversionCache<int, string>
  106. SummaryCache = new ConversionCache<int, string>((count) => $"[{count}]"),
  107. EndSummaryCache = new ConversionCache<int, string>((count) => $"[{count}] + End");
  108. /// <summary>Returns a summary of the `events`.</summary>
  109. public static string GetSummary(Sequence events)
  110. {
  111. var cache =
  112. float.IsNaN(events.NormalizedEndTime) &&
  113. AnimancerEvent.IsNullOrDummy(events.OnEnd)
  114. ? SummaryCache : EndSummaryCache;
  115. return cache.Convert(events.Count);
  116. }
  117. /************************************************************************************************************************/
  118. private static ConversionCache<float, string> _EventTimeCache;
  119. /// <summary>Draws the GUI for the `animancerEvent`.</summary>
  120. public static void Draw(ref Rect area, string name, AnimancerEvent animancerEvent)
  121. {
  122. area.height = AnimancerGUI.LineHeight;
  123. if (_EventTimeCache == null)
  124. _EventTimeCache = new ConversionCache<float, string>((time)
  125. => float.IsNaN(time) ? "Time = Auto" : $"Time = {time.ToStringCached()}x");
  126. EditorGUI.LabelField(area, name, _EventTimeCache.Convert(animancerEvent.normalizedTime));
  127. AnimancerGUI.NextVerticalArea(ref area);
  128. EditorGUI.indentLevel++;
  129. DrawInvocationList(ref area, animancerEvent.callback);
  130. EditorGUI.indentLevel--;
  131. }
  132. /************************************************************************************************************************/
  133. /// <summary>Calculates the number of vertical pixels required to draw the specified <see cref="Delegate"/>.</summary>
  134. public static float CalculateHeight(MulticastDelegate del)
  135. => CalculateHeight(CalculateLineCount(del));
  136. /// <summary>Calculates the number of lines required to draw the specified <see cref="Delegate"/>.</summary>
  137. public static int CalculateLineCount(MulticastDelegate del)
  138. {
  139. if (del == null)
  140. return 1;
  141. var delegates = GetInvocationListIfMulticast(del);
  142. return delegates == null ? 2 : delegates.Length * 2;
  143. }
  144. /************************************************************************************************************************/
  145. /// <summary>Draws the target and name of the specified <see cref="Delegate"/>.</summary>
  146. public static void DrawInvocationList(ref Rect area, MulticastDelegate del)
  147. {
  148. if (del == null)
  149. {
  150. EditorGUI.LabelField(area, "Delegate", "Null");
  151. AnimancerGUI.NextVerticalArea(ref area);
  152. return;
  153. }
  154. var delegates = GetInvocationListIfMulticast(del);
  155. if (delegates == null)
  156. {
  157. Draw(ref area, del);
  158. }
  159. else
  160. {
  161. for (int i = 0; i < delegates.Length; i++)
  162. Draw(ref area, delegates[i]);
  163. }
  164. }
  165. /************************************************************************************************************************/
  166. private static Delegate[] GetInvocationListIfMulticast(MulticastDelegate del)
  167. => AnimancerUtilities.TryGetInvocationListNonAlloc(del, out var delegates) ? delegates : del.GetInvocationList();
  168. /************************************************************************************************************************/
  169. /// <summary>Draws the target and name of the specified <see cref="Delegate"/>.</summary>
  170. public static void Draw(ref Rect area, Delegate del)
  171. {
  172. area.height = AnimancerGUI.LineHeight;
  173. if (del == null)
  174. {
  175. EditorGUI.LabelField(area, "Callback", "Null");
  176. AnimancerGUI.NextVerticalArea(ref area);
  177. return;
  178. }
  179. var method = del.Method;
  180. EditorGUI.LabelField(area, "Method", method.Name);
  181. AnimancerGUI.NextVerticalArea(ref area);
  182. var target = del.Target;
  183. if (target is Object obj)
  184. {
  185. var enabled = GUI.enabled;
  186. GUI.enabled = false;
  187. EditorGUI.ObjectField(area, "Target", obj, obj.GetType(), true);
  188. GUI.enabled = enabled;
  189. }
  190. else if (target != null)
  191. {
  192. EditorGUI.LabelField(area, "Target", target.ToString());
  193. }
  194. else
  195. {
  196. EditorGUI.LabelField(area, "Declaring Type", method.DeclaringType.GetNameCS());
  197. }
  198. AnimancerGUI.NextVerticalArea(ref area);
  199. }
  200. /************************************************************************************************************************/
  201. }
  202. }
  203. #endif