DelegateGUI.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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 UnityEditor;
  6. using UnityEngine;
  7. namespace Animancer.Editor
  8. {
  9. /// <summary>[Editor-Only] An <see cref="ICustomGUI"/> for <see cref="MulticastDelegate"/>.</summary>
  10. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/DelegateGUI
  11. [CustomGUI(typeof(MulticastDelegate))]
  12. public class DelegateGUI : CustomGUI<MulticastDelegate>
  13. {
  14. /************************************************************************************************************************/
  15. private static readonly HashSet<MulticastDelegate>
  16. ExpandedItems = new();
  17. /************************************************************************************************************************/
  18. /// <summary>Calculates the number of vertical pixels required to draw the specified <see cref="MulticastDelegate"/>.</summary>
  19. public static float CalculateHeight(MulticastDelegate del)
  20. => AnimancerGUI.CalculateHeight(CalculateLineCount(del));
  21. /// <summary>Calculates the number of lines required to draw the specified <see cref="MulticastDelegate"/>.</summary>
  22. public static int CalculateLineCount(MulticastDelegate del)
  23. => del == null || !ExpandedItems.Contains(del)
  24. ? 1
  25. : 1 + CalculateLineCount(AnimancerReflection.GetInvocationList(del));
  26. /// <summary>Calculates the number of lines required to draw the specified `invocationList`.</summary>
  27. public static int CalculateLineCount(Delegate[] invocationList)
  28. => invocationList == null
  29. ? 3
  30. : invocationList.Length * 3;
  31. /************************************************************************************************************************/
  32. /// <inheritdoc/>
  33. public override void DoGUI()
  34. {
  35. var area = AnimancerGUI.LayoutRect(CalculateHeight(Value));
  36. DoGUI(ref area, Label, Value);
  37. }
  38. /// <summary>Draws the GUI for the given delegate.</summary>
  39. public static void DoGUI(
  40. ref Rect area,
  41. GUIContent label,
  42. MulticastDelegate del,
  43. GUIContent valueLabel = null)
  44. {
  45. area.height = AnimancerGUI.LineHeight;
  46. var delegates = AnimancerReflection.GetInvocationList(del);
  47. var isExpanded = del != null && AnimancerGUI.DoHashedFoldoutGUI(area, ExpandedItems, del);
  48. if (valueLabel != null)
  49. {
  50. EditorGUI.LabelField(area, label, valueLabel);
  51. }
  52. else
  53. {
  54. var count = delegates == null ? 0 : delegates.Length;
  55. using (var countLabel = PooledGUIContent.Acquire(count.ToStringCached()))
  56. EditorGUI.LabelField(area, label, countLabel);
  57. }
  58. AnimancerGUI.NextVerticalArea(ref area);
  59. if (!isExpanded)
  60. return;
  61. EditorGUI.indentLevel++;
  62. if (delegates == null)
  63. {
  64. DoSingleGUI(ref area, del);
  65. }
  66. else
  67. {
  68. for (int i = 0; i < delegates.Length; i++)
  69. DoSingleGUI(ref area, delegates[i]);
  70. }
  71. EditorGUI.indentLevel--;
  72. }
  73. /************************************************************************************************************************/
  74. private const int TargetFieldCacheCapacity = 128;
  75. private static readonly Dictionary<object, FastObjectField>
  76. TargetFieldCache = new(TargetFieldCacheCapacity);
  77. /// <summary>Draws the target and name of the specified <see cref="Delegate"/>.</summary>
  78. public static void DoSingleGUI(ref Rect area, Delegate del)
  79. {
  80. area.height = AnimancerGUI.LineHeight;
  81. if (del == null)
  82. {
  83. EditorGUI.LabelField(area, "Delegate", "Null");
  84. AnimancerGUI.NextVerticalArea(ref area);
  85. return;
  86. }
  87. var method = del.Method;
  88. EditorGUI.LabelField(area, "Method", method.ToString());
  89. AnimancerGUI.NextVerticalArea(ref area);
  90. EditorGUI.LabelField(area, "Declaring Type", method.DeclaringType.GetNameCS());
  91. AnimancerGUI.NextVerticalArea(ref area);
  92. var target = del.Target;
  93. FastObjectField field;
  94. if (target is not null)
  95. TargetFieldCache.TryGetValue(target, out field);
  96. else
  97. field = FastObjectField.Null;
  98. field.Draw(area, "Target", target);
  99. if (target is not null)
  100. {
  101. if (TargetFieldCache.Count == TargetFieldCacheCapacity)
  102. TargetFieldCache.Clear();
  103. TargetFieldCache[target] = field;
  104. }
  105. AnimancerGUI.NextVerticalArea(ref area);
  106. }
  107. /************************************************************************************************************************/
  108. }
  109. }
  110. #endif