SizeChangeTracker.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEngine;
  6. using UnityEngine.EventSystems;
  7. using UnityEngine.UI;
  8. namespace TheraBytes.BetterUi
  9. {
  10. [HelpURL("https://documentation.therabytes.de/better-ui/SizeChangeTracker.html")]
  11. [AddComponentMenu("Better UI/Helpers/Size Change Tracker", 30)]
  12. [RequireComponent(typeof(RectTransform))]
  13. [RequireComponent(typeof(ILayoutElement))]
  14. public class SizeChangeTracker : UIBehaviour, ILayoutSelfController
  15. {
  16. [Flags]
  17. public enum Axis
  18. {
  19. None = 0,
  20. Horizontal = 1 << 0,
  21. Vertical = 1 << 1,
  22. HorizontalAndVertical = Horizontal | Vertical,
  23. }
  24. public enum TrackMode
  25. {
  26. PreferredSize,
  27. MinSize,
  28. }
  29. public TrackMode SizeMode = TrackMode.PreferredSize;
  30. public Axis AffectedAxis = Axis.HorizontalAndVertical;
  31. public RectTransform[] AffectedObjects;
  32. bool isInRecursion = false;
  33. Vector2 previousSize;
  34. RectTransform rectTransform;
  35. public RectTransform RectTransform
  36. {
  37. get
  38. {
  39. if (rectTransform == null)
  40. {
  41. rectTransform = transform as RectTransform;
  42. }
  43. return rectTransform;
  44. }
  45. }
  46. protected override void OnEnable()
  47. {
  48. base.OnEnable();
  49. CallForAffectedObjects((dp) => dp.ChildAddedOrEnabled(this.transform), force: true);
  50. }
  51. protected override void OnDisable()
  52. {
  53. base.OnDisable();
  54. CallForAffectedObjects((dp) => dp.ChildRemovedOrDisabled(this.transform), force: true);
  55. previousSize = Vector2.zero;
  56. }
  57. protected override void OnTransformParentChanged()
  58. {
  59. base.OnTransformParentChanged();
  60. CallForAffectedObjects((dp) => dp.ChildRemovedOrDisabled(this.transform), force: true);
  61. previousSize = Vector2.zero;
  62. }
  63. protected override void OnRectTransformDimensionsChange()
  64. {
  65. base.OnRectTransformDimensionsChange();
  66. CallForAffectedObjects((dp) => dp.ChildSizeChanged(this.transform));
  67. }
  68. void ILayoutController.SetLayoutHorizontal()
  69. {
  70. if (!AffectedAxis.HasFlag(Axis.Horizontal))
  71. return;
  72. CallForAffectedObjects((dp) => dp.ChildSizeChanged(this.transform));
  73. }
  74. void ILayoutController.SetLayoutVertical()
  75. {
  76. if (!AffectedAxis.HasFlag(Axis.Vertical))
  77. return;
  78. CallForAffectedObjects((dp) => dp.ChildSizeChanged(this.transform));
  79. }
  80. private void CallForAffectedObjects(Action<ILayoutChildDependency> function, bool force = false)
  81. {
  82. if (function == null)
  83. throw new ArgumentNullException("function must not be null");
  84. if (isInRecursion)
  85. return;
  86. Vector2 size = GetCurrentSize();
  87. if (!force && !HasSizeChanged(size, previousSize))
  88. return;
  89. previousSize = size;
  90. isInRecursion = true;
  91. try
  92. {
  93. foreach (RectTransform rt in AffectedObjects)
  94. {
  95. if (rt == null)
  96. continue;
  97. foreach (ILayoutChildDependency dp in rt.GetComponents<ILayoutChildDependency>())
  98. {
  99. if (dp == null)
  100. continue;
  101. function(dp);
  102. }
  103. }
  104. }
  105. catch (Exception ex)
  106. {
  107. Debug.LogException(ex);
  108. }
  109. finally
  110. {
  111. isInRecursion = false;
  112. }
  113. }
  114. private Vector2 GetCurrentSize()
  115. {
  116. switch (SizeMode)
  117. {
  118. case TrackMode.PreferredSize:
  119. return new Vector2(
  120. LayoutUtility.GetPreferredWidth(RectTransform),
  121. LayoutUtility.GetPreferredHeight(RectTransform));
  122. case TrackMode.MinSize:
  123. return new Vector2(
  124. LayoutUtility.GetMinWidth(RectTransform),
  125. LayoutUtility.GetMinHeight(RectTransform));
  126. default: throw new ArgumentException();
  127. }
  128. }
  129. private bool HasSizeChanged(Vector2 size, Vector2 previousSize)
  130. {
  131. switch (AffectedAxis)
  132. {
  133. case Axis.None: return false;
  134. case Axis.Horizontal: return size.x != previousSize.x;
  135. case Axis.Vertical: return size.y != previousSize.y;
  136. case Axis.HorizontalAndVertical: return size != previousSize;
  137. default: throw new ArgumentException();
  138. }
  139. }
  140. }
  141. }