Przeglądaj źródła

Merge branch 'master' of tig:migueldeicaza/gui.cs

Charlie Kindel 5 lat temu
rodzic
commit
faaf89e20f
48 zmienionych plików z 2494 dodań i 122 usunięć
  1. 1 0
      Designer/Designer.csproj
  2. 1 0
      Example/Example.csproj
  3. 611 0
      Example/demo.cs.orig
  4. 50 20
      Terminal.Gui/Core.cs
  5. 4 1
      Terminal.Gui/Dialogs/Dialog.cs
  6. 4 3
      Terminal.Gui/Dialogs/FileDialog.cs
  7. 5 3
      Terminal.Gui/Drivers/ConsoleDriver.cs
  8. 32 19
      Terminal.Gui/Drivers/CursesDriver.cs
  9. 2 1
      Terminal.Gui/Drivers/NetDriver.cs
  10. 76 53
      Terminal.Gui/Drivers/WindowsDriver.cs
  11. 29 4
      Terminal.Gui/Event.cs
  12. 5 1
      Terminal.Gui/MonoCurses/UnmanagedLibrary.cs
  13. 3 1
      Terminal.Gui/MonoCurses/binding.cs
  14. 2 0
      Terminal.Gui/MonoCurses/constants.cs
  15. 3 1
      Terminal.Gui/MonoCurses/handles.cs
  16. 4 0
      Terminal.Gui/MonoCurses/mainloop.cs
  17. 6 0
      Terminal.Gui/Views/Button.cs
  18. 4 0
      Terminal.Gui/Views/Checkbox.cs
  19. 6 0
      Terminal.Gui/Views/Clipboard.cs
  20. 16 2
      Terminal.Gui/Views/DateField.cs
  21. 3 0
      Terminal.Gui/Views/HexView.cs
  22. 6 2
      Terminal.Gui/Views/Label.cs
  23. 66 3
      Terminal.Gui/Views/ListView.cs
  24. 30 7
      Terminal.Gui/Views/Menu.cs
  25. 8 1
      Terminal.Gui/Views/ScrollView.cs
  26. 8 0
      Terminal.Gui/Views/StatusBar.cs
  27. 6 0
      Terminal.Gui/Views/TextView.cs
  28. 10 0
      Terminal.sln
  29. 23 0
      UICatalog/.editorconfig
  30. 9 0
      UICatalog/.gitignore
  31. 271 0
      UICatalog/Program.cs
  32. 8 0
      UICatalog/Properties/launchSettings.json
  33. 122 0
      UICatalog/README.md
  34. 181 0
      UICatalog/Scenario.cs
  35. 105 0
      UICatalog/Scenarios/Buttons.cs
  36. 82 0
      UICatalog/Scenarios/DimAndPosLayout.cs
  37. 150 0
      UICatalog/Scenarios/Editor.cs
  38. 17 0
      UICatalog/Scenarios/Generic.cs
  39. 154 0
      UICatalog/Scenarios/HexEditor.cs
  40. 181 0
      UICatalog/Scenarios/Keys.cs
  41. 42 0
      UICatalog/Scenarios/MessageBoxes.cs
  42. 34 0
      UICatalog/Scenarios/Mouse.cs
  43. 26 0
      UICatalog/Scenarios/TextAlignment.cs
  44. 40 0
      UICatalog/Scenarios/TopLevelNoWindowBug.cs
  45. 35 0
      UICatalog/Scenarios/UnicodeInMenu.cs
  46. 13 0
      UICatalog/UICatalog.csproj
  47. BIN
      UICatalog/generic_screenshot.png
  48. BIN
      UICatalog/screenshot.png

+ 1 - 0
Designer/Designer.csproj

@@ -8,6 +8,7 @@
     <RootNamespace>Designer</RootNamespace>
     <RootNamespace>Designer</RootNamespace>
     <AssemblyName>Designer</AssemblyName>
     <AssemblyName>Designer</AssemblyName>
     <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
     <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <RuntimeIdentifier>win-x86</RuntimeIdentifier>
     <TargetFrameworkProfile />
     <TargetFrameworkProfile />
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">

+ 1 - 0
Example/Example.csproj

@@ -8,6 +8,7 @@
     <RootNamespace>Terminal</RootNamespace>
     <RootNamespace>Terminal</RootNamespace>
     <AssemblyName>Terminal</AssemblyName>
     <AssemblyName>Terminal</AssemblyName>
     <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
     <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
     <NuGetPackageImportStamp>
     <NuGetPackageImportStamp>
     </NuGetPackageImportStamp>
     </NuGetPackageImportStamp>
     <TargetFrameworkProfile />
     <TargetFrameworkProfile />

+ 611 - 0
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<string> () { "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<string> ();
+		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 ();
+	}
+}

+ 50 - 20
Terminal.Gui/Core.cs

@@ -899,10 +899,7 @@ namespace Terminal.Gui {
 				Move (frame.X, frame.Y);
 				Move (frame.X, frame.Y);
 		}
 		}
 
 
