Bar.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #nullable enable
  2. namespace Terminal.Gui;
  3. /// <summary>
  4. /// Provides a horizontally or vertically oriented container for <see cref="Shortcut"/>s to be used as a menu, toolbar, or status
  5. /// bar.
  6. /// </summary>
  7. /// <remarks>
  8. /// <para>
  9. /// Any <see cref="View"/> can be added to a <see cref="Bar"/>. However, the <see cref="Bar"/> is designed to work with
  10. /// <see cref="Shortcut"/> objects. The <see cref="Shortcut"/> class provides a way to display a command, help, and key and
  11. /// align them in a specific order.
  12. /// </para>
  13. /// </remarks>
  14. public class Bar : View, IOrientation, IDesignable
  15. {
  16. private readonly OrientationHelper _orientationHelper;
  17. /// <inheritdoc/>
  18. public Bar () : this ([]) { }
  19. /// <inheritdoc/>
  20. public Bar (IEnumerable<Shortcut> shortcuts)
  21. {
  22. CanFocus = true;
  23. Width = Dim.Auto ();
  24. Height = Dim.Auto ();
  25. _orientationHelper = new (this);
  26. _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
  27. _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
  28. Initialized += Bar_Initialized;
  29. if (shortcuts is null)
  30. {
  31. return;
  32. }
  33. foreach (Shortcut shortcut in shortcuts)
  34. {
  35. Add (shortcut);
  36. }
  37. }
  38. private void Bar_Initialized (object? sender, EventArgs e) { ColorScheme = Colors.ColorSchemes ["Menu"]; }
  39. /// <inheritdoc/>
  40. public override void SetBorderStyle (LineStyle value)
  41. {
  42. // The default changes the thickness. We don't want that. We just set the style.
  43. Border.LineStyle = value;
  44. }
  45. #region IOrientation members
  46. /// <summary>
  47. /// Gets or sets the <see cref="Orientation"/> for this <see cref="Bar"/>. The default is
  48. /// <see cref="Orientation.Horizontal"/>.
  49. /// </summary>
  50. /// <remarks>
  51. /// <para>
  52. /// Horizontal orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from right to left
  53. /// Vertical orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from left to right.
  54. /// </para>
  55. /// </remarks>
  56. public Orientation Orientation
  57. {
  58. get => _orientationHelper.Orientation;
  59. set => _orientationHelper.Orientation = value;
  60. }
  61. /// <inheritdoc/>
  62. public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
  63. /// <inheritdoc/>
  64. public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
  65. /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
  66. /// <param name="newOrientation"></param>
  67. public void OnOrientationChanged (Orientation newOrientation)
  68. {
  69. SetNeedsLayout ();
  70. }
  71. #endregion
  72. private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd;
  73. /// <summary>
  74. /// Gets or sets the <see cref="AlignmentModes"/> for this <see cref="Bar"/>. The default is
  75. /// <see cref="AlignmentModes.StartToEnd"/>.
  76. /// </summary>
  77. public AlignmentModes AlignmentModes
  78. {
  79. get => _alignmentModes;
  80. set
  81. {
  82. _alignmentModes = value;
  83. SetNeedsLayout ();
  84. }
  85. }
  86. // TODO: Move this to View
  87. /// <summary>Inserts a <see cref="Shortcut"/> in the specified index of <see cref="View.Subviews"/>.</summary>
  88. /// <param name="index">The zero-based index at which item should be inserted.</param>
  89. /// <param name="item">The item to insert.</param>
  90. public void AddShortcutAt (int index, Shortcut item)
  91. {
  92. List<View> savedSubViewList = Subviews.ToList ();
  93. int count = savedSubViewList.Count;
  94. RemoveAll ();
  95. for (var i = 0; i <= count; i++)
  96. {
  97. if (i == index)
  98. {
  99. Add (item);
  100. }
  101. if (i < count)
  102. {
  103. Add (savedSubViewList [i]);
  104. }
  105. }
  106. SetNeedsDisplay ();
  107. }
  108. // TODO: Move this to View
  109. /// <summary>Removes a <see cref="Shortcut"/> at specified index of <see cref="View.Subviews"/>.</summary>
  110. /// <param name="index">The zero-based index of the item to remove.</param>
  111. /// <returns>The <see cref="Shortcut"/> removed.</returns>
  112. public Shortcut? RemoveShortcut (int index)
  113. {
  114. View? toRemove = null;
  115. for (var i = 0; i < Subviews.Count; i++)
  116. {
  117. if (i == index)
  118. {
  119. toRemove = Subviews [i];
  120. }
  121. }
  122. if (toRemove is { })
  123. {
  124. Remove (toRemove);
  125. SetNeedsDisplay ();
  126. }
  127. return toRemove as Shortcut;
  128. }
  129. /// <inheritdoc />
  130. internal override void OnLayoutStarted (LayoutEventArgs args)
  131. {
  132. base.OnLayoutStarted (args);
  133. View? prevBarItem = null;
  134. switch (Orientation)
  135. {
  136. case Orientation.Horizontal:
  137. for (var index = 0; index < Subviews.Count; index++)
  138. {
  139. View barItem = Subviews [index];
  140. barItem.ColorScheme = ColorScheme;
  141. barItem.X = Pos.Align (Alignment.Start, AlignmentModes);
  142. barItem.Y = 0; //Pos.Center ();
  143. // HACK: This should not be needed
  144. barItem.SetRelativeLayout (GetContentSize ());
  145. }
  146. break;
  147. case Orientation.Vertical:
  148. // Set the overall size of the Bar and arrange the views vertically
  149. var minKeyWidth = 0;
  150. List<Shortcut> shortcuts = Subviews.Where (s => s is Shortcut && s.Visible).Cast<Shortcut> ().ToList ();
  151. foreach (Shortcut shortcut in shortcuts)
  152. {
  153. // Let DimAuto do its thing to get the minimum width of each CommandView and HelpView
  154. //shortcut.CommandView.SetRelativeLayout (new Size (int.MaxValue, int.MaxValue));
  155. minKeyWidth = int.Max (minKeyWidth, shortcut.KeyView.Text.GetColumns ());
  156. }
  157. var maxBarItemWidth = 0;
  158. var totalHeight = 0;
  159. for (var index = 0; index < Subviews.Count; index++)
  160. {
  161. View barItem = Subviews [index];
  162. barItem.ColorScheme = ColorScheme;
  163. if (!barItem.Visible)
  164. {
  165. continue;
  166. }
  167. if (barItem is Shortcut scBarItem)
  168. {
  169. scBarItem.MinimumKeyTextSize = minKeyWidth;
  170. // HACK: This should not be needed
  171. scBarItem.SetRelativeLayout (GetContentSize ());
  172. maxBarItemWidth = Math.Max (maxBarItemWidth, scBarItem.Frame.Width);
  173. }
  174. if (prevBarItem == null)
  175. {
  176. barItem.Y = 0;
  177. }
  178. else
  179. {
  180. // Align the view to the bottom of the previous view
  181. barItem.Y = Pos.Bottom (prevBarItem);
  182. }
  183. prevBarItem = barItem;
  184. barItem.X = 0;
  185. totalHeight += barItem.Frame.Height;
  186. }
  187. foreach (View barItem in Subviews)
  188. {
  189. barItem.Width = maxBarItemWidth;
  190. if (barItem is Line line)
  191. {
  192. }
  193. }
  194. Height = Dim.Auto (DimAutoStyle.Content, totalHeight);
  195. break;
  196. }
  197. }
  198. /// <inheritdoc />
  199. public bool EnableForDesign ()
  200. {
  201. var shortcut = new Shortcut
  202. {
  203. Text = "Quit",
  204. Title = "Q_uit",
  205. Key = Key.Z.WithCtrl,
  206. };
  207. Add (shortcut);
  208. shortcut = new Shortcut
  209. {
  210. Text = "Help Text",
  211. Title = "Help",
  212. Key = Key.F1,
  213. };
  214. Add (shortcut);
  215. return true;
  216. }
  217. }