Toplevel.cs 8.7 KB

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