-		/// <summary>
-		/// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.View"/> has focus.
-		/// </summary>
-		/// <value><c>true</c> if has focus; otherwise, <c>false</c>.</value>
+		/// <inheritdoc cref="HasFocus"/>
 		public override bool HasFocus {
 		public override bool HasFocus {
 			get {
 			get {
 				return base.HasFocus;
 				return base.HasFocus;
@@ -925,12 +922,14 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		/// <inheritdoc cref="OnEnter"/>
 		public override bool OnEnter ()
 		public override bool OnEnter ()
 		{
 		{
 			Enter?.Invoke (this, new EventArgs ());
 			Enter?.Invoke (this, new EventArgs ());
 			return base.OnEnter ();
 			return base.OnEnter ();
 		}
 		}
 
 
+		/// <inheritdoc cref="OnLeave"/>
 		public override bool OnLeave ()
 		public override bool OnLeave ()
 		{
 		{
 			Leave?.Invoke (this, new EventArgs ());
 			Leave?.Invoke (this, new EventArgs ());
@@ -1067,11 +1066,11 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// 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.
 		/// </summary>
 		/// </summary>
 		public Action<KeyEvent> OnKeyPress;
 		public Action<KeyEvent> OnKeyPress;
 
 
-		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		/// <inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent keyEvent)
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
 		{
 			OnKeyPress?.Invoke (keyEvent);
 			OnKeyPress?.Invoke (keyEvent);
@@ -1081,10 +1080,9 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
-		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		/// <inheritdoc cref="ProcessHotKey"/>
 		public override bool ProcessHotKey (KeyEvent keyEvent)
 		public override bool ProcessHotKey (KeyEvent keyEvent)
 		{
 		{
-			OnKeyPress?.Invoke (keyEvent);
 			if (subviews == null || subviews.Count == 0)
 			if (subviews == null || subviews.Count == 0)
 				return false;
 				return false;
 			foreach (var view in subviews)
 			foreach (var view in subviews)
@@ -1093,10 +1091,9 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
-		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		/// <inheritdoc cref="ProcessColdKey"/>
 		public override bool ProcessColdKey (KeyEvent keyEvent)
 		public override bool ProcessColdKey (KeyEvent keyEvent)
 		{
 		{
-			OnKeyPress?.Invoke (keyEvent);
 			if (subviews == null || subviews.Count == 0)
 			if (subviews == null || subviews.Count == 0)
 				return false;
 				return false;
 			foreach (var view in subviews)
 			foreach (var view in subviews)
@@ -1110,7 +1107,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public Action<KeyEvent> OnKeyDown;
 		public Action<KeyEvent> OnKeyDown;
 
 
-		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		/// <inheritdoc cref="KeyDown"/>
 		public override bool KeyDown (KeyEvent keyEvent)
 		public override bool KeyDown (KeyEvent keyEvent)
 		{
 		{
 			OnKeyDown?.Invoke (keyEvent);
 			OnKeyDown?.Invoke (keyEvent);
@@ -1128,7 +1125,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public Action<KeyEvent> OnKeyUp;
 		public Action<KeyEvent> OnKeyUp;
 
 
-		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		/// <inheritdoc cref="KeyUp"/>
 		public override bool KeyUp (KeyEvent keyEvent)
 		public override bool KeyUp (KeyEvent keyEvent)
 		{
 		{
 			OnKeyUp?.Invoke (keyEvent);
 			OnKeyUp?.Invoke (keyEvent);
@@ -1140,6 +1137,7 @@ namespace Terminal.Gui {
 
 
 			return false;
 			return false;
 		}
 		}
+
 		/// <summary>
 		/// <summary>
 		/// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, it does nothing.
 		/// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, it does nothing.
 		/// </summary>
 		/// </summary>
@@ -1410,15 +1408,13 @@ namespace Terminal.Gui {
 			layoutNeeded = false;
 			layoutNeeded = false;
 		}
 		}
 
 
-		/// <summary>
-		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:Terminal.Gui.View"/>.
-		/// </summary>
-		/// <returns>A <see cref="T:System.String"/> that represents the current <see cref="T:Terminal.Gui.View"/>.</returns>
+		/// <inheritdoc cref="ToString"/>
 		public override string ToString ()
 		public override string ToString ()
 		{
 		{
 			return $"{GetType ().Name}({Id})({Frame})";
 			return $"{GetType ().Name}({Id})({Frame})";
 		}
 		}
 
 
+		/// <inheritdoc cref="OnMouseEnter(Gui.MouseEvent)"/>
 		public override bool OnMouseEnter (MouseEvent mouseEvent)
 		public override bool OnMouseEnter (MouseEvent mouseEvent)
 		{
 		{
 			if (!base.OnMouseEnter (mouseEvent)) {
 			if (!base.OnMouseEnter (mouseEvent)) {
@@ -1428,6 +1424,7 @@ namespace Terminal.Gui {
 			return true;
 			return true;
 		}
 		}
 
 
+		/// <inheritdoc cref="OnMouseLeave(Gui.MouseEvent)"/>
 		public override bool OnMouseLeave (MouseEvent mouseEvent)
 		public override bool OnMouseLeave (MouseEvent mouseEvent)
 		{
 		{
 			if (!base.OnMouseLeave (mouseEvent)) {
 			if (!base.OnMouseLeave (mouseEvent)) {
@@ -1470,10 +1467,25 @@ namespace Terminal.Gui {
 	/// </remarks>
 	/// </remarks>
 	public class Toplevel : View {
 	public class Toplevel : View {
 		/// <summary>
 		/// <summary>
-		/// This flag is checked on each iteration of the mainloop and it continues
-		/// running until this flag is set to false.
+		/// Gets or sets whether the <see cref="T:Terminal.Gui.MainLoop"/> for this <see cref="T:Terminal.Gui.Toplevel"/> is running or not. Setting
+		/// this property to false will cause the MainLoop to exit. 
 		/// </summary>
 		/// </summary>
-		public bool Running;
+		public bool Running { get; set; }
+
+		/// <summary>
+		/// Fired once the Toplevel's <see cref="T:Terminal.Gui.MainLoop"/> has started it's first iteration. 
+		/// Subscribe to this event to perform tasks when the <see cref="T:Terminal.Gui.Toplevel"/> has been laid out and focus has been set.
+		/// changes. A Ready event handler is a good place to finalize initialization after calling `<see cref="T:Terminal.Gui.Application.Run"/>(topLevel)`. 
+		/// </summary>
+		public event EventHandler Ready;
+
+		/// <summary>
+		/// Called from Application.RunLoop after the <see cref="T:Terminal.Gui.Toplevel"/> has entered it's first iteration of the loop. 
+		/// </summary>
+		internal virtual void OnReady ()
+		{
+			Ready?.Invoke (this, EventArgs.Empty);
+		}
 
 
 		/// <summary>
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Toplevel"/> class with the specified absolute layout.
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Toplevel"/> class with the specified absolute layout.
@@ -1508,6 +1520,10 @@ namespace Terminal.Gui {
 			return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
 			return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
 		}
 		}
 
 
+		/// <summary>
+		/// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.Toplevel"/> can focus.
+		/// </summary>
+		/// <value><c>true</c> if can focus; otherwise, <c>false</c>.</value>
 		public override bool CanFocus {
 		public override bool CanFocus {
 			get => true;
 			get => true;
 		}
 		}
@@ -1529,6 +1545,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public bool HasStatusBar { get; set; }
 		public bool HasStatusBar { get; set; }
 
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent keyEvent)
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
 		{
 			if (base.ProcessKey (keyEvent))
 			if (base.ProcessKey (keyEvent))
@@ -1580,6 +1597,7 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
+		///<inheritdoc cref="Add"/>
 		public override void Add (View view)
 		public override void Add (View view)
 		{
 		{
 			if (this == Application.Top) {
 			if (this == Application.Top) {
@@ -1591,6 +1609,7 @@ namespace Terminal.Gui {
 			base.Add (view);
 			base.Add (view);
 		}
 		}
 
 
+		///<inheritdoc cref="Remove"/>
 		public override void Remove (View view)
 		public override void Remove (View view)
 		{
 		{
 			if (this == Application.Top) {
 			if (this == Application.Top) {
@@ -1602,6 +1621,7 @@ namespace Terminal.Gui {
 			base.Remove (view);
 			base.Remove (view);
 		}
 		}
 
 
+		///<inheritdoc cref="RemoveAll"/>
 		public override void RemoveAll ()
 		public override void RemoveAll ()
 		{
 		{
 			if (this == Application.Top) {
 			if (this == Application.Top) {
@@ -1656,6 +1676,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		public override void Redraw (Rect region)
 		{
 		{
 			Application.CurrentView = this;
 			Application.CurrentView = this;
@@ -1838,6 +1859,7 @@ namespace Terminal.Gui {
 			contentView.RemoveAll ();
 			contentView.RemoveAll ();
 		}
 		}
 
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect bounds)
 		public override void Redraw (Rect bounds)
 		{
 		{
 			Application.CurrentView = this;
 			Application.CurrentView = this;
@@ -1873,6 +1895,7 @@ namespace Terminal.Gui {
 		//
 		//
 		internal static Point? dragPosition;
 		internal static Point? dragPosition;
 		Point start;
 		Point start;
+		///<inheritdoc cref="MouseEvent(Gui.MouseEvent)"/>
 		public override bool MouseEvent (MouseEvent mouseEvent)
 		public override bool MouseEvent (MouseEvent mouseEvent)
 		{
 		{
 			// FIXED:The code is currently disabled, because the
 			// FIXED:The code is currently disabled, because the
@@ -2409,8 +2432,15 @@ namespace Terminal.Gui {
 			if (state.Toplevel == null)
 			if (state.Toplevel == null)
 				throw new ObjectDisposedException ("state");
 				throw new ObjectDisposedException ("state");
 
 
+			bool firstIteration = true;
 			for (state.Toplevel.Running = true; state.Toplevel.Running;) {
 			for (state.Toplevel.Running = true; state.Toplevel.Running;) {
 				if (MainLoop.EventsPending (wait)) {
 				if (MainLoop.EventsPending (wait)) {
+					// Notify Toplevel it's ready
+					if (firstIteration) {
+						state.Toplevel.OnReady ();
+					}
+					firstIteration = false;
+
 					MainLoop.MainIteration ();
 					MainLoop.MainIteration ();
 					Iteration?.Invoke (null, EventArgs.Empty);
 					Iteration?.Invoke (null, EventArgs.Empty);
 				} else if (wait == false)
 				} else if (wait == false)
@@ -2426,7 +2456,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
-		internal static bool DebugDrawBounds;
+		internal static bool DebugDrawBounds = false;
 
 
 		// Need to look into why this does not work properly.
 		// Need to look into why this does not work properly.
 		static void DrawBounds (View v)
 		static void DrawBounds (View v)

+ 4 - 1
Terminal.Gui/Dialogs/Dialog.cs

@@ -61,7 +61,9 @@ namespace Terminal.Gui {
 			Add (button);
 			Add (button);
 		}
 		}
 
 
-
+		/// <summary>
+		/// Lays out the subviews for the Dialog. 
+		/// </summary>
 		public override void LayoutSubviews ()
 		public override void LayoutSubviews ()
 		{
 		{
 			base.LayoutSubviews ();
 			base.LayoutSubviews ();
@@ -86,6 +88,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent kb)
 		public override bool ProcessKey (KeyEvent kb)
 		{
 		{
 			switch (kb.Key) {
 			switch (kb.Key) {

+ 4 - 3
Terminal.Gui/Dialogs/FileDialog.cs

@@ -212,9 +212,9 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
-		public Action<(string,bool)> SelectedChanged;
-		public Action<ustring> DirectoryChanged;
-		public Action<ustring> FileChanged;
+		public Action<(string, bool)> SelectedChanged { get; set; }
+		public Action<ustring> DirectoryChanged { get; set; }
+		public Action<ustring> FileChanged { get; set; }
 
 
 		void SelectionChanged ()
 		void SelectionChanged ()
 		{
 		{
@@ -494,6 +494,7 @@ namespace Terminal.Gui {
 
 
 		internal bool canceled;
 		internal bool canceled;
 
 
+		///<inheritdoc cref="WillPresent"/>
 		public override void WillPresent ()
 		public override void WillPresent ()
 		{
 		{
 			base.WillPresent ();
 			base.WillPresent ();

+ 5 - 3
Terminal.Gui/Drivers/ConsoleDriver.cs

@@ -466,9 +466,11 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// <summary>
 		/// Prepare the driver and set the key and mouse events handlers.
 		/// Prepare the driver and set the key and mouse events handlers.
 		/// </summary>
 		/// </summary>
-		/// <param name="mainLoop"></param>
-		/// <param name="keyHandler"></param>
-		/// <param name="mouseHandler"></param>
+		/// <param name="mainLoop">The main loop.</param>
+		/// <param name="keyHandler">The handler for ProcessKey</param>
+		/// <param name="keyDownHandler">The handler for key down events</param>
+		/// <param name="keyUpHandler">The handler for key up events</param>
+		/// <param name="mouseHandler">The handler for mouse events</param>
 		public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler);
 		public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler);
 
 
 		/// <summary>
 		/// <summary>

+ 32 - 19
Terminal.Gui/Drivers/CursesDriver.cs

@@ -17,6 +17,7 @@ namespace Terminal.Gui {
 	/// This is the Curses driver for the gui.cs/Terminal framework.
 	/// This is the Curses driver for the gui.cs/Terminal framework.
 	/// </summary>
 	/// </summary>
 	public class CursesDriver : ConsoleDriver {
 	public class CursesDriver : ConsoleDriver {
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
 		public override int Cols => Curses.Cols;
 		public override int Cols => Curses.Cols;
 		public override int Rows => Curses.Lines;
 		public override int Rows => Curses.Lines;
 
 
@@ -37,7 +38,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
-		static bool sync;
+		static bool sync = false;
 		public override void AddRune (Rune rune)
 		public override void AddRune (Rune rune)
 		{
 		{
 			if (Clip.Contains (ccol, crow)) {
 			if (Clip.Contains (ccol, crow)) {
@@ -51,7 +52,7 @@ namespace Terminal.Gui {
 			if (sync)
 			if (sync)
 				Application.Driver.Refresh ();
 				Application.Driver.Refresh ();
 			ccol++;
 			ccol++;
-			var runeWidth = Rune.ColumnWidth(rune);
+			var runeWidth = Rune.ColumnWidth (rune);
 			if (runeWidth > 1) {
 			if (runeWidth > 1) {
 				for (int i = 1; i < runeWidth; i++) {
 				for (int i = 1; i < runeWidth; i++) {
 					ccol++;
 					ccol++;
@@ -66,8 +67,13 @@ namespace Terminal.Gui {
 				AddRune (rune);
 				AddRune (rune);
 		}
 		}
 
 
-		public override void Refresh () => Curses.refresh ();
-		public override void UpdateCursor () => Curses.refresh ();
+		public override void Refresh () {
+			Curses.refresh ();
+			if (Curses.CheckWinChange ()) {
+				TerminalResized?.Invoke ();
+			}
+		}
+		public override void UpdateCursor () => Refresh ();
 		public override void End () => Curses.endwin ();
 		public override void End () => Curses.endwin ();
 		public override void UpdateScreen () => window.redrawwin ();
 		public override void UpdateScreen () => window.redrawwin ();
 		public override void SetAttribute (Attribute c) => Curses.attrset (c.value);
 		public override void SetAttribute (Attribute c) => Curses.attrset (c.value);
@@ -186,7 +192,7 @@ namespace Terminal.Gui {
 			};
 			};
 		}
 		}
 
 
-		void ProcessInput (Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
+		void ProcessInput (Action<KeyEvent> keyHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 		{
 			int wch;
 			int wch;
 			var code = Curses.get_wch (out wch);
 			var code = Curses.get_wch (out wch);
@@ -206,6 +212,7 @@ namespace Terminal.Gui {
 					return;
 					return;
 				}
 				}
 				keyHandler (new KeyEvent (MapCursesKey (wch)));
 				keyHandler (new KeyEvent (MapCursesKey (wch)));
+				keyUpHandler (new KeyEvent (MapCursesKey (wch)));
 				return;
 				return;
 			}
 			}
 
 
@@ -213,7 +220,7 @@ namespace Terminal.Gui {
 			if (wch == 27) {
 			if (wch == 27) {
 				Curses.timeout (200);
 				Curses.timeout (200);
 
 
-				code = Curses.get_wch (out wch);
+				code = Curses.get_wch (out int wch2);
 				if (code == Curses.KEY_CODE_YES)
 				if (code == Curses.KEY_CODE_YES)
 					keyHandler (new KeyEvent (Key.AltMask | MapCursesKey (wch)));
 					keyHandler (new KeyEvent (Key.AltMask | MapCursesKey (wch)));
 				if (code == 0) {
 				if (code == 0) {
@@ -221,23 +228,28 @@ namespace Terminal.Gui {
 
 
 					// The ESC-number handling, debatable.
 					// The ESC-number handling, debatable.
 					// Simulates the AltMask itself by pressing Alt + Space.
 					// Simulates the AltMask itself by pressing Alt + Space.
-					if (wch == (int)Key.Space)
+					if (wch2 == (int)Key.Space)
 						key = new KeyEvent (Key.AltMask);
 						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);
 						key = new KeyEvent (Key.F10);
-					else if (wch == 27)
-						key = new KeyEvent ((Key)wch);
+					else if (wch2 == 27)
+						key = new KeyEvent ((Key)wch2);
 					else
 					else
-						key = new KeyEvent (Key.AltMask | (Key)wch);
+						key = new KeyEvent (Key.AltMask | (Key)wch2);
 					keyHandler (key);
 					keyHandler (key);
-				} else
+				} else {
 					keyHandler (new KeyEvent (Key.Esc));
 					keyHandler (new KeyEvent (Key.Esc));
-			} else
+				}
+			} else {
 				keyHandler (new KeyEvent ((Key)wch));
 				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<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
@@ -246,7 +258,7 @@ namespace Terminal.Gui {
 			Curses.timeout (-1);
 			Curses.timeout (-1);
 
 
 			(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {
 			(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {
-				ProcessInput (keyHandler, mouseHandler);
+				ProcessInput (keyHandler, keyUpHandler, mouseHandler);
 				return true;
 				return true;
 			});
 			});
 
 
@@ -308,7 +320,7 @@ namespace Terminal.Gui {
 				Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
 				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.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.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.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
 				Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
 				Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
@@ -490,6 +502,7 @@ namespace Terminal.Gui {
 			killpg (0, signal);
 			killpg (0, signal);
 			return true;
 			return true;
 		}
 		}
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
 	}
 	}
 
 
 }
 }

+ 2 - 1
Terminal.Gui/Drivers/NetDriver.cs

@@ -37,7 +37,7 @@ namespace Terminal.Gui {
 				dirtyLine [row] = true;
 				dirtyLine [row] = true;
 		}
 		}
 
 
-		static bool sync;
+		static bool sync = false;
 
 
 		public NetDriver ()
 		public NetDriver ()
 		{
 		{
@@ -328,6 +328,7 @@ namespace Terminal.Gui {
 				if (map == (Key)0xffffffff)
 				if (map == (Key)0xffffffff)
 					return;
 					return;
 				keyHandler (new KeyEvent (map));
 				keyHandler (new KeyEvent (map));
+				keyUpHandler (new KeyEvent (map));
 			};
 			};
 		}
 		}
 
 

+ 76 - 53
Terminal.Gui/Drivers/WindowsDriver.cs

@@ -97,7 +97,7 @@ namespace Terminal.Gui {
 		public void Cleanup ()
 		public void Cleanup ()
 		{
 		{
 			ConsoleMode = originalConsoleMode;
 			ConsoleMode = originalConsoleMode;
-			ContinueListeningForConsoleEvents = false;
+			//ContinueListeningForConsoleEvents = false;
 			if (!SetConsoleActiveScreenBuffer (OutputHandle)) {
 			if (!SetConsoleActiveScreenBuffer (OutputHandle)) {
 				var err = Marshal.GetLastWin32Error ();
 				var err = Marshal.GetLastWin32Error ();
 				Console.WriteLine ("Error: {0}", err);
 				Console.WriteLine ("Error: {0}", err);
@@ -109,7 +109,7 @@ namespace Terminal.Gui {
 			ScreenBuffer = IntPtr.Zero;
 			ScreenBuffer = IntPtr.Zero;
 		}
 		}
 
 
-		bool ContinueListeningForConsoleEvents = true;
+		//bool ContinueListeningForConsoleEvents = true;
 
 
 		public uint ConsoleMode {
 		public uint ConsoleMode {
 			get {
 			get {
@@ -426,7 +426,7 @@ namespace Terminal.Gui {
 	}
 	}
 
 
 	internal class WindowsDriver : ConsoleDriver, Mono.Terminal.IMainLoopDriver {
 	internal class WindowsDriver : ConsoleDriver, Mono.Terminal.IMainLoopDriver {
-		static bool sync;
+		static bool sync = false;
 		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
 		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
 		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
 		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
 		MainLoop mainLoop;
 		MainLoop mainLoop;
@@ -440,15 +440,10 @@ namespace Terminal.Gui {
 
 
 		public WindowsDriver ()
 		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 ();
 			winConsole = new WindowsConsole ();
 
 
+			SetupColorsAndBorders ();
+
 			cols = Console.WindowWidth;
 			cols = Console.WindowWidth;
 			rows = Console.WindowHeight;
 			rows = Console.WindowHeight;
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
@@ -459,6 +454,54 @@ namespace Terminal.Gui {
 			Task.Run ((Action)WindowsInputHandler);
 			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)]
 		[StructLayout (LayoutKind.Sequential)]
 		public struct ConsoleKeyInfoEx {
 		public struct ConsoleKeyInfoEx {
 			public ConsoleKeyInfo consoleKeyInfo;
 			public ConsoleKeyInfo consoleKeyInfo;
@@ -564,11 +607,24 @@ namespace Terminal.Gui {
 			case WindowsConsole.EventType.Key:
 			case WindowsConsole.EventType.Key:
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
 				if (map == (Key)0xffffffff) {
 				if (map == (Key)0xffffffff) {
-					KeyEvent key = default;
+					KeyEvent key = new KeyEvent ();
+
 					// Shift = VK_SHIFT = 0x10
 					// Shift = VK_SHIFT = 0x10
 					// Ctrl = VK_CONTROL = 0x11
 					// Ctrl = VK_CONTROL = 0x11
 					// Alt = VK_MENU = 0x12
 					// 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) {
 					switch (inputEvent.KeyEvent.dwControlKeyState) {
 					case WindowsConsole.ControlKeyState.RightAltPressed:
 					case WindowsConsole.ControlKeyState.RightAltPressed:
 					case WindowsConsole.ControlKeyState.RightAltPressed |
 					case WindowsConsole.ControlKeyState.RightAltPressed |
@@ -617,10 +673,10 @@ namespace Terminal.Gui {
 						keyUpHandler (key);
 						keyUpHandler (key);
 				} else {
 				} else {
 					if (inputEvent.KeyEvent.bKeyDown) {
 					if (inputEvent.KeyEvent.bKeyDown) {
-						// Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
 						keyDownHandler (new KeyEvent (map));
 						keyDownHandler (new KeyEvent (map));
-						keyHandler (new KeyEvent (map));
 					} else {
 					} else {
+						// Key Up - Fire KeyDown Event and KeyStroke (ProcessKey) Event
+						keyHandler (new KeyEvent (map));
 						keyUpHandler (new KeyEvent (map));
 						keyUpHandler (new KeyEvent (map));
 					}
 					}
 				}
 				}
@@ -807,6 +863,8 @@ namespace Terminal.Gui {
 
 
 			} else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
 			} else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
 				mouseFlag = MouseFlags.ReportMousePosition;
 				mouseFlag = MouseFlags.ReportMousePosition;
+			} else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) {
+				mouseFlag = 0;
 			}
 			}
 
 
 			mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag);
 			mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag);
@@ -916,6 +974,9 @@ namespace Terminal.Gui {
 			case ConsoleKey.OemComma:
 			case ConsoleKey.OemComma:
 			case ConsoleKey.OemPlus:
 			case ConsoleKey.OemPlus:
 			case ConsoleKey.OemMinus:
 			case ConsoleKey.OemMinus:
+				if (keyInfo.KeyChar == 0)
+					return Key.Unknown;
+
 				return (Key)((uint)keyInfo.KeyChar);
 				return (Key)((uint)keyInfo.KeyChar);
 			}
 			}
 
 
@@ -969,48 +1030,10 @@ namespace Terminal.Gui {
 		public override void Init (Action terminalResized)
 		public override void Init (Action terminalResized)
 		{
 		{
 			TerminalResized = 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 ()
 		void ResizeScreen ()
 		{
 		{
 			OutputBuffer = new WindowsConsole.CharInfo [Rows * Cols];
 			OutputBuffer = new WindowsConsole.CharInfo [Rows * Cols];

+ 29 - 4
Terminal.Gui/Event.cs

@@ -45,7 +45,7 @@ namespace Terminal.Gui {
 		ControlSpace = 0,
 		ControlSpace = 0,
 
 
 		/// <summary>
 		/// <summary>
-	        /// The key code for the user pressing Control-A
+		/// The key code for the user pressing Control-A
 		/// </summary>
 		/// </summary>
 		ControlA = 1,
 		ControlA = 1,
 		/// <summary>
 		/// <summary>
@@ -288,8 +288,7 @@ namespace Terminal.Gui {
 	/// <summary>
 	/// <summary>
 	/// Describes a keyboard event.
 	/// Describes a keyboard event.
 	/// </summary>
 	/// </summary>
-	public struct KeyEvent {
-
+	public class KeyEvent {
 		/// <summary>
 		/// <summary>
 		/// Symb olid definition for the key.
 		/// Symb olid definition for the key.
 		/// </summary>
 		/// </summary>
@@ -321,6 +320,10 @@ namespace Terminal.Gui {
 		//public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
 		//public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
 		public bool IsCtrl => (Key & Key.CtrlMask) != 0;
 		public bool IsCtrl => (Key & Key.CtrlMask) != 0;
 
 
+		public KeyEvent ()
+		{
+			Key = Key.Unknown;
+		}
 		/// <summary>
 		/// <summary>
 		///   Constructs a new KeyEvent from the provided Key value - can be a rune cast into a Key value
 		///   Constructs a new KeyEvent from the provided Key value - can be a rune cast into a Key value
 		/// </summary>
 		/// </summary>
@@ -328,6 +331,28 @@ namespace Terminal.Gui {
 		{
 		{
 			Key = k;
 			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;
+		}
 	}
 	}
 
 
 	/// <summary>
 	/// <summary>
@@ -486,7 +511,7 @@ namespace Terminal.Gui {
 		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:Terminal.Gui.MouseEvent"/>.
 		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:Terminal.Gui.MouseEvent"/>.
 		/// </summary>
 		/// </summary>
 		/// <returns>A <see cref="T:System.String"/> that represents the current <see cref="T:Terminal.Gui.MouseEvent"/>.</returns>
 		/// <returns>A <see cref="T:System.String"/> that represents the current <see cref="T:Terminal.Gui.MouseEvent"/>.</returns>
-		public override string ToString()
+		public override string ToString ()
 		{
 		{
 			return $"({X},{Y}:{Flags}";
 			return $"({X},{Y}:{Flags}";
 		}
 		}

+ 5 - 1
Terminal.Gui/MonoCurses/UnmanagedLibrary.cs

@@ -37,7 +37,11 @@ namespace Mono.Terminal.Internal {
 		const string XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS";
 		const string XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS";
 		static bool IsWindows, IsLinux, IsMacOS;
 		static bool IsWindows, IsLinux, IsMacOS;
 		static bool Is64Bit;
 		static bool Is64Bit;
+#if GUICS
+		static bool IsMono;
+#else
 		static bool IsMono, IsUnity, IsXamarinIOS, IsXamarinAndroid, IsXamarin;
 		static bool IsMono, IsUnity, IsXamarinIOS, IsXamarinAndroid, IsXamarin;
+#endif
 		static bool IsNetCore;
 		static bool IsNetCore;
 
 
 		public static bool IsMacOSPlatform => IsMacOS;
 		public static bool IsMacOSPlatform => IsMacOS;
@@ -75,7 +79,7 @@ namespace Mono.Terminal.Internal {
 				IsNetCore = Type.GetType ("System.MathF") != null;
 				IsNetCore = Type.GetType ("System.MathF") != null;
 			}
 			}
 #if GUICS
 #if GUICS
-			IsUnity = IsXamarinIOS = IsXamarinAndroid = IsXamarin = false;
+			//IsUnity = IsXamarinIOS = IsXamarinAndroid = IsXamarin = false;
 #else
 #else
 			IsUnity = Type.GetType (UnityEngineApplicationClassName) != null;
 			IsUnity = Type.GetType (UnityEngineApplicationClassName) != null;
 			IsXamarinIOS = Type.GetType (XamarinIOSObjectClassName) != null;
 			IsXamarinIOS = Type.GetType (XamarinIOSObjectClassName) != null;

+ 3 - 1
Terminal.Gui/MonoCurses/binding.cs

@@ -47,6 +47,7 @@ using System.Runtime.InteropServices;
 using Mono.Terminal.Internal;
 using Mono.Terminal.Internal;
 
 
 namespace Unix.Terminal {
 namespace Unix.Terminal {
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
 
 
 	public partial class Curses {
 	public partial class Curses {
 		[StructLayout (LayoutKind.Sequential)]
 		[StructLayout (LayoutKind.Sequential)]
@@ -61,7 +62,7 @@ namespace Unix.Terminal {
 		static IntPtr curses_handle, curscr_ptr, lines_ptr, cols_ptr;
 		static IntPtr curses_handle, curscr_ptr, lines_ptr, cols_ptr;
 
 
 		// If true, uses the DllImport into "ncurses", otherwise "libncursesw.so.5"
 		// If true, uses the DllImport into "ncurses", otherwise "libncursesw.so.5"
-		static bool use_naked_driver;
+		//static bool use_naked_driver;
 
 
 		static UnmanagedLibrary curses_library;
 		static UnmanagedLibrary curses_library;
 		static NativeMethods methods;
 		static NativeMethods methods;
@@ -451,4 +452,5 @@ namespace Unix.Terminal {
 			mousemask = lib.GetNativeMethodDelegate<Delegates.mousemask> ("mousemask");
 			mousemask = lib.GetNativeMethodDelegate<Delegates.mousemask> ("mousemask");
 		}
 		}
 	}
 	}
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
 }
 }

+ 2 - 0
Terminal.Gui/MonoCurses/constants.cs

@@ -5,6 +5,7 @@
 using System;
 using System;
 
 
 namespace Unix.Terminal {
 namespace Unix.Terminal {
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
 	public partial class Curses {
 	public partial class Curses {
 		public const int A_NORMAL = unchecked((int)0x0);
 		public const int A_NORMAL = unchecked((int)0x0);
 		public const int A_STANDOUT = unchecked((int)0x10000);
 		public const int A_STANDOUT = unchecked((int)0x10000);
@@ -139,4 +140,5 @@ namespace Unix.Terminal {
 			return 0 + n * 256;
 			return 0 + n * 256;
 		}
 		}
 	}
 	}
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
 }
 }

+ 3 - 1
Terminal.Gui/MonoCurses/handles.cs

@@ -31,6 +31,7 @@ using System.Runtime.InteropServices;
 namespace Unix.Terminal {
 namespace Unix.Terminal {
 
 
 	public partial class Curses {
 	public partial class Curses {
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
 		public class Window {
 		public class Window {
 			public readonly IntPtr Handle;
 			public readonly IntPtr Handle;
 			static Window curscr;
 			static Window curscr;
@@ -167,7 +168,8 @@ namespace Unix.Terminal {
 	 			Handle = handle;
 	 			Handle = handle;
 	 		}
 	 		}
 	 	}
 	 	}
-	 
+
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
 	}
 	}
 
 
 }
 }

+ 4 - 0
Terminal.Gui/MonoCurses/mainloop.cs

@@ -53,6 +53,10 @@ namespace Mono.Terminal {
 		/// <returns><c>true</c>, if there were pending events, <c>false</c> otherwise.</returns>
 		/// <returns><c>true</c>, if there were pending events, <c>false</c> otherwise.</returns>
 		/// <param name="wait">If set to <c>true</c> wait until an event is available, otherwise return immediately.</param>
 		/// <param name="wait">If set to <c>true</c> wait until an event is available, otherwise return immediately.</param>
 		bool EventsPending (bool wait);
 		bool EventsPending (bool wait);
+
+		/// <summary>
+		/// The interation function.
+		/// </summary>
 		void MainIteration ();
 		void MainIteration ();
 	}
 	}
 
 

+ 6 - 0
Terminal.Gui/Views/Button.cs

@@ -153,6 +153,7 @@ namespace Terminal.Gui {
 			Text = text;
 			Text = text;
 		}
 		}
 
 
+		///<inheritdoc cref="Redraw(Rect)"/>
 		public override void Redraw (Rect region)
 		public override void Redraw (Rect region)
 		{
 		{
 			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
 			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
@@ -166,6 +167,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="PositionCursor"/>
 		public override void PositionCursor ()
 		public override void PositionCursor ()
 		{
 		{
 			Move (hot_pos == -1 ? 1 : hot_pos, 0);
 			Move (hot_pos == -1 ? 1 : hot_pos, 0);
@@ -181,6 +183,7 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessHotKey"/>
 		public override bool ProcessHotKey (KeyEvent kb)
 		public override bool ProcessHotKey (KeyEvent kb)
 		{
 		{
 			if (kb.IsAlt)
 			if (kb.IsAlt)
@@ -189,6 +192,7 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessColdKey"/>
 		public override bool ProcessColdKey (KeyEvent kb)
 		public override bool ProcessColdKey (KeyEvent kb)
 		{
 		{
 			if (IsDefault && kb.KeyValue == '\n') {
 			if (IsDefault && kb.KeyValue == '\n') {
@@ -199,6 +203,7 @@ namespace Terminal.Gui {
 			return CheckKey (kb);
 			return CheckKey (kb);
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent kb)
 		public override bool ProcessKey (KeyEvent kb)
 		{
 		{
 			var c = kb.KeyValue;
 			var c = kb.KeyValue;
@@ -210,6 +215,7 @@ namespace Terminal.Gui {
 			return base.ProcessKey (kb);
 			return base.ProcessKey (kb);
 		}
 		}
 
 
+		///<inheritdoc cref="MouseEvent"/>
 		public override bool MouseEvent(MouseEvent me)
 		public override bool MouseEvent(MouseEvent me)
 		{
 		{
 			if (me.Flags == MouseFlags.Button1Clicked) {
 			if (me.Flags == MouseFlags.Button1Clicked) {

+ 4 - 0
Terminal.Gui/Views/Checkbox.cs

@@ -99,6 +99,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		public override void Redraw (Rect region)
 		{
 		{
 			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
 			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
@@ -113,11 +114,13 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="PositionCursor"/>
 		public override void PositionCursor ()
 		public override void PositionCursor ()
 		{
 		{
 			Move (1, 0);
 			Move (1, 0);
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent kb)
 		public override bool ProcessKey (KeyEvent kb)
 		{
 		{
 			if (kb.KeyValue == ' ') {
 			if (kb.KeyValue == ' ') {
@@ -132,6 +135,7 @@ namespace Terminal.Gui {
 			return base.ProcessKey (kb);
 			return base.ProcessKey (kb);
 		}
 		}
 
 
+		///<inheritdoc cref="MouseEvent"/>
 		public override bool MouseEvent (MouseEvent me)
 		public override bool MouseEvent (MouseEvent me)
 		{
 		{
 			if (!me.Flags.HasFlag (MouseFlags.Button1Clicked))
 			if (!me.Flags.HasFlag (MouseFlags.Button1Clicked))

+ 6 - 0
Terminal.Gui/Views/Clipboard.cs

@@ -2,7 +2,13 @@
 using NStack;
 using NStack;
 
 
 namespace Terminal.Gui {
 namespace Terminal.Gui {
+	/// <summary>
+	/// 
+	/// </summary>
 	public static class Clipboard {
 	public static class Clipboard {
+		/// <summary>
+		/// 
+		/// </summary>
 		public static ustring Contents { get; set; }
 		public static ustring Contents { get; set; }
 	}
 	}
 }
 }

+ 16 - 2
Terminal.Gui/Views/DateField.cs

@@ -41,7 +41,7 @@ namespace Terminal.Gui {
 		{
 		{
 			CultureInfo cultureInfo = CultureInfo.CurrentCulture;
 			CultureInfo cultureInfo = CultureInfo.CurrentCulture;
 			sepChar = cultureInfo.DateTimeFormat.DateSeparator;
 			sepChar = cultureInfo.DateTimeFormat.DateSeparator;
-			longFormat = $" {cultureInfo.DateTimeFormat.ShortDatePattern}";
+			longFormat = GetLongFormat (cultureInfo.DateTimeFormat.ShortDatePattern);
 			shortFormat = GetShortFormat(longFormat);
 			shortFormat = GetShortFormat(longFormat);
 			this.isShort = isShort;
 			this.isShort = isShort;
 			CursorPosition = 1;
 			CursorPosition = 1;
@@ -55,7 +55,21 @@ namespace Terminal.Gui {
 				Text = e;
 				Text = e;
 		}
 		}
 
 
-		string GetShortFormat(string lf)
+		string GetLongFormat (string lf)
+		{
+			ustring [] frm = ustring.Make (lf).Split (ustring.Make (sepChar));
+			for (int i = 0; i < frm.Length; i++) {
+				if (frm [i].Contains ("M") && frm [i].Length < 2)
+					lf = lf.Replace ("M", "MM");
+				if (frm [i].Contains ("d") && frm [i].Length < 2)
+					lf = lf.Replace ("d", "dd");
+				if (frm [i].Contains ("y") && frm [i].Length < 4)
+					lf = lf.Replace ("yy", "yyyy");
+			}
+			return $" {lf}";
+		}
+
+		string GetShortFormat (string lf)
 		{
 		{
 			return lf.Replace("yyyy", "yy");
 			return lf.Replace("yyyy", "yy");
 		}
 		}

+ 3 - 0
Terminal.Gui/Views/HexView.cs

@@ -98,6 +98,7 @@ namespace Terminal.Gui {
 		const int bsize = 4;
 		const int bsize = 4;
 		int bytesPerLine;
 		int bytesPerLine;
 
 
+		/// <inheritdoc cref="Frame"/>
 		public override Rect Frame {
 		public override Rect Frame {
 			get => base.Frame;
 			get => base.Frame;
 			set {
 			set {
@@ -128,6 +129,7 @@ namespace Terminal.Gui {
 			return buffer [offset];
 			return buffer [offset];
 		}
 		}
 
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		public override void Redraw (Rect region)
 		{
 		{
 			Attribute currentAttribute;
 			Attribute currentAttribute;
@@ -280,6 +282,7 @@ namespace Terminal.Gui {
 				RedisplayLine (position);			
 				RedisplayLine (position);			
 		}
 		}
 
 
+		/// <inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent keyEvent)
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
 		{
 			switch (keyEvent.Key) {
 			switch (keyEvent.Key) {

+ 6 - 2
Terminal.Gui/Views/Label.cs

@@ -124,7 +124,7 @@ namespace Terminal.Gui {
 							for (int i = 0; i < spaces; i++)
 							for (int i = 0; i < spaces; i++)
 								s.Append (' ');
 								s.Append (' ');
 						if (extras > 0) {
 						if (extras > 0) {
-							s.Append ('_');
+							//s.Append ('_');
 							extras--;
 							extras--;
 						}
 						}
 					}
 					}
@@ -160,6 +160,7 @@ namespace Terminal.Gui {
 			lineResult.Add(ClipAndJustify(textStr[lp, textLen], width, talign));
 			lineResult.Add(ClipAndJustify(textStr[lp, textLen], width, talign));
 		}
 		}
 
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		public override void Redraw (Rect region)
 		{
 		{
 			if (recalcPending)
 			if (recalcPending)
@@ -178,8 +179,11 @@ namespace Terminal.Gui {
 				int x;
 				int x;
 				switch (textAlignment) {
 				switch (textAlignment) {
 				case TextAlignment.Left:
 				case TextAlignment.Left:
+					x = Frame.Left;
+					break;
 				case TextAlignment.Justified:
 				case TextAlignment.Justified:
-					x = 0;
+					Recalc ();
+					x = Frame.Left;
 					break;
 					break;
 				case TextAlignment.Right:
 				case TextAlignment.Right:
 					x = Frame.Right - str.Length;
 					x = Frame.Right - str.Length;

+ 66 - 3
Terminal.Gui/Views/ListView.cs

@@ -294,6 +294,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public event Action SelectedChanged;
 		public event Action SelectedChanged;
 
 
+		/// <summary>
+		/// This event is raised on Enter key or Double Click to open the selected item.
+		/// </summary>
+		public event EventHandler OpenSelectedItem;
+
 		/// <summary>
 		/// <summary>
 		/// Handles cursor movement for this view, passes all other events.
 		/// Handles cursor movement for this view, passes all other events.
 		/// </summary>
 		/// </summary>
@@ -325,10 +330,19 @@ namespace Terminal.Gui {
 					return true;
 					return true;
 				else
 				else
 					break;
 					break;
+
+			case Key.Enter:
+				OpenSelectedItem?.Invoke (this, new EventArgs ());
+				break;
+
 			}
 			}
 			return base.ProcessKey (kb);
 			return base.ProcessKey (kb);
 		}
 		}
 
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool AllowsAll ()
 		public virtual bool AllowsAll ()
 		{
 		{
 			if (!allowsMarking)
 			if (!allowsMarking)
@@ -344,6 +358,10 @@ namespace Terminal.Gui {
 			return true;
 			return true;
 		}
 		}
 
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MarkUnmarkRow(){
 		public virtual bool MarkUnmarkRow(){
 			if (AllowsAll ()) {
 			if (AllowsAll ()) {
 				Source.SetMark(SelectedItem, !Source.IsMarked(SelectedItem));
 				Source.SetMark(SelectedItem, !Source.IsMarked(SelectedItem));
@@ -354,6 +372,10 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MovePageUp(){
 		public virtual bool MovePageUp(){
 			int n = (selected - Frame.Height);
 			int n = (selected - Frame.Height);
 			if (n < 0)
 			if (n < 0)
@@ -369,6 +391,10 @@ namespace Terminal.Gui {
 			return true;
 			return true;
 		}
 		}
 
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MovePageDown(){
 		public virtual bool MovePageDown(){
 			var n = (selected + Frame.Height);
 			var n = (selected + Frame.Height);
 			if (n > source.Count)
 			if (n > source.Count)
@@ -387,6 +413,10 @@ namespace Terminal.Gui {
 			return true;
 			return true;
 		}
 		}
 
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MoveDown(){
 		public virtual bool MoveDown(){
 			if (selected + 1 < source.Count){
 			if (selected + 1 < source.Count){
 				selected++;
 				selected++;
@@ -400,6 +430,10 @@ namespace Terminal.Gui {
 			return true;
 			return true;
 		}
 		}
 
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MoveUp(){
 		public virtual bool MoveUp(){
 			if (selected > 0){
 			if (selected > 0){
 				selected--;
 				selected--;
@@ -424,9 +458,10 @@ namespace Terminal.Gui {
 				Move (0, selected - top);
 				Move (0, selected - top);
 		}
 		}
 
 
+		///<inheritdoc cref="MouseEvent(Gui.MouseEvent)"/>
 		public override bool MouseEvent(MouseEvent me)
 		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;
 				return false;
 
 
 			if (!HasFocus)
 			if (!HasFocus)
@@ -444,9 +479,10 @@ namespace Terminal.Gui {
 				SetNeedsDisplay ();
 				SetNeedsDisplay ();
 				return true;
 				return true;
 			}
 			}
-			if (SelectedChanged != null)
-				SelectedChanged();
+			SelectedChanged?.Invoke ();
 			SetNeedsDisplay ();
 			SetNeedsDisplay ();
+			if (me.Flags == MouseFlags.Button1DoubleClicked)
+				OpenSelectedItem?.Invoke (this, new EventArgs ());
 			return true;
 			return true;
 		}
 		}
 	}
 	}
@@ -460,6 +496,10 @@ namespace Terminal.Gui {
 		BitArray marks;
 		BitArray marks;
 		int count;
 		int count;
 
 
+		/// <summary>
+		/// constructor
+		/// </summary>
+		/// <param name="source"></param>
 		public ListWrapper (IList source)
 		public ListWrapper (IList source)
 		{
 		{
 			count = source.Count;
 			count = source.Count;
@@ -467,6 +507,9 @@ namespace Terminal.Gui {
 			this.src = source;
 			this.src = source;
 		}
 		}
 
 
+		/// <summary>
+		/// Count of items.
+		/// </summary>
 		public int Count => src.Count;
 		public int Count => src.Count;
 
 
 		void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width)
 		void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width)
@@ -487,6 +530,16 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		/// <summary>
+		/// Renders an item in the the list.
+		/// </summary>
+		/// <param name="container"></param>
+		/// <param name="driver"></param>
+		/// <param name="marked"></param>
+		/// <param name="item"></param>
+		/// <param name="col"></param>
+		/// <param name="line"></param>
+		/// <param name="width"></param>
 		public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width)
 		public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width)
 		{
 		{
 			container.Move (col, line);
 			container.Move (col, line);
@@ -499,6 +552,11 @@ namespace Terminal.Gui {
 				RenderUstr (driver, t.ToString (), col, line, width);
 				RenderUstr (driver, t.ToString (), col, line, width);
 		}
 		}
 
 
+		/// <summary>
+		/// Returns true of the item is marked. false if not.
+		/// </summary>
+		/// <param name="item"></param>
+		/// <returns></returns>
 		public bool IsMarked (int item)
 		public bool IsMarked (int item)
 		{
 		{
 			if (item >= 0 && item < count)
 			if (item >= 0 && item < count)
@@ -506,6 +564,11 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
+		/// <summary>
+		/// Sets the marked state of an item.
+		/// </summary>
+		/// <param name="item"></param>
+		/// <param name="value"></param>
 		public void SetMark (int item, bool value)
 		public void SetMark (int item, bool value)
 		{
 		{
 			if (item >= 0 && item < count)
 			if (item >= 0 && item < count)

+ 30 - 7
Terminal.Gui/Views/Menu.cs

@@ -21,6 +21,9 @@ namespace Terminal.Gui {
 	/// </summary>
 	/// </summary>
 	public class MenuItem {
 	public class MenuItem {
 
 
+		/// <summary>
+		/// constructor
+		/// </summary>
 		public MenuItem ()
 		public MenuItem ()
 		{
 		{
 			Title = "";
 			Title = "";
@@ -207,10 +210,10 @@ namespace Terminal.Gui {
 			return len;
 			return len;
 		}
 		}
 
 
-		/// <summary>
-		/// Gets or sets the title to display.
-		/// </summary>
-		/// <value>The title.</value>
+		///// <summary>
+		///// Gets or sets the title to display.
+		///// </summary>
+		///// <value>The title.</value>
 		//public ustring Title { get; set; }
 		//public ustring Title { get; set; }
 
 
 		/// <summary>
 		/// <summary>
@@ -455,7 +458,7 @@ namespace Terminal.Gui {
 			}
 			}
 			host.handled = false;
 			host.handled = false;
 			bool disabled;
 			bool disabled;
-			if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
+			if (me.Flags == MouseFlags.Button1Clicked) {
 				disabled = false;
 				disabled = false;
 				if (me.Y < 1)
 				if (me.Y < 1)
 					return true;
 					return true;
@@ -559,6 +562,8 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		bool openedByAltKey;
 		bool openedByAltKey;
+
+		///<inheritdoc cref="KeyDown"/>
 		public override bool KeyDown (KeyEvent keyEvent)
 		public override bool KeyDown (KeyEvent keyEvent)
 		{
 		{
 			if (keyEvent.IsAlt) {
 			if (keyEvent.IsAlt) {
@@ -615,6 +620,7 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		public override void Redraw (Rect region)
 		{
 		{
 			Move (0, 0);
 			Move (0, 0);
@@ -645,6 +651,7 @@ namespace Terminal.Gui {
 			PositionCursor ();
 			PositionCursor ();
 		}
 		}
 
 
+		///<inheritdoc cref="PositionCursor"/>
 		public override void PositionCursor ()
 		public override void PositionCursor ()
 		{
 		{
 			int pos = 0;
 			int pos = 0;
@@ -672,8 +679,16 @@ namespace Terminal.Gui {
 			action = item.Action;
 			action = item.Action;
 		}
 		}
 
 
+		/// <summary>
+		/// Raised as a menu is opened.
+		/// </summary>
 		public event EventHandler OnOpenMenu;
 		public event EventHandler OnOpenMenu;
+
+		/// <summary>
+		/// Raised when a menu is closing.
+		/// </summary>
 		public event EventHandler OnCloseMenu;
 		public event EventHandler OnCloseMenu;
+
 		internal Menu openMenu;
 		internal Menu openMenu;
 		Menu openCurrentMenu;
 		Menu openCurrentMenu;
 		internal List<Menu> openSubMenu;
 		internal List<Menu> openSubMenu;
@@ -681,7 +696,12 @@ namespace Terminal.Gui {
 		internal bool isMenuOpening;
 		internal bool isMenuOpening;
 		internal bool isMenuClosing;
 		internal bool isMenuClosing;
 		internal bool isMenuClosed;
 		internal bool isMenuClosed;
-		public bool MenuOpen;
+
+		/// <summary>
+		/// True of the menu is open; otherwise false.
+		/// </summary>
+		public bool MenuOpen { get; set; }
+
 		View lastFocused;
 		View lastFocused;
 
 
 		/// <summary>
 		/// <summary>
@@ -940,7 +960,7 @@ namespace Terminal.Gui {
 		bool openedByHotKey;
 		bool openedByHotKey;
 		internal bool FindAndOpenMenuByHotkey (KeyEvent kb)
 		internal bool FindAndOpenMenuByHotkey (KeyEvent kb)
 		{
 		{
-			int pos = 0;
+			//int pos = 0;
 			var c = ((uint)kb.Key & (uint)Key.CharMask);
 			var c = ((uint)kb.Key & (uint)Key.CharMask);
 			for (int i = 0; i < Menus.Length; i++) {
 			for (int i = 0; i < Menus.Length; i++) {
 				// TODO: this code is duplicated, hotkey should be part of the MenuBarItem
 				// TODO: this code is duplicated, hotkey should be part of the MenuBarItem
@@ -969,6 +989,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessHotKey"/>
 		public override bool ProcessHotKey (KeyEvent kb)
 		public override bool ProcessHotKey (KeyEvent kb)
 		{
 		{
 			if (kb.Key == Key.F9) {
 			if (kb.Key == Key.F9) {
@@ -993,6 +1014,7 @@ namespace Terminal.Gui {
 			return base.ProcessHotKey (kb);
 			return base.ProcessHotKey (kb);
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent kb)
 		public override bool ProcessKey (KeyEvent kb)
 		{
 		{
 			switch (kb.Key) {
 			switch (kb.Key) {
@@ -1047,6 +1069,7 @@ namespace Terminal.Gui {
 			return true;
 			return true;
 		}
 		}
 
 
+		///<inheritdoc cref="MouseEvent"/>
 		public override bool MouseEvent (MouseEvent me)
 		public override bool MouseEvent (MouseEvent me)
 		{
 		{
 			if (!handled && !HandleGrabView (me, this)) {
 			if (!handled && !HandleGrabView (me, this)) {

+ 8 - 1
Terminal.Gui/Views/ScrollView.cs

@@ -189,6 +189,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="MouseEvent"/>
 		public override bool MouseEvent(MouseEvent me)
 		public override bool MouseEvent(MouseEvent me)
 		{
 		{
 			if (me.Flags != MouseFlags.Button1Pressed && me.Flags != MouseFlags.Button1Clicked &&
 			if (me.Flags != MouseFlags.Button1Pressed && me.Flags != MouseFlags.Button1Clicked &&
@@ -249,6 +250,10 @@ namespace Terminal.Gui {
 		View contentView;
 		View contentView;
 		ScrollBarView vertical, horizontal;
 		ScrollBarView vertical, horizontal;
 
 
+		/// <summary>
+		/// Constructs a ScrollView
+		/// </summary>
+		/// <param name="frame"></param>
 		public ScrollView (Rect frame) : base (frame)
 		public ScrollView (Rect frame) : base (frame)
 		{
 		{
 			contentView = new View (frame);
 			contentView = new View (frame);
@@ -363,7 +368,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// <summary>
 		/// This event is raised when the contents have scrolled
 		/// This event is raised when the contents have scrolled
 		/// </summary>
 		/// </summary>
-		public event Action<ScrollView> Scrolled;
+		//public event Action<ScrollView> Scrolled;
 
 
 		public override void Redraw(Rect region)
 		public override void Redraw(Rect region)
 		{
 		{
@@ -383,6 +388,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="PositionCursor"/>
 		public override void PositionCursor()
 		public override void PositionCursor()
 		{
 		{
 			if (InternalSubviews.Count == 0)
 			if (InternalSubviews.Count == 0)
@@ -448,6 +454,7 @@ namespace Terminal.Gui {
 			return true;
 			return true;
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey(KeyEvent kb)
 		public override bool ProcessKey(KeyEvent kb)
 		{
 		{
 			if (base.ProcessKey (kb))
 			if (base.ProcessKey (kb))

+ 8 - 0
Terminal.Gui/Views/StatusBar.cs

@@ -85,8 +85,14 @@ namespace Terminal.Gui {
 
 
 		public StatusBarStyle Style { get; set; } = StatusBarStyle.Default;
 		public StatusBarStyle Style { get; set; } = StatusBarStyle.Default;
 #endif
 #endif
+		/// <summary>
+		/// The parent view of the StatusBar.
+		/// </summary>
 		public View Parent { get; set; }
 		public View Parent { get; set; }
 
 
+		/// <summary>
+		/// The items that compose the StatusBar
+		/// </summary>
 		public StatusItem [] Items { get; set; }
 		public StatusItem [] Items { get; set; }
 
 
 		/// <summary>
 		/// <summary>
@@ -132,6 +138,7 @@ namespace Terminal.Gui {
 			return result;
 			return result;
 		}
 		}
 
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		public override void Redraw (Rect region)
 		{
 		{
 			if (Frame.Y != Driver.Rows - 1) {
 			if (Frame.Y != Driver.Rows - 1) {
@@ -161,6 +168,7 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		///<inheritdoc cref="ProcessHotKey"/>
 		public override bool ProcessHotKey (KeyEvent kb)
 		public override bool ProcessHotKey (KeyEvent kb)
 		{
 		{
 			foreach (var item in Items) {
 			foreach (var item in Items) {

+ 6 - 0
Terminal.Gui/Views/TextView.cs

@@ -266,6 +266,9 @@ namespace Terminal.Gui {
 		bool selecting;
 		bool selecting;
 		//bool used;
 		//bool used;
 
 
+		/// <summary>
+		/// Raised when the Text of the TextView changes.
+		/// </summary>
 		public event EventHandler TextChanged;
 		public event EventHandler TextChanged;
 
 
 #if false
 #if false
@@ -535,6 +538,7 @@ namespace Terminal.Gui {
 			PositionCursor ();
 			PositionCursor ();
 		}
 		}
 
 
+		///<inheritdoc cref="CanFocus"/>
 		public override bool CanFocus {
 		public override bool CanFocus {
 			get => true;
 			get => true;
 			set { base.CanFocus = value; }
 			set { base.CanFocus = value; }
@@ -682,6 +686,7 @@ namespace Terminal.Gui {
 
 
 		bool lastWasKill;
 		bool lastWasKill;
 
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent kb)
 		public override bool ProcessKey (KeyEvent kb)
 		{
 		{
 			int restCount;
 			int restCount;
@@ -1139,6 +1144,7 @@ namespace Terminal.Gui {
 			return null;
 			return null;
 		}
 		}
 
 
+		///<inheritdoc cref="MouseEvent"/>
 		public override bool MouseEvent (MouseEvent ev)
 		public override bool MouseEvent (MouseEvent ev)
 		{
 		{
 			if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked)) {
 			if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked)) {

+ 10 - 0
Terminal.sln

@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal.Gui", "Terminal.Gu
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UICatalog", "UICatalog\UICatalog.csproj", "{88979F89-9A42-448F-AE3E-3060145F6375}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x86 = Debug|x86
 		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}.Debug|x86.Build.0 = Debug|x86
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.ActiveCfg = Release|x86
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.ActiveCfg = Release|x86
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.Build.0 = 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
 	EndGlobalSection
 	GlobalSection(MonoDevelopProperties) = preSolution
 	GlobalSection(MonoDevelopProperties) = preSolution
 		Policies = $0
 		Policies = $0

+ 23 - 0
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

+ 9 - 0
UICatalog/.gitignore

@@ -0,0 +1,9 @@
+###############
+#    folder   #
+###############
+/**/DROP/
+/**/TEMP/
+/**/packages/
+/**/bin/
+/**/obj/
+_site

+ 271 - 0
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 {
+	/// <summary>
+	/// 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.
+	/// </summary>
+	class Program {
+		private static Toplevel _top;
+		private static MenuBar _menu;
+		private static int _nameColumnWidth;
+		private static Window _leftPane;
+		private static List<string> _categories;
+		private static ListView _categoryListView;
+		private static Window _rightPane;
+		private static List<Type> _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 ();
+			}
+		}
+
+		/// <summary>
+		/// Create all controls. This gets called once and the controls remain with their state between Sceanrio runs.
+		/// </summary>
+		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();
+					}
+				}),
+			});
+		}
+
+		/// <summary>
+		/// This shows the selection UI. Each time it is run, it calls Application.Init to reset everything.
+		/// </summary>
+		/// <returns></returns>
+		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<Type> Scenarios { get; set; }
+
+			public bool IsMarked (int item) => false;//  Scenarios [item].IsMarked;
+
+			public int Count => Scenarios.Count;
+
+			public ScenarioListDataSource (List<Type> 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++;
+				}
+			}
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <param name="ke"></param>
+		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<Type> 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;
+		}
+	}
+}

+ 8 - 0
UICatalog/Properties/launchSettings.json

@@ -0,0 +1,8 @@
+{
+  "profiles": {
+    "UICatalog": {
+      "commandName": "Project",
+      "commandLineArgs": "HexEditor"
+    }
+  }
+}

+ 122 - 0
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.
+
+![screenshot](screenshot.png)
+
+## 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 <Scenario Name>
+```
+
+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. 
+
+![screenshot](generic_screenshot.png)
+
+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: ".

+ 181 - 0
UICatalog/Scenario.cs

@@ -0,0 +1,181 @@
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+	/// <summary>
+	/// 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.
+	/// </summary>
+	public class Scenario {
+		/// <summary>
+		/// The Top level for the Scenario. This should be set to `Application.Top` in most cases.
+		/// </summary>
+		public Toplevel Top { get; set; }
+
+		/// <summary>
+		/// </summary>
+		public Window Win { get; set; }
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <param name="top"></param>
+		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 {
+			/// <summary>
+			/// Scenario Name
+			/// </summary>
+			public string Name { get; set; }
+
+			/// <summary>
+			/// Scenario Description
+			/// </summary>
+			public string Description { get; set; }
+
+			public ScenarioMetadata (string Name, string Description)
+			{
+				this.Name = Name;
+				this.Description = Description;
+			}
+
+			/// <summary>
+			/// Static helper function to get the Scenario Name given a Type
+			/// </summary>
+			/// <param name="t"></param>
+			/// <returns></returns>
+			public static string GetName (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Name;
+
+			/// <summary>
+			/// Static helper function to get the Scenario Description given a Type
+			/// </summary>
+			/// <param name="t"></param>
+			/// <returns></returns>
+			public static string GetDescription (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Description;
+		}
+
+		/// <summary>
+		/// Helper to get the Scenario Name
+		/// </summary>
+		/// <returns></returns>
+		public string GetName () => ScenarioMetadata.GetName (this.GetType ());
+
+		/// <summary>
+		/// Helper to get the Scenario Descripiton
+		/// </summary>
+		/// <returns></returns>
+		public string GetDescription () => ScenarioMetadata.GetDescription (this.GetType ());
+
+		[System.AttributeUsage (System.AttributeTargets.Class, AllowMultiple = true)]
+		public class ScenarioCategory : System.Attribute {
+			/// <summary>
+			/// Category Name
+			/// </summary>
+			public string Name { get; set; }
+
+			public ScenarioCategory (string Name) => this.Name = Name;
+
+			/// <summary>
+			/// Static helper function to get the Scenario Name given a Type
+			/// </summary>
+			/// <param name="t"></param>
+			/// <returns></returns>
+			public static string GetName (Type t) => ((ScenarioCategory)System.Attribute.GetCustomAttributes (t) [0]).Name;
+
+			/// <summary>
+			/// Static helper function to get the Scenario Categories given a Type
+			/// </summary>
+			/// <param name="t"></param>
+			/// <returns></returns>
+			public static List<string> GetCategories (Type t) => System.Attribute.GetCustomAttributes (t)
+				.ToList ()
+				.Where (a => a is ScenarioCategory)
+				.Select (a => ((ScenarioCategory)a).Name)
+				.ToList ();
+		}
+
+		/// <summary>
+		/// Helper function to get the Categories of a Scenario
+		/// </summary>
+		/// <returns></returns>
+		public List<string> GetCategories () => ScenarioCategory.GetCategories (this.GetType ());
+
+		public override string ToString () => $"{GetName (),-30}{GetDescription ()}";
+
+		/// <summary>
+		/// Override this to implement the Scenario setup logic (create controls, etc...). 
+		/// </summary>
+		public virtual void Setup ()
+		{
+		}
+
+		/// <summary>
+		/// Runs the scenario. Override to start the scearnio using a Top level different than `Top`.
+		/// </summary>
+		public virtual void Run ()
+		{
+			Application.Run (Top);
+		}
+
+		/// <summary>
+		/// Stops the scenario. Override to implement shutdown behavior for the Scenario.
+		/// </summary>
+		public virtual void RequestStop ()
+		{
+			Application.RequestStop ();
+		}
+
+		/// <summary>
+		/// Returns a list of all Categories set by all of the scenarios defined in the project.
+		/// </summary>
+		internal static List<string> GetAllCategories ()
+		{
+			List<string> categories = new List<string> () { "All" };
+			foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
+			    .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
+				List<System.Attribute> attrs = System.Attribute.GetCustomAttributes (type).ToList ();
+				categories = categories.Union (attrs.Where (a => a is ScenarioCategory).Select (a => ((ScenarioCategory)a).Name)).ToList ();
+			}
+			return categories;
+		}
+
+		/// <summary>
+		/// Returns an instance of each Scenario defined in the project. 
+		/// https://stackoverflow.com/questions/5411694/get-all-inherited-classes-of-an-abstract-class
+		/// </summary>
+		internal static List<Type> GetDerivedClassesCollection ()
+		{
+			List<Type> objects = new List<Type> ();
+			foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
+			    .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
+				objects.Add (type);
+			}
+			return objects;
+		}
+	}
+}

+ 105 - 0
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;
+				},
+			});
+
+
+		}
+	}
+}

+ 82 - 0
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 {
+	/// <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: "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<Label> ();
+			labelList.Add (new Label ($"Label:"));
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1, ColorScheme = Colors.Dialog });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1, ColorScheme = Colors.Dialog });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1, ColorScheme = Colors.Dialog });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1, ColorScheme = Colors.Dialog });
+
+			subWin.Add (labelList.ToArray ());
+			//subWin.LayoutSubviews ();
+		}
+
+		public override void Run ()
+		{
+			base.Run ();
+		}
+	}
+
+	public 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 ();
+		}
+	}
+}

+ 150 - 0
UICatalog/Scenarios/Editor.cs

@@ -0,0 +1,150 @@
+using System;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Editor", Description: "A Terminal.Gui Text Editor via TextView")]
+	[ScenarioCategory ("Controls")]
+	[ScenarioCategory ("Text")]
+	class Editor : Scenario {
+		private string _fileName = "demo.txt";
+		private TextView _textView;
+		private bool _saved = true;
+
+
+		public override void Init (Toplevel top)
+		{
+			Top = top;
+		}
+
+		public override void Setup ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("_File", new MenuItem [] {
+					new MenuItem ("_New", "", () => New()),
+					new MenuItem ("_Open", "", () => Open()),
+					new MenuItem ("_Save", "", () => Save()),
+					null,
+					new MenuItem ("_Quit", "", () => Quit()),
+				}),
+				new MenuBarItem ("_Edit", new MenuItem [] {
+					new MenuItem ("_Copy", "", () => Copy()),
+					new MenuItem ("C_ut", "", () => Cut()),
+					new MenuItem ("_Paste", "", () => Paste())
+				}),
+			});
+			Top.Add (menu);
+
+			var statusBar = new StatusBar (new StatusItem [] {
+				new StatusItem(Key.F2, "~F2~ Open", () => Open()),
+				new StatusItem(Key.F3, "~F3~ Save", () => Save()),
+				new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()),
+			});
+			Top.Add (statusBar);
+
+			CreateDemoFile (_fileName);
+
+			Win = new Window (_fileName ?? "Untitled") {
+				X = 0,
+				Y = 1,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			Top.Add (Win);
+
+			_textView = new TextView () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				
+			};
+
+			LoadFile ();
+
+			Win.Add (_textView);
+		}
+
+		private void New ()
+		{
+			Win.Title = _fileName = "Untitled";
+			throw new NotImplementedException ();
+		}
+
+		private void LoadFile ()
+		{
+			if (!_saved) {
+				MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			}
+
+			if (_fileName != null) {
+				// BUGBUG: #452 TextView.LoadFile keeps file open and provides no way of closing it
+				//_textView.LoadFile(_fileName);
+				_textView.Text = System.IO.File.ReadAllText (_fileName);
+				Win.Title = _fileName;
+				_saved = true;
+			}
+		}
+
+		private void Paste ()
+		{
+			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+		}
+
+		private void Cut ()
+		{
+			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+		}
+
+		private void Copy ()
+		{
+			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			//if (_textView != null && _textView.SelectedLength != 0) {
+			//	_textView.Copy ();
+			//}
+		}
+
+		private void Open ()
+		{
+			var d = new OpenDialog ("Open", "Open a file") { AllowsMultipleSelection = false };
+			Application.Run (d);
+
+			if (!d.Canceled) {
+				_fileName = d.FilePaths [0];
+				LoadFile ();
+			}
+		}
+
+		private void Save ()
+		{
+			if (_fileName != null) {
+				// BUGBUG: #279 TextView does not know how to deal with \r\n, only \r 
+				// As a result files saved on Windows and then read back will show invalid chars.
+				System.IO.File.WriteAllText (_fileName, _textView.Text.ToString());
+				_saved = true;
+			}
+		}
+
+		private void Quit ()
+		{
+			Application.RequestStop ();
+		}
+
+		private void CreateDemoFile(string fileName)
+		{
+			var sb = new StringBuilder ();
+			// BUGBUG: #279 TextView does not know how to deal with \r\n, only \r
+			sb.Append ("Hello world.\n");
+			sb.Append ("This is a test of the Emergency Broadcast System.\n");
+
+			var sw = System.IO.File.CreateText (fileName);
+			sw.Write (sb.ToString ());
+			sw.Close ();
+		}
+
+		public override void Run ()
+		{
+			Application.Run (Top);
+		}
+	}
+}

+ 17 - 0
UICatalog/Scenarios/Generic.cs

@@ -0,0 +1,17 @@
+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")
+			});
+		}
+	}
+}

+ 154 - 0
UICatalog/Scenarios/HexEditor.cs

@@ -0,0 +1,154 @@
+using System;
+using System.IO;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "HexEditor", Description: "A Terminal.Gui binary (hex) editor via HexView")]
+	[ScenarioCategory ("Controls")]
+	[ScenarioCategory ("Text")]
+	class HexEditor : Scenario {
+		private string _fileName = "demo.bin";
+		private HexView _hexView;
+		private bool _saved = true;
+
+
+		public override void Init (Toplevel top)
+		{
+			Top = top;
+		}
+
+		public override void Setup ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("_File", new MenuItem [] {
+					new MenuItem ("_New", "", () => New()),
+					new MenuItem ("_Open", "", () => Open()),
+					new MenuItem ("_Save", "", () => Save()),
+					null,
+					new MenuItem ("_Quit", "", () => Quit()),
+				}),
+				new MenuBarItem ("_Edit", new MenuItem [] {
+					new MenuItem ("_Copy", "", () => Copy()),
+					new MenuItem ("C_ut", "", () => Cut()),
+					new MenuItem ("_Paste", "", () => Paste())
+				}),
+			});
+			Top.Add (menu);
+
+			var statusBar = new StatusBar (new StatusItem [] {
+				//new StatusItem(Key.Enter, "~ENTER~ ApplyEdits", () => { _hexView.ApplyEdits(); }),
+				new StatusItem(Key.F2, "~F2~ Open", () => Open()),
+				new StatusItem(Key.F3, "~F3~ Save", () => Save()),
+				new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()),
+			});
+			Top.Add (statusBar);
+
+			CreateDemoFile (_fileName);
+
+			Win = new Window (_fileName ?? "Untitled") {
+				X = 0,
+				Y = 1,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			Top.Add (Win);
+
+			_hexView = new HexView (LoadFile()) {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+			};
+
+
+			Win.Add (_hexView);
+		}
+
+		private void New ()
+		{
+			Win.Title = _fileName = "Untitled";
+			throw new NotImplementedException ();
+		}
+
+		private Stream LoadFile ()
+		{
+			MemoryStream stream = null;
+			if (!_saved) {
+				MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			}
+
+			if (_fileName != null) {
+				var bin = System.IO.File.ReadAllBytes (_fileName);
+				stream = new MemoryStream (bin);
+				Win.Title = _fileName;
+				_saved = true;
+			}
+			return stream;
+		}
+
+		private void Paste ()
+		{
+			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+		}
+
+		private void Cut ()
+		{
+			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+		}
+
+		private void Copy ()
+		{
+			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			//if (_textView != null && _textView.SelectedLength != 0) {
+			//	_textView.Copy ();
+			//}
+		}
+
+		private void Open ()
+		{
+			var d = new OpenDialog ("Open", "Open a file") { AllowsMultipleSelection = false };
+			Application.Run (d);
+
+			if (!d.Canceled) {
+				_fileName = d.FilePaths [0];
+				_hexView.Source = LoadFile ();
+				_hexView.DisplayStart = 0;
+			}
+		}
+
+		private void Save ()
+		{
+			if (_fileName != null) {
+				using (FileStream fs = new FileStream (_fileName, FileMode.OpenOrCreate)) {
+					_hexView.ApplyEdits ();
+					_hexView.Source.CopyTo (fs);
+					fs.Flush ();
+				}
+				_saved = true;
+			}
+		}
+
+		private void Quit ()
+		{
+			Application.RequestStop ();
+		}
+
+		private void CreateDemoFile(string fileName)
+		{
+			var sb = new StringBuilder ();
+			// BUGBUG: #279 TextView does not know how to deal with \r\n, only \r
+			sb.Append ("Hello world.\n");
+			sb.Append ("This is a test of the Emergency Broadcast System.\n");
+
+			var sw = System.IO.File.CreateText (fileName);
+			sw.Write (sb.ToString ());
+			sw.Close ();
+		}
+
+		public override void Run ()
+		{
+			Application.Run (Top);
+		}
+	}
+}

+ 181 - 0
UICatalog/Scenarios/Keys.cs

@@ -0,0 +1,181 @@
+using NStack;
+using System.Collections.Generic;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Keys", Description: "Shows how to handle keyboard input")]
+	[ScenarioCategory ("Input")]
+	class Keys : Scenario {
+
+		static List<string> _processKeyList = new List<string> ();
+		static List<string> _processHotKeyList = new List<string> ();
+		static List<string> _processColdKeyList = new List<string> ();
+
+		class TestWindow : Window {
+			public TestWindow (ustring title = null) : base (title)
+			{
+			}
+
+			public TestWindow (Rect frame, ustring title = null) : base (frame, title)
+			{
+			}
+
+			public TestWindow (ustring title = null, int padding = 0) : base (title, padding)
+			{
+			}
+
+			public TestWindow (Rect frame, ustring title = null, int padding = 0) : base (frame, title, padding)
+			{
+			}
+
+			public override bool ProcessKey (KeyEvent keyEvent)
+			{
+				_processKeyList.Add (keyEvent.ToString ());
+				return base.ProcessKey (keyEvent);
+			}
+
+			public override bool ProcessHotKey (KeyEvent keyEvent)
+			{
+				_processHotKeyList.Add (keyEvent.ToString ());
+				return base.ProcessHotKey (keyEvent);
+			}
+
+			public override bool ProcessColdKey (KeyEvent keyEvent)
+			{
+				_processColdKeyList.Add (keyEvent.ToString ());
+				return base.ProcessHotKey (keyEvent);
+
+			}
+		}
+
+		public override void Init (Toplevel top)
+		{
+			Top = top;
+
+			Win = new TestWindow ($"CTRL-Q to Close - Scenario: {GetName ()}") {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			Top.Add (Win);
+		}
+
+		public override void Setup ()
+		{
+			// Type text here: ______
+			var editLabel = new Label ("Type text here:") {
+				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);
+
+			// Last KeyPress: ______
+			var keyPressedLabel = new Label ("Last KeyPress:") {
+				X = Pos.Left (editLabel),
+				Y = Pos.Top (editLabel) + 2,
+			};
+			Win.Add (keyPressedLabel);
+			// BUGBUG: Label is not positioning right with Pos, so using TextField instead
+			var labelKeypress = new TextField ("") {
+				X = Pos.Right (keyPressedLabel) + 1,
+				Y = Pos.Top (keyPressedLabel),
+				Width = 20,
+				//TextAlignment = Terminal.Gui.TextAlignment.Left,
+				ColorScheme = Colors.Error,
+			};
+			Win.Add (labelKeypress);
+
+			Win.OnKeyPress += (KeyEvent keyEvent) => labelKeypress.Text = keyEvent.ToString ();
+
+			// Key stroke log:
+			var keyLogLabel = new Label ("Key stroke log:") {
+				X = Pos.Left (editLabel),
+				Y = Pos.Top (editLabel) + 4,
+			};
+			Win.Add (keyLogLabel);
+
+			var yOffset = (Top == Application.Top ? 1 : 6);
+			var keyStrokelist = new List<string> ();
+			var keyStrokeListView = new ListView (keyStrokelist) {
+				X = 0,
+				Y = Pos.Top (keyLogLabel) + yOffset,
+				Width = 25,
+				Height = Dim.Fill (),
+			};
+			keyStrokeListView.ColorScheme = Colors.TopLevel;
+			Win.Add (keyStrokeListView);
+
+			void KeyDownPressUp (KeyEvent keyEvent, string updown)
+			{
+				var msg = $"Key{updown,-5}: {keyEvent.ToString ()}";
+				keyStrokelist.Add (msg);
+				keyStrokeListView.MoveDown ();
+			}
+
+			Win.OnKeyDown += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Down");
+			Win.OnKeyPress += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Press");
+			Win.OnKeyUp += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Up");
+
+			// ProcessKey log:
+			// BUGBUG: Label is not positioning right with Pos, so using TextField instead
+			var processKeyLogLabel = new Label ("ProcessKey log:") {
+				X = Pos.Right (keyStrokeListView) + 1,
+				Y = Pos.Top (editLabel) + 4,
+			};
+			Win.Add (processKeyLogLabel);
+
+			yOffset = (Top == Application.Top ? 1 : 6);
+			var processKeyListView = new ListView (_processKeyList) {
+				X = Pos.Left (processKeyLogLabel),
+				Y = Pos.Top (processKeyLogLabel) + yOffset,
+				Width = 25,
+				Height = Dim.Fill (),
+			};
+			processKeyListView.ColorScheme = Colors.TopLevel;
+			Win.Add (processKeyListView);
+
+			// ProcessHotKey log:
+			// BUGBUG: Label is not positioning right with Pos, so using TextField instead
+			var processHotKeyLogLabel = new Label ("ProcessHotKey log:") {
+				X = Pos.Right (processKeyListView) + 1,
+				Y = Pos.Top (editLabel) + 4,
+			};
+			Win.Add (processHotKeyLogLabel);
+
+			yOffset = (Top == Application.Top ? 1 : 6);
+			var processHotKeyListView = new ListView (_processHotKeyList) {
+				X = Pos.Left (processHotKeyLogLabel),
+				Y = Pos.Top (processHotKeyLogLabel) + yOffset,
+				Width = 25,
+				Height = Dim.Fill (),
+			};
+			processHotKeyListView.ColorScheme = Colors.TopLevel;
+			Win.Add (processHotKeyListView);
+
+			// ProcessColdKey log:
+			// BUGBUG: Label is not positioning right with Pos, so using TextField instead
+			var processColdKeyLogLabel = new Label ("ProcessColdKey log:") {
+				X = Pos.Right (processHotKeyListView) + 1,
+				Y = Pos.Top (editLabel) + 4,
+			};
+			Win.Add (processColdKeyLogLabel);
+
+			yOffset = (Top == Application.Top ? 1 : 6);
+			var processColdKeyListView = new ListView (_processColdKeyList) {
+				X = Pos.Left (processColdKeyLogLabel),
+				Y = Pos.Top (processColdKeyLogLabel) + yOffset,
+				Width = 25,
+				Height = Dim.Fill (),
+			};
+			processColdKeyListView.ColorScheme = Colors.TopLevel;
+			Win.Add (processColdKeyListView);
+		}
+	}
+}

+ 42 - 0
UICatalog/Scenarios/MessageBoxes.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "MessageBoxes", Description: "Demonstrates how to use MessageBoxes")]
+	[ScenarioCategory ("Controls")]
+	[ScenarioCategory ("Dialogs")]
+	[ScenarioCategory ("Bug Repro")]
+	class MessageBoxes : Scenario {
+		public override void Setup ()
+		{
+			Top = new Toplevel ();
+
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("_File", new MenuItem [] {
+					new MenuItem ("_Quit", "", () => Application.RequestStop() )
+				}),
+				new MenuBarItem ("_Simple Query...", "A simple query message box", () =>  MessageBox.Query (0, 6, "MessageBox.Query", "Minimum size was specified", "Ok")),
+				new MenuBarItem ("_Error Query...", "A error query message box", () =>  MessageBox.ErrorQuery (0, 6, "MessageBox.Query", "Minimum size was specified", "Ok")),
+				// BUGBUG: Illustrates MessageBoxes do not deal with long text gracefully. Issue #432
+				new MenuBarItem ("_Long Text...", "Demo long text", () =>  MessageBox.Query (0, 6, "About UI Catalog", "This is a very long title. It is longer than the width of the screen. Will it Wrap? I bet  it will not wrap", "Ok")),
+			});
+			Top.Add (menu);
+
+			Win = new Window ($"Scenario: {GetName ()}") {
+				X = 0,
+				Y = 1,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			Top.Add (Win);
+
+		}
+
+		public override void Run ()
+		{
+			Application.Run (Top);
+		}
+	}
+}

+ 34 - 0
UICatalog/Scenarios/Mouse.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Mouse", Description: "Demonstrates how to capture mouse events")]
+	[ScenarioCategory ("Input")]
+	class Mouse : Scenario {
+		public override void Setup () {
+			Label ml;
+			int count = 0;
+			ml = new Label (new Rect (1, 1, 50, 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 (1, 2, "Se iniciará el análisis");
+			Win.Add (test);
+			Win.Add (ml);
+
+			// I have no idea what this was intended to show off in demo.c
+			var drag = new Label ("Drag: ") { X = 1, Y = 4 };
+			var dragText = new TextField ("") {
+				X = Pos.Right (drag),
+				Y = Pos.Top (drag),
+				Width = 40
+			};
+			Win.Add (drag, dragText);
+		}
+	}
+}

+ 26 - 0
UICatalog/Scenarios/TextAlignment.cs

@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Text Alignment", Description: "Demonstrates text alignment")]
+	[ScenarioCategory ("Text")]
+	class TextAlignment : Scenario {
+		public override void Setup ()
+		{
+			int i = 1;
+			string txt = "Hello world, how are you doing today";
+			var labelList = new List<Label> ();
+			labelList.Add (new Label ($"Label:"));
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1 });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1 });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1 });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1 });
+			txt += "\nSecond line";
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (1), Height = 4, X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1 });
+
+			Win.Add (labelList.ToArray ());
+			Win.LayoutSubviews ();
+		}
+	}
+}

+ 40 - 0
UICatalog/Scenarios/TopLevelNoWindowBug.cs

@@ -0,0 +1,40 @@
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "TopLevelNoWindowBug", Description: "Illustrates that not having a Window causes MenuBar to misbehave. #437")]
+	[ScenarioCategory ("Bug Repro")]
+
+	class TopLevelNoWindowBug : Scenario {
+
+		public override void Run ()
+		{
+			var ntop = 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 ("_Выход", "", () => ntop.Running = false )
+				}),
+				new MenuBarItem ("_Edit", new MenuItem [] {
+					new MenuItem ("_Copy", "", null),
+					new MenuItem ("C_ut", "", null),
+					new MenuItem ("_Paste", "", null)
+				})
+			});
+			ntop.Add (menu);
+
+			// BUGBUG: #437 This being commmented out causes menu to mis-behave
+			var win = new Window ($"Scenario: {GetName ()}") {
+				X = 0,
+				Y = 1,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			ntop.Add (win);
+
+			Application.Run (ntop);
+		}
+	}
+}

+ 35 - 0
UICatalog/Scenarios/UnicodeInMenu.cs

@@ -0,0 +1,35 @@
+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);
+		}
+	}
+}

+ 13 - 0
UICatalog/UICatalog.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <StartupObject>UICatalog.Program</StartupObject>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
+  </ItemGroup>
+
+</Project>

BIN
UICatalog/generic_screenshot.png


BIN
UICatalog/screenshot.png