diff --git a/Designer/Designer.csproj b/Designer/Designer.csproj
index 545c2b0b9..84de5ca1d 100644
--- a/Designer/Designer.csproj
+++ b/Designer/Designer.csproj
@@ -8,6 +8,7 @@
Designer
Designer
v4.7.2
+ win-x86
diff --git a/Example/Example.csproj b/Example/Example.csproj
index dc751b396..001459c47 100644
--- a/Example/Example.csproj
+++ b/Example/Example.csproj
@@ -8,6 +8,7 @@
Terminal
Terminal
v4.7.2
+ win-x64
diff --git a/Example/demo.cs.orig b/Example/demo.cs.orig
new file mode 100644
index 000000000..f3363488b
--- /dev/null
+++ b/Example/demo.cs.orig
@@ -0,0 +1,611 @@
+using Terminal.Gui;
+using System;
+using Mono.Terminal;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
+using NStack;
+using System.Text;
+
+static class Demo {
+ //class Box10x : View, IScrollView {
+ class Box10x : View {
+ int w = 40;
+ int h = 50;
+
+ public bool WantCursorPosition { get; set; } = false;
+
+ public Box10x (int x, int y) : base (new Rect (x, y, 20, 10))
+ {
+ }
+
+ public Size GetContentSize ()
+ {
+ return new Size (w, h);
+ }
+
+ public void SetCursorPosition (Point pos)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override void Redraw (Rect region)
+ {
+ //Point pos = new Point (region.X, region.Y);
+ Driver.SetAttribute (ColorScheme.Focus);
+
+ for (int y = 0; y < h; y++) {
+ Move (0, y);
+ Driver.AddStr (y.ToString ());
+ for (int x = 0; x < w - y.ToString ().Length; x++) {
+ //Driver.AddRune ((Rune)('0' + (x + y) % 10));
+ if (y.ToString ().Length < w)
+ Driver.AddStr (" ");
+ }
+ }
+ //Move (pos.X, pos.Y);
+ }
+ }
+
+ class Filler : View {
+ public Filler (Rect rect) : base (rect)
+ {
+ }
+
+ public override void Redraw (Rect region)
+ {
+ Driver.SetAttribute (ColorScheme.Focus);
+ var f = Frame;
+
+ for (int y = 0; y < f.Width; y++) {
+ Move (0, y);
+ for (int x = 0; x < f.Height; x++) {
+ Rune r;
+ switch (x % 3) {
+ case 0:
+ Driver.AddRune (y.ToString ().ToCharArray (0, 1) [0]);
+ if (y > 9)
+ Driver.AddRune (y.ToString ().ToCharArray (1, 1) [0]);
+ r = '.';
+ break;
+ case 1:
+ r = 'o';
+ break;
+ default:
+ r = 'O';
+ break;
+ }
+ Driver.AddRune (r);
+ }
+ }
+ }
+ }
+
+ static void ShowTextAlignments ()
+ {
+<<<<<<< HEAD
+ var container = new Window ($"Show Text Alignments") {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = Dim.Fill ()
+ };
+ container.OnKeyUp += (KeyEvent ke) => {
+ if (ke.Key == Key.Esc)
+ container.Running = false;
+ };
+=======
+ var container = new Dialog (
+ "Text Alignments", 70, 20,
+ new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
+ new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
+
+>>>>>>> cb40c5c2491a559658481d20dd4b6a3343c0183f
+
+ int i = 0;
+ string txt = "Hello world, how are you doing today?";
+ container.Add (
+<<<<<<< HEAD
+ new Label ($"{i+1}-{txt}") { TextAlignment = TextAlignment.Left, Y = 3, Width = Dim.Fill () },
+ new Label ($"{i+2}-{txt}") { TextAlignment = TextAlignment.Right, Y = 5, Width = Dim.Fill () },
+ new Label ($"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered, Y = 7, Width = Dim.Fill () },
+ new Label ($"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified, Y = 9, Width = Dim.Fill () }
+=======
+ new Label (new Rect (0, 1, 50, 3), $"{i+1}-{txt}") { TextAlignment = TextAlignment.Left },
+ new Label (new Rect (0, 3, 50, 3), $"{i+2}-{txt}") { TextAlignment = TextAlignment.Right },
+ new Label (new Rect (0, 5, 50, 3), $"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered },
+ new Label (new Rect (0, 7, 50, 3), $"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified }
+>>>>>>> cb40c5c2491a559658481d20dd4b6a3343c0183f
+ );
+
+ Application.Run (container);
+ }
+
+ static void ShowEntries (View container)
+ {
+ var scrollView = new ScrollView (new Rect (50, 10, 20, 8)) {
+ ContentSize = new Size (20, 50),
+ //ContentOffset = new Point (0, 0),
+ ShowVerticalScrollIndicator = true,
+ ShowHorizontalScrollIndicator = true
+ };
+#if false
+ scrollView.Add (new Box10x (0, 0));
+#else
+ scrollView.Add (new Filler (new Rect (0, 0, 40, 40)));
+#endif
+
+ // This is just to debug the visuals of the scrollview when small
+ var scrollView2 = new ScrollView (new Rect (72, 10, 3, 3)) {
+ ContentSize = new Size (100, 100),
+ ShowVerticalScrollIndicator = true,
+ ShowHorizontalScrollIndicator = true
+ };
+ scrollView2.Add (new Box10x (0, 0));
+ var progress = new ProgressBar (new Rect (68, 1, 10, 1));
+ bool timer (MainLoop caller)
+ {
+ progress.Pulse ();
+ return true;
+ }
+
+ Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
+
+
+ // A little convoluted, this is because I am using this to test the
+ // layout based on referencing elements of another view:
+
+ var login = new Label ("Login: ") { X = 3, Y = 6 };
+ var password = new Label ("Password: ") {
+ X = Pos.Left (login),
+ Y = Pos.Bottom (login) + 1
+ };
+ var loginText = new TextField ("") {
+ X = Pos.Right (password),
+ Y = Pos.Top (login),
+ Width = 40
+ };
+
+ var passText = new TextField ("") {
+ Secret = true,
+ X = Pos.Left (loginText),
+ Y = Pos.Top (password),
+ Width = Dim.Width (loginText)
+ };
+
+ var tf = new Button (3, 19, "Ok");
+ // Add some content
+ container.Add (
+ login,
+ loginText,
+ password,
+ passText,
+ new FrameView (new Rect (3, 10, 25, 6), "Options"){
+ new CheckBox (1, 0, "Remember me"),
+ new RadioGroup (1, 2, new [] { "_Personal", "_Company" }),
+ },
+ new ListView (new Rect (59, 6, 16, 4), new string [] {
+ "First row",
+ "<>",
+ "This is a very long row that should overflow what is shown",
+ "4th",
+ "There is an empty slot on the second row",
+ "Whoa",
+ "This is so cool"
+ }),
+ scrollView,
+ scrollView2,
+ tf,
+ new Button (10, 19, "Cancel"),
+ new TimeField (3, 20, DateTime.Now),
+ new TimeField (23, 20, DateTime.Now, true),
+ new DateField (3, 22, DateTime.Now),
+ new DateField (23, 22, DateTime.Now, true),
+ progress,
+ new Label (3, 24, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar"),
+ menuKeysStyle,
+ menuAutoMouseNav
+
+ );
+ container.SendSubviewToBack (tf);
+ }
+
+ public static Label ml2;
+
+ static void NewFile ()
+ {
+ var d = new Dialog (
+ "New File", 50, 20,
+ new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
+ new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
+ ml2 = new Label (1, 1, "Mouse Debug Line");
+ d.Add (ml2);
+ Application.Run (d);
+ }
+
+ //
+ // Creates a nested editor
+ static void Editor (Toplevel top)
+ {
+ var tframe = top.Frame;
+ var ntop = new Toplevel (tframe);
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_File", new MenuItem [] {
+ new MenuItem ("_Close", "", () => {Application.RequestStop ();}),
+ }),
+ new MenuBarItem ("_Edit", new MenuItem [] {
+ new MenuItem ("_Copy", "", null),
+ new MenuItem ("C_ut", "", null),
+ new MenuItem ("_Paste", "", null)
+ }),
+ });
+ ntop.Add (menu);
+
+ string fname = null;
+ foreach (var s in new [] { "/etc/passwd", "c:\\windows\\win.ini" })
+ if (System.IO.File.Exists (s)) {
+ fname = s;
+ break;
+ }
+
+ var win = new Window (fname ?? "Untitled") {
+ X = 0,
+ Y = 1,
+ Width = Dim.Fill (),
+ Height = Dim.Fill ()
+ };
+ ntop.Add (win);
+
+ var text = new TextView (new Rect (0, 0, tframe.Width - 2, tframe.Height - 3));
+
+ if (fname != null)
+ text.Text = System.IO.File.ReadAllText (fname);
+ win.Add (text);
+
+ Application.Run (ntop);
+ }
+
+ static bool Quit ()
+ {
+ var n = MessageBox.Query (50, 7, "Quit Demo", "Are you sure you want to quit this demo?", "Yes", "No");
+ return n == 0;
+ }
+
+ static void Close ()
+ {
+ MessageBox.ErrorQuery (50, 7, "Error", "There is nothing to close", "Ok");
+ }
+
+ // Watch what happens when I try to introduce a newline after the first open brace
+ // it introduces a new brace instead, and does not indent. Then watch me fight
+ // the editor as more oddities happen.
+
+ public static void Open ()
+ {
+ var d = new OpenDialog ("Open", "Open a file") { AllowsMultipleSelection = true };
+ Application.Run (d);
+
+ if (!d.Canceled)
+ MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "Ok");
+ }
+
+ public static void ShowHex (Toplevel top)
+ {
+ var tframe = top.Frame;
+ var ntop = new Toplevel (tframe);
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_File", new MenuItem [] {
+ new MenuItem ("_Close", "", () => {Application.RequestStop ();}),
+ }),
+ });
+ ntop.Add (menu);
+
+ var win = new Window ("/etc/passwd") {
+ X = 0,
+ Y = 1,
+ Width = Dim.Fill (),
+ Height = Dim.Fill ()
+ };
+ ntop.Add (win);
+
+ var source = System.IO.File.OpenRead ("/etc/passwd");
+ var hex = new HexView (source) {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = Dim.Fill ()
+ };
+ win.Add (hex);
+ Application.Run (ntop);
+
+ }
+
+ public class MenuItemDetails : MenuItem {
+ ustring title;
+ string help;
+ Action action;
+
+ public MenuItemDetails (ustring title, string help, Action action) : base (title, help, action)
+ {
+ this.title = title;
+ this.help = help;
+ this.action = action;
+ }
+
+ public static MenuItemDetails Instance (MenuItem mi)
+ {
+ return (MenuItemDetails)mi.GetMenuItem ();
+ }
+ }
+
+ public delegate MenuItem MenuItemDelegate (MenuItemDetails menuItem);
+
+ public static void ShowMenuItem (MenuItem mi)
+ {
+ BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
+ MethodInfo minfo = typeof (MenuItemDetails).GetMethod ("Instance", flags);
+ MenuItemDelegate mid = (MenuItemDelegate)Delegate.CreateDelegate (typeof (MenuItemDelegate), minfo);
+ MessageBox.Query (70, 7, mi.Title.ToString (),
+ $"{mi.Title.ToString ()} selected. Is from submenu: {mi.GetMenuBarItem ()}", "Ok");
+ }
+
+ static void MenuKeysStyle_Toggled (object sender, EventArgs e)
+ {
+ menu.UseKeysUpDownAsKeysLeftRight = menuKeysStyle.Checked;
+ }
+
+ static void MenuAutoMouseNav_Toggled (object sender, EventArgs e)
+ {
+ menu.WantMousePositionReports = menuAutoMouseNav.Checked;
+ }
+
+
+ static void Copy ()
+ {
+ TextField textField = menu.LastFocused as TextField;
+ if (textField != null && textField.SelectedLength != 0) {
+ textField.Copy ();
+ }
+ }
+
+ static void Cut ()
+ {
+ TextField textField = menu.LastFocused as TextField;
+ if (textField != null && textField.SelectedLength != 0) {
+ textField.Cut ();
+ }
+ }
+
+ static void Paste ()
+ {
+ TextField textField = menu.LastFocused as TextField;
+ if (textField != null) {
+ textField.Paste ();
+ }
+ }
+
+ static void Help ()
+ {
+ MessageBox.Query (50, 7, "Help", "This is a small help\nBe kind.", "Ok");
+ }
+
+ #region Selection Demo
+
+ static void ListSelectionDemo (bool multiple)
+ {
+ var d = new Dialog ("Selection Demo", 60, 20,
+ new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
+ new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
+
+ var animals = new List () { "Alpaca", "Llama", "Lion", "Shark", "Goat" };
+ var msg = new Label ("Use space bar or control-t to toggle selection") {
+ X = 1,
+ Y = 1,
+ Width = Dim.Fill () - 1,
+ Height = 1
+ };
+
+ var list = new ListView (animals) {
+ X = 1,
+ Y = 3,
+ Width = Dim.Fill () - 4,
+ Height = Dim.Fill () - 4,
+ AllowsMarking = true,
+ AllowsMultipleSelection = multiple
+ };
+ d.Add (msg, list);
+ Application.Run (d);
+
+ var result = "";
+ for (int i = 0; i < animals.Count; i++) {
+ if (list.Source.IsMarked (i)) {
+ result += animals [i] + " ";
+ }
+ }
+ MessageBox.Query (60, 10, "Selected Animals", result == "" ? "No animals selected" : result, "Ok");
+ }
+ #endregion
+
+
+ #region OnKeyDown / OnKeyUp Demo
+ private static void OnKeyDownUpDemo ()
+ {
+ var container = new Dialog (
+ "OnKeyDown & OnKeyUp demo", 80, 20,
+ new Button ("Close") { Clicked = () => { Application.RequestStop (); } }) {
+ Width = Dim.Fill (),
+ Height = Dim.Fill (),
+ };
+
+ var list = new List ();
+ var listView = new ListView (list) {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill () - 1,
+ Height = Dim.Fill () - 2,
+ };
+ listView.ColorScheme = Colors.TopLevel;
+ container.Add (listView);
+
+ void KeyUpDown (KeyEvent keyEvent, string updown)
+ {
+ if ((keyEvent.Key & Key.CtrlMask) != 0) {
+ list.Add ($"Key{updown,-4}: Ctrl ");
+ } else if ((keyEvent.Key & Key.AltMask) != 0) {
+ list.Add ($"Key{updown,-4}: Alt ");
+ } else {
+ list.Add ($"Key{updown,-4}: {(((uint)keyEvent.KeyValue & (uint)Key.CharMask) > 26 ? $"{(char)keyEvent.KeyValue}" : $"{keyEvent.Key}")}");
+ }
+ listView.MoveDown ();
+ }
+
+ container.OnKeyDown += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Down");
+ container.OnKeyUp += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Up");
+ Application.Run (container);
+ }
+ #endregion
+
+ public static Label ml;
+ public static MenuBar menu;
+ public static CheckBox menuKeysStyle;
+ public static CheckBox menuAutoMouseNav;
+ static void Main ()
+ {
+ if (Debugger.IsAttached)
+ CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
+
+ //Application.UseSystemConsole = true;
+
+ Application.Init ();
+
+ var top = Application.Top;
+
+ //Open ();
+#if true
+ int margin = 3;
+ var win = new Window ("Hello") {
+ X = 1,
+ Y = 1,
+
+ Width = Dim.Fill () - margin,
+ Height = Dim.Fill () - margin
+ };
+#else
+ var tframe = top.Frame;
+
+ var win = new Window (new Rect (0, 1, tframe.Width, tframe.Height - 1), "Hello");
+#endif
+ MenuItemDetails [] menuItems = {
+ new MenuItemDetails ("F_ind", "", null),
+ new MenuItemDetails ("_Replace", "", null),
+ new MenuItemDetails ("_Item1", "", null),
+ new MenuItemDetails ("_Not From Sub Menu", "", null)
+ };
+
+ menuItems [0].Action = () => ShowMenuItem (menuItems [0]);
+ menuItems [1].Action = () => ShowMenuItem (menuItems [1]);
+ menuItems [2].Action = () => ShowMenuItem (menuItems [2]);
+ menuItems [3].Action = () => ShowMenuItem (menuItems [3]);
+
+ menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_File", new MenuItem [] {
+ new MenuItem ("Text _Editor Demo", "", () => { Editor (top); }),
+ new MenuItem ("_New", "Creates new file", NewFile),
+ new MenuItem ("_Open", "", Open),
+ new MenuItem ("_Hex", "", () => ShowHex (top)),
+ new MenuItem ("_Close", "", () => Close ()),
+ new MenuItem ("_Disabled", "", () => { }, () => false),
+ null,
+ new MenuItem ("_Quit", "", () => { if (Quit ()) top.Running = false; })
+ }),
+ new MenuBarItem ("_Edit", new MenuItem [] {
+ new MenuItem ("_Copy", "", Copy),
+ new MenuItem ("C_ut", "", Cut),
+ new MenuItem ("_Paste", "", Paste),
+ new MenuItem ("_Find and Replace",
+ new MenuBarItem (new MenuItem[] {menuItems [0], menuItems [1] })),
+ menuItems[3]
+ }),
+ new MenuBarItem ("_List Demos", new MenuItem [] {
+ new MenuItem ("Select _Multiple Items", "", () => ListSelectionDemo (true)),
+ new MenuItem ("Select _Single Item", "", () => ListSelectionDemo (false)),
+ }),
+ new MenuBarItem ("A_ssorted", new MenuItem [] {
+ new MenuItem ("_Show text alignments", "", () => ShowTextAlignments ()),
+ new MenuItem ("_OnKeyDown/Up", "", () => OnKeyDownUpDemo ())
+ }),
+ new MenuBarItem ("_Test Menu and SubMenus", new MenuItem [] {
+ new MenuItem ("SubMenu1Item_1",
+ new MenuBarItem (new MenuItem[] {
+ new MenuItem ("SubMenu2Item_1",
+ new MenuBarItem (new MenuItem [] {
+ new MenuItem ("SubMenu3Item_1",
+ new MenuBarItem (new MenuItem [] { menuItems [2] })
+ )
+ })
+ )
+ })
+ )
+ }),
+ new MenuBarItem ("_About...", "Demonstrates top-level menu item", () => MessageBox.ErrorQuery (50, 7, "About Demo", "This is a demo app for gui.cs", "Ok")),
+ });
+
+ menuKeysStyle = new CheckBox (3, 25, "UseKeysUpDownAsKeysLeftRight", true);
+ menuKeysStyle.Toggled += MenuKeysStyle_Toggled;
+ menuAutoMouseNav = new CheckBox (40, 25, "UseMenuAutoNavigation", true);
+ menuAutoMouseNav.Toggled += MenuAutoMouseNav_Toggled;
+
+ ShowEntries (win);
+
+ int count = 0;
+ ml = new Label (new Rect (3, 17, 47, 1), "Mouse: ");
+ Application.RootMouseEvent += delegate (MouseEvent me) {
+ ml.TextColor = Colors.TopLevel.Normal;
+ ml.Text = $"Mouse: ({me.X},{me.Y}) - {me.Flags} {count++}";
+ };
+
+ var test = new Label (3, 18, "Se iniciará el análisis");
+ win.Add (test);
+ win.Add (ml);
+
+ var drag = new Label ("Drag: ") { X = 70, Y = 24 };
+ var dragText = new TextField ("") {
+ X = Pos.Right (drag),
+ Y = Pos.Top (drag),
+ Width = 40
+ };
+
+ var statusBar = new StatusBar (new StatusItem [] {
+ new StatusItem(Key.F1, "~F1~ Help", () => Help()),
+ new StatusItem(Key.F2, "~F2~ Load", null),
+ new StatusItem(Key.F3, "~F3~ Save", null),
+ new StatusItem(Key.ControlX, "~^X~ Quit", () => { if (Quit ()) top.Running = false; }),
+ }) {
+ Parent = null,
+ };
+
+ win.Add (drag, dragText);
+#if true
+ // FIXED: This currently causes a stack overflow, because it is referencing a window that has not had its size allocated yet
+
+ var bottom = new Label ("This should go on the bottom of the same top-level!");
+ win.Add (bottom);
+ var bottom2 = new Label ("This should go on the bottom of another top-level!");
+ top.Add (bottom2);
+
+ Application.OnLoad = () => {
+ bottom.X = win.X;
+ bottom.Y = Pos.Bottom (win) - Pos.Top (win) - margin;
+ bottom2.X = Pos.Left (win);
+ bottom2.Y = Pos.Bottom (win);
+ };
+#endif
+
+
+ top.Add (win);
+ //top.Add (menu);
+ top.Add (menu, statusBar);
+ Application.Run ();
+ }
+}
diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs
index 08fa9a53a..4bed3fbf6 100644
--- a/Terminal.Gui/Core.cs
+++ b/Terminal.Gui/Core.cs
@@ -1066,7 +1066,7 @@ namespace Terminal.Gui {
}
///
- /// Invoked when a character key is pressed and occurs after the key down event.
+ /// Invoked when a character key is pressed and occurs after the key up event.
///
public Action OnKeyPress;
@@ -1083,7 +1083,6 @@ namespace Terminal.Gui {
///
public override bool ProcessHotKey (KeyEvent keyEvent)
{
- OnKeyPress?.Invoke (keyEvent);
if (subviews == null || subviews.Count == 0)
return false;
foreach (var view in subviews)
@@ -1095,7 +1094,6 @@ namespace Terminal.Gui {
///
public override bool ProcessColdKey (KeyEvent keyEvent)
{
- OnKeyPress?.Invoke (keyEvent);
if (subviews == null || subviews.Count == 0)
return false;
foreach (var view in subviews)
diff --git a/Terminal.Gui/Drivers/CursesDriver.cs b/Terminal.Gui/Drivers/CursesDriver.cs
index 0edc7e23c..95a658108 100644
--- a/Terminal.Gui/Drivers/CursesDriver.cs
+++ b/Terminal.Gui/Drivers/CursesDriver.cs
@@ -52,7 +52,7 @@ namespace Terminal.Gui {
if (sync)
Application.Driver.Refresh ();
ccol++;
- var runeWidth = Rune.ColumnWidth(rune);
+ var runeWidth = Rune.ColumnWidth (rune);
if (runeWidth > 1) {
for (int i = 1; i < runeWidth; i++) {
ccol++;
@@ -192,7 +192,7 @@ namespace Terminal.Gui {
};
}
- void ProcessInput (Action keyHandler, Action mouseHandler)
+ void ProcessInput (Action keyHandler, Action keyUpHandler, Action mouseHandler)
{
int wch;
var code = Curses.get_wch (out wch);
@@ -212,6 +212,7 @@ namespace Terminal.Gui {
return;
}
keyHandler (new KeyEvent (MapCursesKey (wch)));
+ keyUpHandler (new KeyEvent (MapCursesKey (wch)));
return;
}
@@ -219,7 +220,7 @@ namespace Terminal.Gui {
if (wch == 27) {
Curses.timeout (200);
- code = Curses.get_wch (out wch);
+ code = Curses.get_wch (out int wch2);
if (code == Curses.KEY_CODE_YES)
keyHandler (new KeyEvent (Key.AltMask | MapCursesKey (wch)));
if (code == 0) {
@@ -227,23 +228,28 @@ namespace Terminal.Gui {
// The ESC-number handling, debatable.
// Simulates the AltMask itself by pressing Alt + Space.
- if (wch == (int)Key.Space)
+ if (wch2 == (int)Key.Space)
key = new KeyEvent (Key.AltMask);
- else if (wch - (int)Key.Space >= 'A' && wch - (int)Key.Space <= 'Z')
- key = new KeyEvent ((Key)((uint)Key.AltMask + (wch - (int)Key.Space)));
- else if (wch >= '1' && wch <= '9')
- key = new KeyEvent ((Key)((int)Key.F1 + (wch - '0' - 1)));
- else if (wch == '0')
+ else if (wch2 - (int)Key.Space >= 'A' && wch2 - (int)Key.Space <= 'Z')
+ key = new KeyEvent ((Key)((uint)Key.AltMask + (wch2 - (int)Key.Space)));
+ else if (wch2 >= '1' && wch <= '9')
+ key = new KeyEvent ((Key)((int)Key.F1 + (wch2 - '0' - 1)));
+ else if (wch2 == '0')
key = new KeyEvent (Key.F10);
- else if (wch == 27)
- key = new KeyEvent ((Key)wch);
+ else if (wch2 == 27)
+ key = new KeyEvent ((Key)wch2);
else
- key = new KeyEvent (Key.AltMask | (Key)wch);
+ key = new KeyEvent (Key.AltMask | (Key)wch2);
keyHandler (key);
- } else
+ } else {
keyHandler (new KeyEvent (Key.Esc));
- } else
+ }
+ } else {
keyHandler (new KeyEvent ((Key)wch));
+ }
+ // Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above
+ // will not impact KeyUp.
+ keyUpHandler (new KeyEvent ((Key)wch));
}
public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
@@ -252,7 +258,7 @@ namespace Terminal.Gui {
Curses.timeout (-1);
(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {
- ProcessInput (keyHandler, mouseHandler);
+ ProcessInput (keyHandler, keyUpHandler, mouseHandler);
return true;
});
@@ -314,7 +320,7 @@ namespace Terminal.Gui {
Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
Colors.Menu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
Colors.Menu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
- Colors.Menu.Disabled = MakeColor(Curses.COLOR_WHITE, Curses.COLOR_CYAN);
+ Colors.Menu.Disabled = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
Colors.Dialog.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
diff --git a/Terminal.Gui/Drivers/NetDriver.cs b/Terminal.Gui/Drivers/NetDriver.cs
index 99cef7ac5..3b7fa188d 100644
--- a/Terminal.Gui/Drivers/NetDriver.cs
+++ b/Terminal.Gui/Drivers/NetDriver.cs
@@ -328,6 +328,7 @@ namespace Terminal.Gui {
if (map == (Key)0xffffffff)
return;
keyHandler (new KeyEvent (map));
+ keyUpHandler (new KeyEvent (map));
};
}
diff --git a/Terminal.Gui/Drivers/WindowsDriver.cs b/Terminal.Gui/Drivers/WindowsDriver.cs
index 8de136777..343182dc0 100644
--- a/Terminal.Gui/Drivers/WindowsDriver.cs
+++ b/Terminal.Gui/Drivers/WindowsDriver.cs
@@ -440,15 +440,10 @@ namespace Terminal.Gui {
public WindowsDriver ()
{
- Colors.TopLevel = new ColorScheme ();
-
- Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
- Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
- Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
- Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.DarkCyan);
-
winConsole = new WindowsConsole ();
+ SetupColorsAndBorders ();
+
cols = Console.WindowWidth;
rows = Console.WindowHeight;
WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
@@ -459,6 +454,54 @@ namespace Terminal.Gui {
Task.Run ((Action)WindowsInputHandler);
}
+ private void SetupColorsAndBorders ()
+ {
+ Colors.TopLevel = new ColorScheme ();
+ Colors.Base = new ColorScheme ();
+ Colors.Dialog = new ColorScheme ();
+ Colors.Menu = new ColorScheme ();
+ Colors.Error = new ColorScheme ();
+
+ Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
+ Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
+ Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
+ Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
+
+ Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkBlue);
+ Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
+ Colors.Base.HotNormal = MakeColor (ConsoleColor.DarkCyan, ConsoleColor.DarkBlue);
+ Colors.Base.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
+
+ Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
+ Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
+ Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.DarkGray);
+ Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
+ Colors.Menu.Disabled = MakeColor (ConsoleColor.Gray, ConsoleColor.DarkGray);
+
+ Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
+ Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkGray);
+ Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
+ Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
+
+ Colors.Error.Normal = MakeColor (ConsoleColor.DarkRed, ConsoleColor.White);
+ Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed);
+ Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White);
+ Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
+
+ HLine = '\u2500';
+ VLine = '\u2502';
+ Stipple = '\u2592';
+ Diamond = '\u25c6';
+ ULCorner = '\u250C';
+ LLCorner = '\u2514';
+ URCorner = '\u2510';
+ LRCorner = '\u2518';
+ LeftTee = '\u251c';
+ RightTee = '\u2524';
+ TopTee = '\u22a4';
+ BottomTee = '\u22a5';
+ }
+
[StructLayout (LayoutKind.Sequential)]
public struct ConsoleKeyInfoEx {
public ConsoleKeyInfo consoleKeyInfo;
@@ -564,11 +607,24 @@ namespace Terminal.Gui {
case WindowsConsole.EventType.Key:
var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
if (map == (Key)0xffffffff) {
- KeyEvent key = default;
+ KeyEvent key = new KeyEvent ();
+
// Shift = VK_SHIFT = 0x10
// Ctrl = VK_CONTROL = 0x11
// Alt = VK_MENU = 0x12
+ if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) {
+ inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn;
+ }
+
+ if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) {
+ inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn;
+ }
+
+ if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) {
+ inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn;
+ }
+
switch (inputEvent.KeyEvent.dwControlKeyState) {
case WindowsConsole.ControlKeyState.RightAltPressed:
case WindowsConsole.ControlKeyState.RightAltPressed |
@@ -617,10 +673,10 @@ namespace Terminal.Gui {
keyUpHandler (key);
} else {
if (inputEvent.KeyEvent.bKeyDown) {
- // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
keyDownHandler (new KeyEvent (map));
- keyHandler (new KeyEvent (map));
} else {
+ // Key Up - Fire KeyDown Event and KeyStroke (ProcessKey) Event
+ keyHandler (new KeyEvent (map));
keyUpHandler (new KeyEvent (map));
}
}
@@ -918,6 +974,9 @@ namespace Terminal.Gui {
case ConsoleKey.OemComma:
case ConsoleKey.OemPlus:
case ConsoleKey.OemMinus:
+ if (keyInfo.KeyChar == 0)
+ return Key.Unknown;
+
return (Key)((uint)keyInfo.KeyChar);
}
@@ -971,48 +1030,10 @@ namespace Terminal.Gui {
public override void Init (Action terminalResized)
{
TerminalResized = terminalResized;
-
- Colors.Base = new ColorScheme ();
- Colors.Dialog = new ColorScheme ();
- Colors.Menu = new ColorScheme ();
- Colors.Error = new ColorScheme ();
-
- HLine = '\u2500';
- VLine = '\u2502';
- Stipple = '\u2592';
- Diamond = '\u25c6';
- ULCorner = '\u250C';
- LLCorner = '\u2514';
- URCorner = '\u2510';
- LRCorner = '\u2518';
- LeftTee = '\u251c';
- RightTee = '\u2524';
- TopTee = '\u22a4';
- BottomTee = '\u22a5';
-
- Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Blue);
- Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
- Colors.Base.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Blue);
- Colors.Base.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
-
- Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
- Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
- Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
- Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
- Colors.Menu.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Cyan);
-
- Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
- Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
- Colors.Dialog.HotNormal = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
- Colors.Dialog.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Cyan);
-
- Colors.Error.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Red);
- Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
- Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
- Colors.Error.HotFocus = Colors.Error.HotNormal;
- Console.Clear ();
+ SetupColorsAndBorders ();
}
+
void ResizeScreen ()
{
OutputBuffer = new WindowsConsole.CharInfo [Rows * Cols];
diff --git a/Terminal.Gui/Event.cs b/Terminal.Gui/Event.cs
index c4e9b9e1a..169b60084 100644
--- a/Terminal.Gui/Event.cs
+++ b/Terminal.Gui/Event.cs
@@ -45,7 +45,7 @@ namespace Terminal.Gui {
ControlSpace = 0,
///
- /// The key code for the user pressing Control-A
+ /// The key code for the user pressing Control-A
///
ControlA = 1,
///
@@ -288,8 +288,7 @@ namespace Terminal.Gui {
///
/// Describes a keyboard event.
///
- public struct KeyEvent {
-
+ public class KeyEvent {
///
/// Symb olid definition for the key.
///
@@ -321,6 +320,10 @@ namespace Terminal.Gui {
//public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
public bool IsCtrl => (Key & Key.CtrlMask) != 0;
+ public KeyEvent ()
+ {
+ Key = Key.Unknown;
+ }
///
/// Constructs a new KeyEvent from the provided Key value - can be a rune cast into a Key value
///
@@ -328,6 +331,28 @@ namespace Terminal.Gui {
{
Key = k;
}
+
+ public override string ToString ()
+ {
+ string msg = "";
+ var key = this.Key;
+ if ((this.Key & Key.ShiftMask) != 0) {
+ msg += "Shift-";
+ }
+ if ((this.Key & Key.CtrlMask) != 0) {
+ msg += "Ctrl-";
+ }
+ if ((this.Key & Key.AltMask) != 0) {
+ msg += "Alt-";
+ }
+
+ if (string.IsNullOrEmpty (msg)) {
+ msg += $"{(((uint)this.KeyValue & (uint)Key.CharMask) > 27 ? $"{(char)this.KeyValue}" : $"{key}")}";
+ } else {
+ msg += $"{(((uint)this.KeyValue & (uint)Key.CharMask) > 27 ? $"{(char)this.KeyValue}" : $"")}";
+ }
+ return msg;
+ }
}
///
@@ -486,7 +511,7 @@ namespace Terminal.Gui {
/// Returns a that represents the current .
///
/// A that represents the current .
- public override string ToString()
+ public override string ToString ()
{
return $"({X},{Y}:{Flags}";
}
diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs
index 29808e781..b265fccda 100644
--- a/Terminal.Gui/Views/Label.cs
+++ b/Terminal.Gui/Views/Label.cs
@@ -124,7 +124,7 @@ namespace Terminal.Gui {
for (int i = 0; i < spaces; i++)
s.Append (' ');
if (extras > 0) {
- s.Append ('_');
+ //s.Append ('_');
extras--;
}
}
@@ -179,8 +179,11 @@ namespace Terminal.Gui {
int x;
switch (textAlignment) {
case TextAlignment.Left:
+ x = Frame.Left;
+ break;
case TextAlignment.Justified:
- x = 0;
+ Recalc ();
+ x = Frame.Left;
break;
case TextAlignment.Right:
x = Frame.Right - str.Length;
diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs
index 9cb5bfe16..309c408e0 100644
--- a/Terminal.Gui/Views/ListView.cs
+++ b/Terminal.Gui/Views/ListView.cs
@@ -294,6 +294,11 @@ namespace Terminal.Gui {
///
public event Action SelectedChanged;
+ ///
+ /// This event is raised on Enter key or Double Click to open the selected item.
+ ///
+ public event EventHandler OpenSelectedItem;
+
///
/// Handles cursor movement for this view, passes all other events.
///
@@ -325,6 +330,11 @@ namespace Terminal.Gui {
return true;
else
break;
+
+ case Key.Enter:
+ OpenSelectedItem?.Invoke (this, new EventArgs ());
+ break;
+
}
return base.ProcessKey (kb);
}
@@ -451,7 +461,7 @@ namespace Terminal.Gui {
///
public override bool MouseEvent(MouseEvent me)
{
- if (!me.Flags.HasFlag (MouseFlags.Button1Clicked))
+ if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
return false;
if (!HasFocus)
@@ -469,9 +479,10 @@ namespace Terminal.Gui {
SetNeedsDisplay ();
return true;
}
- if (SelectedChanged != null)
- SelectedChanged();
+ SelectedChanged?.Invoke ();
SetNeedsDisplay ();
+ if (me.Flags == MouseFlags.Button1DoubleClicked)
+ OpenSelectedItem?.Invoke (this, new EventArgs ());
return true;
}
}
diff --git a/Terminal.sln b/Terminal.sln
index 5bace6618..77680ffd0 100644
--- a/Terminal.sln
+++ b/Terminal.sln
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal.Gui", "Terminal.Gu
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UICatalog", "UICatalog\UICatalog.csproj", "{88979F89-9A42-448F-AE3E-3060145F6375}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
@@ -25,6 +27,14 @@ Global
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Debug|x86.Build.0 = Debug|x86
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.ActiveCfg = Release|x86
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.Build.0 = Release|x86
+ {88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.Build.0 = Debug|Any CPU
+ {88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.Build.0 = Release|Any CPU
+ {88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.ActiveCfg = Release|Any CPU
+ {88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
diff --git a/UICatalog/.editorconfig b/UICatalog/.editorconfig
new file mode 100644
index 000000000..040e7abd9
--- /dev/null
+++ b/UICatalog/.editorconfig
@@ -0,0 +1,23 @@
+[*.cs]
+indent_style = tab
+indent_size = 8
+tab_width = 8
+csharp_new_line_before_open_brace = methods,local_functions
+csharp_new_line_before_else = false
+csharp_new_line_before_catch = false
+csharp_new_line_before_finally = false
+end_of_line = crlf
+
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = false
+csharp_indent_labels = flush_left
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_preserve_single_line_blocks = true
+dotnet_style_require_accessibility_modifiers = never
+csharp_style_var_when_type_is_apparent = true
+csharp_prefer_braces = false
+csharp_space_before_open_square_brackets = true
+csharp_space_between_method_call_name_and_opening_parenthesis = true
+csharp_space_between_method_declaration_name_and_open_parenthesis = true
\ No newline at end of file
diff --git a/UICatalog/.gitignore b/UICatalog/.gitignore
new file mode 100644
index 000000000..4378419e7
--- /dev/null
+++ b/UICatalog/.gitignore
@@ -0,0 +1,9 @@
+###############
+# folder #
+###############
+/**/DROP/
+/**/TEMP/
+/**/packages/
+/**/bin/
+/**/obj/
+_site
diff --git a/UICatalog/Program.cs b/UICatalog/Program.cs
new file mode 100644
index 000000000..14a7956db
--- /dev/null
+++ b/UICatalog/Program.cs
@@ -0,0 +1,271 @@
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+ ///
+ /// Main program for the Terminal.gui UI Catalog app. This app provides a chooser that allows
+ /// for a calalog of UI demos, examples, and tests.
+ ///
+ class Program {
+ private static Toplevel _top;
+ private static MenuBar _menu;
+ private static int _nameColumnWidth;
+ private static Window _leftPane;
+ private static List _categories;
+ private static ListView _categoryListView;
+ private static Window _rightPane;
+ private static List _scenarios;
+ private static ListView _scenarioListView;
+ private static StatusBar _statusBar;
+
+ private static Scenario _runningScenario = null;
+
+ static void Main (string [] args)
+ {
+ if (Debugger.IsAttached)
+ CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
+
+ _scenarios = Scenario.GetDerivedClassesCollection ().ToList ();
+
+ if (args.Length > 0) {
+ var item = _scenarios.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals (args [0], StringComparison.OrdinalIgnoreCase));
+ _runningScenario = (Scenario)Activator.CreateInstance (_scenarios [item]);
+ Application.Init ();
+ _runningScenario.Init (Application.Top);
+ _runningScenario.Setup ();
+ _runningScenario.Run ();
+ _runningScenario = null;
+ return;
+ }
+
+ Scenario scenario = GetScenarioToRun ();
+ while (scenario != null) {
+ Application.Init ();
+ scenario.Init (Application.Top);
+ scenario.Setup ();
+ scenario.Run ();
+ scenario = GetScenarioToRun ();
+ }
+ }
+
+ ///
+ /// Create all controls. This gets called once and the controls remain with their state between Sceanrio runs.
+ ///
+ private static void Setup ()
+ {
+ _menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_File", new MenuItem [] {
+ new MenuItem ("_Quit", "", () => Application.RequestStop() )
+ }),
+ new MenuBarItem ("_About...", "About this app", () => MessageBox.Query (0, 6, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")),
+ });
+
+ _leftPane = new Window ("Categories") {
+ X = 0,
+ Y = 1, // for menu
+ Width = 25,
+ Height = Dim.Fill (),
+ CanFocus = false,
+ };
+
+
+ _categories = Scenario.GetAllCategories ();
+ _categoryListView = new ListView (_categories) {
+ X = 1,
+ Y = 0,
+ Width = Dim.Fill (0),
+ Height = Dim.Fill (2),
+ AllowsMarking = false,
+ CanFocus = true,
+ };
+ _categoryListView.OpenSelectedItem += (o, a) => {
+ _top.SetFocus (_rightPane);
+ };
+ _categoryListView.SelectedChanged += CategoryListView_SelectedChanged;
+ _leftPane.Add (_categoryListView);
+
+ _rightPane = new Window ("Scenarios") {
+ X = 25,
+ Y = 1, // for menu
+ Width = Dim.Fill (),
+ Height = Dim.Fill (),
+ CanFocus = false,
+
+ };
+
+ _nameColumnWidth = Scenario.ScenarioMetadata.GetName (_scenarios.OrderByDescending (t => Scenario.ScenarioMetadata.GetName (t).Length).FirstOrDefault ()).Length;
+
+ _scenarioListView = new ListView () {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (0),
+ Height = Dim.Fill (0),
+ AllowsMarking = false,
+ CanFocus = true,
+ };
+
+ //_scenarioListView.OnKeyPress += (KeyEvent ke) => {
+ // if (_top.MostFocused == _scenarioListView && ke.Key == Key.Enter) {
+ // _scenarioListView_OpenSelectedItem (null, null);
+ // }
+ //};
+
+ _scenarioListView.OpenSelectedItem += _scenarioListView_OpenSelectedItem;
+ _rightPane.Add (_scenarioListView);
+
+ _categoryListView.SelectedItem = 0;
+ CategoryListView_SelectedChanged ();
+
+ _statusBar = new StatusBar (new StatusItem [] {
+ //new StatusItem(Key.F1, "~F1~ Help", () => Help()),
+ new StatusItem(Key.ControlQ, "~CTRL-Q~ Quit", () => {
+ if (_runningScenario is null){
+ // This causes GetScenarioToRun to return null
+ _runningScenario = null;
+ Application.RequestStop();
+ } else {
+ _runningScenario.RequestStop();
+ }
+ }),
+ });
+ }
+
+ ///
+ /// This shows the selection UI. Each time it is run, it calls Application.Init to reset everything.
+ ///
+ ///
+ private static Scenario GetScenarioToRun ()
+ {
+ Application.Init ();
+
+ if (_menu == null) {
+ Setup ();
+ }
+
+ _top = Application.Top;
+ _top.OnKeyUp += KeyUpHandler;
+ _top.Add (_menu);
+ _top.Add (_leftPane);
+ _top.Add (_rightPane);
+ _top.Add (_statusBar);
+
+ // HACK: There is no other way to SetFocus before Application.Run. See Issue #445
+#if false
+ if (_runningScenario != null)
+ Application.Iteration += Application_Iteration;
+#else
+ _top.Ready += (o, a) => {
+ if (_runningScenario != null) {
+ _top.SetFocus (_rightPane);
+ _runningScenario = null;
+ }
+ };
+#endif
+
+ Application.Run (_top);
+ return _runningScenario;
+ }
+
+#if false
+ private static void Application_Iteration (object sender, EventArgs e)
+ {
+ Application.Iteration -= Application_Iteration;
+ _top.SetFocus (_rightPane);
+ }
+#endif
+ private static void _scenarioListView_OpenSelectedItem (object sender, EventArgs e)
+ {
+ if (_runningScenario is null) {
+ var source = _scenarioListView.Source as ScenarioListDataSource;
+ _runningScenario = (Scenario)Activator.CreateInstance (source.Scenarios [_scenarioListView.SelectedItem]);
+ Application.RequestStop ();
+ }
+ }
+
+ internal class ScenarioListDataSource : IListDataSource {
+ public List Scenarios { get; set; }
+
+ public bool IsMarked (int item) => false;// Scenarios [item].IsMarked;
+
+ public int Count => Scenarios.Count;
+
+ public ScenarioListDataSource (List itemList) => Scenarios = itemList;
+
+ public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width)
+ {
+ container.Move (col, line);
+ // Equivalent to an interpolated string like $"{Scenarios[item].Name, -widtestname}"; if such a thing were possible
+ var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [item]));
+ RenderUstr (driver, $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [item])}", col, line, width);
+ }
+
+ public void SetMark (int item, bool value)
+ {
+ }
+
+ // A slightly adapted method from: https://github.com/migueldeicaza/gui.cs/blob/fc1faba7452ccbdf49028ac49f0c9f0f42bbae91/Terminal.Gui/Views/ListView.cs#L433-L461
+ private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width)
+ {
+ int used = 0;
+ int index = 0;
+ while (index < ustr.Length) {
+ (var rune, var size) = Utf8.DecodeRune (ustr, index, index - ustr.Length);
+ var count = Rune.ColumnWidth (rune);
+ if (used + count >= width) break;
+ driver.AddRune (rune);
+ used += count;
+ index += size;
+ }
+
+ while (used < width) {
+ driver.AddRune (' ');
+ used++;
+ }
+ }
+ }
+
+ ///
+ /// When Scenarios are running we need to override the behavior of the Menu
+ /// and Statusbar to enable Scenarios that use those (or related key input)
+ /// to not be impacted. Same as for tabs.
+ ///
+ ///
+ private static void KeyUpHandler (KeyEvent ke)
+ {
+ if (_runningScenario != null) {
+ //switch (ke.Key) {
+ //case Key.Esc:
+ // //_runningScenario.RequestStop ();
+ // break;
+ //case Key.Enter:
+ // break;
+ //}
+ } else if (ke.Key == Key.Tab || ke.Key == Key.BackTab) {
+ // BUGBUG: Work around Issue #434 by implementing our own TAB navigation
+ if (_top.MostFocused == _categoryListView)
+ _top.SetFocus (_rightPane);
+ else
+ _top.SetFocus (_leftPane);
+ }
+ }
+
+ private static void CategoryListView_SelectedChanged ()
+ {
+ var item = _categories [_categoryListView.SelectedItem];
+ List newlist;
+ if (item.Equals ("All")) {
+ newlist = _scenarios;
+
+ } else {
+ newlist = _scenarios.Where (t => Scenario.ScenarioCategory.GetCategories (t).Contains (item)).ToList ();
+ }
+ _scenarioListView.Source = new ScenarioListDataSource (newlist);
+ _scenarioListView.SelectedItem = 0;
+ }
+ }
+}
diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json
new file mode 100644
index 000000000..1d6b7542f
--- /dev/null
+++ b/UICatalog/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "UICatalog": {
+ "commandName": "Project",
+ "commandLineArgs": "HexEditor"
+ }
+ }
+}
\ No newline at end of file
diff --git a/UICatalog/README.md b/UICatalog/README.md
new file mode 100644
index 000000000..6f6b019f8
--- /dev/null
+++ b/UICatalog/README.md
@@ -0,0 +1,122 @@
+# Terminal.Gui UI Catalog
+
+UI Catalog is a comprehensive sample library for Terminal.Gui. It attempts to satisfy the following goals:
+
+1. Be an easy to use showcase for Terminal.Gui concepts and features.
+2. Provide sample code that illustrates how to properly implement said concepts & features.
+3. Make it easy for contributors to add additional samples in a structured way.
+
+
+
+## Motivation
+
+The original `demo.cs` sample app for Terminal.Gui is neither good to showcase, nor does it explain different concepts. In addition, because it is built on a single source file, it has proven to cause friction when multiple contributors are simultaneously working on different aspects of Terminal.Gui. See [Issue #368](https://github.com/migueldeicaza/Terminal.Gui/issues/368) for more background.
+
+## How To Use
+
+`Program.cs` is the main app and provides a UI for selecting and running **Scenarios**. Each **Scenario* is implemented as a class derived from `Scenario` and `Program.cs` uses reflection to dynamically build the UI.
+
+**Scenarios** are tagged with categories using the `[ScenarioCategory]` attribute. The left pane of the main screen lists the categories. Clicking on a category shows all the scenarios in that category.
+
+**Scenarios** can be run either from the **UICatalog.exe** app UI or by being specified on the command line:
+
+```
+UICatalog.exe
+```
+
+e.g.
+
+```
+UICatalog.exe Buttons
+```
+
+When a **Scenario** is run, it runs as though it were a standalone `Terminal.Gui` app. However, scaffolding is provided (in the `Scenario` base class) that (optionally) takes care of `Terminal.Gui` initialization.
+
+## Contributing by Adding Scenarios
+
+To add a new **Scenario** simply:
+
+1. Create a new `.cs` file in the `Scenarios` directory that derives from `Scenario`.
+2. Add a `[ScenarioMetaData]` attribute to the class specifying the scenario's name and description.
+3. Add one or more `[ScenarioCategory]` attributes to the class specifying which categories the sceanrio belongs to. If you don't specify a category the sceanrio will show up in "All".
+4. Implement the `Setup` override which will be called when a user selects the scenario to run.
+5. Optionally, implement the `Init` and/or `Run` overrides to provide a custom implementation.
+
+The sample below is provided in the `Scenarios` directory as a generic sample that can be copied and re-named:
+
+```csharp
+using Terminal.Gui;
+
+namespace UICatalog {
+ [ScenarioMetadata (Name: "Generic", Description: "Generic sample - A template for creating new Scenarios")]
+ [ScenarioCategory ("Controls")]
+ class MyScenario : Scenario {
+ public override void Setup ()
+ {
+ // Put your scenario code here, e.g.
+ Win.Add (new Button ("Press me!") {
+ X = Pos.Center (),
+ Y = Pos.Center (),
+ Clicked = () => MessageBox.Query (20, 7, "Hi", "Neat?", "Yes", "No")
+ });
+ }
+ }
+}
+```
+
+`Scenario` provides a `Toplevel` and `Window` the provides a canvas for the Scenario to operate. The default `Window` shows the Scenario name and supports exiting the Scenario through the `Esc` key.
+
+
+
+To build a more advanced scenario, where control of the `Toplevel` and `Window` is needed (e.g. for scenarios using `MenuBar` or `StatusBar`), simply set the `Top` and `Window` properties as appropriate, as seen in the `UnicodeInMenu` scenario:
+
+```csharp
+using Terminal.Gui;
+
+namespace UICatalog {
+ [ScenarioMetadata (Name: "Unicode In Menu", Description: "Unicode menus per PR #204")]
+ [ScenarioCategory ("Text")]
+ [ScenarioCategory ("Controls")]
+ class UnicodeInMenu : Scenario {
+ public override void Setup ()
+ {
+ Top = new Toplevel (new Rect (0, 0, Application.Driver.Cols, Application.Driver.Rows));
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_Файл", new MenuItem [] {
+ new MenuItem ("_Создать", "Creates new file", null),
+ new MenuItem ("_Открыть", "", null),
+ new MenuItem ("Со_хранить", "", null),
+ new MenuItem ("_Выход", "", () => Application.RequestStop() )
+ }),
+ new MenuBarItem ("_Edit", new MenuItem [] {
+ new MenuItem ("_Copy", "", null),
+ new MenuItem ("C_ut", "", null),
+ new MenuItem ("_Paste", "", null)
+ })
+ });
+ Top.Add (menu);
+
+ Win = new Window ($"Scenario: {GetName ()}") {
+ X = 0,
+ Y = 1,
+ Width = Dim.Fill (),
+ Height = Dim.Fill ()
+ };
+ Top.Add (Win);
+ }
+ }
+}
+```
+
+For complete control, the `Init` and `Run` overrides can be implemented. The `base.Init` assigns `Application.Top` to `Top` and creates `Win`. The `base.Run` simply calls `Application.Run(Top)`.
+
+## Contribution Guidelines
+
+- Provide a terse, descriptive name for `Scenarios`. Keep them short; the `ListView` that displays them dynamically sizes the column width and long names will make it hard for people to use.
+- Provide a clear description.
+- Comment `Scenario` code to describe to others why it's a useful `Scenario`.
+- Annotate `Scenarios` with `[ScenarioCategory]` attributes. Try to minimize the number of new categories created.
+- Use the `Bug Rero` Category for `Scnarios` that reproduce bugs.
+ - Include the Github Issue # in the Description.
+ - Once the bug has been fixed in `master` submit another PR to remove the `Scenario` (or modify it to provide a good regression test).
+- Tag bugs or suggestions for `UI Catalog` in the main `Terminal.Gui` Github Issues with "UICatalog: ".
\ No newline at end of file
diff --git a/UICatalog/Scenario.cs b/UICatalog/Scenario.cs
new file mode 100644
index 000000000..e491a4c8e
--- /dev/null
+++ b/UICatalog/Scenario.cs
@@ -0,0 +1,181 @@
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+ ///
+ /// Base class for each demo/scenario. To define a new sceanrio simply
+ ///
+ /// 1) declare a class derived from Scenario,
+ /// 2) Set Name and Description as appropriate using [ScenarioMetadata] attribute
+ /// 3) Set one or more categories with the [ScenarioCategory] attribute
+ /// 4) Implement Setup.
+ /// 5) Optionally, implement Run.
+ ///
+ /// The Main program uses reflection to find all sceanarios and adds them to the
+ /// ListViews. Press ENTER to run the selected sceanrio. Press CTRL-Q to exit it.
+ ///
+ public class Scenario {
+ ///
+ /// The Top level for the Scenario. This should be set to `Application.Top` in most cases.
+ ///
+ public Toplevel Top { get; set; }
+
+ ///
+ ///
+ public Window Win { get; set; }
+
+ ///
+ /// Helper that provides the default Window implementation with a frame and
+ /// label showing the name of the Scenario and logic to exit back to
+ /// the Scenario picker UI.
+ /// Override Init to provide any `Toplevel` behavior needed.
+ ///
+ ///
+ public virtual void Init(Toplevel top)
+ {
+ Top = top;
+ Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = Dim.Fill ()
+ };
+ Top.Add (Win);
+ }
+
+ [System.AttributeUsage (System.AttributeTargets.Class)]
+ public class ScenarioMetadata : System.Attribute {
+ ///
+ /// Scenario Name
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Scenario Description
+ ///
+ public string Description { get; set; }
+
+ public ScenarioMetadata (string Name, string Description)
+ {
+ this.Name = Name;
+ this.Description = Description;
+ }
+
+ ///
+ /// Static helper function to get the Scenario Name given a Type
+ ///
+ ///
+ ///
+ public static string GetName (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Name;
+
+ ///
+ /// Static helper function to get the Scenario Description given a Type
+ ///
+ ///
+ ///
+ public static string GetDescription (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Description;
+ }
+
+ ///
+ /// Helper to get the Scenario Name
+ ///
+ ///
+ public string GetName () => ScenarioMetadata.GetName (this.GetType ());
+
+ ///
+ /// Helper to get the Scenario Descripiton
+ ///
+ ///
+ public string GetDescription () => ScenarioMetadata.GetDescription (this.GetType ());
+
+ [System.AttributeUsage (System.AttributeTargets.Class, AllowMultiple = true)]
+ public class ScenarioCategory : System.Attribute {
+ ///
+ /// Category Name
+ ///
+ public string Name { get; set; }
+
+ public ScenarioCategory (string Name) => this.Name = Name;
+
+ ///
+ /// Static helper function to get the Scenario Name given a Type
+ ///
+ ///
+ ///
+ public static string GetName (Type t) => ((ScenarioCategory)System.Attribute.GetCustomAttributes (t) [0]).Name;
+
+ ///
+ /// Static helper function to get the Scenario Categories given a Type
+ ///
+ ///
+ ///
+ public static List GetCategories (Type t) => System.Attribute.GetCustomAttributes (t)
+ .ToList ()
+ .Where (a => a is ScenarioCategory)
+ .Select (a => ((ScenarioCategory)a).Name)
+ .ToList ();
+ }
+
+ ///
+ /// Helper function to get the Categories of a Scenario
+ ///
+ ///
+ public List GetCategories () => ScenarioCategory.GetCategories (this.GetType ());
+
+ public override string ToString () => $"{GetName (),-30}{GetDescription ()}";
+
+ ///
+ /// Override this to implement the Scenario setup logic (create controls, etc...).
+ ///
+ public virtual void Setup ()
+ {
+ }
+
+ ///
+ /// Runs the scenario. Override to start the scearnio using a Top level different than `Top`.
+ ///
+ public virtual void Run ()
+ {
+ Application.Run (Top);
+ }
+
+ ///
+ /// Stops the scenario. Override to implement shutdown behavior for the Scenario.
+ ///
+ public virtual void RequestStop ()
+ {
+ Application.RequestStop ();
+ }
+
+ ///
+ /// Returns a list of all Categories set by all of the scenarios defined in the project.
+ ///
+ internal static List GetAllCategories ()
+ {
+ List categories = new List () { "All" };
+ foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
+ .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
+ List attrs = System.Attribute.GetCustomAttributes (type).ToList ();
+ categories = categories.Union (attrs.Where (a => a is ScenarioCategory).Select (a => ((ScenarioCategory)a).Name)).ToList ();
+ }
+ return categories;
+ }
+
+ ///
+ /// Returns an instance of each Scenario defined in the project.
+ /// https://stackoverflow.com/questions/5411694/get-all-inherited-classes-of-an-abstract-class
+ ///
+ internal static List GetDerivedClassesCollection ()
+ {
+ List objects = new List ();
+ foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
+ .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
+ objects.Add (type);
+ }
+ return objects;
+ }
+ }
+}
diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs
new file mode 100644
index 000000000..64b92bfce
--- /dev/null
+++ b/UICatalog/Scenarios/Buttons.cs
@@ -0,0 +1,105 @@
+using Terminal.Gui;
+
+namespace UICatalog {
+ [ScenarioMetadata (Name: "Buttons", Description: "Demonstrates all sorts of Buttons")]
+ [ScenarioCategory ("Controls")]
+ [ScenarioCategory ("Layout")]
+ class Buttons : Scenario {
+ public override void Setup ()
+ {
+ // Add a label & text field so we can demo IsDefault
+ var editLabel = new Label ("TextField (to demo IsDefault):") {
+ X = 0,
+ Y = 0,
+ };
+ Win.Add (editLabel);
+ var edit = new TextField ("") {
+ X = Pos.Right (editLabel) + 1,
+ Y = Pos.Top (editLabel),
+ Width = Dim.Fill (2),
+ };
+ Win.Add (edit);
+
+ // This is the default button (IsDefault = true); if user presses ENTER in the TextField
+ // the scenario will quit
+ var defaultButton = new Button ("Quit") {
+ X = Pos.Center (),
+ // BUGBUG: Throws an exception
+ //Y= Pos.Bottom(Win),
+ Y = 20,
+ IsDefault = true,
+ Clicked = () => Application.RequestStop (),
+ };
+ Win.Add (defaultButton);
+
+ var y = 2;
+ var button = new Button (10, y, "Base Color") {
+ ColorScheme = Colors.Base,
+ Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
+ };
+ Win.Add (button);
+
+ y += 2;
+ Win.Add (new Button (10, y, "Error Color") {
+ ColorScheme = Colors.Error,
+ Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
+ });
+
+ y += 2;
+ Win.Add (new Button (10, y, "Dialog Color") {
+ ColorScheme = Colors.Dialog,
+ Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
+ });
+
+ y += 2;
+ Win.Add (new Button (10, y, "Menu Color") {
+ ColorScheme = Colors.Menu,
+ Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
+ });
+
+ y += 2;
+ Win.Add (new Button (10, y, "TopLevel Color") {
+ ColorScheme = Colors.TopLevel,
+ Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
+ });
+
+ y += 2;
+ Win.Add (new Button (10, y, "A super long button that will probably expose a bug in clipping or wrapping of text. Will it?") {
+ Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
+ });
+
+ y += 2;
+ // Note the 'N' in 'Newline' will be the hotkey
+ Win.Add (new Button (10, y, "a Newline\nin the button") {
+ Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
+ });
+
+ y += 2;
+ // BUGBUG: Buttons don't support specifying hotkeys with _?!?
+ Win.Add (button = new Button (10, y, "Te_xt Changer") {
+ });
+ button.Clicked = () => button.Text += $"{y++}";
+
+ Win.Add (new Button ("Lets see if this will move as \"Text Changer\" grows") {
+ X = Pos.Right(button) + 10,
+ Y = y,
+ });
+
+ y += 2;
+ Win.Add (new Button (10, y, "Delete") {
+ ColorScheme = Colors.Error,
+ Clicked = () => Win.Remove (button)
+ });
+
+ y += 2;
+ Win.Add (new Button (10, y, "Change Default") {
+ Clicked = () => {
+ defaultButton.IsDefault = !defaultButton.IsDefault;
+ button.IsDefault = !button.IsDefault;
+ },
+ });
+
+
+ }
+ }
+}
diff --git a/UICatalog/Scenarios/DimAndPosLayout.cs b/UICatalog/Scenarios/DimAndPosLayout.cs
new file mode 100644
index 000000000..f1f543a57
--- /dev/null
+++ b/UICatalog/Scenarios/DimAndPosLayout.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog {
+ ///
+ /// 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
+ /// [ ] - ...
+ ///
+ [ScenarioMetadata (Name: "DimAndPosLayout", Description: "Demonstrates using the Dim and Pos Layout System")]
+ [ScenarioCategory ("Layout")]
+ class DimAndPosLayout : Scenario {
+
+ public override void Setup ()
+ {
+ Top.LayoutStyle = LayoutStyle.Computed;
+ // Demonstrate using Dim to create a ruler that always measures the top-level window's width
+ // BUGBUG: Dim.Fill returns too big a value sometimes.
+ //const string rule = "|123456789";
+ //var labelRuler = new Label ("ruler") {
+ // 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
+ //};
+
+ //Application.OnResized += () => {
+ // labelRuler.Text = rule.Repeat ((int)Math.Ceiling((double)(labelRuler.Bounds.Width) / (double)rule.Length))[0..(labelRuler.Bounds.Width)];
+ //};
+
+ //win.Add (labelRuler);
+
+ // Demonstrate using Dim to create a window that fills the parent with a margin
+ int margin = 20;
+ var subWin = new Window ($"Sub Windoww with {margin} character margin") {
+ X = margin,
+ Y = 2,
+ Width = Dim.Fill (margin),
+ Height = Dim.Fill ()
+ };
+ Win.Add (subWin);
+
+ int i = 1;
+ string txt = "Hello world, how are you doing today";
+ var labelList = new List