ComputedLayout.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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 Terminal.Gui's Dim and Pos Layout System.
  8. /// </summary>
  9. [ScenarioMetadata ("Computed Layout", "Demonstrates the Computed (Dim and Pos) Layout System.")]
  10. [ScenarioCategory ("Layout")]
  11. public class ComputedLayout : Scenario
  12. {
  13. public override void Main ()
  14. {
  15. Application.Init ();
  16. Window app = new ()
  17. {
  18. Title = GetQuitKeyAndName ()
  19. };
  20. // Demonstrate using Dim to create a horizontal ruler that always measures the parent window's width
  21. const string rule = "|123456789";
  22. var horizontalRuler = new Label
  23. {
  24. X = 0,
  25. Y = 0,
  26. Width = Dim.Fill (),
  27. Height = 1,
  28. ColorScheme = Colors.ColorSchemes ["Error"],
  29. Text = rule
  30. };
  31. app.Add (horizontalRuler);
  32. // Demonstrate using Dim to create a vertical ruler that always measures the parent window's height
  33. const string vrule = "|\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
  34. var verticalRuler = new Label
  35. {
  36. X = 0,
  37. Y = 0,
  38. Width = 1,
  39. Height = Dim.Fill (),
  40. ColorScheme = Colors.ColorSchemes ["Error"],
  41. Text = vrule
  42. };
  43. app.SubviewsLaidOut += (s, a) =>
  44. {
  45. if (horizontalRuler.Viewport.Width == 0 || horizontalRuler.Viewport.Height == 0)
  46. {
  47. return;
  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 = "Absolute(2,1)", X = Pos.Absolute (2), Y = Pos.Absolute (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
  66. {
  67. X = Pos.Center (), Y = 2, Width = Dim.Fill (margin), Height = 7,
  68. Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable | ViewArrangement.Overlapped
  69. };
  70. subWin.Initialized += (s, a) =>
  71. {
  72. subWin.Title =
  73. $"{subWin.GetType ().Name} {{X={subWin.X},Y={subWin.Y},Width={subWin.Width},Height={subWin.Height}}}";
  74. };
  75. app.Add (subWin);
  76. var i = 1;
  77. var txt = "Resize the terminal to see computed layout in action.";
  78. List<Label> labelList = new ();
  79. labelList.Add (new() { Text = "The lines below show different alignment" });
  80. labelList.Add (
  81. new()
  82. {
  83. TextAlignment = Alignment.Start,
  84. Width = Dim.Fill (),
  85. X = 0,
  86. Y = Pos.Bottom (labelList.LastOrDefault ()),
  87. ColorScheme = Colors.ColorSchemes ["Dialog"],
  88. Text = $"{i++}-{txt}"
  89. }
  90. );
  91. labelList.Add (
  92. new()
  93. {
  94. TextAlignment = Alignment.End,
  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()
  104. {
  105. TextAlignment = Alignment.Center,
  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()
  115. {
  116. TextAlignment = Alignment.Fill,
  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 ();
  134. labelList.Add (new() { Text = "The lines below show different alignment" });
  135. labelList.Add (
  136. new()
  137. {
  138. TextAlignment = Alignment.Start,
  139. Width = Dim.Fill (),
  140. X = 0,
  141. Y = Pos.Bottom (labelList.LastOrDefault ()),
  142. ColorScheme = Colors.ColorSchemes ["Dialog"],
  143. Text = $"{i++}-{txt}"
  144. }
  145. );
  146. labelList.Add (
  147. new()
  148. {
  149. TextAlignment = Alignment.End,
  150. Width = Dim.Fill (),
  151. X = 0,
  152. Y = Pos.Bottom (labelList.LastOrDefault ()),
  153. ColorScheme = Colors.ColorSchemes ["Dialog"],
  154. Text = $"{i++}-{txt}"
  155. }
  156. );
  157. labelList.Add (
  158. new()
  159. {
  160. TextAlignment = Alignment.Center,
  161. Width = Dim.Fill (),
  162. X = 0,
  163. Y = Pos.Bottom (labelList.LastOrDefault ()),
  164. ColorScheme = Colors.ColorSchemes ["Dialog"],
  165. Text = $"{i++}-{txt}"
  166. }
  167. );
  168. labelList.Add (
  169. new()
  170. {
  171. TextAlignment = Alignment.Fill,
  172. Width = Dim.Fill (),
  173. X = 0,
  174. Y = Pos.Bottom (labelList.LastOrDefault ()),
  175. ColorScheme = Colors.ColorSchemes ["Dialog"],
  176. Text = $"{i++}-{txt}"
  177. }
  178. );
  179. frameView.Add (labelList.ToArray ());
  180. app.Add (frameView);
  181. frameView = new()
  182. {
  183. X = Pos.Right (frameView), Y = Pos.Top (frameView), Width = Dim.Fill (), Height = 7
  184. };
  185. frameView.Initialized += (sender, args) =>
  186. {
  187. var fv = sender as FrameView;
  188. fv.Title =
  189. $"{frameView.GetType ().Name} {{X={fv.X},Y={fv.Y},Width={fv.Width},Height={fv.Height}}}";
  190. };
  191. labelList = new ();
  192. labelList.Add (new() { Text = "The lines below show different alignment" });
  193. labelList.Add (
  194. new()
  195. {
  196. TextAlignment = Alignment.Start,
  197. Width = Dim.Fill (),
  198. X = 0,
  199. Y = Pos.Bottom (labelList.LastOrDefault ()),
  200. ColorScheme = Colors.ColorSchemes ["Dialog"],
  201. Text = $"{i++}-{txt}"
  202. }
  203. );
  204. labelList.Add (
  205. new()
  206. {
  207. TextAlignment = Alignment.End,
  208. Width = Dim.Fill (),
  209. X = 0,
  210. Y = Pos.Bottom (labelList.LastOrDefault ()),
  211. ColorScheme = Colors.ColorSchemes ["Dialog"],
  212. Text = $"{i++}-{txt}"
  213. }
  214. );
  215. labelList.Add (
  216. new()
  217. {
  218. TextAlignment = Alignment.Center,
  219. Width = Dim.Fill (),
  220. X = 0,
  221. Y = Pos.Bottom (labelList.LastOrDefault ()),
  222. ColorScheme = Colors.ColorSchemes ["Dialog"],
  223. Text = $"{i++}-{txt}"
  224. }
  225. );
  226. labelList.Add (
  227. new()
  228. {
  229. TextAlignment = Alignment.Fill,
  230. Width = Dim.Fill (),
  231. X = 0,
  232. Y = Pos.Bottom (labelList.LastOrDefault ()),
  233. ColorScheme = Colors.ColorSchemes ["Dialog"],
  234. Text = $"{i++}-{txt}"
  235. }
  236. );
  237. frameView.Add (labelList.ToArray ());
  238. app.Add (frameView);
  239. // Demonstrate Dim & Pos using percentages - a TextField that is 30% height and 80% wide
  240. var textView = new TextView
  241. {
  242. X = Pos.Center (),
  243. Y = Pos.Percent (50),
  244. Width = Dim.Percent (80),
  245. Height = Dim.Percent (10),
  246. ColorScheme = Colors.ColorSchemes ["TopLevel"]
  247. };
  248. textView.Text =
  249. "This TextView should horizontally & vertically centered and \n10% of the screeen height, and 80% of its width.";
  250. app.Add (textView);
  251. var oddballButton = new Button
  252. {
  253. Text = "These buttons demo convoluted PosCombine scenarios",
  254. X = Pos.Center (),
  255. Y = Pos.Bottom (textView) + 1
  256. };
  257. app.Add (oddballButton);
  258. #region Issue2358
  259. // Demonstrate odd-ball Combine scenarios
  260. // Until https://github.com/gui-cs/Terminal.Gui/issues/2358 is fixed these won't work right
  261. oddballButton = new() { Text = "Center + 0", X = Pos.Center () + 0, Y = Pos.Bottom (oddballButton) };
  262. app.Add (oddballButton);
  263. oddballButton = new() { Text = "Center + 1", X = Pos.Center () + 1, Y = Pos.Bottom (oddballButton) };
  264. app.Add (oddballButton);
  265. oddballButton = new() { Text = "0 + Center", X = 0 + Pos.Center (), Y = Pos.Bottom (oddballButton) };
  266. app.Add (oddballButton);
  267. oddballButton = new() { Text = "1 + Center", X = 1 + Pos.Center (), Y = Pos.Bottom (oddballButton) };
  268. app.Add (oddballButton);
  269. oddballButton = new() { Text = "Center - 1", X = Pos.Center () - 1, Y = Pos.Bottom (oddballButton) };
  270. app.Add (oddballButton);
  271. // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
  272. // The `- Pos.Percent(5)` is there so at least something is visible
  273. oddballButton = new()
  274. {
  275. Text = "Center + Center - Percent(50)",
  276. X = Pos.Center () + Pos.Center () - Pos.Percent (50),
  277. Y = Pos.Bottom (oddballButton)
  278. };
  279. app.Add (oddballButton);
  280. // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
  281. // The `- Pos.Percent(5)` is there so at least something is visible
  282. oddballButton = new()
  283. {
  284. Text = "Percent(50) + Center - Percent(50)",
  285. X = Pos.Percent (50) + Pos.Center () - Pos.Percent (50),
  286. Y = Pos.Bottom (oddballButton)
  287. };
  288. app.Add (oddballButton);
  289. // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
  290. // The `- Pos.Percent(5)` is there so at least something is visible
  291. oddballButton = new()
  292. {
  293. Text = "Center + Percent(50) - Percent(50)",
  294. X = Pos.Center () + Pos.Percent (50) - Pos.Percent (50),
  295. Y = Pos.Bottom (oddballButton)
  296. };
  297. app.Add (oddballButton);
  298. #endregion
  299. // This demonstrates nonsense: Same as At(0)
  300. oddballButton = new()
  301. {
  302. Text = "Center - Center - Percent(50)",
  303. X = Pos.Center () + Pos.Center () - Pos.Percent (50),
  304. Y = Pos.Bottom (oddballButton)
  305. };
  306. app.Add (oddballButton);
  307. // This demonstrates combining Percents)
  308. oddballButton = new()
  309. {
  310. Text = "Percent(40) + Percent(10)", X = Pos.Percent (40) + Pos.Percent (10), Y = Pos.Bottom (oddballButton)
  311. };
  312. app.Add (oddballButton);
  313. // Demonstrate AnchorEnd - Button is anchored to bottom/right
  314. var anchorButton = new Button { Text = "Button using AnchorEnd", Y = Pos.AnchorEnd () };
  315. anchorButton.X = Pos.AnchorEnd ();
  316. anchorButton.Accepting += (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. anchorButton.Text += "!";
  323. };
  324. app.Add (anchorButton);
  325. // Demonstrate AnchorEnd(n)
  326. // This is intentionally convoluted to illustrate potential bugs.
  327. var anchorEndLabel1 = new Label
  328. {
  329. Text = "This Label should be the 3rd to last line (AnchorEnd (3)).",
  330. TextAlignment = Alignment.Center,
  331. ColorScheme = Colors.ColorSchemes ["Menu"],
  332. Width = Dim.Fill (5),
  333. X = 5,
  334. Y = Pos.AnchorEnd (3)
  335. };
  336. app.Add (anchorEndLabel1);
  337. // Demonstrate DimCombine (via AnchorEnd(n) - 1)
  338. // This is intentionally convoluted to illustrate potential bugs.
  339. var anchorEndLabel2 = new TextField
  340. {
  341. Text =
  342. "This TextField should be the 4th to last line (AnchorEnd (3) - 1).",
  343. TextAlignment = Alignment.Start,
  344. ColorScheme = Colors.ColorSchemes ["Menu"],
  345. Width = Dim.Fill (5),
  346. X = 5,
  347. Y = Pos.AnchorEnd (3) - 1 // Pos.Combine
  348. };
  349. app.Add (anchorEndLabel2);
  350. // Demonstrate AnchorEnd() in combination with Pos.Align to align a set of buttons centered across the
  351. // bottom - 1
  352. // This is intentionally convoluted to illustrate potential bugs.
  353. var leftButton = new Button
  354. {
  355. Text = "Left",
  356. X = Pos.Align (Alignment.Center),
  357. Y = Pos.AnchorEnd () - 1
  358. };
  359. leftButton.Accepting += (s, e) =>
  360. {
  361. // This demonstrates how to have a dynamically sized button
  362. // Each time the button is clicked the button's text gets longer
  363. leftButton.Text += "!";
  364. };
  365. // show positioning vertically using Pos.AnchorEnd
  366. var centerButton = new Button
  367. {
  368. Text = "Center",
  369. X = Pos.Align (Alignment.Center),
  370. Y = Pos.AnchorEnd (2)
  371. };
  372. centerButton.Accepting += (s, e) =>
  373. {
  374. // This demonstrates how to have a dynamically sized button
  375. // Each time the button is clicked the button's text gets longer
  376. centerButton.Text += "!";
  377. };
  378. // show positioning vertically using another window and Pos.Bottom
  379. var rightButton = new Button
  380. {
  381. Text = "Right",
  382. X = Pos.Align (Alignment.Center),
  383. Y = Pos.Y (centerButton)
  384. };
  385. rightButton.Accepting += (s, e) =>
  386. {
  387. // This demonstrates how to have a dynamically sized button
  388. // Each time the button is clicked the button's text gets longer
  389. rightButton.Text += "!";
  390. };
  391. View [] buttons = { leftButton, centerButton, rightButton };
  392. app.Add (leftButton);
  393. app.Add (centerButton);
  394. app.Add (rightButton);
  395. Application.Run (app);
  396. app.Dispose ();
  397. Application.Shutdown ();
  398. }
  399. }