StackPanel.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using Avalonia.Input;
  5. namespace Avalonia.Controls
  6. {
  7. /// <summary>
  8. /// A panel which lays out its children horizontally or vertically.
  9. /// </summary>
  10. public class StackPanel : Panel, INavigableContainer
  11. {
  12. /// <summary>
  13. /// Defines the <see cref="Gap"/> property.
  14. /// </summary>
  15. public static readonly StyledProperty<double> GapProperty =
  16. AvaloniaProperty.Register<StackPanel, double>(nameof(Gap));
  17. /// <summary>
  18. /// Defines the <see cref="Orientation"/> property.
  19. /// </summary>
  20. public static readonly StyledProperty<Orientation> OrientationProperty =
  21. AvaloniaProperty.Register<StackPanel, Orientation>(nameof(Orientation), Orientation.Vertical);
  22. /// <summary>
  23. /// Initializes static members of the <see cref="StackPanel"/> class.
  24. /// </summary>
  25. static StackPanel()
  26. {
  27. AffectsMeasure(GapProperty);
  28. AffectsMeasure(OrientationProperty);
  29. }
  30. /// <summary>
  31. /// Gets or sets the size of the gap to place between child controls.
  32. /// </summary>
  33. public double Gap
  34. {
  35. get { return GetValue(GapProperty); }
  36. set { SetValue(GapProperty, value); }
  37. }
  38. /// <summary>
  39. /// Gets or sets the orientation in which child controls will be layed out.
  40. /// </summary>
  41. public Orientation Orientation
  42. {
  43. get { return GetValue(OrientationProperty); }
  44. set { SetValue(OrientationProperty, value); }
  45. }
  46. /// <summary>
  47. /// Gets the next control in the specified direction.
  48. /// </summary>
  49. /// <param name="direction">The movement direction.</param>
  50. /// <param name="from">The control from which movement begins.</param>
  51. /// <returns>The control.</returns>
  52. IInputElement INavigableContainer.GetControl(NavigationDirection direction, IInputElement from)
  53. {
  54. var fromControl = from as IControl;
  55. return (fromControl != null) ? GetControlInDirection(direction, fromControl) : null;
  56. }
  57. /// <summary>
  58. /// Gets the next control in the specified direction.
  59. /// </summary>
  60. /// <param name="direction">The movement direction.</param>
  61. /// <param name="from">The control from which movement begins.</param>
  62. /// <returns>The control.</returns>
  63. protected virtual IInputElement GetControlInDirection(NavigationDirection direction, IControl from)
  64. {
  65. var horiz = Orientation == Orientation.Horizontal;
  66. int index = Children.IndexOf((IControl)from);
  67. switch (direction)
  68. {
  69. case NavigationDirection.First:
  70. index = 0;
  71. break;
  72. case NavigationDirection.Last:
  73. index = Children.Count - 1;
  74. break;
  75. case NavigationDirection.Next:
  76. ++index;
  77. break;
  78. case NavigationDirection.Previous:
  79. --index;
  80. break;
  81. case NavigationDirection.Left:
  82. index = horiz ? index - 1 : -1;
  83. break;
  84. case NavigationDirection.Right:
  85. index = horiz ? index + 1 : -1;
  86. break;
  87. case NavigationDirection.Up:
  88. index = horiz ? -1 : index - 1;
  89. break;
  90. case NavigationDirection.Down:
  91. index = horiz ? -1 : index + 1;
  92. break;
  93. default:
  94. index = -1;
  95. break;
  96. }
  97. if (index >= 0 && index < Children.Count)
  98. {
  99. return Children[index];
  100. }
  101. else
  102. {
  103. return null;
  104. }
  105. }
  106. /// <summary>
  107. /// Measures the control.
  108. /// </summary>
  109. /// <param name="availableSize">The available size.</param>
  110. /// <returns>The desired size of the control.</returns>
  111. protected override Size MeasureOverride(Size availableSize)
  112. {
  113. double childAvailableWidth = double.PositiveInfinity;
  114. double childAvailableHeight = double.PositiveInfinity;
  115. if (Orientation == Orientation.Vertical)
  116. {
  117. childAvailableWidth = availableSize.Width;
  118. if (!double.IsNaN(Width))
  119. {
  120. childAvailableWidth = Width;
  121. }
  122. childAvailableWidth = Math.Min(childAvailableWidth, MaxWidth);
  123. childAvailableWidth = Math.Max(childAvailableWidth, MinWidth);
  124. }
  125. else
  126. {
  127. childAvailableHeight = availableSize.Height;
  128. if (!double.IsNaN(Height))
  129. {
  130. childAvailableHeight = Height;
  131. }
  132. childAvailableHeight = Math.Min(childAvailableHeight, MaxHeight);
  133. childAvailableHeight = Math.Max(childAvailableHeight, MinHeight);
  134. }
  135. double measuredWidth = 0;
  136. double measuredHeight = 0;
  137. double gap = Gap;
  138. foreach (Control child in Children)
  139. {
  140. child.Measure(new Size(childAvailableWidth, childAvailableHeight));
  141. Size size = child.DesiredSize;
  142. if (Orientation == Orientation.Vertical)
  143. {
  144. measuredHeight += size.Height + gap;
  145. measuredWidth = Math.Max(measuredWidth, size.Width);
  146. }
  147. else
  148. {
  149. measuredWidth += size.Width + gap;
  150. measuredHeight = Math.Max(measuredHeight, size.Height);
  151. }
  152. }
  153. if (Orientation == Orientation.Vertical)
  154. {
  155. measuredHeight -= gap;
  156. }
  157. else
  158. {
  159. measuredWidth -= gap;
  160. }
  161. return new Size(measuredWidth, measuredHeight);
  162. }
  163. /// <summary>
  164. /// Arranges the control's children.
  165. /// </summary>
  166. /// <param name="finalSize">The size allocated to the control.</param>
  167. /// <returns>The space taken.</returns>
  168. protected override Size ArrangeOverride(Size finalSize)
  169. {
  170. var orientation = Orientation;
  171. double arrangedWidth = finalSize.Width;
  172. double arrangedHeight = finalSize.Height;
  173. double gap = Gap;
  174. if (Orientation == Orientation.Vertical)
  175. {
  176. arrangedHeight = 0;
  177. }
  178. else
  179. {
  180. arrangedWidth = 0;
  181. }
  182. foreach (Control child in Children)
  183. {
  184. double childWidth = child.DesiredSize.Width;
  185. double childHeight = child.DesiredSize.Height;
  186. if (orientation == Orientation.Vertical)
  187. {
  188. double width = Math.Max(childWidth, arrangedWidth);
  189. Rect childFinal = new Rect(0, arrangedHeight, width, childHeight);
  190. ArrangeChild(child, childFinal, finalSize, orientation);
  191. arrangedWidth = Math.Max(arrangedWidth, childWidth);
  192. arrangedHeight += childHeight + gap;
  193. }
  194. else
  195. {
  196. double height = Math.Max(childHeight, arrangedHeight);
  197. Rect childFinal = new Rect(arrangedWidth, 0, childWidth, height);
  198. ArrangeChild(child, childFinal, finalSize, orientation);
  199. arrangedWidth += childWidth + gap;
  200. arrangedHeight = Math.Max(arrangedHeight, childHeight);
  201. }
  202. }
  203. if (orientation == Orientation.Vertical)
  204. {
  205. arrangedHeight = Math.Max(arrangedHeight - gap, finalSize.Height);
  206. }
  207. else
  208. {
  209. arrangedWidth = Math.Max(arrangedWidth - gap, finalSize.Width);
  210. }
  211. return new Size(arrangedWidth, arrangedHeight);
  212. }
  213. internal virtual void ArrangeChild(
  214. IControl child,
  215. Rect rect,
  216. Size panelSize,
  217. Orientation orientation)
  218. {
  219. child.Arrange(rect);
  220. }
  221. }
  222. }