PosAlignDemo.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Terminal.Gui;
  5. namespace UICatalog.Scenarios;
  6. [ScenarioMetadata ("Pos.Align", "Demonstrates Pos.Align")]
  7. [ScenarioCategory ("Layout")]
  8. public sealed class PosAlignDemo : Scenario
  9. {
  10. private readonly Aligner _horizAligner = new () { AlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems};
  11. private int _leftMargin;
  12. private readonly Aligner _vertAligner = new () { AlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems };
  13. private int _topMargin;
  14. public override void Main ()
  15. {
  16. // Init
  17. Application.Init ();
  18. // Setup - Create a top-level application window and configure it.
  19. Window appWindow = new ()
  20. {
  21. Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()} - {GetDescription ()}"
  22. };
  23. SetupControls (appWindow, Dimension.Width, Colors.ColorSchemes ["TopLevel"]);
  24. SetupControls (appWindow, Dimension.Height, Colors.ColorSchemes ["Error"]);
  25. Setup3By3Grid (appWindow);
  26. // Run - Start the application.
  27. Application.Run (appWindow);
  28. appWindow.Dispose ();
  29. // Shutdown - Calling Application.Shutdown is required.
  30. Application.Shutdown ();
  31. }
  32. private void SetupControls (Window appWindow, Dimension dimension, ColorScheme colorScheme)
  33. {
  34. RadioGroup alignRadioGroup = new ()
  35. {
  36. RadioLabels = Enum.GetNames<Alignment> (),
  37. ColorScheme = colorScheme
  38. };
  39. if (dimension == Dimension.Width)
  40. {
  41. alignRadioGroup.X = Pos.Align (_horizAligner.Alignment);
  42. alignRadioGroup.Y = Pos.Center ();
  43. }
  44. else
  45. {
  46. alignRadioGroup.X = Pos.Center ();
  47. alignRadioGroup.Y = Pos.Align (_vertAligner.Alignment);
  48. }
  49. alignRadioGroup.SelectedItemChanged += (s, e) =>
  50. {
  51. if (dimension == Dimension.Width)
  52. {
  53. _horizAligner.Alignment =
  54. (Alignment)Enum.Parse (
  55. typeof (Alignment),
  56. alignRadioGroup.RadioLabels [alignRadioGroup.SelectedItem]);
  57. UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
  58. }
  59. else
  60. {
  61. _vertAligner.Alignment =
  62. (Alignment)Enum.Parse (
  63. typeof (Alignment),
  64. alignRadioGroup.RadioLabels [alignRadioGroup.SelectedItem]);
  65. UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
  66. }
  67. };
  68. appWindow.Add (alignRadioGroup);
  69. CheckBox endToStartCheckBox = new ()
  70. {
  71. ColorScheme = colorScheme,
  72. Text = "EndToStart"
  73. };
  74. if (dimension == Dimension.Width)
  75. {
  76. endToStartCheckBox.State = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.EndToStart) ? CheckState.Checked : CheckState.UnChecked;
  77. endToStartCheckBox.X = Pos.Align (_horizAligner.Alignment);
  78. endToStartCheckBox.Y = Pos.Top (alignRadioGroup);
  79. }
  80. else
  81. {
  82. endToStartCheckBox.State = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.EndToStart) ? CheckState.Checked : CheckState.UnChecked;
  83. endToStartCheckBox.X = Pos.Left (alignRadioGroup);
  84. endToStartCheckBox.Y = Pos.Align (_vertAligner.Alignment);
  85. }
  86. endToStartCheckBox.Toggle += (s, e) =>
  87. {
  88. if (dimension == Dimension.Width)
  89. {
  90. _horizAligner.AlignmentModes = e.NewValue == CheckState.Checked
  91. ? _horizAligner.AlignmentModes | AlignmentModes.EndToStart
  92. : _horizAligner.AlignmentModes & ~AlignmentModes.EndToStart;
  93. UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
  94. }
  95. else
  96. {
  97. _vertAligner.AlignmentModes = e.NewValue == CheckState.Checked
  98. ? _vertAligner.AlignmentModes | AlignmentModes.EndToStart
  99. : _vertAligner.AlignmentModes & ~AlignmentModes.EndToStart;
  100. UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
  101. }
  102. };
  103. appWindow.Add (endToStartCheckBox);
  104. CheckBox ignoreFirstOrLast = new ()
  105. {
  106. ColorScheme = colorScheme,
  107. Text = "IgnoreFirstOrLast"
  108. };
  109. if (dimension == Dimension.Width)
  110. {
  111. ignoreFirstOrLast.State = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.IgnoreFirstOrLast) ? CheckState.Checked : CheckState.UnChecked;
  112. ignoreFirstOrLast.X = Pos.Align (_horizAligner.Alignment);
  113. ignoreFirstOrLast.Y = Pos.Top (alignRadioGroup);
  114. }
  115. else
  116. {
  117. ignoreFirstOrLast.State = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.IgnoreFirstOrLast) ? CheckState.Checked : CheckState.UnChecked;
  118. ignoreFirstOrLast.X = Pos.Left (alignRadioGroup);
  119. ignoreFirstOrLast.Y = Pos.Align (_vertAligner.Alignment);
  120. }
  121. ignoreFirstOrLast.Toggle += (s, e) =>
  122. {
  123. if (dimension == Dimension.Width)
  124. {
  125. _horizAligner.AlignmentModes = e.NewValue == CheckState.Checked
  126. ? _horizAligner.AlignmentModes | AlignmentModes.IgnoreFirstOrLast
  127. : _horizAligner.AlignmentModes & ~AlignmentModes.IgnoreFirstOrLast;
  128. UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
  129. }
  130. else
  131. {
  132. _vertAligner.AlignmentModes = e.NewValue == CheckState.Checked
  133. ? _vertAligner.AlignmentModes | AlignmentModes.IgnoreFirstOrLast
  134. : _vertAligner.AlignmentModes & ~AlignmentModes.IgnoreFirstOrLast;
  135. UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
  136. }
  137. };
  138. appWindow.Add (ignoreFirstOrLast);
  139. CheckBox addSpacesBetweenItems = new ()
  140. {
  141. ColorScheme = colorScheme,
  142. Text = "AddSpaceBetweenItems"
  143. };
  144. if (dimension == Dimension.Width)
  145. {
  146. addSpacesBetweenItems.State = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.AddSpaceBetweenItems) ? CheckState.Checked : CheckState.UnChecked;
  147. addSpacesBetweenItems.X = Pos.Align (_horizAligner.Alignment);
  148. addSpacesBetweenItems.Y = Pos.Top (alignRadioGroup);
  149. }
  150. else
  151. {
  152. addSpacesBetweenItems.State = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.AddSpaceBetweenItems) ? CheckState.Checked : CheckState.UnChecked;
  153. addSpacesBetweenItems.X = Pos.Left (alignRadioGroup);
  154. addSpacesBetweenItems.Y = Pos.Align (_vertAligner.Alignment);
  155. }
  156. addSpacesBetweenItems.Toggle += (s, e) =>
  157. {
  158. if (dimension == Dimension.Width)
  159. {
  160. _horizAligner.AlignmentModes = e.NewValue == CheckState.Checked
  161. ? _horizAligner.AlignmentModes | AlignmentModes.AddSpaceBetweenItems
  162. : _horizAligner.AlignmentModes & ~AlignmentModes.AddSpaceBetweenItems;
  163. UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
  164. }
  165. else
  166. {
  167. _vertAligner.AlignmentModes = e.NewValue == CheckState.Checked
  168. ? _vertAligner.AlignmentModes | AlignmentModes.AddSpaceBetweenItems
  169. : _vertAligner.AlignmentModes & ~AlignmentModes.AddSpaceBetweenItems;
  170. UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
  171. }
  172. };
  173. appWindow.Add (addSpacesBetweenItems);
  174. CheckBox margin = new ()
  175. {
  176. ColorScheme = colorScheme,
  177. Text = "Margin"
  178. };
  179. if (dimension == Dimension.Width)
  180. {
  181. margin.X = Pos.Align (_horizAligner.Alignment);
  182. margin.Y = Pos.Top (alignRadioGroup);
  183. }
  184. else
  185. {
  186. margin.X = Pos.Left (addSpacesBetweenItems);
  187. margin.Y = Pos.Align (_vertAligner.Alignment);
  188. }
  189. margin.Toggle += (s, e) =>
  190. {
  191. if (dimension == Dimension.Width)
  192. {
  193. _leftMargin = e.NewValue == CheckState.Checked ? 1 : 0;
  194. UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
  195. }
  196. else
  197. {
  198. _topMargin = e.NewValue == CheckState.Checked ? 1 : 0;
  199. UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
  200. }
  201. };
  202. appWindow.Add (margin);
  203. List<Button> addedViews =
  204. [
  205. new ()
  206. {
  207. X = dimension == Dimension.Width ? Pos.Align (_horizAligner.Alignment) : Pos.Left (alignRadioGroup),
  208. Y = dimension == Dimension.Width ? Pos.Top (alignRadioGroup) : Pos.Align (_vertAligner.Alignment),
  209. Text = NumberToWords.Convert (0)
  210. }
  211. ];
  212. Buttons.NumericUpDown<int> addedViewsUpDown = new()
  213. {
  214. Width = 9,
  215. Title = "Added",
  216. ColorScheme = colorScheme,
  217. BorderStyle = LineStyle.None,
  218. Value = addedViews.Count
  219. };
  220. if (dimension == Dimension.Width)
  221. {
  222. addedViewsUpDown.X = Pos.Align (_horizAligner.Alignment);
  223. addedViewsUpDown.Y = Pos.Top (alignRadioGroup);
  224. addedViewsUpDown.Border.Thickness = new (0, 1, 0, 0);
  225. }
  226. else
  227. {
  228. addedViewsUpDown.X = Pos.Left (alignRadioGroup);
  229. addedViewsUpDown.Y = Pos.Align (_vertAligner.Alignment);
  230. addedViewsUpDown.Border.Thickness = new (1, 0, 0, 0);
  231. }
  232. addedViewsUpDown.ValueChanging += (s, e) =>
  233. {
  234. if (e.NewValue < 0)
  235. {
  236. e.Cancel = true;
  237. return;
  238. }
  239. // Add or remove buttons
  240. if (e.NewValue < e.CurrentValue)
  241. {
  242. // Remove buttons
  243. for (int i = e.CurrentValue - 1; i >= e.NewValue; i--)
  244. {
  245. Button button = addedViews [i];
  246. appWindow.Remove (button);
  247. addedViews.RemoveAt (i);
  248. button.Dispose ();
  249. }
  250. }
  251. if (e.NewValue > e.CurrentValue)
  252. {
  253. // Add buttons
  254. for (int i = e.CurrentValue; i < e.NewValue; i++)
  255. {
  256. var button = new Button
  257. {
  258. X = dimension == Dimension.Width ? Pos.Align (_horizAligner.Alignment) : Pos.Left (alignRadioGroup),
  259. Y = dimension == Dimension.Width ? Pos.Top (alignRadioGroup) : Pos.Align (_vertAligner.Alignment),
  260. Text = NumberToWords.Convert (i + 1)
  261. };
  262. appWindow.Add (button);
  263. addedViews.Add (button);
  264. }
  265. }
  266. };
  267. appWindow.Add (addedViewsUpDown);
  268. appWindow.Add (addedViews [0]);
  269. }
  270. private void UpdatePosAlignObjects (View superView, Dimension dimension, Aligner aligner)
  271. {
  272. foreach (View view in superView.Subviews.Where (v => dimension == Dimension.Width ? v.X is PosAlign : v.Y is PosAlign))
  273. {
  274. if (dimension == Dimension.Width ? view.X is PosAlign : view.Y is PosAlign)
  275. {
  276. //posAlign.Aligner.Alignment = _horizAligner.Alignment;
  277. //posAlign.Aligner.AlignmentMode = _horizAligner.AlignmentMode;
  278. // BUGBUG: Create and assign a new Pos object because we currently have no way for X to be notified
  279. // BUGBUG: of changes in the Pos object. See https://github.com/gui-cs/Terminal.Gui/issues/3485
  280. if (dimension == Dimension.Width)
  281. {
  282. var posAlign = view.X as PosAlign;
  283. view.X = Pos.Align (
  284. aligner.Alignment,
  285. aligner.AlignmentModes,
  286. posAlign!.GroupId);
  287. view.Margin.Thickness = new (_leftMargin, view.Margin.Thickness.Top, view.Margin.Thickness.Right, view.Margin.Thickness.Bottom);
  288. }
  289. else
  290. {
  291. var posAlign = view.Y as PosAlign;
  292. view.Y = Pos.Align (
  293. aligner.Alignment,
  294. aligner.AlignmentModes,
  295. posAlign!.GroupId);
  296. view.Margin.Thickness = new (view.Margin.Thickness.Left, _topMargin, view.Margin.Thickness.Right, view.Margin.Thickness.Bottom);
  297. }
  298. }
  299. }
  300. superView.LayoutSubviews ();
  301. }
  302. /// <summary>
  303. /// Creates a 3x3 grid of views with two GroupIds: One for aligning X and one for aligning Y.
  304. /// Demonstrates using PosAlign to create a grid of views that flow.
  305. /// </summary>
  306. /// <param name="appWindow"></param>
  307. private void Setup3By3Grid (View appWindow)
  308. {
  309. var container = new FrameView
  310. {
  311. Title = "3 by 3",
  312. X = Pos.AnchorEnd (),
  313. Y = Pos.AnchorEnd (),
  314. Width = Dim.Percent (40),
  315. Height = Dim.Percent (40)
  316. };
  317. container.Padding.Thickness = new (8, 1, 0, 0);
  318. container.Padding.ColorScheme = Colors.ColorSchemes ["error"];
  319. Aligner widthAligner = new () { AlignmentModes = AlignmentModes.StartToEnd };
  320. RadioGroup widthAlignRadioGroup = new ()
  321. {
  322. RadioLabels = Enum.GetNames<Alignment> (),
  323. Orientation = Orientation.Horizontal,
  324. X = Pos.Center ()
  325. };
  326. container.Padding.Add (widthAlignRadioGroup);
  327. widthAlignRadioGroup.SelectedItemChanged += (sender, e) =>
  328. {
  329. widthAligner.Alignment =
  330. (Alignment)Enum.Parse (
  331. typeof (Alignment),
  332. widthAlignRadioGroup.RadioLabels [widthAlignRadioGroup.SelectedItem]);
  333. UpdatePosAlignObjects (container, Dimension.Width, widthAligner);
  334. };
  335. Aligner heightAligner = new () { AlignmentModes = AlignmentModes.StartToEnd };
  336. RadioGroup heightAlignRadioGroup = new ()
  337. {
  338. RadioLabels = Enum.GetNames<Alignment> (),
  339. Orientation = Orientation.Vertical,
  340. Y = Pos.Center ()
  341. };
  342. container.Padding.Add (heightAlignRadioGroup);
  343. heightAlignRadioGroup.SelectedItemChanged += (sender, e) =>
  344. {
  345. heightAligner.Alignment =
  346. (Alignment)Enum.Parse (
  347. typeof (Alignment),
  348. heightAlignRadioGroup.RadioLabels [heightAlignRadioGroup.SelectedItem]);
  349. UpdatePosAlignObjects (container, Dimension.Height, heightAligner);
  350. };
  351. for (var i = 0; i < 9; i++)
  352. {
  353. var v = new View
  354. {
  355. Title = $"{i}",
  356. BorderStyle = LineStyle.Dashed,
  357. Height = 3,
  358. Width = 5
  359. };
  360. v.X = Pos.Align (widthAligner.Alignment, widthAligner.AlignmentModes, i / 3);
  361. v.Y = Pos.Align (heightAligner.Alignment, heightAligner.AlignmentModes, i % 3 + 10);
  362. container.Add (v);
  363. }
  364. appWindow.Add (container);
  365. }
  366. }