Distribute.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using System;
  2. using UnityEditor;
  3. using UnityEngine;
  4. namespace TheraBytes.BetterUi.Editor.AlignDistribute
  5. {
  6. public static class Distribute
  7. {
  8. private static AlignMode alignMode;
  9. private static DistanceOption distanceOption;
  10. private static SortOrder sortOrder;
  11. private static float paddingLeftBottomPixels;
  12. private static float paddingRightTopPixels;
  13. public static void DistributeSelection(AlignMode alignMode, DistanceOption distanceOption, SortOrder sortOrder, float paddingLeftBottomPixels, float paddingRightTopPixels)
  14. {
  15. Distribute.alignMode = alignMode;
  16. Distribute.distanceOption = distanceOption;
  17. Distribute.sortOrder = sortOrder;
  18. Distribute.paddingLeftBottomPixels = paddingLeftBottomPixels;
  19. Distribute.paddingRightTopPixels = paddingRightTopPixels;
  20. Transform[] selection = GetSortedSelection();
  21. RectTransform targetRectTransform = GetTargetRectTransform(selection);
  22. Vector2 start = CalculateStart(targetRectTransform, selection[0] as RectTransform);
  23. Vector2 end = CalculateEnd(targetRectTransform, selection[selection.Length - 1].transform as RectTransform);
  24. Vector2 distanceBetweenElements = CalculateDistanceBetweenElements(start, end, selection);
  25. // Debug.Log("[Distribute]\tTarget: \t" + alignMode + "\n\t\tMode:\t" + distanceOption);
  26. Vector2 currentPosition = start;
  27. for (int i = 0; i < selection.Length; i++)
  28. {
  29. Undo.RecordObject(selection[i].transform, "Distribute " + distanceOption);
  30. // DrawDebugLines(currentPosition, selection[i]);
  31. Vector3 oldPosition = selection[i].position;
  32. Quaternion originalParentRotation = selection[i].parent.rotation;
  33. selection[i].parent.rotation = new Quaternion();
  34. Vector3 targetPosition = selection[i].position;
  35. switch (alignMode)
  36. {
  37. case AlignMode.Horizontal:
  38. targetPosition.x = currentPosition.x + GetDistributionOffset(selection[i] as RectTransform).x;
  39. break;
  40. case AlignMode.Vertical:
  41. targetPosition.y = currentPosition.y + GetDistributionOffset(selection[i] as RectTransform).y;
  42. break;
  43. default:
  44. Debug.LogError("Invalid target: " + alignMode);
  45. break;
  46. }
  47. selection[i].position = targetPosition;
  48. selection[i].parent.rotation = originalParentRotation;
  49. Utility.AdjustAnchors(selection[i] as RectTransform, oldPosition);
  50. if (distanceOption == DistanceOption.Space)
  51. {
  52. currentPosition += Utility.GetTransformSize(selection[i]);
  53. }
  54. currentPosition += distanceBetweenElements;
  55. }
  56. Utility.CleanUp();
  57. }
  58. private static RectTransform GetTargetRectTransform(Transform[] selection)
  59. {
  60. if (AlignDistributeWindow.distributeTo == DistributeTo.Parent)
  61. {
  62. return selection[0].parent as RectTransform;
  63. }
  64. else
  65. {
  66. return Utility.GetBoundingBoxRectTransform(selection);
  67. }
  68. }
  69. private static Vector2 CalculateStart(RectTransform targetRectTransform, RectTransform firstSelectionElement)
  70. {
  71. Vector2 start = (Vector2) targetRectTransform.position - Utility.GetTransformSize(targetRectTransform) * 0.5f;
  72. start += Vector2.one * paddingLeftBottomPixels;
  73. start -= Utility.GetPivotAndCenterLocalDistance(targetRectTransform);
  74. if (distanceOption == DistanceOption.Space)
  75. {
  76. return start;
  77. }
  78. // Make sure the first object never overlaps the Parent's borders.
  79. start += Utility.GetTransformSize(firstSelectionElement) * 0.5f;
  80. start += Utility.GetPivotAndCenterLocalDistance(firstSelectionElement);
  81. start -= GetDistributionOffset(firstSelectionElement);
  82. return start;
  83. }
  84. private static Vector2 CalculateEnd(RectTransform targetRectTransform, RectTransform lastSelectionElement)
  85. {
  86. Vector2 end = (Vector2) targetRectTransform.position + Utility.GetTransformSize(targetRectTransform) * 0.5f;
  87. end -= Vector2.one * paddingRightTopPixels;
  88. end -= Utility.GetPivotAndCenterLocalDistance(targetRectTransform);
  89. if (distanceOption == DistanceOption.Space)
  90. {
  91. return end;
  92. }
  93. // Make sure the last object never overlaps the Parent's borders.
  94. end -= Utility.GetTransformSize(lastSelectionElement) * 0.5f;
  95. end += Utility.GetPivotAndCenterLocalDistance(lastSelectionElement);
  96. end -= GetDistributionOffset(lastSelectionElement);
  97. return end;
  98. }
  99. private static Transform[] GetSortedSelection()
  100. {
  101. switch (sortOrder)
  102. {
  103. case SortOrder.Hierarchical:
  104. return Utility.SortHierarchically(Selection.transforms);
  105. case SortOrder.Positional:
  106. if (alignMode == AlignMode.Horizontal)
  107. {
  108. return Utility.SortByPositionX(Selection.transforms);
  109. }
  110. else
  111. {
  112. return Utility.SortByPositionY(Selection.transforms);
  113. }
  114. default:
  115. Debug.LogError("Unknown SortOrder: " + sortOrder);
  116. throw new ArgumentOutOfRangeException();
  117. }
  118. }
  119. private static Vector2 CalculateDistanceBetweenElements(Vector2 start, Vector2 end, Transform[] elements)
  120. {
  121. Vector2 totalLength = end - start;
  122. totalLength.x = Mathf.Abs(totalLength.x);
  123. totalLength.y = Mathf.Abs(totalLength.y);
  124. Vector2 totalElementSize = Vector2.zero;
  125. if (distanceOption == DistanceOption.Space)
  126. {
  127. for (int i = 0; i < elements.Length; i++)
  128. {
  129. totalElementSize += Utility.GetTransformSize(elements[i]);
  130. }
  131. }
  132. return (totalLength - totalElementSize) / (elements.Length - 1);
  133. }
  134. private static Vector2 GetDistributionOffset(RectTransform rectTransform)
  135. {
  136. Vector2 offset = Vector2.zero;
  137. switch (distanceOption)
  138. {
  139. case DistanceOption.Center:
  140. offset = Utility.GetPivotAndCenterLocalDistance(rectTransform);
  141. break;
  142. case DistanceOption.Pivot:
  143. break;
  144. case DistanceOption.LeftBottom:
  145. offset = Utility.GetLocalPivotPosition(rectTransform);
  146. break;
  147. case DistanceOption.RightTop:
  148. offset = Utility.GetLocalPivotPosition(rectTransform) - Utility.GetTransformSize(rectTransform);
  149. break;
  150. case DistanceOption.Space:
  151. offset = Utility.GetLocalPivotPosition(rectTransform);
  152. break;
  153. default:
  154. Debug.LogError("Unknown DistanceOption: " + distanceOption);
  155. throw new ArgumentOutOfRangeException();
  156. }
  157. return offset;
  158. }
  159. private static void DrawDebugLines(Vector2 currentPosition, Transform currentSelection)
  160. {
  161. // CURRENT POS DEBUG
  162. Debug.DrawLine(currentPosition + Vector2.up * 100, currentPosition + Vector2.down * 100, Color.yellow, 1f);
  163. Debug.DrawLine(currentPosition + Vector2.right * 100, currentPosition + Vector2.left * 100, Color.yellow, 1f);
  164. // DEBUG PIVOT
  165. Debug.DrawLine(currentSelection.position + Vector3.up, currentSelection.position + Vector3.down, Color.cyan, 1f);
  166. Debug.DrawLine(currentSelection.position + Vector3.up, currentSelection.position + Vector3.left, Color.cyan, 1f);
  167. Debug.DrawLine(currentSelection.position + Vector3.up, currentSelection.position + Vector3.right, Color.cyan, 1f);
  168. Debug.DrawLine(currentSelection.position + Vector3.down, currentSelection.position + Vector3.left, Color.cyan, 1f);
  169. Debug.DrawLine(currentSelection.position + Vector3.down, currentSelection.position + Vector3.right, Color.cyan, 1f);
  170. Debug.DrawLine(currentSelection.position + Vector3.left, currentSelection.position + Vector3.right, Color.cyan, 1f);
  171. SceneView.RepaintAll();
  172. }
  173. }
  174. }