Răsfoiți Sursa

fixed merge issues and one bug

Charlie Kindel 5 ani în urmă
părinte
comite
95d05dae68
48 a modificat fișierele cu 1907 adăugiri și 126 ștergeri
  1. 25 0
      .github/workflows/dotnetcore.yml
  2. 1 0
      Designer/Designer.csproj
  3. 1 0
      Example/Example.csproj
  4. 48 16
      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. 32 15
      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. 7 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

+ 25 - 0
.github/workflows/dotnetcore.yml

@@ -0,0 +1,25 @@
+name: .NET Core
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Setup .NET Core
+      uses: actions/setup-dotnet@v1
+      with:
+        dotnet-version: 3.1.101
+    - name: Install dependencies
+      run: dotnet restore
+    - name: Build
+      run: dotnet build --configuration Release --no-restore
+    - name: Test
+      run: dotnet test --no-restore --verbosity normal

+ 1 - 0
Designer/Designer.csproj

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

+ 1 - 0
Example/Example.csproj

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

+ 48 - 16
Terminal.Gui/Core.cs

@@ -899,10 +899,7 @@ namespace Terminal.Gui {
 				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 {
 			get {
 				return base.HasFocus;
@@ -925,12 +922,14 @@ namespace Terminal.Gui {
 			}
 		}
 
+		/// <inheritdoc cref="OnEnter"/>
 		public override bool OnEnter ()
 		{
 			Enter?.Invoke (this, new EventArgs ());
 			return base.OnEnter ();
 		}
 
+		/// <inheritdoc cref="OnLeave"/>
 		public override bool OnLeave ()
 		{
 			Leave?.Invoke (this, new EventArgs ());
@@ -1072,11 +1071,11 @@ namespace Terminal.Gui {
 		}
 
 		/// <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>
 		public event EventHandler<KeyEventEventArgs> KeyPress;
 
-		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		/// <inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
 			KeyPress?.Invoke (this, new KeyEventEventArgs(keyEvent));
@@ -1086,7 +1085,7 @@ namespace Terminal.Gui {
 			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)
 		{
 			KeyPress?.Invoke (this, new KeyEventEventArgs (keyEvent));
@@ -1098,7 +1097,7 @@ namespace Terminal.Gui {
 			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)
 		{
 			KeyPress?.Invoke (this, new KeyEventEventArgs(keyEvent));
@@ -1145,6 +1144,7 @@ namespace Terminal.Gui {
 
 			return false;
 		}
+
 		/// <summary>
 		/// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, it does nothing.
 		/// </summary>
@@ -1415,15 +1415,13 @@ namespace Terminal.Gui {
 			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 ()
 		{
 			return $"{GetType ().Name}({Id})({Frame})";
 		}
 
+		/// <inheritdoc cref="OnMouseEnter(Gui.MouseEvent)"/>
 		public override bool OnMouseEnter (MouseEvent mouseEvent)
 		{
 			if (!base.OnMouseEnter (mouseEvent)) {
@@ -1433,6 +1431,7 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		/// <inheritdoc cref="OnMouseLeave(Gui.MouseEvent)"/>
 		public override bool OnMouseLeave (MouseEvent mouseEvent)
 		{
 			if (!base.OnMouseLeave (mouseEvent)) {
@@ -1475,10 +1474,25 @@ namespace Terminal.Gui {
 	/// </remarks>
 	public class Toplevel : View {
 		/// <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>
+		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>
-		public bool Running;
+		internal virtual void OnReady ()
+		{
+			Ready?.Invoke (this, EventArgs.Empty);
+		}
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Toplevel"/> class with the specified absolute layout.
@@ -1513,6 +1527,10 @@ namespace Terminal.Gui {
 			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 {
 			get => true;
 		}
@@ -1534,6 +1552,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool HasStatusBar { get; set; }
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
 			if (base.ProcessKey (keyEvent))
@@ -1585,6 +1604,7 @@ namespace Terminal.Gui {
 			return false;
 		}
 
+		///<inheritdoc cref="Add"/>
 		public override void Add (View view)
 		{
 			if (this == Application.Top) {
@@ -1596,6 +1616,7 @@ namespace Terminal.Gui {
 			base.Add (view);
 		}
 
+		///<inheritdoc cref="Remove"/>
 		public override void Remove (View view)
 		{
 			if (this == Application.Top) {
@@ -1607,6 +1628,7 @@ namespace Terminal.Gui {
 			base.Remove (view);
 		}
 
+		///<inheritdoc cref="RemoveAll"/>
 		public override void RemoveAll ()
 		{
 			if (this == Application.Top) {
@@ -1661,6 +1683,7 @@ namespace Terminal.Gui {
 			}
 		}
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		{
 			Application.CurrentView = this;
@@ -1843,6 +1866,7 @@ namespace Terminal.Gui {
 			contentView.RemoveAll ();
 		}
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect bounds)
 		{
 			Application.CurrentView = this;
@@ -1878,6 +1902,7 @@ namespace Terminal.Gui {
 		//
 		internal static Point? dragPosition;
 		Point start;
+		///<inheritdoc cref="MouseEvent(Gui.MouseEvent)"/>
 		public override bool MouseEvent (MouseEvent mouseEvent)
 		{
 			// FIXED:The code is currently disabled, because the
@@ -2414,8 +2439,15 @@ namespace Terminal.Gui {
 			if (state.Toplevel == null)
 				throw new ObjectDisposedException ("state");
 
+			bool firstIteration = true;
 			for (state.Toplevel.Running = true; state.Toplevel.Running;) {
 				if (MainLoop.EventsPending (wait)) {
+					// Notify Toplevel it's ready
+					if (firstIteration) {
+						state.Toplevel.OnReady ();
+					}
+					firstIteration = false;
+
 					MainLoop.MainIteration ();
 					Iteration?.Invoke (null, EventArgs.Empty);
 				} else if (wait == false)
@@ -2431,7 +2463,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		internal static bool DebugDrawBounds;
+		internal static bool DebugDrawBounds = false;
 
 		// Need to look into why this does not work properly.
 		static void DrawBounds (View v)

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

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

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

@@ -466,9 +466,11 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Prepare the driver and set the key and mouse events handlers.
 		/// </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);
 
 		/// <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.
 	/// </summary>
 	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 Rows => Curses.Lines;
 
@@ -37,7 +38,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		static bool sync;
+		static bool sync = false;
 		public override void AddRune (Rune rune)
 		{
 			if (Clip.Contains (ccol, crow)) {
@@ -51,7 +52,7 @@ namespace Terminal.Gui {
 			if (sync)
 				Application.Driver.Refresh ();
 			ccol++;
-			var runeWidth = Rune.ColumnWidth(rune);
+			var runeWidth = Rune.ColumnWidth (rune);
 			if (runeWidth > 1) {
 				for (int i = 1; i < runeWidth; i++) {
 					ccol++;
@@ -66,8 +67,13 @@ namespace Terminal.Gui {
 				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 UpdateScreen () => window.redrawwin ();
 		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;
 			var code = Curses.get_wch (out wch);
@@ -206,6 +212,7 @@ namespace Terminal.Gui {
 					return;
 				}
 				keyHandler (new KeyEvent (MapCursesKey (wch)));
+				keyUpHandler (new KeyEvent (MapCursesKey (wch)));
 				return;
 			}
 
@@ -213,7 +220,7 @@ namespace Terminal.Gui {
 			if (wch == 27) {
 				Curses.timeout (200);
 
-				code = Curses.get_wch (out wch);
+				code = Curses.get_wch (out int wch2);
 				if (code == Curses.KEY_CODE_YES)
 					keyHandler (new KeyEvent (Key.AltMask | MapCursesKey (wch)));
 				if (code == 0) {
@@ -221,23 +228,28 @@ namespace Terminal.Gui {
 
 					// The ESC-number handling, debatable.
 					// Simulates the AltMask itself by pressing Alt + Space.
-					if (wch == (int)Key.Space)
+					if (wch2 == (int)Key.Space)
 						key = new KeyEvent (Key.AltMask);
-					else if (wch - (int)Key.Space >= 'A' && wch - (int)Key.Space <= 'Z')
-						key = new KeyEvent ((Key)((uint)Key.AltMask + (wch - (int)Key.Space)));
-					else if (wch >= '1' && wch <= '9')
-						key = new KeyEvent ((Key)((int)Key.F1 + (wch - '0' - 1)));
-					else if (wch == '0')
+					else if (wch2 - (int)Key.Space >= 'A' && wch2 - (int)Key.Space <= 'Z')
+						key = new KeyEvent ((Key)((uint)Key.AltMask + (wch2 - (int)Key.Space)));
+					else if (wch2 >= '1' && wch <= '9')
+						key = new KeyEvent ((Key)((int)Key.F1 + (wch2 - '0' - 1)));
+					else if (wch2 == '0')
 						key = new KeyEvent (Key.F10);
-					else if (wch == 27)
-						key = new KeyEvent ((Key)wch);
+					else if (wch2 == 27)
+						key = new KeyEvent ((Key)wch2);
 					else
-						key = new KeyEvent (Key.AltMask | (Key)wch);
+						key = new KeyEvent (Key.AltMask | (Key)wch2);
 					keyHandler (key);
-				} else
+				} else {
 					keyHandler (new KeyEvent (Key.Esc));
-			} else
+				}
+			} else {
 				keyHandler (new KeyEvent ((Key)wch));
+			}
+			// Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above 
+			// will not impact KeyUp.
+			keyUpHandler (new KeyEvent ((Key)wch));
 		}
 
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
@@ -246,7 +258,7 @@ namespace Terminal.Gui {
 			Curses.timeout (-1);
 
 			(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {
-				ProcessInput (keyHandler, mouseHandler);
+				ProcessInput (keyHandler, keyUpHandler, mouseHandler);
 				return true;
 			});
 
@@ -308,7 +320,7 @@ namespace Terminal.Gui {
 				Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
 				Colors.Menu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
 				Colors.Menu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
-				Colors.Menu.Disabled = MakeColor(Curses.COLOR_WHITE, Curses.COLOR_CYAN);
+				Colors.Menu.Disabled = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
 
 				Colors.Dialog.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
 				Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
@@ -490,6 +502,7 @@ namespace Terminal.Gui {
 			killpg (0, signal);
 			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;
 		}
 
-		static bool sync;
+		static bool sync = false;
 
 		public NetDriver ()
 		{
@@ -328,6 +328,7 @@ namespace Terminal.Gui {
 				if (map == (Key)0xffffffff)
 					return;
 				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 ()
 		{
 			ConsoleMode = originalConsoleMode;
-			ContinueListeningForConsoleEvents = false;
+			//ContinueListeningForConsoleEvents = false;
 			if (!SetConsoleActiveScreenBuffer (OutputHandle)) {
 				var err = Marshal.GetLastWin32Error ();
 				Console.WriteLine ("Error: {0}", err);
@@ -109,7 +109,7 @@ namespace Terminal.Gui {
 			ScreenBuffer = IntPtr.Zero;
 		}
 
-		bool ContinueListeningForConsoleEvents = true;
+		//bool ContinueListeningForConsoleEvents = true;
 
 		public uint ConsoleMode {
 			get {
@@ -426,7 +426,7 @@ namespace Terminal.Gui {
 	}
 
 	internal class WindowsDriver : ConsoleDriver, Mono.Terminal.IMainLoopDriver {
-		static bool sync;
+		static bool sync = false;
 		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
 		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
 		MainLoop mainLoop;
@@ -440,15 +440,10 @@ namespace Terminal.Gui {
 
 		public WindowsDriver ()
 		{
-			Colors.TopLevel = new ColorScheme ();
-
-			Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
-			Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
-			Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
-			Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.DarkCyan);
-
 			winConsole = new WindowsConsole ();
 
+			SetupColorsAndBorders ();
+
 			cols = Console.WindowWidth;
 			rows = Console.WindowHeight;
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
@@ -459,6 +454,54 @@ namespace Terminal.Gui {
 			Task.Run ((Action)WindowsInputHandler);
 		}
 
+		private void SetupColorsAndBorders ()
+		{
+			Colors.TopLevel = new ColorScheme ();
+			Colors.Base = new ColorScheme ();
+			Colors.Dialog = new ColorScheme ();
+			Colors.Menu = new ColorScheme ();
+			Colors.Error = new ColorScheme ();
+
+			Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
+			Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
+			Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
+			Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
+
+			Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkBlue);
+			Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
+			Colors.Base.HotNormal = MakeColor (ConsoleColor.DarkCyan, ConsoleColor.DarkBlue);
+			Colors.Base.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
+
+			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
+			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
+			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.DarkGray);
+			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
+			Colors.Menu.Disabled = MakeColor (ConsoleColor.Gray, ConsoleColor.DarkGray);
+
+			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
+			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkGray);
+			Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
+			Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
+
+			Colors.Error.Normal = MakeColor (ConsoleColor.DarkRed, ConsoleColor.White);
+			Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed);
+			Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White);
+			Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
+
+			HLine = '\u2500';
+			VLine = '\u2502';
+			Stipple = '\u2592';
+			Diamond = '\u25c6';
+			ULCorner = '\u250C';
+			LLCorner = '\u2514';
+			URCorner = '\u2510';
+			LRCorner = '\u2518';
+			LeftTee = '\u251c';
+			RightTee = '\u2524';
+			TopTee = '\u22a4';
+			BottomTee = '\u22a5';
+		}
+
 		[StructLayout (LayoutKind.Sequential)]
 		public struct ConsoleKeyInfoEx {
 			public ConsoleKeyInfo consoleKeyInfo;
@@ -564,11 +607,24 @@ namespace Terminal.Gui {
 			case WindowsConsole.EventType.Key:
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
 				if (map == (Key)0xffffffff) {
-					KeyEvent key = default;
+					KeyEvent key = new KeyEvent ();
+
 					// Shift = VK_SHIFT = 0x10
 					// Ctrl = VK_CONTROL = 0x11
 					// Alt = VK_MENU = 0x12
 
+					if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) {
+						inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn;
+					}
+
+					if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) {
+						inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn;
+					}
+
+					if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) {
+						inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn;
+					}
+
 					switch (inputEvent.KeyEvent.dwControlKeyState) {
 					case WindowsConsole.ControlKeyState.RightAltPressed:
 					case WindowsConsole.ControlKeyState.RightAltPressed |
@@ -617,10 +673,10 @@ namespace Terminal.Gui {
 						keyUpHandler (key);
 				} else {
 					if (inputEvent.KeyEvent.bKeyDown) {
-						// Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
 						keyDownHandler (new KeyEvent (map));
-						keyHandler (new KeyEvent (map));
 					} else {
+						// Key Up - Fire KeyDown Event and KeyStroke (ProcessKey) Event
+						keyHandler (new KeyEvent (map));
 						keyUpHandler (new KeyEvent (map));
 					}
 				}
@@ -807,6 +863,8 @@ namespace Terminal.Gui {
 
 			} else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
 				mouseFlag = MouseFlags.ReportMousePosition;
+			} else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) {
+				mouseFlag = 0;
 			}
 
 			mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag);
@@ -916,6 +974,9 @@ namespace Terminal.Gui {
 			case ConsoleKey.OemComma:
 			case ConsoleKey.OemPlus:
 			case ConsoleKey.OemMinus:
+				if (keyInfo.KeyChar == 0)
+					return Key.Unknown;
+
 				return (Key)((uint)keyInfo.KeyChar);
 			}
 
@@ -969,48 +1030,10 @@ namespace Terminal.Gui {
 		public override void Init (Action terminalResized)
 		{
 			TerminalResized = terminalResized;
-
-			Colors.Base = new ColorScheme ();
-			Colors.Dialog = new ColorScheme ();
-			Colors.Menu = new ColorScheme ();
-			Colors.Error = new ColorScheme ();
-
-			HLine = '\u2500';
-			VLine = '\u2502';
-			Stipple = '\u2592';
-			Diamond = '\u25c6';
-			ULCorner = '\u250C';
-			LLCorner = '\u2514';
-			URCorner = '\u2510';
-			LRCorner = '\u2518';
-			LeftTee = '\u251c';
-			RightTee = '\u2524';
-			TopTee = '\u22a4';
-			BottomTee = '\u22a5';
-
-			Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Blue);
-			Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
-			Colors.Base.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Blue);
-			Colors.Base.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
-
-			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
-			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
-			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
-			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
-			Colors.Menu.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Cyan);
-
-			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
-			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
-			Colors.Dialog.HotNormal = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
-			Colors.Dialog.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Cyan);
-
-			Colors.Error.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Red);
-			Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
-			Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
-			Colors.Error.HotFocus = Colors.Error.HotNormal;
-			Console.Clear ();
+			SetupColorsAndBorders ();
 		}
 
+		
 		void ResizeScreen ()
 		{
 			OutputBuffer = new WindowsConsole.CharInfo [Rows * Cols];

+ 29 - 4
Terminal.Gui/Event.cs

@@ -45,7 +45,7 @@ namespace Terminal.Gui {
 		ControlSpace = 0,
 
 		/// <summary>
-	        /// The key code for the user pressing Control-A
+		/// The key code for the user pressing Control-A
 		/// </summary>
 		ControlA = 1,
 		/// <summary>
@@ -288,8 +288,7 @@ namespace Terminal.Gui {
 	/// <summary>
 	/// Describes a keyboard event.
 	/// </summary>
-	public struct KeyEvent {
-
+	public class KeyEvent {
 		/// <summary>
 		/// Symb olid definition for the key.
 		/// </summary>
@@ -321,6 +320,10 @@ namespace Terminal.Gui {
 		//public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
 		public bool IsCtrl => (Key & Key.CtrlMask) != 0;
 
+		public KeyEvent ()
+		{
+			Key = Key.Unknown;
+		}
 		/// <summary>
 		///   Constructs a new KeyEvent from the provided Key value - can be a rune cast into a Key value
 		/// </summary>
@@ -328,6 +331,28 @@ namespace Terminal.Gui {
 		{
 			Key = k;
 		}
+
+		public override string ToString ()
+		{
+			string msg = "";
+			var key = this.Key;
+			if ((this.Key & Key.ShiftMask) != 0) {
+				msg += "Shift-";
+			}
+			if ((this.Key & Key.CtrlMask) != 0) {
+				msg += "Ctrl-";
+			}
+			if ((this.Key & Key.AltMask) != 0) {
+				msg += "Alt-";
+			}
+
+			if (string.IsNullOrEmpty (msg)) {
+				msg += $"{(((uint)this.KeyValue & (uint)Key.CharMask) > 27 ? $"{(char)this.KeyValue}" : $"{key}")}";
+			} else {
+				msg += $"{(((uint)this.KeyValue & (uint)Key.CharMask) > 27 ? $"{(char)this.KeyValue}" : $"")}";
+			}
+			return msg;
+		}
 	}
 
 	/// <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"/>.
 		/// </summary>
 		/// <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}";
 		}

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

@@ -37,7 +37,11 @@ namespace Mono.Terminal.Internal {
 		const string XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS";
 		static bool IsWindows, IsLinux, IsMacOS;
 		static bool Is64Bit;
+#if GUICS
+		static bool IsMono;
+#else
 		static bool IsMono, IsUnity, IsXamarinIOS, IsXamarinAndroid, IsXamarin;
+#endif
 		static bool IsNetCore;
 
 		public static bool IsMacOSPlatform => IsMacOS;
@@ -75,7 +79,7 @@ namespace Mono.Terminal.Internal {
 				IsNetCore = Type.GetType ("System.MathF") != null;
 			}
 #if GUICS
-			IsUnity = IsXamarinIOS = IsXamarinAndroid = IsXamarin = false;
+			//IsUnity = IsXamarinIOS = IsXamarinAndroid = IsXamarin = false;
 #else
 			IsUnity = Type.GetType (UnityEngineApplicationClassName) != 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;
 
 namespace Unix.Terminal {
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
 
 	public partial class Curses {
 		[StructLayout (LayoutKind.Sequential)]
@@ -61,7 +62,7 @@ namespace Unix.Terminal {
 		static IntPtr curses_handle, curscr_ptr, lines_ptr, cols_ptr;
 
 		// 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 NativeMethods methods;
@@ -451,4 +452,5 @@ namespace Unix.Terminal {
 			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;
 
 namespace Unix.Terminal {
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
 	public partial class Curses {
 		public const int A_NORMAL = unchecked((int)0x0);
 		public const int A_STANDOUT = unchecked((int)0x10000);
@@ -139,4 +140,5 @@ namespace Unix.Terminal {
 			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 {
 
 	public partial class Curses {
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
 		public class Window {
 			public readonly IntPtr Handle;
 			static Window curscr;
@@ -167,7 +168,8 @@ namespace Unix.Terminal {
 	 			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>
 		/// <param name="wait">If set to <c>true</c> wait until an event is available, otherwise return immediately.</param>
 		bool EventsPending (bool wait);
+
+		/// <summary>
+		/// The interation function.
+		/// </summary>
 		void MainIteration ();
 	}
 

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

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

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

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

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

@@ -41,7 +41,7 @@ namespace Terminal.Gui {
 		{
 			CultureInfo cultureInfo = CultureInfo.CurrentCulture;
 			sepChar = cultureInfo.DateTimeFormat.DateSeparator;
-			longFormat = $" {cultureInfo.DateTimeFormat.ShortDatePattern}";
+			longFormat = GetLongFormat (cultureInfo.DateTimeFormat.ShortDatePattern);
 			shortFormat = GetShortFormat(longFormat);
 			this.isShort = isShort;
 			CursorPosition = 1;
@@ -55,7 +55,21 @@ namespace Terminal.Gui {
 				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");
 		}

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

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

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

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

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

@@ -294,6 +294,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		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>
 		/// Handles cursor movement for this view, passes all other events.
 		/// </summary>
@@ -325,10 +330,19 @@ namespace Terminal.Gui {
 					return true;
 				else
 					break;
+
+			case Key.Enter:
+				OpenSelectedItem?.Invoke (this, new EventArgs ());
+				break;
+
 			}
 			return base.ProcessKey (kb);
 		}
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool AllowsAll ()
 		{
 			if (!allowsMarking)
@@ -344,6 +358,10 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MarkUnmarkRow(){
 			if (AllowsAll ()) {
 				Source.SetMark(SelectedItem, !Source.IsMarked(SelectedItem));
@@ -354,6 +372,10 @@ namespace Terminal.Gui {
 			return false;
 		}
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MovePageUp(){
 			int n = (selected - Frame.Height);
 			if (n < 0)
@@ -369,6 +391,10 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MovePageDown(){
 			var n = (selected + Frame.Height);
 			if (n > source.Count)
@@ -387,6 +413,10 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MoveDown(){
 			if (selected + 1 < source.Count){
 				selected++;
@@ -400,6 +430,10 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool MoveUp(){
 			if (selected > 0){
 				selected--;
@@ -424,9 +458,10 @@ namespace Terminal.Gui {
 				Move (0, selected - top);
 		}
 
+		///<inheritdoc cref="MouseEvent(Gui.MouseEvent)"/>
 		public override bool MouseEvent(MouseEvent me)
 		{
-			if (!me.Flags.HasFlag (MouseFlags.Button1Clicked))
+			if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
 				return false;
 
 			if (!HasFocus)
@@ -444,9 +479,10 @@ namespace Terminal.Gui {
 				SetNeedsDisplay ();
 				return true;
 			}
-			if (SelectedChanged != null)
-				SelectedChanged();
+			SelectedChanged?.Invoke ();
 			SetNeedsDisplay ();
+			if (me.Flags == MouseFlags.Button1DoubleClicked)
+				OpenSelectedItem?.Invoke (this, new EventArgs ());
 			return true;
 		}
 	}
@@ -460,6 +496,10 @@ namespace Terminal.Gui {
 		BitArray marks;
 		int count;
 
+		/// <summary>
+		/// constructor
+		/// </summary>
+		/// <param name="source"></param>
 		public ListWrapper (IList source)
 		{
 			count = source.Count;
@@ -467,6 +507,9 @@ namespace Terminal.Gui {
 			this.src = source;
 		}
 
+		/// <summary>
+		/// Count of items.
+		/// </summary>
 		public int Count => src.Count;
 
 		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)
 		{
 			container.Move (col, line);
@@ -499,6 +552,11 @@ namespace Terminal.Gui {
 				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)
 		{
 			if (item >= 0 && item < count)
@@ -506,6 +564,11 @@ namespace Terminal.Gui {
 			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)
 		{
 			if (item >= 0 && item < count)

+ 32 - 15
Terminal.Gui/Views/Menu.cs

@@ -21,6 +21,9 @@ namespace Terminal.Gui {
 	/// </summary>
 	public class MenuItem {
 
+		/// <summary>
+		/// constructor
+		/// </summary>
 		public MenuItem ()
 		{
 			Title = "";
@@ -207,10 +210,10 @@ namespace Terminal.Gui {
 			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; }
 
 		/// <summary>
@@ -357,7 +360,7 @@ namespace Terminal.Gui {
 		public override bool ProcessHotKey (KeyEvent keyEvent)
 		{
 			// To ncurses simulate a AltMask key pressing Alt+Space because
-			// it can´t detect an alone special key down was pressed.
+			// it can�t detect an alone special key down was pressed.
 			if (keyEvent.IsAlt && keyEvent.Key == Key.AltMask) {
 				OnKeyDown (keyEvent);
 				return true;
@@ -455,7 +458,7 @@ namespace Terminal.Gui {
 			}
 			host.handled = false;
 			bool disabled;
-			if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
+			if (me.Flags == MouseFlags.Button1Clicked) {
 				disabled = false;
 				if (me.Y < 1)
 					return true;
@@ -559,6 +562,7 @@ namespace Terminal.Gui {
 		}
 
 		bool openedByAltKey;
+		///<inheritdoc cref="OnKeyDown"/>
 		public override bool OnKeyDown (KeyEvent keyEvent)
 		{
 			if (keyEvent.IsAlt) {
@@ -569,12 +573,7 @@ namespace Terminal.Gui {
 			return false;
 		}
 
-		/// <summary>
-		/// Track Alt key-up events. On Windows, when a user releases Alt (without another key), the menu gets focus but doesn't open.
-		/// We mimic that behavior here.
-		/// </summary>
-		/// <param name="keyEvent"></param>
-		/// <returns></returns>
+		///<inheritdoc cref="OnKeyUp"/>
 		public override bool OnKeyUp (KeyEvent keyEvent)
 		{
 			if (keyEvent.IsAlt) {
@@ -615,6 +614,7 @@ namespace Terminal.Gui {
 			return false;
 		}
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		{
 			Move (0, 0);
@@ -645,6 +645,7 @@ namespace Terminal.Gui {
 			PositionCursor ();
 		}
 
+		///<inheritdoc cref="PositionCursor"/>
 		public override void PositionCursor ()
 		{
 			int pos = 0;
@@ -672,8 +673,16 @@ namespace Terminal.Gui {
 			action = item.Action;
 		}
 
+		/// <summary>
+		/// Raised as a menu is opened.
+		/// </summary>
 		public event EventHandler OnOpenMenu;
+
+		/// <summary>
+		/// Raised when a menu is closing.
+		/// </summary>
 		public event EventHandler OnCloseMenu;
+
 		internal Menu openMenu;
 		Menu openCurrentMenu;
 		internal List<Menu> openSubMenu;
@@ -681,7 +690,12 @@ namespace Terminal.Gui {
 		internal bool isMenuOpening;
 		internal bool isMenuClosing;
 		internal bool isMenuClosed;
-		public bool MenuOpen;
+
+		/// <summary>
+		/// True of the menu is open; otherwise false.
+		/// </summary>
+		public bool MenuOpen { get; set; }
+
 		View lastFocused;
 
 		/// <summary>
@@ -940,7 +954,7 @@ namespace Terminal.Gui {
 		bool openedByHotKey;
 		internal bool FindAndOpenMenuByHotkey (KeyEvent kb)
 		{
-			int pos = 0;
+			//int pos = 0;
 			var c = ((uint)kb.Key & (uint)Key.CharMask);
 			for (int i = 0; i < Menus.Length; i++) {
 				// TODO: this code is duplicated, hotkey should be part of the MenuBarItem
@@ -969,6 +983,7 @@ namespace Terminal.Gui {
 			}
 		}
 
+		///<inheritdoc cref="ProcessHotKey"/>
 		public override bool ProcessHotKey (KeyEvent kb)
 		{
 			if (kb.Key == Key.F9) {
@@ -980,7 +995,7 @@ namespace Terminal.Gui {
 			}
 
 			// To ncurses simulate a AltMask key pressing Alt+Space because
-			// it can´t detect an alone special key down was pressed.
+			// it can�t detect an alone special key down was pressed.
 			if (kb.IsAlt && kb.Key == Key.AltMask && openMenu == null) {
 				OnKeyDown (kb);
 				OnKeyUp (kb);
@@ -993,6 +1008,7 @@ namespace Terminal.Gui {
 			return base.ProcessHotKey (kb);
 		}
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent kb)
 		{
 			switch (kb.Key) {
@@ -1047,6 +1063,7 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		///<inheritdoc cref="MouseEvent"/>
 		public override bool MouseEvent (MouseEvent me)
 		{
 			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)
 		{
 			if (me.Flags != MouseFlags.Button1Pressed && me.Flags != MouseFlags.Button1Clicked &&
@@ -249,6 +250,10 @@ namespace Terminal.Gui {
 		View contentView;
 		ScrollBarView vertical, horizontal;
 
+		/// <summary>
+		/// Constructs a ScrollView
+		/// </summary>
+		/// <param name="frame"></param>
 		public ScrollView (Rect frame) : base (frame)
 		{
 			contentView = new View (frame);
@@ -363,7 +368,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// This event is raised when the contents have scrolled
 		/// </summary>
-		public event Action<ScrollView> Scrolled;
+		//public event Action<ScrollView> Scrolled;
 
 		public override void Redraw(Rect region)
 		{
@@ -383,6 +388,7 @@ namespace Terminal.Gui {
 			}
 		}
 
+		///<inheritdoc cref="PositionCursor"/>
 		public override void PositionCursor()
 		{
 			if (InternalSubviews.Count == 0)
@@ -448,6 +454,7 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey(KeyEvent 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;
 #endif
+		/// <summary>
+		/// The parent view of the StatusBar.
+		/// </summary>
 		public View Parent { get; set; }
 
+		/// <summary>
+		/// The items that compose the StatusBar
+		/// </summary>
 		public StatusItem [] Items { get; set; }
 
 		/// <summary>
@@ -132,6 +138,7 @@ namespace Terminal.Gui {
 			return result;
 		}
 
+		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		{
 			if (Frame.Y != Driver.Rows - 1) {
@@ -161,6 +168,7 @@ namespace Terminal.Gui {
 			}
 		}
 
+		///<inheritdoc cref="ProcessHotKey"/>
 		public override bool ProcessHotKey (KeyEvent kb)
 		{
 			foreach (var item in Items) {

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

@@ -266,6 +266,9 @@ namespace Terminal.Gui {
 		bool selecting;
 		//bool used;
 
+		/// <summary>
+		/// Raised when the Text of the TextView changes.
+		/// </summary>
 		public event EventHandler TextChanged;
 
 #if false
@@ -535,6 +538,7 @@ namespace Terminal.Gui {
 			PositionCursor ();
 		}
 
+		///<inheritdoc cref="CanFocus"/>
 		public override bool CanFocus {
 			get => true;
 			set { base.CanFocus = value; }
@@ -682,6 +686,7 @@ namespace Terminal.Gui {
 
 		bool lastWasKill;
 
+		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent kb)
 		{
 			int restCount;
@@ -1139,6 +1144,7 @@ namespace Terminal.Gui {
 			return null;
 		}
 
+		///<inheritdoc cref="MouseEvent"/>
 		public override bool MouseEvent (MouseEvent ev)
 		{
 			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
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UICatalog", "UICatalog\UICatalog.csproj", "{88979F89-9A42-448F-AE3E-3060145F6375}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x86 = Debug|x86
@@ -25,6 +27,14 @@ Global
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Debug|x86.Build.0 = Debug|x86
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.ActiveCfg = Release|x86
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.Build.0 = Release|x86
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.Build.0 = Debug|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.Build.0 = Release|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.ActiveCfg = Release|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(MonoDevelopProperties) = preSolution
 		Policies = $0

+ 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.KeyUp += 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 (object sender, View.KeyEventEventArgs a)
+		{
+			if (_runningScenario != null) {
+				//switch (ke.Key) {
+				//case Key.Esc:
+				//	//_runningScenario.RequestStop ();
+				//	break;
+				//case Key.Enter:
+				//	break;
+				//}
+			} else if (a.KeyEvent.Key == Key.Tab || a.KeyEvent.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;
+		}
+	}
+}

+ 7 - 0
UICatalog/Properties/launchSettings.json

@@ -0,0 +1,7 @@
+{
+  "profiles": {
+    "UICatalog": {
+      "commandName": "Project"
+    }
+  }
+}

+ 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.ProcessColdKey (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.KeyPress += (sender, a) => labelKeypress.Text = a.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.KeyDown += (sender, a) => KeyDownPressUp (a.KeyEvent, "Down");
+			Win.KeyPress += (sender, a) => KeyDownPressUp (a.KeyEvent, "Press");
+			Win.KeyUp += (sender, a) => KeyDownPressUp (a.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