ComputedLayout.cs 17 KB

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