ComputedLayout.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. using NStack;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using Terminal.Gui;
  8. namespace UICatalog.Scenarios {
  9. /// <summary>
  10. /// This Scenario demonstrates how to use Termina.gui's Dim and Pos Layout System.
  11. /// [x] - Using Dim.Fill to fill a window
  12. /// [x] - Using Dim.Fill and Dim.Pos to automatically align controls based on an initial control
  13. /// [ ] - ...
  14. /// </summary>
  15. [ScenarioMetadata (Name: "Computed Layout", Description: "Demonstrates the Computed (Dim and Pos) Layout System.")]
  16. [ScenarioCategory ("Layout")]
  17. public class ComputedLayout : Scenario {
  18. public override void Setup ()
  19. {
  20. var menu = new MenuBar (new MenuBarItem [] {
  21. new MenuBarItem ("_Settings", new MenuItem [] {
  22. null,
  23. new MenuItem ("_Quit", "", () => Quit()),
  24. }),
  25. });
  26. Application.Top.Add (menu);
  27. var statusBar = new StatusBar (new StatusItem [] {
  28. new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
  29. });
  30. Application.Top.Add (statusBar);
  31. //Top.LayoutStyle = LayoutStyle.Computed;
  32. // Demonstrate using Dim to create a horizontal ruler that always measures the parent window's width
  33. // BUGBUG: Dim.Fill returns too big a value sometimes.
  34. const string rule = "|123456789";
  35. var horizontalRuler = new Label ("") {
  36. X = 0,
  37. Y = 0,
  38. Width = Dim.Fill (), // FIXED: I don't think this should be needed; DimFill() should respect container's frame. X does.
  39. ColorScheme = Colors.Error
  40. };
  41. Win.Add (horizontalRuler);
  42. // Demonstrate using Dim to create a vertical ruler that always measures the parent window's height
  43. // TODO: Either build a custom control for this or implement linewrap in Label #352
  44. const string vrule = "|\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
  45. var verticalRuler = new Label ("") {
  46. X = 0,
  47. Y = 0,
  48. Width = 1,
  49. Height = Dim.Fill (),
  50. ColorScheme = Colors.Error
  51. };
  52. Win.LayoutComplete += (a) => {
  53. horizontalRuler.Text = rule.Repeat ((int)Math.Ceiling ((double)(horizontalRuler.Bounds.Width) / (double)rule.Length)) [0..(horizontalRuler.Bounds.Width)];
  54. verticalRuler.Text = vrule.Repeat ((int)Math.Ceiling ((double)(verticalRuler.Bounds.Height * 2) / (double)rule.Length)) [0..(verticalRuler.Bounds.Height * 2)];
  55. };
  56. Win.Add (verticalRuler);
  57. // Demonstrate At - Absolute Layout using Pos
  58. var absoluteButton = new Button ("Absolute At(2,1)") {
  59. X = Pos.At (2),
  60. Y = Pos.At (1)
  61. };
  62. Win.Add (absoluteButton);
  63. // Demonstrate using Dim to create a window that fills the parent with a margin
  64. int margin = 10;
  65. var subWin = new Window ($"Centered Sub Window with {margin} character margin") {
  66. X = Pos.Center (),
  67. Y = 2,
  68. Width = Dim.Fill (margin),
  69. Height = 7
  70. };
  71. Win.Add (subWin);
  72. int i = 1;
  73. string txt = "Resize the terminal to see computed layout in action.";
  74. var labelList = new List<Label> ();
  75. labelList.Add (new Label ($"The lines below show different TextAlignments"));
  76. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  77. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  78. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  79. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  80. subWin.Add (labelList.ToArray ());
  81. // #522 repro?
  82. var frameView = new FrameView ($"Centered FrameView with {margin} character margin") {
  83. X = Pos.Center (),
  84. Y = Pos.Bottom (subWin),
  85. Width = Dim.Fill (margin),
  86. Height = 7
  87. };
  88. Win.Add (frameView);
  89. i = 1;
  90. labelList = new List<Label> ();
  91. labelList.Add (new Label ($"The lines below show different TextAlignments"));
  92. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  93. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  94. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  95. labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
  96. frameView.Add (labelList.ToArray ());
  97. // Demonstrate Dim & Pos using percentages - a TextField that is 30% height and 80% wide
  98. var textView = new TextView () {
  99. X = Pos.Center (),
  100. Y = Pos.Percent (50),
  101. Width = Dim.Percent (80),
  102. Height = Dim.Percent (30),
  103. ColorScheme = Colors.TopLevel,
  104. };
  105. textView.Text = "This text view should be half-way down the terminal,\n20% of its height, and 80% of its width.";
  106. Win.Add (textView);
  107. // Demonstrate AnchorEnd - Button is anchored to bottom/right
  108. var anchorButton = new Button ("Anchor End") {
  109. Y = Pos.AnchorEnd () - 1,
  110. };
  111. // TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
  112. anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
  113. anchorButton.Clicked += () => {
  114. // Ths demonstrates how to have a dynamically sized button
  115. // Each time the button is clicked the button's text gets longer
  116. // The call to Win.LayoutSubviews causes the Computed layout to
  117. // get updated.
  118. anchorButton.Text += "!";
  119. Win.LayoutSubviews ();
  120. };
  121. Win.Add (anchorButton);
  122. // Centering multiple controls horizontally.
  123. // This is intentionally convoluted to illustrate potential bugs.
  124. var bottomLabel = new Label ("This should be the 2nd to last line (Bug #xxx).") {
  125. TextAlignment = Terminal.Gui.TextAlignment.Centered,
  126. ColorScheme = Colors.Menu,
  127. Width = Dim.Fill (),
  128. X = Pos.Center (),
  129. Y = Pos.AnchorEnd () - 2 // FIXED: -2 should be two lines above border; but it has to be -4
  130. };
  131. Win.Add (bottomLabel);
  132. // Show positioning vertically using Pos.Bottom
  133. // BUGBUG: -1 should be just above border; but it has to be -3
  134. var leftButton = new Button ("Left") {
  135. Y = Pos.AnchorEnd () - 1
  136. };
  137. leftButton.Clicked += () => {
  138. // Ths demonstrates how to have a dynamically sized button
  139. // Each time the button is clicked the button's text gets longer
  140. // The call to Win.LayoutSubviews causes the Computed layout to
  141. // get updated.
  142. leftButton.Text += "!";
  143. Win.LayoutSubviews ();
  144. };
  145. // show positioning vertically using Pos.AnchorEnd
  146. var centerButton = new Button ("Center") {
  147. X = Pos.Center (),
  148. Y = Pos.AnchorEnd () - 1
  149. };
  150. centerButton.Clicked += () => {
  151. // Ths demonstrates how to have a dynamically sized button
  152. // Each time the button is clicked the button's text gets longer
  153. // The call to Win.LayoutSubviews causes the Computed layout to
  154. // get updated.
  155. centerButton.Text += "!";
  156. Win.LayoutSubviews ();
  157. };
  158. // show positioning vertically using another window and Pos.Bottom
  159. var rightButton = new Button ("Right") {
  160. Y = Pos.Y (centerButton)
  161. };
  162. rightButton.Clicked += () => {
  163. // Ths demonstrates how to have a dynamically sized button
  164. // Each time the button is clicked the button's text gets longer
  165. // The call to Win.LayoutSubviews causes the Computed layout to
  166. // get updated.
  167. rightButton.Text += "!";
  168. Win.LayoutSubviews ();
  169. };
  170. // Center three buttons with 5 spaces between them
  171. // TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
  172. leftButton.X = Pos.Left (centerButton) - (Pos.Right (leftButton) - Pos.Left (leftButton)) - 5;
  173. rightButton.X = Pos.Right (centerButton) + 5;
  174. Win.Add (leftButton);
  175. Win.Add (centerButton);
  176. Win.Add (rightButton);
  177. }
  178. public override void Run ()
  179. {
  180. base.Run ();
  181. }
  182. private void Quit ()
  183. {
  184. Application.RequestStop ();
  185. }
  186. }
  187. internal static class StringExtensions {
  188. public static string Repeat (this string instr, int n)
  189. {
  190. if (n <= 0) {
  191. return null;
  192. }
  193. if (string.IsNullOrEmpty (instr) || n == 1) {
  194. return instr;
  195. }
  196. return new StringBuilder (instr.Length * n)
  197. .Insert (0, instr, n)
  198. .ToString ();
  199. }
  200. }
  201. }