ComputedLayout.cs 17 KB

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