StatusBar.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. //
  2. // StatusBar.cs: a statusbar for an application
  3. //
  4. // Authors:
  5. // Miguel de Icaza ([email protected])
  6. //
  7. // TODO:
  8. // Add mouse support
  9. using System;
  10. using NStack;
  11. namespace Terminal.Gui {
  12. /// <summary>
  13. /// <see cref="StatusItem"/> objects are contained by <see cref="StatusBar"/> <see cref="View"/>s.
  14. /// Each <see cref="StatusItem"/> has a title, a shortcut (hotkey), and an <see cref="Action"/> that will be invoked when the
  15. /// <see cref="StatusItem.Shortcut"/> is pressed.
  16. /// The <see cref="StatusItem.Shortcut"/> will be a global hotkey for the application in the current context of the screen.
  17. /// The colour of the <see cref="StatusItem.Title"/> will be changed after each ~.
  18. /// A <see cref="StatusItem.Title"/> set to `~F1~ Help` will render as *F1* using <see cref="ColorScheme.HotNormal"/> and
  19. /// *Help* as <see cref="ColorScheme.HotNormal"/>.
  20. /// </summary>
  21. public class StatusItem {
  22. /// <summary>
  23. /// Initializes a new <see cref="StatusItem"/>.
  24. /// </summary>
  25. /// <param name="shortcut">Shortcut to activate the <see cref="StatusItem"/>.</param>
  26. /// <param name="title">Title for the <see cref="StatusItem"/>.</param>
  27. /// <param name="action">Action to invoke when the <see cref="StatusItem"/> is activated.</param>
  28. public StatusItem (Key shortcut, ustring title, Action action)
  29. {
  30. Title = title ?? "";
  31. Shortcut = shortcut;
  32. Action = action;
  33. }
  34. /// <summary>
  35. /// Gets the global shortcut to invoke the action on the menu.
  36. /// </summary>
  37. public Key Shortcut { get; }
  38. /// <summary>
  39. /// Gets or sets the title.
  40. /// </summary>
  41. /// <value>The title.</value>
  42. /// <remarks>
  43. /// The colour of the <see cref="StatusItem.Title"/> will be changed after each ~.
  44. /// A <see cref="StatusItem.Title"/> set to `~F1~ Help` will render as *F1* using <see cref="ColorScheme.HotNormal"/> and
  45. /// *Help* as <see cref="ColorScheme.HotNormal"/>.
  46. /// </remarks>
  47. public ustring Title { get; set;}
  48. /// <summary>
  49. /// Gets or sets the action to be invoked when the statusbar item is triggered
  50. /// </summary>
  51. /// <value>Action to invoke.</value>
  52. public Action Action { get; }
  53. };
  54. /// <summary>
  55. /// A status bar is a <see cref="View"/> that snaps to the bottom of a <see cref="Toplevel"/> displaying set of <see cref="StatusItem"/>s.
  56. /// The <see cref="StatusBar"/> should be context sensitive. This means, if the main menu and an open text editor are visible, the items probably shown will
  57. /// be ~F1~ Help ~F2~ Save ~F3~ Load. While a dialog to ask a file to load is executed, the remaining commands will probably be ~F1~ Help.
  58. /// So for each context must be a new instance of a statusbar.
  59. /// </summary>
  60. public class StatusBar : View {
  61. // After attempting to implement this, I noticed that there are hard dependencies
  62. // on StatusBar and MenuBars within core. They will need to be refactored for having the
  63. // StatusBar work at the top
  64. #if SNAP_TO_TOP
  65. /// <summary>
  66. /// The style supported by StatusBar
  67. /// </summary>
  68. public enum StatusBarStyle {
  69. Default = 0,
  70. /// <summary>
  71. /// The StatusBar will snap at the the bottom line of the Parent view.
  72. /// If the console window is made larger while the app is runing, the StatusBar
  73. /// will continue to snap to the bottom line of the Parent, staying visible.
  74. /// On consoles that support resizing of console apps (e.g. Windows Terminal and ConEmu),
  75. /// if the console window is subsequently made shorter, the status bar will remain visible
  76. /// as the Parent view resizes. If Parent is null, the StatusBar will snap to the bottom line
  77. /// of the console window.
  78. /// This is the default.
  79. /// </summary>
  80. SnapToBottom = Default,
  81. /// <summary>
  82. /// The StatusBar will act identically to MenuBar, snapping to the first line of the
  83. /// console window.
  84. /// </summary>
  85. SnapToTop = 1,
  86. }
  87. public StatusBarStyle Style { get; set; } = StatusBarStyle.Default;
  88. #endif
  89. /// <summary>
  90. /// The parent view of the <see cref="StatusBar"/>.
  91. /// </summary>
  92. public View Parent { get; set; }
  93. /// <summary>
  94. /// The items that compose the <see cref="StatusBar"/>
  95. /// </summary>
  96. public StatusItem [] Items { get; set; }
  97. /// <summary>
  98. /// Initializes a new instance of the <see cref="StatusBar"/> class with the specified set of <see cref="StatusItem"/>s.
  99. /// The <see cref="StatusBar"/> will be drawn on the lowest line of the terminal or <see cref="StatusBar.Parent"/> (if not null).
  100. /// </summary>
  101. /// <param name="items">A list of statusbar items.</param>
  102. public StatusBar (StatusItem [] items) : base ()
  103. {
  104. Width = Dim.Fill ();
  105. Height = 1;
  106. Items = items;
  107. CanFocus = false;
  108. ColorScheme = Colors.Menu;
  109. X = 0;
  110. Y = Driver.Rows - 1;
  111. Width = Dim.Fill ();
  112. Height = 1;
  113. Application.Loaded += (sender, e) => {
  114. X = 0;
  115. Height = 1;
  116. #if SNAP_TO_TOP
  117. switch (Style) {
  118. case StatusBarStyle.SnapToTop:
  119. X = 0;
  120. Y = 0;
  121. break;
  122. case StatusBarStyle.SnapToBottom:
  123. #endif
  124. if (Parent == null) {
  125. Y = e.Rows - 1;
  126. } else {
  127. Y = Pos.Bottom (Parent);
  128. }
  129. #if SNAP_TO_TOP
  130. break;
  131. }
  132. #endif
  133. };
  134. }
  135. Attribute ToggleScheme (Attribute scheme)
  136. {
  137. var result = scheme == ColorScheme.Normal ? ColorScheme.HotNormal : ColorScheme.Normal;
  138. Driver.SetAttribute (result);
  139. return result;
  140. }
  141. ///<inheritdoc cref="Redraw"/>
  142. public override void Redraw (Rect region)
  143. {
  144. //if (Frame.Y != Driver.Rows - 1) {
  145. // Frame = new Rect (Frame.X, Driver.Rows - 1, Frame.Width, Frame.Height);
  146. // Y = Driver.Rows - 1;
  147. // SetNeedsDisplay ();
  148. //}
  149. Move (0, 0);
  150. Driver.SetAttribute (ColorScheme.Normal);
  151. for (int i = 0; i < Frame.Width; i++)
  152. Driver.AddRune (' ');
  153. Move (1, 0);
  154. var scheme = ColorScheme.Normal;
  155. Driver.SetAttribute (scheme);
  156. for (int i = 0; i < Items.Length; i++) {
  157. var title = Items [i].Title;
  158. for (int n = 0; n < title.Length; n++) {
  159. if (title [n] == '~') {
  160. scheme = ToggleScheme (scheme);
  161. continue;
  162. }
  163. Driver.AddRune (title [n]);
  164. }
  165. Driver.AddRune (' ');
  166. }
  167. }
  168. ///<inheritdoc cref="ProcessHotKey"/>
  169. public override bool ProcessHotKey (KeyEvent kb)
  170. {
  171. foreach (var item in Items) {
  172. if (kb.Key == item.Shortcut) {
  173. if (item.Action != null) item.Action ();
  174. return true;
  175. }
  176. }
  177. return false;
  178. }
  179. }
  180. }