ComputedLayout.cs 12 KB

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