ComputedLayout.cs 17 KB

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