ComputedLayout.cs 16 KB

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