ComputedLayout.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. using NStack;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using Terminal.Gui;
  8. namespace UICatalog.Scenarios {
  9. /// <summary>
  10. /// This Scenario demonstrates how to use Termina.gui's Dim and Pos Layout System.
  11. /// [x] - Using Dim.Fill to fill a window
  12. /// [x] - Using Dim.Fill and Dim.Pos to automatically align controls based on an initial control
  13. /// [ ] - ...
  14. /// </summary>
  15. [ScenarioMetadata (Name: "Computed Layout", Description: "Demonstrates the Computed (Dim and Pos) Layout System.")]
  16. [ScenarioCategory ("Layout")]
  17. public class ComputedLayout : Scenario {
  18. public override void Setup ()
  19. {
  20. var statusBar = new StatusBar (new StatusItem [] {
  21. new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
  22. });
  23. Application.Top.Add (statusBar);
  24. // Demonstrate using Dim to create a horizontal ruler that always measures the parent window's width
  25. const string rule = "|123456789";
  26. var horizontalRuler = new Label ("") {
  27. X = 0,
  28. Y = 0,
  29. Width = Dim.Fill (),
  30. ColorScheme = Colors.Error
  31. };
  32. Win.Add (horizontalRuler);
  33. // Demonstrate using Dim to create a vertical ruler that always measures the parent window's height
  34. const string vrule = "|\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
  35. var verticalRuler = new Label ("") {
  36. X = 0,
  37. Y = 0,
  38. Width = 1,
  39. Height = Dim.Fill (),
  40. ColorScheme = Colors.Error
  41. };
  42. Win.LayoutComplete += (a) => {
  43. horizontalRuler.Text = rule.Repeat ((int)Math.Ceiling ((double)(horizontalRuler.Bounds.Width) / (double)rule.Length)) [0..(horizontalRuler.Bounds.Width)];
  44. verticalRuler.Text = vrule.Repeat ((int)Math.Ceiling ((double)(verticalRuler.Bounds.Height * 2) / (double)rule.Length)) [0..(verticalRuler.Bounds.Height * 2)];
  45. };
  46. Win.Add (verticalRuler);
  47. // Demonstrate At - Using Pos.At to locate a view in an absolute location
  48. var atButton = new Button ("At(2,1)") {
  49. X = Pos.At (2),
  50. Y = Pos.At (1)
  51. };
  52. Win.Add (atButton);
  53. // Throw in a literal absolute - Should funciton identically to above
  54. var absoluteButton = new Button ("X = 30, Y = 1") {
  55. X = 30,
  56. Y = 1
  57. };
  58. Win.Add (absoluteButton);
  59. // Demonstrate using Dim to create a window that fills the parent with a margin
  60. int margin = 10;
  61. var subWin = new Window ($"Centered Window with {margin} character margin") {
  62. X = Pos.Center (),
  63. Y = 2,
  64. Width = Dim.Fill (margin),
  65. Height = 7
  66. };
  67. Win.Add (subWin);
  68. int i = 1;
  69. string txt = "Resize the terminal to see computed layout in action.";
  70. var labelList = new List<Label> ();
  71. labelList.Add (new Label ($"The lines below show different TextAlignments"));
  72. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  73. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  74. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  75. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  76. subWin.Add (labelList.ToArray ());
  77. // #522 repro?
  78. var frameView = new FrameView ($"Centered FrameView with {margin} character margin") {
  79. X = Pos.Center (),
  80. Y = Pos.Bottom (subWin),
  81. Width = Dim.Fill (margin),
  82. Height = 7
  83. };
  84. Win.Add (frameView);
  85. i = 1;
  86. labelList = new List<Label> ();
  87. labelList.Add (new Label ($"The lines below show different TextAlignments"));
  88. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  89. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  90. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  91. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  92. frameView.Add (labelList.ToArray ());
  93. // Demonstrate Dim & Pos using percentages - a TextField that is 30% height and 80% wide
  94. var textView = new TextView () {
  95. X = Pos.Center (),
  96. Y = Pos.Percent (50),
  97. Width = Dim.Percent (80),
  98. Height = Dim.Percent (10),
  99. ColorScheme = Colors.TopLevel,
  100. };
  101. textView.Text = $"This TextView should horizontally & vertically centered and \n10% of the screeen height, and 80% of its width.";
  102. Win.Add (textView);
  103. var oddballButton = new Button ("The Buttons below should be centered") {
  104. X = Pos.Center (),
  105. Y = Pos.Bottom (textView) + 1
  106. };
  107. Win.Add (oddballButton);
  108. #region Issue2358
  109. // Demonstrate odd-ball Combine scenarios
  110. // Until https://github.com/gui-cs/Terminal.Gui/issues/2358 is fixed these won't work right
  111. oddballButton = new Button ("Center + 0") {
  112. X = Pos.Center () + 0,
  113. Y = Pos.Bottom (oddballButton)
  114. };
  115. Win.Add (oddballButton);
  116. oddballButton = new Button ("0 + Center") {
  117. X = 0 + Pos.Center (),
  118. Y = Pos.Bottom (oddballButton)
  119. };
  120. Win.Add (oddballButton);
  121. // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
  122. // The `- Pos.Percent(5)` is there so at least something is visible
  123. oddballButton = new Button ("Center + Center - Percent(50)") {
  124. X = Pos.Center () + Pos.Center () - Pos.Percent(50),
  125. Y = Pos.Bottom (oddballButton)
  126. };
  127. Win.Add (oddballButton);
  128. // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
  129. // The `- Pos.Percent(5)` is there so at least something is visible
  130. oddballButton = new Button ("Percent(50) + Center - Percent(50)") {
  131. X = Pos.Percent (50) + Pos.Center () - Pos.Percent (50),
  132. Y = Pos.Bottom (oddballButton)
  133. };
  134. Win.Add (oddballButton);
  135. // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
  136. // The `- Pos.Percent(5)` is there so at least something is visible
  137. oddballButton = new Button ("Center + Percent(50) - Percent(50)") {
  138. X = Pos.Center () + Pos.Percent (50) - Pos.Percent (50),
  139. Y = Pos.Bottom (oddballButton)
  140. };
  141. Win.Add (oddballButton);
  142. #endregion
  143. // This demonstrates nonsense: Same as At(0)
  144. oddballButton = new Button ("Center - Center - Percent(50)") {
  145. X = Pos.Center () + Pos.Center () - Pos.Percent (50),
  146. Y = Pos.Bottom (oddballButton)
  147. };
  148. Win.Add (oddballButton);
  149. // This demonstrates combining Percents)
  150. oddballButton = new Button ("Percent(40) + Percent(10)") {
  151. X = Pos.Percent (40) + Pos.Percent(10),
  152. Y = Pos.Bottom (oddballButton)
  153. };
  154. Win.Add (oddballButton);
  155. // Demonstrate AnchorEnd - Button is anchored to bottom/right
  156. var anchorButton = new Button ("Button using AnchorEnd") {
  157. Y = Pos.AnchorEnd () - 1,
  158. };
  159. anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
  160. anchorButton.Clicked += () => {
  161. // Ths demonstrates how to have a dynamically sized button
  162. // Each time the button is clicked the button's text gets longer
  163. // The call to Win.LayoutSubviews causes the Computed layout to
  164. // get updated.
  165. anchorButton.Text += "!";
  166. Win.LayoutSubviews ();
  167. };
  168. Win.Add (anchorButton);
  169. // Demonstrate AnchorEnd(n)
  170. // This is intentionally convoluted to illustrate potential bugs.
  171. var anchorEndLabel1 = new Label ("This Button should be the 2nd to last line (AnchorEnd (2)).") {
  172. TextAlignment = Terminal.Gui.TextAlignment.Centered,
  173. ColorScheme = Colors.Menu,
  174. Width = Dim.Fill (1),
  175. X = 1,
  176. Y = Pos.AnchorEnd (2)
  177. };
  178. Win.Add (anchorEndLabel1);
  179. // Demonstrate DimCombine (via AnchorEnd(n) - 1)
  180. // This is intentionally convoluted to illustrate potential bugs.
  181. var anchorEndLabel2 = new Label ("This Button should be the 3rd to last line (AnchorEnd (2) - 1).") {
  182. TextAlignment = Terminal.Gui.TextAlignment.Centered,
  183. ColorScheme = Colors.Menu,
  184. Width = Dim.Fill (1),
  185. X = 1,
  186. Y = Pos.AnchorEnd (2) - 1 // Pos.Combine
  187. };
  188. Win.Add (anchorEndLabel2);
  189. // Show positioning vertically using Pos.AnchorEnd via Pos.Combine
  190. var leftButton = new Button ("Left") {
  191. Y = Pos.AnchorEnd () - 1 // Pos.Combine
  192. };
  193. leftButton.Clicked += () => {
  194. // Ths demonstrates how to have a dynamically sized button
  195. // Each time the button is clicked the button's text gets longer
  196. // The call to Win.LayoutSubviews causes the Computed layout to
  197. // get updated.
  198. leftButton.Text += "!";
  199. Win.LayoutSubviews ();
  200. };
  201. // show positioning vertically using Pos.AnchorEnd
  202. var centerButton = new Button ("Center") {
  203. X = Pos.Center (),
  204. Y = Pos.AnchorEnd (1) // Pos.AnchorEnd(1)
  205. };
  206. centerButton.Clicked += () => {
  207. // Ths demonstrates how to have a dynamically sized button
  208. // Each time the button is clicked the button's text gets longer
  209. // The call to Win.LayoutSubviews causes the Computed layout to
  210. // get updated.
  211. centerButton.Text += "!";
  212. Win.LayoutSubviews ();
  213. };
  214. // show positioning vertically using another window and Pos.Bottom
  215. var rightButton = new Button ("Right") {
  216. Y = Pos.Y (centerButton)
  217. };
  218. rightButton.Clicked += () => {
  219. // Ths demonstrates how to have a dynamically sized button
  220. // Each time the button is clicked the button's text gets longer
  221. // The call to Win.LayoutSubviews causes the Computed layout to
  222. // get updated.
  223. rightButton.Text += "!";
  224. Win.LayoutSubviews ();
  225. };
  226. // Center three buttons with 5 spaces between them
  227. leftButton.X = Pos.Left (centerButton) - (Pos.Right (leftButton) - Pos.Left (leftButton)) - 5;
  228. rightButton.X = Pos.Right (centerButton) + 5;
  229. Win.Add (leftButton);
  230. Win.Add (centerButton);
  231. Win.Add (rightButton);
  232. }
  233. public override void Run ()
  234. {
  235. base.Run ();
  236. }
  237. private void Quit ()
  238. {
  239. Application.RequestStop ();
  240. }
  241. }
  242. internal static class StringExtensions {
  243. public static string Repeat (this string instr, int n)
  244. {
  245. if (n <= 0) {
  246. return null;
  247. }
  248. if (string.IsNullOrEmpty (instr) || n == 1) {
  249. return instr;
  250. }
  251. return new StringBuilder (instr.Length * n)
  252. .Insert (0, instr, n)
  253. .ToString ();
  254. }
  255. }
  256. }