123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- using NStack;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- using Terminal.Gui;
- namespace UICatalog {
- /// <summary>
- /// This Scenario demonstrates how to use Termina.gui's Dim and Pos Layout System.
- /// [x] - Using Dim.Fill to fill a window
- /// [x] - Using Dim.Fill and Dim.Pos to automatically align controls based on an initial control
- /// [ ] - ...
- /// </summary>
- [ScenarioMetadata (Name: "Computed Layout", Description: "Demonstrates using the Computed (Dim and Pos) Layout System")]
- [ScenarioCategory ("Layout")]
- class ComputedLayout : Scenario {
- public override void Setup ()
- {
- var menu = new MenuBar (new MenuBarItem [] {
- new MenuBarItem ("_Settings", new MenuItem [] {
- null,
- new MenuItem ("_Quit", "", () => Quit()),
- }),
- new MenuBarItem ("_All Controls", "Tests all controls", () => DemoAllViewClasses() ),
- });
- Top.Add (menu);
- var statusBar = new StatusBar (new StatusItem [] {
- new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()),
- });
- Top.Add (statusBar);
- //Top.LayoutStyle = LayoutStyle.Computed;
- // Demonstrate using Dim to create a horizontal ruler that always measures the parent window's width
- // BUGBUG: Dim.Fill returns too big a value sometimes.
- const string rule = "|123456789";
- var horizontalRuler = new Label ("") {
- X = 0,
- Y = 0,
- Width = Dim.Fill (1), // BUGBUG: I don't think this should be needed; DimFill() should respect container's frame. X does.
- ColorScheme = Colors.Error
- };
- Win.Add (horizontalRuler);
- // Demonstrate using Dim to create a vertical ruler that always measures the parent window's height
- // TODO: Either build a custom control for this or implement linewrap in Label #352
- const string vrule = "|\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
- var verticalRuler = new Label ("") {
- X = 0,
- Y = 0,
- Width = 1,
- Height = Dim.Fill (),
- ColorScheme = Colors.Error
- };
- Win.LayoutComplete += (sender, a) => {
- horizontalRuler.Text = rule.Repeat ((int)Math.Ceiling ((double)(horizontalRuler.Bounds.Width) / (double)rule.Length)) [0..(horizontalRuler.Bounds.Width)];
- verticalRuler.Text = vrule.Repeat ((int)Math.Ceiling ((double)(verticalRuler.Bounds.Height * 2) / (double)rule.Length)) [0..(verticalRuler.Bounds.Height * 2)];
- };
- Win.Add (verticalRuler);
- // Demonstrate At - Absolute Layout using Pos
- var absoluteButton = new Button ("Absolute At(2,1)") {
- X = Pos.At (2),
- Y = Pos.At (1)
- };
- Win.Add (absoluteButton);
- // Demonstrate using Dim to create a window that fills the parent with a margin
- int margin = 10;
- var subWin = new Window ($"Centered Sub Window with {margin} character margin") {
- X = Pos.Center (),
- Y = 2,
- Width = Dim.Fill (margin),
- Height = 7
- };
- Win.Add (subWin);
- int i = 1;
- string txt = "Resize the terminal to see computed layout in action.";
- var labelList = new List<Label> ();
- labelList.Add (new Label ($"The lines below show different TextAlignments"));
- labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
- labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
- labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
- labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
- subWin.Add (labelList.ToArray ());
- // #522 repro?
- var frameView = new FrameView ($"Centered FrameView with {margin} character margin") {
- X = Pos.Center (),
- Y = Pos.Bottom (subWin),
- Width = Dim.Fill (margin),
- Height = 7
- };
- Win.Add (frameView);
- i = 1;
- labelList = new List<Label> ();
- labelList.Add (new Label ($"The lines below show different TextAlignments"));
- labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
- labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
- labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
- labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
- frameView.Add (labelList.ToArray ());
- // Demonstrate Dim & Pos using percentages - a TextField that is 30% height and 80% wide
- var textView = new TextView () {
- X = Pos.Center (),
- Y = Pos.Percent (50),
- Width = Dim.Percent (80),
- Height = Dim.Percent (30),
- ColorScheme = Colors.TopLevel,
- };
- textView.Text = "This text view should be half-way down the terminal,\n20% of its height, and 80% of its width.";
- Win.Add (textView);
- // Demonstrate AnchorEnd - Button is anchored to bottom/right
- var anchorButton = new Button ("Anchor End") {
- Y = Pos.AnchorEnd () - 1,
- };
- // TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
- anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
- anchorButton.Clicked = () => {
- // Ths demonstrates how to have a dynamically sized button
- // Each time the button is clicked the button's text gets longer
- // The call to Win.LayoutSubviews causes the Computed layout to
- // get updated.
- anchorButton.Text += "!";
- Win.LayoutSubviews ();
- };
- Win.Add (anchorButton);
- // Centering multiple controls horizontally.
- // This is intentionally convoluted to illustrate potential bugs.
- var bottomLabel = new Label ("This should be the 2nd to last line (Bug #xxx).") {
- TextAlignment = Terminal.Gui.TextAlignment.Centered,
- ColorScheme = Colors.Menu,
- Width = Dim.Fill (),
- X = Pos.Center (),
- Y = Pos.Bottom (Win) - 4 // BUGBUG: -2 should be two lines above border; but it has to be -4
- };
- Win.Add (bottomLabel);
- // Show positioning vertically using Pos.Bottom
- // BUGBUG: -1 should be just above border; but it has to be -3
- var leftButton = new Button ("Left") {
- Y = Pos.Bottom (Win) - 3
- };
- leftButton.Clicked = () => {
- // Ths demonstrates how to have a dynamically sized button
- // Each time the button is clicked the button's text gets longer
- // The call to Win.LayoutSubviews causes the Computed layout to
- // get updated.
- leftButton.Text += "!";
- Win.LayoutSubviews ();
- };
- // show positioning vertically using Pos.AnchorEnd
- var centerButton = new Button ("Center") {
- X = Pos.Center (),
- Y = Pos.AnchorEnd () - 1
- };
- centerButton.Clicked = () => {
- // Ths demonstrates how to have a dynamically sized button
- // Each time the button is clicked the button's text gets longer
- // The call to Win.LayoutSubviews causes the Computed layout to
- // get updated.
- centerButton.Text += "!";
- Win.LayoutSubviews ();
- };
- // show positioning vertically using another window and Pos.Bottom
- var rightButton = new Button ("Right") {
- Y = Pos.Y (centerButton)
- };
- rightButton.Clicked = () => {
- // Ths demonstrates how to have a dynamically sized button
- // Each time the button is clicked the button's text gets longer
- // The call to Win.LayoutSubviews causes the Computed layout to
- // get updated.
- rightButton.Text += "!";
- Win.LayoutSubviews ();
- };
- // Center three buttons with 5 spaces between them
- // TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
- leftButton.X = Pos.Left (centerButton) - (Pos.Right (leftButton) - Pos.Left (leftButton)) - 5;
- rightButton.X = Pos.Right (centerButton) + 5;
- Win.Add (leftButton);
- Win.Add (centerButton);
- Win.Add (rightButton);
- }
- /// <summary>
- /// Displays a Dialog that uses a wizard (next/prev) idom to step through each class derived from View
- /// testing various Computed layout scenarios
- /// </summary>
- private void DemoAllViewClasses ()
- {
- List<Type> GetAllViewClassesCollection ()
- {
- List<Type> objects = new List<Type> ();
- foreach (Type type in typeof (View).Assembly.GetTypes ()
- .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsPublic && myType.IsSubclassOf (typeof (View)))) {
- objects.Add (type);
- }
- return objects;
- }
- var viewClasses = GetAllViewClassesCollection ().OrderByDescending (c => c.Name).ToList ();
- var curClass = 0;
- var closeBtn = new Button ("_Close") {
- Clicked = () => {
- Application.RequestStop ();
- },
- };
- var nextBtn = new Button ("_Next");
- var prevBtn = new Button ("_Previous");
- var dialog = new Dialog ("Demoing all View classs", new [] { prevBtn, nextBtn, closeBtn });
- var label = new Label ("Class:") {
- X = 0,
- Y = 0,
- };
- dialog.Add (label);
- var currentClassLabel = new Label ("") {
- X = Pos.Right (label) + 1,
- Y = Pos.Y (label),
- };
- dialog.Add (currentClassLabel);
- View curView = null;
- void SetCurrentClass ()
- {
- currentClassLabel.Text = $"{viewClasses [curClass].Name}";
- // Remove existing class, if any
- if (curView != null) {
- dialog.Remove (curView);
- curView = null;
- }
- // Instantiate view
- curView = (View)Activator.CreateInstance (viewClasses [curClass]);
- curView.X = Pos.Center ();
- curView.Y = Pos.Center ();
- curView.Width = Dim.Fill (5);
- curView.Height = Dim.Fill (5);
- // If the view supports a Text property, set it so we have something to look at
- if (viewClasses [curClass].GetProperty("Text") != null) {
- curView.GetType ().GetProperty ("Text")?.GetSetMethod ()?.Invoke (curView, new [] { ustring.Make("09/10/1966") });
- }
- // If the view supports a Title property, set it so we have something to look at
- if (viewClasses [curClass].GetProperty ("Title") != null) {
- curView.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (curView, new [] { ustring.Make ("Test Title") });
- }
- dialog.Add (curView);
- dialog.LayoutSubviews ();
- }
- nextBtn.Clicked = () => {
- curClass++;
- if (curClass >= viewClasses.Count) {
- curClass = 0;
- }
- SetCurrentClass ();
- };
- prevBtn.Clicked = () => {
- if (curClass == 0) {
- curClass = viewClasses.Count - 1;
- } else {
- curClass--;
- }
- SetCurrentClass ();
- };
- SetCurrentClass ();
- Application.Run (dialog);
- }
- public override void Run ()
- {
- base.Run ();
- }
- private void Quit ()
- {
- Application.RequestStop ();
- }
- }
- internal static class StringExtensions {
- public static string Repeat (this string instr, int n)
- {
- if (n <= 0) {
- return null;
- }
- if (string.IsNullOrEmpty (instr) || n == 1) {
- return instr;
- }
- return new StringBuilder (instr.Length * n)
- .Insert (0, instr, n)
- .ToString ();
- }
- }
- }
|