PosAlign.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. #nullable enable
  2. using System.ComponentModel;
  3. namespace Terminal.Gui;
  4. /// <summary>
  5. /// Enables alignment of a set of views.
  6. /// </summary>
  7. /// <remarks>
  8. /// <para>
  9. /// The Group ID is used to identify a set of views that should be alignment together. When only a single
  10. /// set of views is aligned, setting the Group ID is not needed because it defaults to 0.
  11. /// </para>
  12. /// <para>
  13. /// The first view added to the Superview with a given Group ID is used to determine the alignment of the group.
  14. /// The alignment is applied to all views with the same Group ID.
  15. /// </para>
  16. /// </remarks>
  17. public class PosAlign : Pos
  18. {
  19. /// <summary>
  20. /// The cached location. Used to store the calculated location to avoid recalculating it.
  21. /// </summary>
  22. private int? _location;
  23. /// <summary>
  24. /// Gets the identifier of a set of views that should be aligned together. When only a single
  25. /// set of views is aligned, setting the <see cref="_groupId"/> is not needed because it defaults to 0.
  26. /// </summary>
  27. private readonly int _groupId;
  28. /// <summary>
  29. /// Gets the alignment settings.
  30. /// </summary>
  31. public Aligner Aligner { get; } = new ();
  32. /// <summary>
  33. /// Aligns the views in <paramref name="views"/> that have the same group ID as <paramref name="groupId"/>.
  34. /// </summary>
  35. /// <param name="groupId"></param>
  36. /// <param name="views"></param>
  37. /// <param name="dimension"></param>
  38. /// <param name="size"></param>
  39. private static void AlignGroup (int groupId, IList<View> views, Dimension dimension, int size)
  40. {
  41. if (views is null)
  42. {
  43. return;
  44. }
  45. Aligner firstInGroup = null;
  46. List<int> dimensionsList = new ();
  47. List<View> viewsInGroup = views.Where (
  48. v =>
  49. {
  50. if (dimension == Dimension.Width && v.X is PosAlign alignX)
  51. {
  52. return alignX._groupId == groupId;
  53. }
  54. if (dimension == Dimension.Height && v.Y is PosAlign alignY)
  55. {
  56. return alignY._groupId == groupId;
  57. }
  58. return false;
  59. })
  60. .ToList ();
  61. if (viewsInGroup.Count == 0)
  62. {
  63. return;
  64. }
  65. foreach (View view in viewsInGroup)
  66. {
  67. PosAlign posAlign = dimension == Dimension.Width ? view.X as PosAlign : view.Y as PosAlign;
  68. if (posAlign is { })
  69. {
  70. if (firstInGroup is null)
  71. {
  72. firstInGroup = posAlign.Aligner;
  73. }
  74. dimensionsList.Add (dimension == Dimension.Width ? view.Frame.Width : view.Frame.Height);
  75. }
  76. }
  77. if (firstInGroup is null)
  78. {
  79. return;
  80. }
  81. firstInGroup.ContainerSize = size;
  82. int [] locations = firstInGroup.Align (dimensionsList.ToArray ());
  83. for (var index = 0; index < viewsInGroup.Count; index++)
  84. {
  85. View view = viewsInGroup [index];
  86. PosAlign align = dimension == Dimension.Width ? view.X as PosAlign : view.Y as PosAlign;
  87. if (align is { })
  88. {
  89. align._location = locations [index];
  90. }
  91. }
  92. }
  93. /// <summary>
  94. /// Enables alignment of a set of views.
  95. /// </summary>
  96. /// <param name="alignment"></param>
  97. /// <param name="groupId">The unique identifier for the set of views to align according to <paramref name="alignment"/>.</param>
  98. public PosAlign (Alignment alignment, int groupId = 0)
  99. {
  100. Aligner.SpaceBetweenItems = true;
  101. Aligner.Alignment = alignment;
  102. _groupId = groupId;
  103. Aligner.PropertyChanged += Aligner_PropertyChanged;
  104. }
  105. private void Aligner_PropertyChanged (object? sender, PropertyChangedEventArgs e) { _location = null; }
  106. /// <inheritdoc/>
  107. public override bool Equals (object other)
  108. {
  109. return other is PosAlign align && _groupId == align._groupId && _location == align._location && align.Aligner.Alignment == Aligner.Alignment;
  110. }
  111. /// <inheritdoc/>
  112. public override int GetHashCode () { return Aligner.GetHashCode () ^ _groupId.GetHashCode (); }
  113. /// <inheritdoc/>
  114. public override string ToString () { return $"Align(groupId={_groupId}, alignment={Aligner.Alignment})"; }
  115. internal override int GetAnchor (int width) { return _location ?? 0 - width; }
  116. internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
  117. {
  118. if (_location.HasValue && Aligner.ContainerSize == superviewDimension)
  119. {
  120. return _location.Value;
  121. }
  122. if (us?.SuperView is null)
  123. {
  124. return 0;
  125. }
  126. AlignGroup (_groupId, us.SuperView.Subviews, dimension, superviewDimension);
  127. if (_location.HasValue)
  128. {
  129. return _location.Value;
  130. }
  131. return 0;
  132. }
  133. }