MaskUtilities.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System.Collections.Generic;
  2. using UnityEngine.EventSystems;
  3. namespace UnityEngine.UI
  4. {
  5. /// <summary>
  6. /// Mask related utility class. This class provides masking-specific utility functions.
  7. /// </summary>
  8. public class MaskUtilities
  9. {
  10. /// <summary>
  11. /// Notify all IClippables under the given component that they need to recalculate clipping.
  12. /// </summary>
  13. /// <param name="mask">The object thats changed for whose children should be notified.</param>
  14. public static void Notify2DMaskStateChanged(Component mask)
  15. {
  16. var components = ListPool<Component>.Get();
  17. mask.GetComponentsInChildren(components);
  18. for (var i = 0; i < components.Count; i++)
  19. {
  20. if (components[i] == null || components[i].gameObject == mask.gameObject)
  21. continue;
  22. var toNotify = components[i] as IClippable;
  23. if (toNotify != null)
  24. toNotify.RecalculateClipping();
  25. }
  26. ListPool<Component>.Release(components);
  27. }
  28. /// <summary>
  29. /// Notify all IMaskable under the given component that they need to recalculate masking.
  30. /// </summary>
  31. /// <param name="mask">The object thats changed for whose children should be notified.</param>
  32. public static void NotifyStencilStateChanged(Component mask)
  33. {
  34. var components = ListPool<Component>.Get();
  35. mask.GetComponentsInChildren(components);
  36. for (var i = 0; i < components.Count; i++)
  37. {
  38. if (components[i] == null || components[i].gameObject == mask.gameObject)
  39. continue;
  40. var toNotify = components[i] as IMaskable;
  41. if (toNotify != null)
  42. toNotify.RecalculateMasking();
  43. }
  44. ListPool<Component>.Release(components);
  45. }
  46. /// <summary>
  47. /// Find a root Canvas.
  48. /// </summary>
  49. /// <param name="start">Transform to start the search at going up the hierarchy.</param>
  50. /// <returns>Finds either the most root canvas, or the first canvas that overrides sorting.</returns>
  51. public static Transform FindRootSortOverrideCanvas(Transform start)
  52. {
  53. var canvasList = ListPool<Canvas>.Get();
  54. start.GetComponentsInParent(false, canvasList);
  55. Canvas canvas = null;
  56. for (int i = 0; i < canvasList.Count; ++i)
  57. {
  58. canvas = canvasList[i];
  59. // We found the canvas we want to use break
  60. if (canvas.overrideSorting)
  61. break;
  62. }
  63. ListPool<Canvas>.Release(canvasList);
  64. return canvas != null ? canvas.transform : null;
  65. }
  66. /// <summary>
  67. /// Find the stencil depth for a given element.
  68. /// </summary>
  69. /// <param name="transform">The starting transform to search.</param>
  70. /// <param name="stopAfter">Where the search of parents should stop</param>
  71. /// <returns>What the proper stencil buffer index should be.</returns>
  72. public static int GetStencilDepth(Transform transform, Transform stopAfter)
  73. {
  74. var depth = 0;
  75. if (transform == stopAfter)
  76. return depth;
  77. var t = transform.parent;
  78. var components = ListPool<Mask>.Get();
  79. while (t != null)
  80. {
  81. t.GetComponents<Mask>(components);
  82. for (var i = 0; i < components.Count; ++i)
  83. {
  84. if (components[i] != null && components[i].MaskEnabled() && components[i].graphic.IsActive())
  85. {
  86. ++depth;
  87. break;
  88. }
  89. }
  90. if (t == stopAfter)
  91. break;
  92. t = t.parent;
  93. }
  94. ListPool<Mask>.Release(components);
  95. return depth;
  96. }
  97. /// <summary>
  98. /// Helper function to determine if the child is a descendant of father or is father.
  99. /// </summary>
  100. /// <param name="father">The transform to compare against.</param>
  101. /// <param name="child">The starting transform to search up the hierarchy.</param>
  102. /// <returns>Is child equal to father or is a descendant.</returns>
  103. public static bool IsDescendantOrSelf(Transform father, Transform child)
  104. {
  105. if (father == null || child == null)
  106. return false;
  107. if (father == child)
  108. return true;
  109. while (child.parent != null)
  110. {
  111. if (child.parent == father)
  112. return true;
  113. child = child.parent;
  114. }
  115. return false;
  116. }
  117. /// <summary>
  118. /// Find the correct RectMask2D for a given IClippable.
  119. /// </summary>
  120. /// <param name="clippable">Clippable to search from.</param>
  121. /// <returns>The Correct RectMask2D</returns>
  122. public static RectMask2D GetRectMaskForClippable(IClippable clippable)
  123. {
  124. List<RectMask2D> rectMaskComponents = ListPool<RectMask2D>.Get();
  125. List<Canvas> canvasComponents = ListPool<Canvas>.Get();
  126. RectMask2D componentToReturn = null;
  127. clippable.gameObject.GetComponentsInParent(false, rectMaskComponents);
  128. if (rectMaskComponents.Count > 0)
  129. {
  130. for (int rmi = 0; rmi < rectMaskComponents.Count; rmi++)
  131. {
  132. componentToReturn = rectMaskComponents[rmi];
  133. if (componentToReturn.gameObject == clippable.gameObject)
  134. {
  135. componentToReturn = null;
  136. continue;
  137. }
  138. if (!componentToReturn.isActiveAndEnabled)
  139. {
  140. componentToReturn = null;
  141. continue;
  142. }
  143. clippable.gameObject.GetComponentsInParent(false, canvasComponents);
  144. for (int i = canvasComponents.Count - 1; i >= 0; i--)
  145. {
  146. if (!IsDescendantOrSelf(canvasComponents[i].transform, componentToReturn.transform) && canvasComponents[i].overrideSorting)
  147. {
  148. componentToReturn = null;
  149. break;
  150. }
  151. }
  152. break;
  153. }
  154. }
  155. ListPool<RectMask2D>.Release(rectMaskComponents);
  156. ListPool<Canvas>.Release(canvasComponents);
  157. return componentToReturn;
  158. }
  159. /// <summary>
  160. /// Search for all RectMask2D that apply to the given RectMask2D (includes self).
  161. /// </summary>
  162. /// <param name="clipper">Starting clipping object.</param>
  163. /// <param name="masks">The list of Rect masks</param>
  164. public static void GetRectMasksForClip(RectMask2D clipper, List<RectMask2D> masks)
  165. {
  166. masks.Clear();
  167. List<Canvas> canvasComponents = ListPool<Canvas>.Get();
  168. List<RectMask2D> rectMaskComponents = ListPool<RectMask2D>.Get();
  169. clipper.transform.GetComponentsInParent(false, rectMaskComponents);
  170. if (rectMaskComponents.Count > 0)
  171. {
  172. clipper.transform.GetComponentsInParent(false, canvasComponents);
  173. for (int i = rectMaskComponents.Count - 1; i >= 0; i--)
  174. {
  175. if (!rectMaskComponents[i].IsActive())
  176. continue;
  177. bool shouldAdd = true;
  178. for (int j = canvasComponents.Count - 1; j >= 0; j--)
  179. {
  180. if (!IsDescendantOrSelf(canvasComponents[j].transform, rectMaskComponents[i].transform) && canvasComponents[j].overrideSorting)
  181. {
  182. shouldAdd = false;
  183. break;
  184. }
  185. }
  186. if (shouldAdd)
  187. masks.Add(rectMaskComponents[i]);
  188. }
  189. }
  190. ListPool<RectMask2D>.Release(rectMaskComponents);
  191. ListPool<Canvas>.Release(canvasComponents);
  192. }
  193. }
  194. }