ComputedLayout.cs 17 KB

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