ComputedLayout.cs 16 KB

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