Toplevel.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. //
  2. // Toplevel.cs: Toplevel views can be modally executed
  3. //
  4. // Authors:
  5. // Miguel de Icaza ([email protected])
  6. //
  7. using System;
  8. using System.ComponentModel;
  9. namespace Terminal.Gui {
  10. /// <summary>
  11. /// Toplevel views can be modally executed.
  12. /// </summary>
  13. /// <remarks>
  14. /// <para>
  15. /// Toplevels can be modally executing views, and they return control
  16. /// to the caller when the "Running" property is set to false, or
  17. /// by calling <see cref="M:Terminal.Gui.Application.RequestStop()"/>
  18. /// </para>
  19. /// <para>
  20. /// There will be a toplevel created for you on the first time use
  21. /// and can be accessed from the property <see cref="P:Terminal.Gui.Application.Top"/>,
  22. /// but new toplevels can be created and ran on top of it. To run, create the
  23. /// toplevel and then invoke <see cref="M:Terminal.Gui.Application.Run"/> with the
  24. /// new toplevel.
  25. /// </para>
  26. /// <para>
  27. /// TopLevels can also opt-in to more sophisticated initialization
  28. /// by implementing <see cref="ISupportInitialize"/>. When they do
  29. /// so, the <see cref="ISupportInitialize.BeginInit"/> and
  30. /// <see cref="ISupportInitialize.EndInit"/> methods will be called
  31. /// before running the view.
  32. /// If first-run-only initialization is preferred, the <see cref="ISupportInitializeNotification"/>
  33. /// can be implemented too, in which case the <see cref="ISupportInitialize"/>
  34. /// methods will only be called if <see cref="ISupportInitializeNotification.IsInitialized"/>
  35. /// is <see langword="false"/>. This allows proper View inheritance hierarchies
  36. /// to override base class layout code optimally by doing so only on first run,
  37. /// instead of on every run.
  38. /// </para>
  39. /// </remarks>
  40. public class Toplevel : View {
  41. /// <summary>
  42. /// Gets or sets whether the Mainloop for this <see cref="Toplevel"/> is running or not. Setting
  43. /// this property to false will cause the MainLoop to exit.
  44. /// </summary>
  45. public bool Running { get; set; }
  46. /// <summary>
  47. /// Fired once the Toplevel's MainLoop has started it's first iteration.
  48. /// Subscribe to this event to perform tasks when the <see cref="Toplevel"/> has been laid out and focus has been set.
  49. /// changes. A Ready event handler is a good place to finalize initialization after calling `<see cref="Application.Run()"/>(topLevel)`.
  50. /// </summary>
  51. public event EventHandler Ready;
  52. /// <summary>
  53. /// Called from Application.RunLoop after the <see cref="Toplevel"/> has entered it's first iteration of the loop.
  54. /// </summary>
  55. internal virtual void OnReady ()
  56. {
  57. Ready?.Invoke (this, EventArgs.Empty);
  58. }
  59. /// <summary>
  60. /// Initializes a new instance of the <see cref="Toplevel"/> class with the specified absolute layout.
  61. /// </summary>
  62. /// <param name="frame">Frame.</param>
  63. public Toplevel (Rect frame) : base (frame)
  64. {
  65. Initialize ();
  66. }
  67. /// <summary>
  68. /// Initializes a new instance of the <see cref="Toplevel"/> class with Computed layout, defaulting to <see langword="async"/> full screen.
  69. /// </summary>
  70. public Toplevel () : base ()
  71. {
  72. Initialize ();
  73. Width = Dim.Fill ();
  74. Height = Dim.Fill ();
  75. }
  76. void Initialize ()
  77. {
  78. ColorScheme = Colors.Base;
  79. }
  80. /// <summary>
  81. /// Convenience factory method that creates a new toplevel with the current terminal dimensions.
  82. /// </summary>
  83. /// <returns>The create.</returns>
  84. public static Toplevel Create ()
  85. {
  86. return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
  87. }
  88. /// <summary>
  89. /// Gets or sets a value indicating whether this <see cref="Toplevel"/> can focus.
  90. /// </summary>
  91. /// <value><c>true</c> if can focus; otherwise, <c>false</c>.</value>
  92. public override bool CanFocus {
  93. get => true;
  94. }
  95. /// <summary>
  96. /// Determines whether the <see cref="Toplevel"/> is modal or not.
  97. /// Causes <see cref="ProcessKey(KeyEvent)"/> to propagate keys upwards
  98. /// by default unless set to <see langword="true"/>.
  99. /// </summary>
  100. public bool Modal { get; set; }
  101. /// <summary>
  102. /// Check id current toplevel has menu bar
  103. /// </summary>
  104. public MenuBar MenuBar { get; set; }
  105. /// <summary>
  106. /// Check id current toplevel has status bar
  107. /// </summary>
  108. public StatusBar StatusBar { get; set; }
  109. ///<inheritdoc cref="ProcessKey"/>
  110. public override bool ProcessKey (KeyEvent keyEvent)
  111. {
  112. if (base.ProcessKey (keyEvent))
  113. return true;
  114. switch (keyEvent.Key) {
  115. case Key.ControlQ:
  116. // FIXED: stop current execution of this container
  117. Application.RequestStop ();
  118. break;
  119. case Key.ControlZ:
  120. Driver.Suspend ();
  121. return true;
  122. #if false
  123. case Key.F5:
  124. Application.DebugDrawBounds = !Application.DebugDrawBounds;
  125. SetNeedsDisplay ();
  126. return true;
  127. #endif
  128. case Key.Tab:
  129. case Key.CursorRight:
  130. case Key.CursorDown:
  131. case Key.ControlI: // Unix
  132. var old = Focused;
  133. if (!FocusNext ())
  134. FocusNext ();
  135. if (old != Focused) {
  136. old?.SetNeedsDisplay ();
  137. Focused?.SetNeedsDisplay ();
  138. }
  139. return true;
  140. case Key.CursorLeft:
  141. case Key.CursorUp:
  142. case Key.BackTab:
  143. old = Focused;
  144. if (!FocusPrev ())
  145. FocusPrev ();
  146. if (old != Focused) {
  147. old?.SetNeedsDisplay ();
  148. Focused?.SetNeedsDisplay ();
  149. }
  150. return true;
  151. case Key.ControlL:
  152. Application.Refresh ();
  153. return true;
  154. }
  155. return false;
  156. }
  157. ///<inheritdoc cref="Add"/>
  158. public override void Add (View view)
  159. {
  160. if (this == Application.Top) {
  161. if (view is MenuBar)
  162. MenuBar = view as MenuBar;
  163. if (view is StatusBar)
  164. StatusBar = view as StatusBar;
  165. }
  166. base.Add (view);
  167. }
  168. ///<inheritdoc cref="Remove"/>
  169. public override void Remove (View view)
  170. {
  171. if (this == Application.Top) {
  172. if (view is MenuBar)
  173. MenuBar = null;
  174. if (view is StatusBar)
  175. StatusBar = null;
  176. }
  177. base.Remove (view);
  178. }
  179. ///<inheritdoc cref="RemoveAll"/>
  180. public override void RemoveAll ()
  181. {
  182. if (this == Application.Top) {
  183. MenuBar = null;
  184. StatusBar = null;
  185. }
  186. base.RemoveAll ();
  187. }
  188. internal void EnsureVisibleBounds (Toplevel top, int x, int y, out int nx, out int ny)
  189. {
  190. nx = Math.Max (x, 0);
  191. nx = nx + top.Frame.Width > Driver.Cols ? Math.Max (Driver.Cols - top.Frame.Width, 0) : nx;
  192. bool m, s;
  193. if (SuperView == null || SuperView.GetType () != typeof (Toplevel))
  194. m = Application.Top.MenuBar != null;
  195. else
  196. m = ((Toplevel)SuperView).MenuBar != null;
  197. int l = m ? 1 : 0;
  198. ny = Math.Max (y, l);
  199. if (SuperView == null || SuperView.GetType () != typeof (Toplevel))
  200. s = Application.Top.StatusBar != null;
  201. else
  202. s = ((Toplevel)SuperView).StatusBar != null;
  203. l = s ? Driver.Rows - 1 : Driver.Rows;
  204. ny = Math.Min (ny, l);
  205. ny = ny + top.Frame.Height > l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
  206. }
  207. internal void PositionToplevels ()
  208. {
  209. if (this != Application.Top) {
  210. EnsureVisibleBounds (this, Frame.X, Frame.Y, out int nx, out int ny);
  211. if ((nx != Frame.X || ny != Frame.Y) && LayoutStyle != LayoutStyle.Computed) {
  212. X = nx;
  213. Y = ny;
  214. }
  215. } else {
  216. foreach (var top in Subviews) {
  217. if (top is Toplevel) {
  218. EnsureVisibleBounds ((Toplevel)top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
  219. if ((nx != top.Frame.X || ny != top.Frame.Y) && top.LayoutStyle != LayoutStyle.Computed) {
  220. top.X = nx;
  221. top.Y = ny;
  222. }
  223. if (StatusBar != null) {
  224. if (ny + top.Frame.Height > Driver.Rows - 1) {
  225. if (top.Height is Dim.DimFill)
  226. top.Height = Dim.Fill () - 1;
  227. }
  228. if (StatusBar.Frame.Y != Driver.Rows - 1) {
  229. StatusBar.Y = Driver.Rows - 1;
  230. SetNeedsDisplay ();
  231. }
  232. }
  233. }
  234. }
  235. }
  236. }
  237. ///<inheritdoc cref="Redraw"/>
  238. public override void Redraw (Rect region)
  239. {
  240. Application.CurrentView = this;
  241. if (IsCurrentTop) {
  242. if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
  243. Driver.SetAttribute (Colors.TopLevel.Normal);
  244. Clear (region);
  245. Driver.SetAttribute (Colors.Base.Normal);
  246. }
  247. foreach (var view in Subviews) {
  248. if (view.Frame.IntersectsWith (region)) {
  249. view.SetNeedsLayout ();
  250. view.SetNeedsDisplay (view.Bounds);
  251. }
  252. }
  253. ClearNeedsDisplay ();
  254. }
  255. base.Redraw (base.Bounds);
  256. }
  257. /// <summary>
  258. /// This method is invoked by Application.Begin as part of the Application.Run after
  259. /// the views have been laid out, and before the views are drawn for the first time.
  260. /// </summary>
  261. public virtual void WillPresent ()
  262. {
  263. FocusFirst ();
  264. }
  265. }
  266. }