Browse Source

Merge pull request #163 from BDisp/events-fix

Events fix
Thomas Nind 2 years ago
parent
commit
63bca182cf
86 changed files with 739 additions and 606 deletions
  1. 1 1
      Example/Example.cs
  2. 2 2
      README.md
  3. 19 1
      Terminal.Gui/Configuration/ConfigurationManagerEventArgs.cs
  4. 0 24
      Terminal.Gui/Configuration/ThemeManagerEventArgs.cs
  5. 15 1
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs
  6. 2 0
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  7. 4 4
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs
  8. 46 14
      Terminal.Gui/Core/Application.cs
  9. 21 1
      Terminal.Gui/Core/ConsoleKeyMapping.cs
  10. 0 25
      Terminal.Gui/Core/FocusEventArgs.cs
  11. 0 1
      Terminal.Gui/Core/KeyEventEventArgs.cs
  12. 0 36
      Terminal.Gui/Core/MenuOpenedEventArgs.cs
  13. 31 0
      Terminal.Gui/Core/MouseEventEventArgs.cs
  14. 1 5
      Terminal.Gui/Core/MouseFlagsChangedEventArgs.cs
  15. 1 5
      Terminal.Gui/Core/PointEventArgs.cs
  16. 1 1
      Terminal.Gui/Core/SizeChangedEventArgs.cs
  17. 0 1
      Terminal.Gui/Core/TimeoutEventArgs.cs
  18. 0 4
      Terminal.Gui/Core/ToggleEventArgs.cs
  19. 20 3
      Terminal.Gui/Core/Toplevel.cs
  20. 0 26
      Terminal.Gui/Core/ToplevelClosingEventArgs.cs
  21. 23 0
      Terminal.Gui/Core/ToplevelEventArgs.cs
  22. 11 49
      Terminal.Gui/Core/View.cs
  23. 53 6
      Terminal.Gui/Core/ViewEventArgs.cs
  24. 0 28
      Terminal.Gui/Views/DrawEventArgs.cs
  25. 0 37
      Terminal.Gui/Views/HexViewEditEventArgs.cs
  26. 27 0
      Terminal.Gui/Views/HexViewEventArgs.cs
  27. 1 1
      Terminal.Gui/Views/HistoryTextItem.cs
  28. 1 1
      Terminal.Gui/Views/Label.cs
  29. 24 0
      Terminal.Gui/Views/ListViewEventArgs.cs
  30. 0 27
      Terminal.Gui/Views/ListViewRowEventArgs.cs
  31. 16 5
      Terminal.Gui/Views/Menu.cs
  32. 0 42
      Terminal.Gui/Views/MenuClosingEventArgs.cs
  33. 98 0
      Terminal.Gui/Views/MenuEventArgs.cs
  34. 0 32
      Terminal.Gui/Views/MenuOpeningEventArgs.cs
  35. 1 1
      Terminal.Gui/Views/ScrollBarView.cs
  36. 2 2
      Terminal.Gui/Views/ScrollView.cs
  37. 0 1
      Terminal.Gui/Views/TabMouseEventArgs.cs
  38. 0 38
      Terminal.Gui/Windows/StepChangeEventArgs.cs
  39. 1 1
      Terminal.Gui/Windows/Wizard.cs
  40. 0 24
      Terminal.Gui/Windows/WizardButtonEventArgs.cs
  41. 54 0
      Terminal.Gui/Windows/WizardEventArgs.cs
  42. 10 23
      UICatalog/Scenario.cs
  43. 4 4
      UICatalog/Scenarios/ASCIICustomButton.cs
  44. 9 4
      UICatalog/Scenarios/AllViewsTester.cs
  45. 4 4
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  46. 1 1
      UICatalog/Scenarios/BordersComparisons.cs
  47. 2 2
      UICatalog/Scenarios/BordersOnFrameView.cs
  48. 2 2
      UICatalog/Scenarios/BordersOnToplevel.cs
  49. 2 2
      UICatalog/Scenarios/BordersOnWindow.cs
  50. 1 1
      UICatalog/Scenarios/CharacterMap.cs
  51. 1 1
      UICatalog/Scenarios/Clipping.cs
  52. 3 3
      UICatalog/Scenarios/CollectionNavigatorTester.cs
  53. 5 2
      UICatalog/Scenarios/ComputedLayout.cs
  54. 4 2
      UICatalog/Scenarios/ConfigurationEditor.cs
  55. 1 1
      UICatalog/Scenarios/CsvEditor.cs
  56. 2 2
      UICatalog/Scenarios/DynamicMenuBar.cs
  57. 2 2
      UICatalog/Scenarios/DynamicStatusBar.cs
  58. 9 6
      UICatalog/Scenarios/Editor.cs
  59. 8 7
      UICatalog/Scenarios/Generic.cs
  60. 1 1
      UICatalog/Scenarios/GraphViewExample.cs
  61. 1 1
      UICatalog/Scenarios/HexEditor.cs
  62. 1 1
      UICatalog/Scenarios/InteractiveTree.cs
  63. 6 3
      UICatalog/Scenarios/Keys.cs
  64. 1 1
      UICatalog/Scenarios/LineViewExample.cs
  65. 1 1
      UICatalog/Scenarios/MultiColouredTable.cs
  66. 2 2
      UICatalog/Scenarios/Notepad.cs
  67. 1 1
      UICatalog/Scenarios/RunTExample.cs
  68. 1 1
      UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  69. 2 2
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  70. 1 1
      UICatalog/Scenarios/SyntaxHighlighting.cs
  71. 1 1
      UICatalog/Scenarios/TabViewExample.cs
  72. 2 2
      UICatalog/Scenarios/TableEditor.cs
  73. 2 2
      UICatalog/Scenarios/TextViewAutocompletePopup.cs
  74. 1 1
      UICatalog/Scenarios/TileViewExperiment.cs
  75. 1 1
      UICatalog/Scenarios/TreeUseCases.cs
  76. 2 2
      UICatalog/Scenarios/TreeViewFileSystem.cs
  77. 1 1
      UICatalog/Scenarios/Unicode.cs
  78. 1 1
      UICatalog/Scenarios/WizardAsView.cs
  79. 23 6
      UICatalog/UICatalog.cs
  80. 4 4
      UnitTests/Application/ApplicationTests.cs
  81. 59 2
      UnitTests/Drivers/ConsoleDriverTests.cs
  82. 1 1
      UnitTests/Drivers/KeyTests.cs
  83. 2 2
      UnitTests/Menus/ContextMenuTests.cs
  84. 2 2
      UnitTests/TopLevels/ToplevelTests.cs
  85. 56 32
      UnitTests/UICatalog/ScenarioTests.cs
  86. 17 12
      UnitTests/Views/StatusBarTests.cs

+ 1 - 1
Example/Example.cs

@@ -18,7 +18,7 @@ public class ExampleWindow : Window {
 	
 	public ExampleWindow ()
 	{
-		Title = "Example App (Ctrl+Q to quit)";
+		Title = $"Example App ({Application.QuitKey} to quit)";
 
 		// Create input components and labels
 		var usernameLabel = new Label () { 

+ 2 - 2
README.md

@@ -18,7 +18,7 @@ NOTE: This is the WORK IN PROGRESS `v2.x` branch. The `main` branch is the stabl
 
 Paste these commands into your favorite terminal on Windows, Mac, or Linux. This will install the [Terminal.Gui.Templates](https://github.com/gui-cs/Terminal.Gui.templates), create a new "Hello World" TUI app, and run it.
 
-(Press `CTRL-Q` to exit the app)
+(Press `CTRL-Q` to quit the app)
 
 ```powershell
 dotnet new --install Terminal.Gui.templates
@@ -82,7 +82,7 @@ public class ExampleWindow : Window {
 	
 	public ExampleWindow ()
 	{
-		Title = "Example App (Ctrl+Q to quit)";
+		Title = $"Example App ({Application.QuitKey} to quit)";
 
 		// Create input components and labels
 		var usernameLabel = new Label () { 

+ 19 - 1
Terminal.Gui/Configuration/ConfigurationManagerEventArgs.cs

@@ -14,5 +14,23 @@ namespace Terminal.Gui.Configuration {
 		public ConfigurationManagerEventArgs ()
 		{
 		}
-	}	
+	}
+
+	/// <summary>
+	/// Event arguments for the <see cref="ConfigurationManager.ThemeManager"/> events.
+	/// </summary>
+	public class ThemeManagerEventArgs : EventArgs {
+		/// <summary>
+		/// The name of the new active theme..
+		/// </summary>
+		public string NewTheme { get; set; } = string.Empty;
+
+		/// <summary>
+		/// Initializes a new instance of <see cref="ThemeManagerEventArgs"/>
+		/// </summary>
+		public ThemeManagerEventArgs (string newTheme)
+		{
+			NewTheme = newTheme;
+		}
+	}
 }

+ 0 - 24
Terminal.Gui/Configuration/ThemeManagerEventArgs.cs

@@ -1,24 +0,0 @@
-using System;
-
-#nullable enable
-
-namespace Terminal.Gui.Configuration {
-
-	/// <summary>
-	/// Event arguments for the <see cref="ConfigurationManager.ThemeManager"/> events.
-	/// </summary>
-	public class ThemeManagerEventArgs : EventArgs {
-		/// <summary>
-		/// The name of the new active theme..
-		/// </summary>
-		public string NewTheme { get; set; } = string.Empty;
-
-		/// <summary>
-		/// Initializes a new instance of <see cref="ThemeManagerEventArgs"/>
-		/// </summary>
-		public ThemeManagerEventArgs (string newTheme)
-		{
-			NewTheme = newTheme;
-		}
-	}
-}

+ 15 - 1
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs

@@ -818,10 +818,24 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// 
+		/// A stack of keypresses to return when ReadKey is called.
 		/// </summary>
 		public static Stack<ConsoleKeyInfo> MockKeyPresses = new Stack<ConsoleKeyInfo> ();
 
+		/// <summary>
+		///  Helper to push a <see cref="Key"/> onto <see cref="MockKeyPresses"/>.
+		/// </summary>
+		/// <param name="key"></param>
+		public static void PushMockKeyPress (Key key)
+		{
+			MockKeyPresses.Push (new ConsoleKeyInfo (
+				(char)(key & ~Key.CtrlMask & ~Key.ShiftMask & ~Key.AltMask),
+				ConsoleKeyMapping.GetConsoleKeyFromKey (key),
+				key.HasFlag (Key.ShiftMask),
+				key.HasFlag (Key.AltMask),
+				key.HasFlag (Key.CtrlMask)));
+		}
+
 		//
 		// Summary:
 		//	Obtains the next character or function key pressed by the user. The pressed key

+ 2 - 0
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -220,6 +220,8 @@ namespace Terminal.Gui {
 
 		public override void Init (Action terminalResized)
 		{
+			FakeConsole.MockKeyPresses.Clear ();
+
 			TerminalResized = terminalResized;
 
 			cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH;

+ 4 - 4
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs

@@ -15,7 +15,7 @@ namespace Terminal.Gui {
 		AutoResetEvent waitForProbe = new AutoResetEvent (false);
 		ConsoleKeyInfo? keyResult = null;
 		MainLoop mainLoop;
-		Func<ConsoleKeyInfo> consoleKeyReaderFn = () => FakeConsole.ReadKey (true);
+		//Func<ConsoleKeyInfo> consoleKeyReaderFn = () => ;
 
 		/// <summary>
 		/// Invoked when a Key is pressed.
@@ -31,11 +31,11 @@ namespace Terminal.Gui {
 			// consoleDriver is not needed/used in FakeConsole
 		}
 
-		void WindowsKeyReader ()
+		void MockKeyReader ()
 		{
 			while (true) {
 				waitForProbe.WaitOne ();
-				keyResult = consoleKeyReaderFn ();
+				keyResult = FakeConsole.ReadKey (true);
 				keyReady.Set ();
 			}
 		}
@@ -43,7 +43,7 @@ namespace Terminal.Gui {
 		void IMainLoopDriver.Setup (MainLoop mainLoop)
 		{
 			this.mainLoop = mainLoop;
-			Thread readThread = new Thread (WindowsKeyReader);
+			Thread readThread = new Thread (MockKeyReader);
 			readThread.Start ();
 		}
 

+ 46 - 14
Terminal.Gui/Core/Application.cs

@@ -34,7 +34,7 @@ namespace Terminal.Gui {
 	/// // A simple Terminal.Gui app that creates a window with a frame and title with 
 	/// // 5 rows/columns of padding.
 	/// Application.Init();
-	/// var win = new Window ("Hello World - CTRL-Q to quit") {
+	/// var win = new Window ($"Example App ({Application.QuitKey} to quit)") {
 	///     X = 5,
 	///     Y = 5,
 	///     Width = Dim.Fill (5),
@@ -162,7 +162,7 @@ namespace Terminal.Gui {
 				if (alternateForwardKey != value) {
 					var oldKey = alternateForwardKey;
 					alternateForwardKey = value;
-					OnAlternateForwardKeyChanged (new KeyChangedEventArgs(oldKey,value));
+					OnAlternateForwardKeyChanged (new KeyChangedEventArgs (oldKey, value));
 				}
 			}
 		}
@@ -186,7 +186,7 @@ namespace Terminal.Gui {
 				if (alternateBackwardKey != value) {
 					var oldKey = alternateBackwardKey;
 					alternateBackwardKey = value;
-					OnAlternateBackwardKeyChanged (new KeyChangedEventArgs(oldKey,value));
+					OnAlternateBackwardKeyChanged (new KeyChangedEventArgs (oldKey, value));
 				}
 			}
 		}
@@ -210,7 +210,7 @@ namespace Terminal.Gui {
 				if (quitKey != value) {
 					var oldKey = quitKey;
 					quitKey = value;
-					OnQuitKeyChanged (new KeyChangedEventArgs(oldKey,value));
+					OnQuitKeyChanged (new KeyChangedEventArgs (oldKey, value));
 				}
 			}
 		}
@@ -720,6 +720,16 @@ namespace Terminal.Gui {
 		/// </summary>
 		public static View MouseGrabView => mouseGrabView;
 
+		/// <summary>
+		/// Event to be invoked when a view want grab the mouse which can be canceled.
+		/// </summary>
+		public static event EventHandler<GrabMouseEventArgs> GrabbingMouse;
+
+		/// <summary>
+		/// Event to be invoked when a view want ungrab the mouse which can be canceled.
+		/// </summary>
+		public static event EventHandler<GrabMouseEventArgs> UnGrabbingMouse;
+
 		/// <summary>
 		/// Event to be invoked when a view grab the mouse.
 		/// </summary>
@@ -739,9 +749,11 @@ namespace Terminal.Gui {
 		{
 			if (view == null)
 				return;
-			OnGrabbedMouse (view);
-			mouseGrabView = view;
-			Driver.UncookMouse ();
+			if (!OnGrabbingMouse (view)) {
+				OnGrabbedMouse (view);
+				mouseGrabView = view;
+				Driver.UncookMouse ();
+			}
 		}
 
 		/// <summary>
@@ -751,16 +763,36 @@ namespace Terminal.Gui {
 		{
 			if (mouseGrabView == null)
 				return;
-			OnUnGrabbedMouse (mouseGrabView);
-			mouseGrabView = null;
-			Driver.CookMouse ();
+			if (!OnUnGrabbingMouse (mouseGrabView)) {
+				OnUnGrabbedMouse (mouseGrabView);
+				mouseGrabView = null;
+				Driver.CookMouse ();
+			}
+		}
+
+		static bool OnGrabbingMouse (View view)
+		{
+			if (view == null)
+				return false;
+			var evArgs = new GrabMouseEventArgs (view);
+			GrabbingMouse?.Invoke (view, evArgs);
+			return evArgs.Cancel;
+		}
+
+		static bool OnUnGrabbingMouse (View view)
+		{
+			if (view == null)
+				return false;
+			var evArgs = new GrabMouseEventArgs (view);
+			UnGrabbingMouse?.Invoke (view, evArgs);
+			return evArgs.Cancel;
 		}
 
 		static void OnGrabbedMouse (View view)
 		{
 			if (view == null)
 				return;
-			GrabbedMouse?.Invoke (view, new ViewEventArgs(view));
+			GrabbedMouse?.Invoke (view, new ViewEventArgs (view));
 		}
 
 		static void OnUnGrabbedMouse (View view)
@@ -1029,7 +1061,7 @@ namespace Terminal.Gui {
 				Driver.Refresh ();
 			}
 
-			NotifyNewRunState?.Invoke (toplevel, new RunStateEventArgs(rs));
+			NotifyNewRunState?.Invoke (toplevel, new RunStateEventArgs (rs));
 			return rs;
 		}
 
@@ -1497,7 +1529,7 @@ namespace Terminal.Gui {
 		static void OnNotifyStopRunState (Toplevel top)
 		{
 			if (ExitRunLoopAfterFirstIteration) {
-				NotifyStopRunState?.Invoke (top, new ToplevelEventArgs(top));
+				NotifyStopRunState?.Invoke (top, new ToplevelEventArgs (top));
 			}
 		}
 
@@ -1516,7 +1548,7 @@ namespace Terminal.Gui {
 				t.SetRelativeLayout (full);
 				t.LayoutSubviews ();
 				t.PositionToplevels ();
-				t.OnResized (new SizeChangedEventArgs(full.Size));
+				t.OnResized (new SizeChangedEventArgs (full.Size));
 			}
 			Refresh ();
 		}

+ 21 - 1
Terminal.Gui/Core/ConsoleKeyMapping.cs

@@ -65,10 +65,30 @@ namespace Terminal.Gui {
 				}
 				return sCode;
 			}
-
+			
 			return null;
 		}
 
+		/// <summary>
+		/// Gets the <see cref="ConsoleKey"/> from the provided <see cref="Key"/>.
+		/// </summary>
+		/// <param name="key"></param>
+		/// <returns></returns>
+		public static ConsoleKey GetConsoleKeyFromKey (Key key)
+		{
+			ConsoleModifiers mod = new ConsoleModifiers ();
+			if (key.HasFlag (Key.ShiftMask)) {
+				mod |= ConsoleModifiers.Shift;
+			}
+			if (key.HasFlag (Key.AltMask)) {
+				mod |= ConsoleModifiers.Alt;
+			}
+			if (key.HasFlag (Key.CtrlMask)) {
+				mod |= ConsoleModifiers.Control;
+			}
+			return (ConsoleKey)ConsoleKeyMapping.GetConsoleKeyFromKey ((uint)(key & ~Key.CtrlMask & ~Key.ShiftMask & ~Key.AltMask), mod, out _, out _);
+		}
+
 		/// <summary>
 		/// Get the <see cref="ConsoleKey"/> from a <see cref="Key"/>.
 		/// </summary>

+ 0 - 25
Terminal.Gui/Core/FocusEventArgs.cs

@@ -1,25 +0,0 @@
-using System;
-
-namespace Terminal.Gui {
-
-	/// <summary>
-	/// Defines the event arguments for <see cref="View.SetFocus(View)"/>
-	/// </summary>
-	public class FocusEventArgs : EventArgs {
-		/// <summary>
-		/// Constructs.
-		/// </summary>
-		/// <param name="view">The view that gets or loses focus.</param>
-		public FocusEventArgs (View view) { View = view; }
-		/// <summary>
-		/// Indicates if the current focus event has already been processed and the driver should stop notifying any other event subscriber.
-		/// Its important to set this value to true specially when updating any View's layout from inside the subscriber method.
-		/// </summary>
-		public bool Handled { get; set; }
-		/// <summary>
-		/// Indicates the current view that gets or loses focus.
-		/// </summary>
-		public View View { get; set; }
-	}
-
-}

+ 0 - 1
Terminal.Gui/Core/KeyEventEventArgs.cs

@@ -21,5 +21,4 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool Handled { get; set; } = false;
 	}
-
 }

+ 0 - 36
Terminal.Gui/Core/MenuOpenedEventArgs.cs

@@ -1,36 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Terminal.Gui {
-
-	/// <summary>
-	/// Defines arguments for the <see cref="MenuBar.MenuOpened"/> event
-	/// </summary>
-	public class MenuOpenedEventArgs : EventArgs{
-
-		/// <summary>
-		/// Creates a new instance of the <see cref="MenuOpenedEventArgs"/> class
-		/// </summary>
-		/// <param name="parent"></param>
-		/// <param name="menuItem"></param>
-		public MenuOpenedEventArgs (MenuBarItem parent, MenuItem menuItem)
-		{
-			Parent = parent;
-			MenuItem = menuItem;
-		}
-
-		/// <summary>
-		/// The parent of <see cref="MenuItem"/>.  Will be null if menu opening
-		/// is the root (see <see cref="MenuBarItem.IsTopLevel"/>).
-		/// </summary>
-		public MenuBarItem Parent { get; }
-
-		/// <summary>
-		/// Gets the <see cref="MenuItem"/> being opened.
-		/// </summary>
-		public MenuItem MenuItem { get; }
-	}
-}

+ 31 - 0
Terminal.Gui/Core/MouseEventEventArgs.cs

@@ -0,0 +1,31 @@
+using System;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Specifies the event arguments for <see cref="Terminal.Gui.MouseEvent"/>. This is a higher-level construct
+	/// than the wrapped <see cref="MouseEvent"/> class and is used for the events defined on <see cref="View"/>
+	/// and subclasses of View (e.g. <see cref="View.MouseEnter"/> and <see cref="View.MouseClick"/>).
+	/// </summary>
+	public class MouseEventEventArgs : EventArgs {
+		/// <summary>
+		/// Constructs.
+		/// </summary>
+		/// <param name="me">The mouse event.</param>
+		public MouseEventEventArgs (MouseEvent me) => MouseEvent = me;
+		/// <summary>
+		/// The <see cref="Terminal.Gui.MouseEvent"/> for the event.
+		/// </summary>
+		public MouseEvent MouseEvent { get; set; }
+
+		/// <summary>
+		/// Indicates if the current mouse event has already been processed and the driver should stop notifying any other event subscriber.
+		/// Its important to set this value to true specially when updating any View's layout from inside the subscriber method.
+		/// </summary>
+		/// <remarks>This property forwards to the <see cref="MouseEvent.Handled"/> property and is provided as a convenience and for
+		/// backwards compatibility</remarks>
+		public bool Handled {
+			get => MouseEvent.Handled;
+			set => MouseEvent.Handled = value;
+		}
+	}
+}

+ 1 - 5
Terminal.Gui/Core/MouseFlagsChangedEventArgs.cs

@@ -1,14 +1,10 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Terminal.Gui {
 	/// <summary>
 	/// Args for events that describe a change in <see cref="MouseFlags"/>
 	/// </summary>
-	public class MouseFlagsChangedEventArgs : EventArgs{
+	public class MouseFlagsChangedEventArgs : EventArgs {
 
 		/// <summary>
 		/// Creates a new instance of the <see cref="MouseFlagsChangedEventArgs"/> class.

+ 1 - 5
Terminal.Gui/Core/PointEventArgs.cs

@@ -1,14 +1,10 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Terminal.Gui {
 	/// <summary>
 	/// Event args for events which relate to a single <see cref="Point"/>
 	/// </summary>
-	public class PointEventArgs : EventArgs{
+	public class PointEventArgs : EventArgs {
 
 		/// <summary>
 		/// Creates a new instance of the <see cref="PointEventArgs"/> class

+ 1 - 1
Terminal.Gui/Core/SizeChangedEventArgs.cs

@@ -3,7 +3,7 @@
 namespace Terminal.Gui {
 
 	/// <summary>
-	/// Args for events about about Size (e.g. Resized)
+	/// Args for events about Size (e.g. Resized)
 	/// </summary>
 	public class SizeChangedEventArgs : EventArgs {
 

+ 0 - 1
Terminal.Gui/Core/TimeoutEventArgs.cs

@@ -30,4 +30,3 @@ namespace Terminal.Gui {
 		}
 	}
 }
-

+ 0 - 4
Terminal.Gui/Core/ToggleEventArgs.cs

@@ -1,8 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Terminal.Gui {
 	/// <summary>

+ 20 - 3
Terminal.Gui/Core/Toplevel.cs

@@ -120,7 +120,7 @@ namespace Terminal.Gui {
 
 		internal virtual void OnChildUnloaded (Toplevel top)
 		{
-			ChildUnloaded?.Invoke (this, new ToplevelEventArgs(top));
+			ChildUnloaded?.Invoke (this, new ToplevelEventArgs (top));
 		}
 
 		internal virtual void OnChildLoaded (Toplevel top)
@@ -159,7 +159,7 @@ namespace Terminal.Gui {
 
 		internal virtual void OnActivate (Toplevel deactivated)
 		{
-			Activate?.Invoke (this, new ToplevelEventArgs(deactivated));
+			Activate?.Invoke (this, new ToplevelEventArgs (deactivated));
 		}
 
 		/// <summary>
@@ -221,6 +221,9 @@ namespace Terminal.Gui {
 		{
 			ColorScheme = Colors.TopLevel;
 
+			Application.GrabbingMouse += Application_GrabbingMouse;
+			Application.UnGrabbingMouse += Application_UnGrabbingMouse;
+
 			// Things this view knows how to do
 			AddCommand (Command.QuitToplevel, () => { QuitToplevel (); return true; });
 			AddCommand (Command.Suspend, () => { Driver.Suspend (); ; return true; });
@@ -256,6 +259,20 @@ namespace Terminal.Gui {
 			AddKeyBinding (Key.L | Key.CtrlMask, Command.Refresh);
 		}
 
+		private void Application_UnGrabbingMouse (object sender, GrabMouseEventArgs e)
+		{
+			if (Application.MouseGrabView == this && dragPosition.HasValue) {
+				e.Cancel = true;
+			}
+		}
+
+		private void Application_GrabbingMouse (object sender, GrabMouseEventArgs e)
+		{
+			if (Application.MouseGrabView == this && dragPosition.HasValue) {
+				e.Cancel = true;
+			}
+		}
+
 		/// <summary>
 		/// Invoked when the <see cref="Application.AlternateForwardKey"/> is changed.
 		/// </summary>
@@ -824,8 +841,8 @@ namespace Terminal.Gui {
 			}
 
 			if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && dragPosition.HasValue) {
-				Application.UngrabMouse ();
 				dragPosition = null;
+				Application.UngrabMouse ();
 			}
 
 			//System.Diagnostics.Debug.WriteLine ($"dragPosition after: {dragPosition.HasValue}");

+ 0 - 26
Terminal.Gui/Core/ToplevelClosingEventArgs.cs

@@ -1,26 +0,0 @@
-using System;
-
-namespace Terminal.Gui {
-	/// <summary>
-	/// <see cref="EventArgs"/> implementation for the <see cref="Toplevel.Closing"/> event.
-	/// </summary>
-	public class ToplevelClosingEventArgs : EventArgs {
-		/// <summary>
-		/// The toplevel requesting stop.
-		/// </summary>
-		public View RequestingTop { get; }
-		/// <summary>
-		/// Provides an event cancellation option.
-		/// </summary>
-		public bool Cancel { get; set; }
-
-		/// <summary>
-		/// Initializes the event arguments with the requesting toplevel.
-		/// </summary>
-		/// <param name="requestingTop">The <see cref="RequestingTop"/>.</param>
-		public ToplevelClosingEventArgs (Toplevel requestingTop)
-		{
-			RequestingTop = requestingTop;
-		}
-	}
-}

+ 23 - 0
Terminal.Gui/Core/ToplevelEventArgs.cs

@@ -25,4 +25,27 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public Toplevel Toplevel { get; }
 	}
+
+	/// <summary>
+	/// <see cref="EventArgs"/> implementation for the <see cref="Toplevel.Closing"/> event.
+	/// </summary>
+	public class ToplevelClosingEventArgs : EventArgs {
+		/// <summary>
+		/// The toplevel requesting stop.
+		/// </summary>
+		public View RequestingTop { get; }
+		/// <summary>
+		/// Provides an event cancellation option.
+		/// </summary>
+		public bool Cancel { get; set; }
+
+		/// <summary>
+		/// Initializes the event arguments with the requesting toplevel.
+		/// </summary>
+		/// <param name="requestingTop">The <see cref="RequestingTop"/>.</param>
+		public ToplevelClosingEventArgs (Toplevel requestingTop)
+		{
+			RequestingTop = requestingTop;
+		}
+	}
 }

+ 11 - 49
Terminal.Gui/Core/View.cs

@@ -139,17 +139,17 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Event fired when the view receives the mouse event for the first time.
 		/// </summary>
-		public event EventHandler<MouseEventArgs> MouseEnter;
+		public event EventHandler<MouseEventEventArgs> MouseEnter;
 
 		/// <summary>
 		/// Event fired when the view receives a mouse event for the last time.
 		/// </summary>
-		public event EventHandler<MouseEventArgs> MouseLeave;
+		public event EventHandler<MouseEventEventArgs> MouseLeave;
 
 		/// <summary>
 		/// Event fired when a mouse event is generated.
 		/// </summary>
-		public event EventHandler<MouseEventArgs> MouseClick;
+		public event EventHandler<MouseEventEventArgs> MouseClick;
 
 		/// <summary>
 		/// Event fired when the <see cref="CanFocus"/> value is being changed.
@@ -827,7 +827,7 @@ namespace Terminal.Gui {
 
 		void TextFormatter_HotKeyChanged (object sender, KeyChangedEventArgs e)
 		{
-			HotKeyChanged?.Invoke (this,e);
+			HotKeyChanged?.Invoke (this, e);
 		}
 
 		/// <summary>
@@ -942,7 +942,7 @@ namespace Terminal.Gui {
 			}
 			SetNeedsLayout ();
 			SetNeedsDisplay ();
-			OnAdded (new SuperViewChangedEventArgs (this,view));
+			OnAdded (new SuperViewChangedEventArgs (this, view));
 			if (IsInitialized) {
 				view.BeginInit ();
 				view.EndInit ();
@@ -1344,7 +1344,7 @@ namespace Terminal.Gui {
 			view.width ??= view.frame.Width;
 			view.height ??= view.frame.Height;
 
-			view.Added?.Invoke (this,e);
+			view.Added?.Invoke (this, e);
 		}
 
 		/// <summary>
@@ -1593,7 +1593,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public virtual void OnDrawContent (Rect viewport)
 		{
-			DrawContent?.Invoke (this, new DrawEventArgs(viewport));
+			DrawContent?.Invoke (this, new DrawEventArgs (viewport));
 		}
 
 		/// <summary>
@@ -2239,16 +2239,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-		/// <summary>
-		/// Event arguments for the <see cref="LayoutComplete"/> event.
-		/// </summary>
-		public class LayoutEventArgs : EventArgs {
-			/// <summary>
-			/// The view-relative bounds of the <see cref="View"/> before it was laid out.
-			/// </summary>
-			public Rect OldBounds { get; set; }
-		}
-
 		/// <summary>
 		/// Fired after the View's <see cref="LayoutSubviews"/> method has completed. 
 		/// </summary>
@@ -2795,34 +2785,6 @@ namespace Terminal.Gui {
 			    frame.Size.Height + GetHotKeySpecifierLength (false));
 		}
 
-		/// <summary>
-		/// Specifies the event arguments for <see cref="MouseEvent"/>. This is a higher-level construct
-		/// than the wrapped <see cref="MouseEvent"/> class and is used for the events defined on <see cref="View"/>
-		/// and subclasses of View (e.g. <see cref="View.MouseEnter"/> and <see cref="View.MouseClick"/>).
-		/// </summary>
-		public class MouseEventArgs : EventArgs {
-			/// <summary>
-			/// Constructs.
-			/// </summary>
-			/// <param name="me"></param>
-			public MouseEventArgs (MouseEvent me) => MouseEvent = me;
-			/// <summary>
-			/// The <see cref="MouseEvent"/> for the event.
-			/// </summary>
-			public MouseEvent MouseEvent { get; set; }
-
-			/// <summary>
-			/// Indicates if the current mouse event has already been processed and the driver should stop notifying any other event subscriber.
-			/// Its important to set this value to true specially when updating any View's layout from inside the subscriber method.
-			/// </summary>
-			/// <remarks>This property forwards to the <see cref="MouseEvent.Handled"/> property and is provided as a convenience and for
-			/// backwards compatibility</remarks>
-			public bool Handled {
-				get => MouseEvent.Handled;
-				set => MouseEvent.Handled = value;
-			}
-		}
-
 		/// <inheritdoc/>
 		public override bool OnMouseEnter (MouseEvent mouseEvent)
 		{
@@ -2834,7 +2796,7 @@ namespace Terminal.Gui {
 				return false;
 			}
 
-			var args = new MouseEventArgs (mouseEvent);
+			var args = new MouseEventEventArgs (mouseEvent);
 			MouseEnter?.Invoke (this, args);
 
 			return args.Handled || base.OnMouseEnter (mouseEvent);
@@ -2851,7 +2813,7 @@ namespace Terminal.Gui {
 				return false;
 			}
 
-			var args = new MouseEventArgs (mouseEvent);
+			var args = new MouseEventEventArgs (mouseEvent);
 			MouseLeave?.Invoke (this, args);
 
 			return args.Handled || base.OnMouseLeave (mouseEvent);
@@ -2872,7 +2834,7 @@ namespace Terminal.Gui {
 				return false;
 			}
 
-			var args = new MouseEventArgs (mouseEvent);
+			var args = new MouseEventEventArgs (mouseEvent);
 			if (OnMouseClick (args))
 				return true;
 			if (MouseEvent (mouseEvent))
@@ -2892,7 +2854,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Invokes the MouseClick event.
 		/// </summary>
-		protected bool OnMouseClick (MouseEventArgs args)
+		protected bool OnMouseClick (MouseEventEventArgs args)
 		{
 			if (!Enabled) {
 				return true;

+ 53 - 6
Terminal.Gui/Core/ViewEventArgs.cs

@@ -1,17 +1,13 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Terminal.Gui {
 	/// <summary>
 	/// Args for events that relate to specific <see cref="View"/>
 	/// </summary>
-	public class ViewEventArgs :EventArgs{
+	public class ViewEventArgs : EventArgs {
 
 		/// <summary>
-		/// Creates a new instance of the <see cref="ViewEventArgs"/> class.
+		/// Creates a new instance of the <see cref="Terminal.Gui.View"/> class.
 		/// </summary>
 		/// <param name="view"></param>
 		public ViewEventArgs (View view)
@@ -30,4 +26,55 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public View View { get; }
 	}
+
+	/// <summary>
+	/// Event arguments for the <see cref="View.LayoutComplete"/> event.
+	/// </summary>
+	public class LayoutEventArgs : EventArgs {
+		/// <summary>
+		/// The view-relative bounds of the <see cref="View"/> before it was laid out.
+		/// </summary>
+		public Rect OldBounds { get; set; }
+	}
+
+	/// <summary>
+	/// Event args for draw events
+	/// </summary>
+	public class DrawEventArgs : EventArgs {
+
+		/// <summary>
+		/// Creates a new instance of the <see cref="DrawEventArgs"/> class.
+		/// </summary>
+		/// <param name="rect">Gets the view-relative rectangle describing the currently visible viewport into the <see cref="View"/>.</param>
+		public DrawEventArgs (Rect rect)
+		{
+			Rect = rect;
+		}
+
+		/// <summary>
+		/// Gets the view-relative rectangle describing the currently visible viewport into the <see cref="View"/>.
+		/// </summary>
+		public Rect Rect { get; }
+	}
+
+	/// <summary>
+	/// Defines the event arguments for <see cref="View.SetFocus(View)"/>
+	/// </summary>
+	public class FocusEventArgs : EventArgs {
+		/// <summary>
+		/// Constructs.
+		/// </summary>
+		/// <param name="view">The view that gets or loses focus.</param>
+		public FocusEventArgs (View view) { View = view; }
+		/// <summary>
+		/// Indicates if the current focus event has already been processed and the driver should stop notifying any other event subscriber.
+		/// Its important to set this value to true specially when updating any View's layout from inside the subscriber method.
+		/// </summary>
+		public bool Handled { get; set; }
+		/// <summary>
+		/// Indicates the current view that gets or loses focus.
+		/// </summary>
+		public View View { get; set; }
+	}
+
 }

+ 0 - 28
Terminal.Gui/Views/DrawEventArgs.cs

@@ -1,28 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Terminal.Gui {
-
-	/// <summary>
-	/// Event args for draw events
-	/// </summary>
-	public class DrawEventArgs : EventArgs{
-
-		/// <summary>
-		/// Creates a new instance of the <see cref="DrawEventArgs"/> class.
-		/// </summary>
-		/// <param name="rect">Gets the view-relative rectangle describing the currently visible viewport into the <see cref="View"/>.</param>
-		public DrawEventArgs (Rect rect)
-		{
-			Rect = rect;
-		}
-
-		/// <summary>
-		/// Gets the view-relative rectangle describing the currently visible viewport into the <see cref="View"/>.
-		/// </summary>
-		public Rect Rect { get; }
-	}
-}

+ 0 - 37
Terminal.Gui/Views/HexViewEditEventArgs.cs

@@ -1,37 +0,0 @@
-//
-// HexView.cs: A hexadecimal viewer
-//
-// TODO:
-// - Support searching and highlighting of the search result
-// - Bug showing the last line
-// 
-using System;
-
-namespace Terminal.Gui {
-	/// <summary>
-	/// Defines the event arguments for <see cref="HexView.Edited"/> event.
-	/// </summary>
-	public class HexViewEditEventArgs : EventArgs {
-
-		/// <summary>
-		/// Creates a new instance of the <see cref="HexViewEditEventArgs"/> class.
-		/// </summary>
-		/// <param name="position"></param>
-		/// <param name="newValue"></param>
-		public HexViewEditEventArgs (long position, byte newValue)
-		{
-			Position = position;
-			NewValue = newValue;
-		}
-
-		/// <summary>
-		/// Gets the location of the edit.
-		/// </summary>
-		public long Position { get; }
-
-		/// <summary>
-		/// Gets the new value for that <see cref="Position"/>.
-		/// </summary>
-		public byte NewValue { get; }
-	}
-}

+ 27 - 0
Terminal.Gui/Views/HexViewEventArgs.cs

@@ -40,4 +40,31 @@ namespace Terminal.Gui {
 			BytesPerLine = lineLength;
 		}
 	}
+
+	/// <summary>
+	/// Defines the event arguments for <see cref="HexView.Edited"/> event.
+	/// </summary>
+	public class HexViewEditEventArgs : EventArgs {
+
+		/// <summary>
+		/// Creates a new instance of the <see cref="HexViewEditEventArgs"/> class.
+		/// </summary>
+		/// <param name="position"></param>
+		/// <param name="newValue"></param>
+		public HexViewEditEventArgs (long position, byte newValue)
+		{
+			Position = position;
+			NewValue = newValue;
+		}
+
+		/// <summary>
+		/// Gets the location of the edit.
+		/// </summary>
+		public long Position { get; }
+
+		/// <summary>
+		/// Gets the new value for that <see cref="Position"/>.
+		/// </summary>
+		public byte NewValue { get; }
+	}
 }

+ 1 - 1
Terminal.Gui/Views/HistoryTextItem.cs

@@ -5,7 +5,7 @@ using Rune = System.Rune;
 
 namespace Terminal.Gui {
 	partial class HistoryText {
-		public class HistoryTextItem : EventArgs{
+		public class HistoryTextItem : EventArgs {
 			public List<List<Rune>> Lines;
 			public Point CursorPosition;
 			public LineStatus LineStatus;

+ 1 - 1
Terminal.Gui/Views/Label.cs

@@ -92,7 +92,7 @@ namespace Terminal.Gui {
 		/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
 		public override bool OnMouseEvent (MouseEvent mouseEvent)
 		{
-			MouseEventArgs args = new MouseEventArgs (mouseEvent);
+			MouseEventEventArgs args = new MouseEventEventArgs (mouseEvent);
 			if (OnMouseClick (args))
 				return true;
 			if (MouseEvent (mouseEvent))

+ 24 - 0
Terminal.Gui/Views/ListViewItemEventArgs.cs → Terminal.Gui/Views/ListViewEventArgs.cs

@@ -25,4 +25,28 @@ namespace Terminal.Gui {
 			Value = value;
 		}
 	}
+
+	/// <summary>
+	/// <see cref="EventArgs"/> used by the <see cref="ListView.RowRender"/> event.
+	/// </summary>
+	public class ListViewRowEventArgs : EventArgs {
+		/// <summary>
+		/// The current row being rendered.
+		/// </summary>
+		public int Row { get; }
+		/// <summary>
+		/// The <see cref="Attribute"/> used by current row or
+		/// null to maintain the current attribute.
+		/// </summary>
+		public Attribute? RowAttribute { get; set; }
+
+		/// <summary>
+		/// Initializes with the current row.
+		/// </summary>
+		/// <param name="row"></param>
+		public ListViewRowEventArgs (int row)
+		{
+			Row = row;
+		}
+	}
 }

+ 0 - 27
Terminal.Gui/Views/ListViewRowEventArgs.cs

@@ -1,27 +0,0 @@
-using System;
-
-namespace Terminal.Gui {
-	/// <summary>
-	/// <see cref="EventArgs"/> used by the <see cref="ListView.RowRender"/> event.
-	/// </summary>
-	public class ListViewRowEventArgs : EventArgs {
-		/// <summary>
-		/// The current row being rendered.
-		/// </summary>
-		public int Row { get; }
-		/// <summary>
-		/// The <see cref="Attribute"/> used by current row or
-		/// null to maintain the current attribute.
-		/// </summary>
-		public Attribute? RowAttribute { get; set; }
-
-		/// <summary>
-		/// Initializes with the current row.
-		/// </summary>
-		/// <param name="row"></param>
-		public ListViewRowEventArgs (int row)
-		{
-			Row = row;
-		}
-	}
-}

+ 16 - 5
Terminal.Gui/Views/Menu.cs

@@ -808,7 +808,7 @@ namespace Terminal.Gui {
 				}
 			}
 
-			host?.openMenu.SetNeedsDisplay ();
+			host?.openMenu?.SetNeedsDisplay ();
 			host.SetNeedsDisplay ();
 		}
 
@@ -1025,6 +1025,8 @@ namespace Terminal.Gui {
 			WantMousePositionReports = true;
 			IsMenuOpen = false;
 
+			Added += MenuBar_Added;
+
 			// Things this view knows how to do
 			AddCommand (Command.Left, () => { MoveLeft (); return true; });
 			AddCommand (Command.Right, () => { MoveRight (); return true; });
@@ -1040,6 +1042,14 @@ namespace Terminal.Gui {
 			AddKeyBinding (Key.Enter, Command.Accept);
 		}
 
+		bool _initialCanFocus;
+
+		private void MenuBar_Added (object sender, SuperViewChangedEventArgs e)
+		{
+			_initialCanFocus = CanFocus;
+			Added -= MenuBar_Added;
+		}
+
 		bool openedByAltKey;
 
 		bool isCleaning;
@@ -1049,9 +1059,8 @@ namespace Terminal.Gui {
 		{
 			if ((!(view is MenuBar) && !(view is Menu) || !(view is MenuBar) && !(view is Menu) && openMenu != null) && !isCleaning && !reopen) {
 				CleanUp ();
-				return true;
 			}
-			return false;
+			return base.OnLeave (view);
 		}
 
 		///<inheritdoc/>
@@ -1112,7 +1121,7 @@ namespace Terminal.Gui {
 			openedByAltKey = false;
 			IsMenuOpen = false;
 			selected = -1;
-			CanFocus = false;
+			CanFocus = _initialCanFocus;
 			if (lastFocused != null) {
 				lastFocused.SetFocus ();
 			}
@@ -1267,7 +1276,7 @@ namespace Terminal.Gui {
 				parent = openMenu.barItems;
 				mi = parent.Children [openMenu.current];
 			}
-			MenuOpened?.Invoke (this, new MenuOpenedEventArgs(parent, mi));
+			MenuOpened?.Invoke (this, new MenuOpenedEventArgs (parent, mi));
 		}
 
 		/// <summary>
@@ -1513,6 +1522,8 @@ namespace Terminal.Gui {
 						selected = -1;
 					}
 					LastFocused.SetFocus ();
+				} else if (openSubMenu == null || openSubMenu.Count == 0) {
+					CloseAllMenus ();
 				} else {
 					SetFocus ();
 					PositionCursor ();

+ 0 - 42
Terminal.Gui/Views/MenuClosingEventArgs.cs

@@ -1,42 +0,0 @@
-using System;
-
-namespace Terminal.Gui {
-	/// <summary>
-	/// An <see cref="EventArgs"/> which allows passing a cancelable menu closing event.
-	/// </summary>
-	public class MenuClosingEventArgs : EventArgs {
-		/// <summary>
-		/// The current <see cref="MenuBarItem"/> parent.
-		/// </summary>
-		public MenuBarItem CurrentMenu { get; }
-
-		/// <summary>
-		/// Indicates whether the current menu will reopen.
-		/// </summary>
-		public bool Reopen { get; }
-
-		/// <summary>
-		/// Indicates whether the current menu is a sub-menu.
-		/// </summary>
-		public bool IsSubMenu { get; }
-
-		/// <summary>
-		/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
-		/// event handler, the event will be canceled. 
-		/// </summary>
-		public bool Cancel { get; set; }
-
-		/// <summary>
-		/// Initializes a new instance of <see cref="MenuClosingEventArgs"/>.
-		/// </summary>
-		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
-		/// <param name="reopen">Whether the current menu will reopen.</param>
-		/// <param name="isSubMenu">Indicates whether it is a sub-menu.</param>
-		public MenuClosingEventArgs (MenuBarItem currentMenu, bool reopen, bool isSubMenu)
-		{
-			CurrentMenu = currentMenu;
-			Reopen = reopen;
-			IsSubMenu = isSubMenu;
-		}
-	}
-}

+ 98 - 0
Terminal.Gui/Views/MenuEventArgs.cs

@@ -0,0 +1,98 @@
+using System;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// An <see cref="EventArgs"/> which allows passing a cancelable menu opening event or replacing with a new <see cref="MenuBarItem"/>.
+	/// </summary>
+	public class MenuOpeningEventArgs : EventArgs {
+		/// <summary>
+		/// The current <see cref="MenuBarItem"/> parent.
+		/// </summary>
+		public MenuBarItem CurrentMenu { get; }
+
+		/// <summary>
+		/// The new <see cref="MenuBarItem"/> to be replaced.
+		/// </summary>
+		public MenuBarItem NewMenuBarItem { get; set; }
+		/// <summary>
+		/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
+		/// event handler, the event will be canceled. 
+		/// </summary>
+		public bool Cancel { get; set; }
+
+		/// <summary>
+		/// Initializes a new instance of <see cref="MenuOpeningEventArgs"/>.
+		/// </summary>
+		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
+		public MenuOpeningEventArgs (MenuBarItem currentMenu)
+		{
+			CurrentMenu = currentMenu;
+		}
+	}
+
+	/// <summary>
+	/// Defines arguments for the <see cref="MenuBar.MenuOpened"/> event
+	/// </summary>
+	public class MenuOpenedEventArgs : EventArgs {
+		/// <summary>
+		/// Creates a new instance of the <see cref="MenuOpenedEventArgs"/> class
+		/// </summary>
+		/// <param name="parent"></param>
+		/// <param name="menuItem"></param>
+		public MenuOpenedEventArgs (MenuBarItem parent, MenuItem menuItem)
+		{
+			Parent = parent;
+			MenuItem = menuItem;
+		}
+
+		/// <summary>
+		/// The parent of <see cref="MenuItem"/>.  Will be null if menu opening
+		/// is the root (see <see cref="MenuBarItem.IsTopLevel"/>).
+		/// </summary>
+		public MenuBarItem Parent { get; }
+
+		/// <summary>
+		/// Gets the <see cref="MenuItem"/> being opened.
+		/// </summary>
+		public MenuItem MenuItem { get; }
+	}
+
+	/// <summary>
+	/// An <see cref="EventArgs"/> which allows passing a cancelable menu closing event.
+	/// </summary>
+	public class MenuClosingEventArgs : EventArgs {
+		/// <summary>
+		/// The current <see cref="MenuBarItem"/> parent.
+		/// </summary>
+		public MenuBarItem CurrentMenu { get; }
+
+		/// <summary>
+		/// Indicates whether the current menu will reopen.
+		/// </summary>
+		public bool Reopen { get; }
+
+		/// <summary>
+		/// Indicates whether the current menu is a sub-menu.
+		/// </summary>
+		public bool IsSubMenu { get; }
+
+		/// <summary>
+		/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
+		/// event handler, the event will be canceled. 
+		/// </summary>
+		public bool Cancel { get; set; }
+
+		/// <summary>
+		/// Initializes a new instance of <see cref="MenuClosingEventArgs"/>.
+		/// </summary>
+		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
+		/// <param name="reopen">Whether the current menu will reopen.</param>
+		/// <param name="isSubMenu">Indicates whether it is a sub-menu.</param>
+		public MenuClosingEventArgs (MenuBarItem currentMenu, bool reopen, bool isSubMenu)
+		{
+			CurrentMenu = currentMenu;
+			Reopen = reopen;
+			IsSubMenu = isSubMenu;
+		}
+	}
+}

+ 0 - 32
Terminal.Gui/Views/MenuOpeningEventArgs.cs

@@ -1,32 +0,0 @@
-using System;
-
-namespace Terminal.Gui {
-	/// <summary>
-	/// An <see cref="EventArgs"/> which allows passing a cancelable menu opening event or replacing with a new <see cref="MenuBarItem"/>.
-	/// </summary>
-	public class MenuOpeningEventArgs : EventArgs {
-		/// <summary>
-		/// The current <see cref="MenuBarItem"/> parent.
-		/// </summary>
-		public MenuBarItem CurrentMenu { get; }
-
-		/// <summary>
-		/// The new <see cref="MenuBarItem"/> to be replaced.
-		/// </summary>
-		public MenuBarItem NewMenuBarItem { get; set; }
-		/// <summary>
-		/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
-		/// event handler, the event will be canceled. 
-		/// </summary>
-		public bool Cancel { get; set; }
-
-		/// <summary>
-		/// Initializes a new instance of <see cref="MenuOpeningEventArgs"/>.
-		/// </summary>
-		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
-		public MenuOpeningEventArgs (MenuBarItem currentMenu)
-		{
-			CurrentMenu = currentMenu;
-		}
-	}
-}

+ 1 - 1
Terminal.Gui/Views/ScrollBarView.cs

@@ -149,7 +149,7 @@ namespace Terminal.Gui {
 		//	}
 		//}
 
-		void ContentBottomRightCorner_MouseClick (object sender, MouseEventArgs me)
+		void ContentBottomRightCorner_MouseClick (object sender, MouseEventEventArgs me)
 		{
 			if (me.MouseEvent.Flags == MouseFlags.WheeledDown || me.MouseEvent.Flags == MouseFlags.WheeledUp
 				|| me.MouseEvent.Flags == MouseFlags.WheeledRight || me.MouseEvent.Flags == MouseFlags.WheeledLeft) {

+ 2 - 2
Terminal.Gui/Views/ScrollView.cs

@@ -237,14 +237,14 @@ namespace Terminal.Gui {
 			SetNeedsLayout ();
 		}
 
-		void View_MouseLeave (object sender, MouseEventArgs e)
+		void View_MouseLeave (object sender, MouseEventEventArgs e)
 		{
 			if (Application.MouseGrabView != null && Application.MouseGrabView != vertical && Application.MouseGrabView != horizontal) {
 				Application.UngrabMouse ();
 			}
 		}
 
-		void View_MouseEnter (object sender, MouseEventArgs e)
+		void View_MouseEnter (object sender, MouseEventEventArgs e)
 		{
 			Application.GrabMouse (this);
 		}

+ 0 - 1
Terminal.Gui/Views/TabMouseEventArgs.cs

@@ -32,5 +32,4 @@ namespace Terminal.Gui {
 			MouseEvent = mouseEvent;
 		}
 	}
-
 }

+ 0 - 38
Terminal.Gui/Windows/StepChangeEventArgs.cs

@@ -1,38 +0,0 @@
-using System;
-
-namespace Terminal.Gui {
-
-	public partial class Wizard {
-		/// <summary>
-		/// <see cref="EventArgs"/> for <see cref="WizardStep"/> events.
-		/// </summary>
-		public class StepChangeEventArgs : EventArgs {
-			/// <summary>
-			/// The current (or previous) <see cref="WizardStep"/>.
-			/// </summary>
-			public WizardStep OldStep { get; }
-
-			/// <summary>
-			/// The <see cref="WizardStep"/> the <see cref="Wizard"/> is changing to or has changed to.
-			/// </summary>
-			public WizardStep NewStep { get; }
-
-			/// <summary>
-			/// Event handlers can set to true before returning to cancel the step transition.
-			/// </summary>
-			public bool Cancel { get; set; }
-
-			/// <summary>
-			/// Initializes a new instance of <see cref="StepChangeEventArgs"/>
-			/// </summary>
-			/// <param name="oldStep">The current <see cref="WizardStep"/>.</param>
-			/// <param name="newStep">The new <see cref="WizardStep"/>.</param>
-			public StepChangeEventArgs (WizardStep oldStep, WizardStep newStep)
-			{
-				OldStep = oldStep;
-				NewStep = newStep;
-				Cancel = false;
-			}
-		}
-	}
-}

+ 1 - 1
Terminal.Gui/Windows/Wizard.cs

@@ -54,7 +54,7 @@ namespace Terminal.Gui {
 	/// Application.Shutdown ();
 	/// </code>
 	/// </example>
-	public partial class Wizard : Dialog {
+	public class Wizard : Dialog {
 		/// <summary>
 		/// Represents a basic step that is displayed in a <see cref="Wizard"/>. The <see cref="WizardStep"/> view is divided horizontally in two. On the left is the
 		/// content view where <see cref="View"/>s can be added,  On the right is the help for the step.

+ 0 - 24
Terminal.Gui/Windows/WizardButtonEventArgs.cs

@@ -1,24 +0,0 @@
-using System;
-
-namespace Terminal.Gui {
-
-	public partial class Wizard {
-		/// <summary>	
-		/// <see cref="EventArgs"/> for <see cref="WizardStep"/> transition events.
-		/// </summary>
-		public class WizardButtonEventArgs : EventArgs {
-			/// <summary>
-			/// Set to true to cancel the transition to the next step.
-			/// </summary>
-			public bool Cancel { get; set; }
-
-			/// <summary>
-			/// Initializes a new instance of <see cref="WizardButtonEventArgs"/>
-			/// </summary>
-			public WizardButtonEventArgs ()
-			{
-				Cancel = false;
-			}
-		}
-	}
-}

+ 54 - 0
Terminal.Gui/Windows/WizardEventArgs.cs

@@ -0,0 +1,54 @@
+using System;
+using static Terminal.Gui.Wizard;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// <see cref="EventArgs"/> for <see cref="WizardStep"/> transition events.
+	/// </summary>
+	public class WizardButtonEventArgs : EventArgs {
+		/// <summary>
+		/// Set to true to cancel the transition to the next step.
+		/// </summary>
+		public bool Cancel { get; set; }
+
+		/// <summary>
+		/// Initializes a new instance of <see cref="WizardButtonEventArgs"/>
+		/// </summary>
+		public WizardButtonEventArgs ()
+		{
+			Cancel = false;
+		}
+	}
+
+	/// <summary>
+	/// <see cref="EventArgs"/> for <see cref="WizardStep"/> events.
+	/// </summary>
+	public class StepChangeEventArgs : EventArgs {
+		/// <summary>
+		/// The current (or previous) <see cref="WizardStep"/>.
+		/// </summary>
+		public WizardStep OldStep { get; }
+
+		/// <summary>
+		/// The <see cref="WizardStep"/> the <see cref="Wizard"/> is changing to or has changed to.
+		/// </summary>
+		public WizardStep NewStep { get; }
+
+		/// <summary>
+		/// Event handlers can set to true before returning to cancel the step transition.
+		/// </summary>
+		public bool Cancel { get; set; }
+
+		/// <summary>
+		/// Initializes a new instance of <see cref="StepChangeEventArgs"/>
+		/// </summary>
+		/// <param name="oldStep">The current <see cref="WizardStep"/>.</param>
+		/// <param name="newStep">The new <see cref="WizardStep"/>.</param>
+		public StepChangeEventArgs (WizardStep oldStep, WizardStep newStep)
+		{
+			OldStep = oldStep;
+			NewStep = newStep;
+			Cancel = false;
+		}
+	}
+}

+ 10 - 23
UICatalog/Scenario.cs

@@ -20,7 +20,7 @@ namespace UICatalog {
 	/// </para>
 	/// <para>
 	/// The UI Catalog program uses reflection to find all scenarios and adds them to the
-	/// ListViews. Press ENTER to run the selected scenario. Press CTRL-Q to exit it.	/
+	/// ListViews. Press ENTER to run the selected scenario. Press the default quit key to quit.	/
 	/// </para>
 	/// </summary>
 	/// <example>
@@ -48,6 +48,9 @@ namespace UICatalog {
 	public class Scenario : IDisposable {
 		private bool _disposedValue;
 
+		public string Theme = "Default";
+		public string TopLevelColorScheme = "Base";
+
 		/// <summary>
 		/// The Window for the <see cref="Scenario"/>. This should be set to <see cref="Terminal.Gui.Application.Top"/> in most cases.
 		/// </summary>
@@ -59,7 +62,6 @@ namespace UICatalog {
 		/// the Scenario picker UI.
 		/// Override <see cref="Init"/> to provide any <see cref="Terminal.Gui.Toplevel"/> behavior needed.
 		/// </summary>
-		/// <param name="colorScheme">The colorscheme to use.</param>
 		/// <remarks>
 		/// <para>
 		/// The base implementation calls <see cref="Application.Init"/> and creates a <see cref="Window"/> for <see cref="Win"/> 
@@ -70,34 +72,19 @@ namespace UICatalog {
 		/// before creating any views or calling other Terminal.Gui APIs.
 		/// </para>
 		/// </remarks>
-		public virtual void Init (ColorScheme colorScheme)
+		public virtual void Init ()
 		{
-			//ConfigurationManager.Applied += (a) => {
-			//	if (Application.Top == null) {
-			//		return;
-			//	}
-
-			//	//// Apply changes that apply to either UICatalogTopLevel or a Scenario
-			//	//if (Application.Top.MenuBar != null) {
-			//	//	Application.Top.MenuBar.ColorScheme = Colors.ColorSchemes ["Menu"];
-			//	//	Application.Top.MenuBar.SetNeedsDisplay ();
-			//	//}
-
-			//	//if (Application.Top.StatusBar != null) {
-			//	//	Application.Top.StatusBar.ColorScheme = Colors.ColorSchemes ["Menu"];
-			//	//	Application.Top.StatusBar.SetNeedsDisplay ();
-			//	//}
-			//	//Application.Top.SetNeedsDisplay ();
-			//};
-			
 			Application.Init ();
+			
+			ConfigurationManager.Themes.Theme = Theme;
+			ConfigurationManager.Apply ();
 
-			Win = new Window ($"{Application.QuitKey} to Close - Scenario: {GetName ()}") {
+			Win = new Window ($"{Application.QuitKey} to Quit - Scenario: {GetName ()}") {
 				X = 0,
 				Y = 0,
 				Width = Dim.Fill (),
 				Height = Dim.Fill (),
-				ColorScheme = colorScheme,
+				ColorScheme = Colors.ColorSchemes [TopLevelColorScheme],
 			};
 			Application.Top.Add (Win);
 		}

+ 4 - 4
UICatalog/Scenarios/ASCIICustomButton.cs

@@ -11,7 +11,7 @@ namespace UICatalog.Scenarios {
 		private ScrollViewTestWindow scrollViewTestWindow;
 		private MenuItem miSmallerWindow;
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 			scrollViewTestWindow = new ScrollViewTestWindow ();
@@ -21,7 +21,7 @@ namespace UICatalog.Scenarios {
 						CheckType = MenuItemCheckStyle.Checked
 					},
 					null,
-					new MenuItem("Quit", "",() => Application.RequestStop(),null,null, Key.Q | Key.CtrlMask)
+					new MenuItem("Quit", "",() => Application.RequestStop(),null,null, Application.QuitKey)
 				})
 			});
 			Application.Top.Add (menu, scrollViewTestWindow);
@@ -106,7 +106,7 @@ namespace UICatalog.Scenarios {
 				Add (border, fill, title);
 			}
 
-			private void This_MouseClick (object sender, MouseEventArgs obj)
+			private void This_MouseClick (object sender, MouseEventEventArgs obj)
 			{
 				OnMouseEvent (obj.MouseEvent);
 			}
@@ -258,7 +258,7 @@ namespace UICatalog.Scenarios {
 				}
 			}
 
-			private void Button_MouseClick (object sender, MouseEventArgs obj)
+			private void Button_MouseClick (object sender, MouseEventEventArgs obj)
 			{
 				if (obj.MouseEvent.Flags == MouseFlags.WheeledDown) {
 					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,

+ 9 - 4
UICatalog/Scenarios/AllViewsTester.cs

@@ -7,6 +7,7 @@ using System.Linq;
 using System.Reflection;
 using System.Text;
 using Terminal.Gui;
+using Terminal.Gui.Configuration;
 
 namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "All Views Tester", Description: "Provides a test UI for all classes derived from View.")]
@@ -40,16 +41,20 @@ namespace UICatalog.Scenarios {
 		TextField _hText;
 		int _hVal = 0;
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
+			// Don't create a sub-win (Scenario.Win); just use Application.Top
 			Application.Init ();
-			// Don't create a sub-win; just use Applicatiion.Top
+			ConfigurationManager.Themes.Theme = Theme;
+			ConfigurationManager.Apply ();
+			Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 		}
 
+
 		public override void Setup ()
 		{
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 				new StatusItem(Key.F2, "~F2~ Toggle Frame Ruler", () => {
 					ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler;
 					Application.Top.SetNeedsDisplay ();
@@ -424,7 +429,7 @@ namespace UICatalog.Scenarios {
 			return view;
 		}
 
-		void LayoutCompleteHandler (object sender, View.LayoutEventArgs args)
+		void LayoutCompleteHandler (object sender, LayoutEventArgs args)
 		{
 			UpdateTitle (_curView);
 		}

+ 4 - 4
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -36,16 +36,16 @@ namespace UICatalog.Scenarios {
 						new MenuItem ("_Run Worker", "", () => workerApp.RunWorker(), null, null, Key.CtrlMask | Key.R),
 						new MenuItem ("_Cancel Worker", "", () => workerApp.CancelWorker(), null, null, Key.CtrlMask | Key.C),
 						null,
-						new MenuItem ("_Quit", "", () => Quit(), null, null, Key.CtrlMask | Key.Q)
+						new MenuItem ("_Quit", "", () => Quit(), null, null, Application.QuitKey)
 					}),
 					new MenuBarItem ("_View", new MenuItem [] { }),
 					new MenuBarItem ("_Window", new MenuItem [] { })
-				});
+				}); ;
 				menu.MenuOpening += Menu_MenuOpening;
 				Add (menu);
 
 				var statusBar = new StatusBar (new [] {
-					new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Exit", () => Quit()),
+					new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 					new StatusItem(Key.CtrlMask | Key.R, "~^R~ Run Worker", () => workerApp.RunWorker()),
 					new StatusItem(Key.CtrlMask | Key.C, "~^C~ Cancel Worker", () => workerApp.CancelWorker())
 				});
@@ -312,7 +312,7 @@ namespace UICatalog.Scenarios {
 
 				Title = "Run Worker";
 
-				label = new Label ("Press start to do the work or close to exit.") {
+				label = new Label ("Press start to do the work or close to quit.") {
 					X = Pos.Center (),
 					Y = 1,
 					ColorScheme = Colors.Dialog

+ 1 - 1
UICatalog/Scenarios/BordersComparisons.cs

@@ -5,7 +5,7 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Layout")]
 	[ScenarioCategory ("Borders")]
 	public class BordersComparisons : Scenario {
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 

+ 2 - 2
UICatalog/Scenarios/BordersOnFrameView.cs

@@ -5,12 +5,12 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Layout")]
 	[ScenarioCategory ("Borders")]
 	public class BordersOnFrameView : Scenario {
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 
 			var boc = new BordersOnContainers (
-				$"CTRL-Q to Close - Scenario: {GetName ()}",
+				$"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
 				"FrameView",
 				new FrameView ());
 

+ 2 - 2
UICatalog/Scenarios/BordersOnToplevel.cs

@@ -5,12 +5,12 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Layout")]
 	[ScenarioCategory ("Borders")]
 	public class BordersOnToplevel : Scenario {
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 
 			var boc = new BordersOnContainers (
-				$"CTRL-Q to Close - Scenario: {GetName ()}",
+				$"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
 				"Toplevel",
 				new Border.ToplevelContainer ());
 

+ 2 - 2
UICatalog/Scenarios/BordersOnWindow.cs

@@ -5,12 +5,12 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Layout")]
 	[ScenarioCategory ("Borders")]
 	public class BordersOnWindow : Scenario {
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 
 			var boc = new BordersOnContainers (
-				$"CTRL-Q to Close - Scenario: {GetName ()}",
+				$"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
 				"Window",
 				new Window ());
 

+ 1 - 1
UICatalog/Scenarios/CharacterMap.cs

@@ -282,7 +282,7 @@ namespace UICatalog.Scenarios {
 		}
 
 		ContextMenu _contextMenu = new ContextMenu ();
-		void Handle_MouseClick (object sender, MouseEventArgs args)
+		void Handle_MouseClick (object sender, MouseEventEventArgs args)
 		{
 			var me = args.MouseEvent;
 			if (me.Flags == MouseFlags.ReportMousePosition || (me.Flags != MouseFlags.Button1Clicked &&

+ 1 - 1
UICatalog/Scenarios/Clipping.cs

@@ -7,7 +7,7 @@ namespace UICatalog.Scenarios {
 
 	public class Clipping : Scenario {
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 			Application.Top.ColorScheme = Colors.Base;

+ 3 - 3
UICatalog/Scenarios/CollectionNavigatorTester.cs

@@ -15,7 +15,7 @@ namespace UICatalog.Scenarios {
 	public class CollectionNavigatorTester : Scenario {
 
 		// Don't create a Window, just return the top-level view
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 			Application.Top.ColorScheme = Colors.Base;
@@ -97,9 +97,9 @@ namespace UICatalog.Scenarios {
 					allowMarking,
 					allowMultiSelection,
 					null,
-					new MenuItem ("_Quit", "", () => Quit(), null, null, Key.Q | Key.CtrlMask),
+					new MenuItem ("_Quit", $"{Application.QuitKey}", () => Quit(), null, null, Application.QuitKey),
 				}),
-				new MenuBarItem("_Quit", "CTRL-Q", () => Quit()),
+				new MenuBarItem("_Quit", $"{Application.QuitKey}", () => Quit()),
 			});
 
 			Application.Top.Add (menu);

+ 5 - 2
UICatalog/Scenarios/ComputedLayout.cs

@@ -5,6 +5,7 @@ using System.Linq;
 using System.Reflection;
 using System.Text;
 using Terminal.Gui;
+using Terminal.Gui.Configuration;
 
 namespace UICatalog.Scenarios {
 	/// <summary>
@@ -17,10 +18,12 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Layout")]
 	public class ComputedLayout : Scenario {
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
-			Application.Top.ColorScheme = colorScheme;
+			ConfigurationManager.Themes.Theme = Theme;
+			ConfigurationManager.Apply ();
+			Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 		}
 
 		public override void Setup ()

+ 4 - 2
UICatalog/Scenarios/ConfigurationEditor.cs

@@ -36,10 +36,12 @@ namespace UICatalog.Scenarios {
 		private static Action _editorColorSchemeChanged;
 
 		// Don't create a Window, just return the top-level view
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
-			Application.Top.ColorScheme = colorScheme;
+			ConfigurationManager.Themes.Theme = Theme;
+			ConfigurationManager.Apply ();
+			Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 		}
 
 		public override void Setup ()

+ 1 - 1
UICatalog/Scenarios/CsvEditor.cs

@@ -75,7 +75,7 @@ namespace UICatalog.Scenarios {
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.O, "~^O~ Open", () => Open()),
 				new StatusItem(Key.CtrlMask | Key.S, "~^S~ Save", () => Save()),
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 			});
 			Application.Top.Add (statusBar);
 

+ 2 - 2
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -13,10 +13,10 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Top Level Windows")]
 	[ScenarioCategory ("Menus")]
 	public class DynamicMenuBar : Scenario {
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
-			Application.Top.Add (new DynamicMenuBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}"));
+			Application.Top.Add (new DynamicMenuBarSample ($"{Application.QuitKey} to Quit - Scenario: {GetName ()}"));
 		}
 
 		public class DynamicMenuItemList {

+ 2 - 2
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -12,10 +12,10 @@ namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Dynamic StatusBar", Description: "Demonstrates how to add and remove a StatusBar and change items dynamically.")]
 	[ScenarioCategory ("Top Level Windows")]
 	public class DynamicStatusBar : Scenario {
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
-			Application.Top.Add (new DynamicStatusBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}"));
+			Application.Top.Add (new DynamicStatusBarSample ($"{Application.QuitKey} to Quit - Scenario: {GetName ()}"));
 		}
 
 		public class DynamicStatusItemList {

+ 9 - 6
UICatalog/Scenarios/Editor.cs

@@ -6,6 +6,7 @@ using System.Linq;
 using System.Text.RegularExpressions;
 using System.Threading;
 using System.Globalization;
+using Terminal.Gui.Configuration;
 
 namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Editor", Description: "A Text Editor using the TextView control.")]
@@ -32,17 +33,19 @@ namespace UICatalog.Scenarios {
 		private bool _forceMinimumPosToZero = true;
 		private List<CultureInfo> _cultureInfos;
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 			_cultureInfos = Application.SupportedCultures;
-
+			ConfigurationManager.Themes.Theme = Theme;
+			ConfigurationManager.Apply ();
+			
 			Win = new Window (_fileName ?? "Untitled") {
 				X = 0,
 				Y = 1,
 				Width = Dim.Fill (),
 				Height = Dim.Fill (),
-				ColorScheme = colorScheme,
+				ColorScheme = Colors.ColorSchemes [TopLevelColorScheme],
 			};
 			Application.Top.Add (Win);
 
@@ -121,7 +124,7 @@ namespace UICatalog.Scenarios {
 				new StatusItem(Key.F2, "~F2~ Open", () => Open()),
 				new StatusItem(Key.F3, "~F3~ Save", () => Save()),
 				new StatusItem(Key.F4, "~F4~ Save As", () => SaveAs()),
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 				new StatusItem(Key.Null, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null)
 			});
 			Application.Top.Add (statusBar);
@@ -174,9 +177,9 @@ namespace UICatalog.Scenarios {
 			Win.KeyPress += (s, e) => {
 				var keys = ShortcutHelper.GetModifiersKey (e.KeyEvent);
 				if (_winDialog != null && (e.KeyEvent.Key == Key.Esc
-					|| e.KeyEvent.Key == (Key.Q | Key.CtrlMask))) {
+					|| e.KeyEvent.Key == Application.QuitKey)) {
 					DisposeWinDialog ();
-				} else if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) {
+				} else if (e.KeyEvent.Key == Application.QuitKey) {
 					Quit ();
 					e.Handled = true;
 				} else if (_winDialog != null && keys == (Key.Tab | Key.CtrlMask)) {

+ 8 - 7
UICatalog/Scenarios/Generic.cs

@@ -5,19 +5,20 @@ namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Generic", Description: "Generic sample - A template for creating new Scenarios")]
 	[ScenarioCategory ("Controls")]
 	public class MyScenario : Scenario {
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			// The base `Scenario.Init` implementation:
 			//  - Calls `Application.Init ()`
 			//  - Adds a full-screen Window to Application.Top with a title
 			//    that reads "Press <hotkey> to Quit". Access this Window with `this.Win`.
-			//  - Sets the ColorScheme property of `this.Win` to `colorScheme`.
-			// To overrride this, implement an override of `Init`.
-			base.Init (colorScheme);
-
+			//  - Sets the Theme & the ColorScheme property of `this.Win` to `colorScheme`.
+			// To override this, implement an override of `Init`.
+			base.Init ();
 			// A common, alternate, implementation where `this.Win` is not used:
-			//   Application.Init ();
-			//   Application.Top.ColorScheme = colorScheme;
+			//Application.Init ();
+			//ConfigurationManager.Themes.Theme = Theme;
+			//ConfigurationManager.Apply ();
+			//Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 		}
 
 		public override void Setup ()

+ 1 - 1
UICatalog/Scenarios/GraphViewExample.cs

@@ -89,7 +89,7 @@ namespace UICatalog.Scenarios {
 
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 				new StatusItem(Key.CtrlMask | Key.G, "~^G~ Next", ()=>graphs[currentGraph++%graphs.Length]()),
 			});
 			Application.Top.Add (statusBar);

+ 1 - 1
UICatalog/Scenarios/HexEditor.cs

@@ -57,7 +57,7 @@ namespace UICatalog.Scenarios {
 			statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.F2, "~F2~ Open", () => Open()),
 				new StatusItem(Key.F3, "~F3~ Save", () => Save()),
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 				siPositionChanged = new StatusItem(Key.Null,
 					$"Position: {_hexView.Position} Line: {_hexView.CursorPosition.Y} Col: {_hexView.CursorPosition.X} Line length: {_hexView.BytesPerLine}", () => {})
 			});

+ 1 - 1
UICatalog/Scenarios/InteractiveTree.cs

@@ -40,7 +40,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (treeView);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 				new StatusItem(Key.CtrlMask | Key.C, "~^C~ Add Child", () => AddChildNode()),
 				new StatusItem(Key.CtrlMask | Key.T, "~^T~ Add Root", () => AddRootNode()),
 				new StatusItem(Key.CtrlMask | Key.R, "~^R~ Rename Node", () => RenameNode()),

+ 6 - 3
UICatalog/Scenarios/Keys.cs

@@ -1,6 +1,7 @@
 using NStack;
 using System.Collections.Generic;
 using Terminal.Gui;
+using Terminal.Gui.Configuration;
 
 namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Keys", Description: "Shows how to handle keyboard input")]
@@ -48,16 +49,18 @@ namespace UICatalog.Scenarios {
 			}
 		}
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
+			ConfigurationManager.Themes.Theme = Theme;
+			ConfigurationManager.Apply ();
 			
-			Win = new TestWindow ($"CTRL-Q to Close - Scenario: {GetName ()}") {
+			Win = new TestWindow ($"{Application.QuitKey} to Quit - Scenario: {GetName ()}") {
 				X = 0,
 				Y = 0,
 				Width = Dim.Fill (),
 				Height = Dim.Fill (),
-				ColorScheme = colorScheme,
+				ColorScheme = Colors.ColorSchemes [TopLevelColorScheme],
 			};
 			Application.Top.Add (Win);
 		}

+ 1 - 1
UICatalog/Scenarios/LineViewExample.cs

@@ -92,7 +92,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (verticalArrow);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit())
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 			});
 			Application.Top.Add (statusBar);
 

+ 1 - 1
UICatalog/Scenarios/MultiColouredTable.cs

@@ -33,7 +33,7 @@ namespace UICatalog.Scenarios {
 			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 			});
 			Application.Top.Add (statusBar);
 

+ 2 - 2
UICatalog/Scenarios/Notepad.cs

@@ -16,7 +16,7 @@ namespace UICatalog.Scenarios {
 		private StatusItem lenStatusItem;
 
 		// Don't create a Window, just return the top-level view
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 			Application.Top.ColorScheme = Colors.Base;
@@ -55,7 +55,7 @@ namespace UICatalog.Scenarios {
 
 			lenStatusItem = new StatusItem (Key.CharMask, "Len: ", null);
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 
 				// These shortcut keys don't seem to work correctly in linux 
 				//new StatusItem(Key.CtrlMask | Key.N, "~^O~ Open", () => Open()),

+ 1 - 1
UICatalog/Scenarios/RunTExample.cs

@@ -19,7 +19,7 @@ namespace UICatalog.Scenarios {
 
 			public ExampleWindow ()
 			{
-				Title = "Example App (Ctrl+Q to quit)";
+				Title = $"Example App ({Application.QuitKey} to quit)";
 
 				// Create input components and labels
 				var usernameLabel = new Label () {

+ 1 - 1
UICatalog/Scenarios/RuneWidthGreaterThanOne.cs

@@ -16,7 +16,7 @@ namespace UICatalog.Scenarios {
 		private Window _win;
 		private string _lastRunesUsed;
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 

+ 2 - 2
UICatalog/Scenarios/SingleBackgroundWorker.cs

@@ -36,7 +36,7 @@ namespace UICatalog.Scenarios {
 				Add (menu);
 
 				var statusBar = new StatusBar (new [] {
-					new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Exit", () => Application.RequestStop()),
+					new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Application.RequestStop()),
 					new StatusItem(Key.CtrlMask | Key.P, "~^R~ Run Worker", () => RunWorker())
 				});
 				Add (statusBar);
@@ -136,7 +136,7 @@ namespace UICatalog.Scenarios {
 				top.KeyPress += (s,e) => {
 					// Prevents Ctrl+Q from closing this.
 					// Only Ctrl+C is allowed.
-					if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) {
+					if (e.KeyEvent.Key == Application.QuitKey) {
 						e.Handled = true;
 					}
 				};

+ 1 - 1
UICatalog/Scenarios/SyntaxHighlighting.cs

@@ -45,7 +45,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (textView);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 			});
 
 

+ 1 - 1
UICatalog/Scenarios/TabViewExample.cs

@@ -109,7 +109,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (frameBelow);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 			});
 			Application.Top.Add (statusBar);
 		}

+ 2 - 2
UICatalog/Scenarios/TableEditor.cs

@@ -86,7 +86,7 @@ namespace UICatalog.Scenarios {
 				new StatusItem(Key.F2, "~F2~ OpenExample", () => OpenExample(true)),
 				new StatusItem(Key.F3, "~F3~ CloseExample", () => CloseExample()),
 				new StatusItem(Key.F4, "~F4~ OpenSimple", () => OpenSimple(true)),
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 			});
 			Application.Top.Add (statusBar);
 
@@ -217,7 +217,7 @@ namespace UICatalog.Scenarios {
 			return sort;
 		}
 
-		private void ShowHeaderContextMenu (DataColumn clickedCol, View.MouseEventArgs e)
+		private void ShowHeaderContextMenu (DataColumn clickedCol, MouseEventEventArgs e)
 		{
 			var sort = GetProposedNewSortOrder (clickedCol, out var isAsc);
 

+ 2 - 2
UICatalog/Scenarios/TextViewAutocompletePopup.cs

@@ -85,7 +85,7 @@ namespace UICatalog.Scenarios {
 			miWrap.Checked = textViewTopLeft.WordWrap;
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 				siMultiline = new StatusItem(Key.Null, "", null),
 				siWrap = new StatusItem(Key.Null, "", null)
 			});
@@ -94,7 +94,7 @@ namespace UICatalog.Scenarios {
 			Win.LayoutStarted += Win_LayoutStarted;
 		}
 
-		private void Win_LayoutStarted (object sender, View.LayoutEventArgs obj)
+		private void Win_LayoutStarted (object sender, LayoutEventArgs obj)
 		{
 			miMultiline.Checked = textViewTopLeft.Multiline;
 			miWrap.Checked = textViewTopLeft.WordWrap;

+ 1 - 1
UICatalog/Scenarios/TileViewExperiment.cs

@@ -10,7 +10,7 @@ namespace UICatalog.Scenarios {
 	public class TileViewExperiment : Scenario {
 
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 		}

+ 1 - 1
UICatalog/Scenarios/TreeUseCases.cs

@@ -34,7 +34,7 @@ namespace UICatalog.Scenarios {
 			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 			});
 
 			Application.Top.Add (statusBar);

+ 2 - 2
UICatalog/Scenarios/TreeViewFileSystem.cs

@@ -41,7 +41,7 @@ namespace UICatalog.Scenarios {
 
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
-					new MenuItem ("_Quit", "CTRL-Q", () => Quit()),
+					new MenuItem ("_Quit", $"{Application.QuitKey}", () => Quit()),
 				}),
 				new MenuBarItem ("_View", new MenuItem [] {
 					miFullPaths = new MenuItem ("_Full Paths", "", () => SetFullName()){Checked = false, CheckType = MenuItemCheckStyle.Checked},
@@ -129,7 +129,7 @@ namespace UICatalog.Scenarios {
 			}
 		}
 
-		private void TreeViewFiles_MouseClick (object sender, View.MouseEventArgs obj)
+		private void TreeViewFiles_MouseClick (object sender, MouseEventEventArgs obj)
 		{
 			// if user right clicks
 			if (obj.MouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)) {

+ 1 - 1
UICatalog/Scenarios/Unicode.cs

@@ -37,7 +37,7 @@ namespace UICatalog.Scenarios {
 			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Выход", () => Application.RequestStop()),
+				new StatusItem(Application.QuitKey, $"{Application.QuitKey} Выход", () => Application.RequestStop()),
 				new StatusItem (Key.Unknown, "~F2~ Создать", null),
 				new StatusItem(Key.Unknown, "~F3~ Со_хранить", null),
 			});

+ 1 - 1
UICatalog/Scenarios/WizardAsView.cs

@@ -10,7 +10,7 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Wizards")]
 	public class WizardAsView : Scenario {
 
-		public override void Init (ColorScheme colorScheme)
+		public override void Init ()
 		{
 			Application.Init ();
 

+ 23 - 6
UICatalog/UICatalog.cs

@@ -14,6 +14,8 @@ using Terminal.Gui.Configuration;
 using static Terminal.Gui.Configuration.ConfigurationManager;
 using System.Text.Json.Serialization;
 
+#nullable enable
+
 /// <summary>
 /// UI Catalog is a comprehensive sample library for Terminal.Gui. It provides a simple UI for adding to the catalog of scenarios.
 /// </summary>
@@ -84,7 +86,9 @@ namespace UICatalog {
 				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ());
 				Application.UseSystemConsole = _useSystemConsole;
 				Application.Init ();
-				_selectedScenario.Init (Colors.ColorSchemes [_topLevelColorScheme == null ? "Base" : _topLevelColorScheme]);
+				_selectedScenario.Theme = _cachedTheme;
+				_selectedScenario.TopLevelColorScheme = _topLevelColorScheme;
+				_selectedScenario.Init ();
 				_selectedScenario.Setup ();
 				_selectedScenario.Run ();
 				_selectedScenario.Dispose ();
@@ -111,7 +115,11 @@ namespace UICatalog {
 			Scenario scenario;
 			while ((scenario = RunUICatalogTopLevel ()) != null) {
 				VerifyObjectsWereDisposed ();
-				scenario.Init (Colors.ColorSchemes [_topLevelColorScheme]);
+				ConfigurationManager.Themes.Theme = _cachedTheme;
+				ConfigurationManager.Apply ();
+				scenario.Theme = _cachedTheme;
+				scenario.TopLevelColorScheme = _topLevelColorScheme;
+				scenario.Init ();
 				scenario.Setup ();
 				scenario.Run ();
 				scenario.Dispose ();
@@ -193,10 +201,17 @@ namespace UICatalog {
 			Application.UseSystemConsole = _useSystemConsole;
 
 			// Run UI Catalog UI. When it exits, if _selectedScenario is != null then
-			// a Scenario was selected. Otherwise, the user wants to exit UI Catalog.
+			// a Scenario was selected. Otherwise, the user wants to quit UI Catalog.
 			Application.Init ();
 
-			Application.EnableConsoleScrolling = _enableConsoleScrolling;
+			if (_cachedTheme is null) {
+				_cachedTheme = ConfigurationManager.Themes.Theme;
+			} else {
+				ConfigurationManager.Themes.Theme = _cachedTheme;
+				ConfigurationManager.Apply ();
+			}
+
+			//Application.EnableConsoleScrolling = _enableConsoleScrolling;
 
 			Application.Run<UICatalogTopLevel> ();
 			Application.Shutdown ();
@@ -212,6 +227,8 @@ namespace UICatalog {
 		// main app UI can be restored to previous state
 		static int _cachedScenarioIndex = 0;
 		static int _cachedCategoryIndex = 0;
+		static string? _cachedTheme;
+		
 		static StringBuilder _aboutMessage;
 
 		// If set, holds the scenario the user selected
@@ -575,9 +592,9 @@ namespace UICatalog {
 						Shortcut = Key.AltMask + theme.Key [0]
 					};
 					item.CheckType |= MenuItemCheckStyle.Checked;
-					item.Checked = theme.Key == ConfigurationManager.Themes.Theme;
+					item.Checked = theme.Key == _cachedTheme; // ConfigurationManager.Themes.Theme;
 					item.Action += () => {
-						ConfigurationManager.Themes.Theme = theme.Key;
+						ConfigurationManager.Themes.Theme = _cachedTheme = theme.Key;
 						ConfigurationManager.Apply ();
 					};
 					menuItems.Add (item);

+ 4 - 4
UnitTests/Application/ApplicationTests.cs

@@ -560,16 +560,16 @@ namespace Terminal.Gui.ApplicationTests {
 			var input = "Tests";
 
 			// Put a control-q in at the end
-			Console.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true));
+			FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true));
 			foreach (var c in input.Reverse ()) {
 				if (char.IsLetter (c)) {
-					Console.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
+					FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
 				} else {
-					Console.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false));
+					FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false));
 				}
 			}
 
-			int stackSize = Console.MockKeyPresses.Count;
+			int stackSize = FakeConsole.MockKeyPresses.Count;
 
 			int iterations = 0;
 			Application.Iteration = () => {

+ 59 - 2
UnitTests/Drivers/ConsoleDriverTests.cs

@@ -142,6 +142,63 @@ namespace Terminal.Gui.DriverTests {
 			Application.Shutdown ();
 		}
 
+		//[Theory]
+		//[InlineData (typeof (FakeDriver))]
+		//public void FakeDriver_MockKeyPresses_Press_AfterTimeOut (Type driverType)
+		//{
+		//	var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		//	Application.Init (driver);
+
+		//	// Simulating pressing of QuitKey after a short period of time
+		//	uint quitTime = 100;
+		//	Func<MainLoop, bool> closeCallback = (MainLoop loop) => {
+		//		// Prove the scenario is using Application.QuitKey correctly
+		//		output.WriteLine ($"  {quitTime}ms elapsed; Simulating keypresses...");
+		//		FakeConsole.PushMockKeyPress (Key.F);
+		//		FakeConsole.PushMockKeyPress (Key.U);
+		//		FakeConsole.PushMockKeyPress (Key.C);
+		//		FakeConsole.PushMockKeyPress (Key.K);
+		//		return false;
+		//	};
+		//	output.WriteLine ($"Add timeout to simulate key presses after {quitTime}ms");
+		//	_ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (quitTime), closeCallback);
+
+		//	// If Top doesn't quit within abortTime * 5 (500ms), this will force it
+		//	uint abortTime = quitTime * 5;
+		//	Func<MainLoop, bool> forceCloseCallback = (MainLoop loop) => {
+		//		Application.RequestStop ();
+		//		Assert.Fail ($"  failed to Quit after {abortTime}ms. Force quit.");
+		//		return false;
+		//	};
+		//	output.WriteLine ($"Add timeout to force quit after {abortTime}ms");
+		//	_ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
+
+
+		//	Key key = Key.Unknown;
+			
+		//	Application.Top.KeyPress += (e) => {
+		//		key = e.KeyEvent.Key;
+		//		output.WriteLine ($"  Application.Top.KeyPress: {key}");
+		//		e.Handled = true;
+				
+		//	};
+
+		//	int iterations = 0;
+		//	Application.Iteration += () => {
+		//		output.WriteLine ($"  iteration {++iterations}");
+
+		//		if (Console.MockKeyPresses.Count == 0) {
+		//			output.WriteLine ($"    No more MockKeyPresses; RequestStop");
+		//			Application.RequestStop ();
+		//		}
+		//	};
+
+		//	Application.Run ();
+
+		//	// Shutdown must be called to safely clean up Application if Init has been called
+		//	Application.Shutdown ();
+		//}
+		
 		[Theory]
 		[InlineData (typeof (FakeDriver))]
 		public void TerminalResized_Simulation (Type driverType)
@@ -319,7 +376,7 @@ namespace Terminal.Gui.DriverTests {
 
 			Application.Shutdown ();
 		}
-		
+
 		[Fact, AutoInitShutdown]
 		public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Space ()
 		{
@@ -441,7 +498,7 @@ namespace Terminal.Gui.DriverTests {
 		}
 
 		private static object packetLock = new object ();
-		
+
 		/// <summary>
 		/// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'.
 		/// These are indicated with the wVirtualKeyCode of 231. When we see this code

+ 1 - 1
UnitTests/Drivers/KeyTests.cs

@@ -123,7 +123,7 @@ namespace Terminal.Gui.DriverTests {
 			Assert.Equal ("Y, CtrlMask", key.ToString ());
 
 			// This will be well compared, because the Key.CtrlMask have a high value.
-			Assert.False (key == (Key.Q | Key.CtrlMask));
+			Assert.False (key == Application.QuitKey);
 			switch (key) {
 			case Key.Q | Key.CtrlMask:
 				// Never goes here.

+ 2 - 2
UnitTests/Menus/ContextMenuTests.cs

@@ -640,8 +640,8 @@ namespace Terminal.Gui.MenuTests {
 			win.Add (label, tf);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.F1, "~F1~ Help", null),
-				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", null)
+				new StatusItem (Key.F1, "~F1~ Help", null),
+				new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null)
 			});
 
 			Application.Top.Add (menu, win, statusBar);

+ 2 - 2
UnitTests/TopLevels/ToplevelTests.cs

@@ -106,7 +106,7 @@ namespace Terminal.Gui.TopLevelTests {
 
 				var statusBar = new StatusBar (new [] {
 					new StatusItem(Key.CtrlMask | Key.R, "~^R~ Run Top2", () => Application.Run (Top2 ())),
-					new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Application.RequestStop())
+					new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Application.RequestStop())
 				});
 				top.Add (statusBar);
 
@@ -1060,7 +1060,7 @@ namespace Terminal.Gui.TopLevelTests {
 
 			view.LayoutStarted += view_LayoutStarted;
 
-			void view_LayoutStarted (object sender, View.LayoutEventArgs e)
+			void view_LayoutStarted (object sender, LayoutEventArgs e)
 			{
 				Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay);
 				view.LayoutStarted -= view_LayoutStarted;

+ 56 - 32
UnitTests/UICatalog/ScenarioTests.cs

@@ -1,10 +1,12 @@
 using NStack;
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 using System.Reflection;
 using Terminal.Gui;
 using UICatalog;
+using UICatalog.Scenarios;
 using Xunit;
 using Xunit.Abstractions;
 
@@ -25,14 +27,17 @@ namespace UICatalog.Tests {
 
 		int CreateInput (string input)
 		{
-			// Put a control-q in at the end
-			FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true));
+			FakeConsole.MockKeyPresses.Clear ();
+			// Put a QuitKey in at the end
+			FakeConsole.PushMockKeyPress (Application.QuitKey);
 			foreach (var c in input.Reverse ()) {
+				Key key = Key.Unknown;
 				if (char.IsLetter (c)) {
-					FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (char.ToLower (c), (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
+					key = (Key)char.ToUpper (c) | (char.IsUpper (c) ? Key.ShiftMask : (Key)0);
 				} else {
-					FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false));
+					key = (Key)c;
 				}
+				FakeConsole.PushMockKeyPress (key);
 			}
 			return FakeConsole.MockKeyPresses.Count;
 		}
@@ -53,24 +58,46 @@ namespace UICatalog.Tests {
 			Assert.NotEmpty (scenarios);
 
 			foreach (var scenario in scenarios) {
+				output.WriteLine ($"Running Scenario '{scenario.GetName ()}'");
 
-				output.WriteLine ($"Running Scenario '{scenario}'");
+				Application.Init (new FakeDriver ());
+
+				// Press QuitKey 
+				Assert.Empty (FakeConsole.MockKeyPresses);
+				// BUGBUG: For some reason ReadKey is not returning the QuitKey for some Scenarios
+				// by adding this Space it seems to work.
+				FakeConsole.PushMockKeyPress (Key.Space);
+				FakeConsole.PushMockKeyPress (Application.QuitKey);
+
+				// The only key we care about is the QuitKey
+				Application.Top.KeyPress += (object sender, KeyEventEventArgs args) => {
+					output.WriteLine ($"  Keypress: {args.KeyEvent.Key}");
+					Assert.Equal (Application.QuitKey, args.KeyEvent.Key);
+					args.Handled = false;
+				};
 
-				Func<MainLoop, bool> closeCallback = (MainLoop loop) => {
+				uint abortTime = 500;
+				// If the scenario doesn't close within 500ms, this will force it to quit
+				Func<MainLoop, bool> forceCloseCallback = (MainLoop loop) => {
 					Application.RequestStop ();
+					Assert.Fail ($"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit.");
 					return false;
 				};
+				//output.WriteLine ($"  Add timeout to force quit after {abortTime}ms");
+				_ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
+
+				Application.Iteration += () => {
+					//output.WriteLine ($"  iteration {++iterations}");
+					if (FakeConsole.MockKeyPresses.Count == 0) {
+						Application.RequestStop ();
+						//Assert.Fail ($"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey}. Force quit.");
+					}
+				};
 
-				Application.Init (new FakeDriver ());
-
-				// Close after a short period of time
-				var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), closeCallback);
-
-				scenario.Init (Colors.Base);
+				scenario.Init ();
 				scenario.Setup ();
 				scenario.Run ();
-
-				scenario.Dispose();
+				scenario.Dispose ();
 
 				Application.Shutdown ();
 #if DEBUG_IDISPOSABLE
@@ -96,26 +123,30 @@ namespace UICatalog.Tests {
 
 			var item = scenarios.FindIndex (s => s.GetName ().Equals ("Generic", StringComparison.OrdinalIgnoreCase));
 			var generic = scenarios [item];
-			// Setup some fake keypresses 
-			// Passing empty string will cause just a ctrl-q to be fired
-			int stackSize = CreateInput ("");
 
 			Application.Init (new FakeDriver ());
-			Application.QuitKey = Key.CtrlMask | Key.Q; // Config manager may have set this to a different key
+			// BUGBUG: For some reason ReadKey is not
+			// returning the QuitKey for some Scenarios
+			// by adding this Space it seems to work.
+			FakeConsole.PushMockKeyPress (Key.Space);
+			FakeConsole.PushMockKeyPress (Application.QuitKey);
 
 			int iterations = 0;
 			Application.Iteration = () => {
 				iterations++;
+				output.WriteLine ($"'Generic' iteration {iterations}");
 				// Stop if we run out of control...
 				if (iterations == 10) {
+					output.WriteLine ($"'Generic' had to be force quit!");
 					Application.RequestStop ();
 				}
 			};
 
-			var ms = 1000;
+			var ms = 100;
 			var abortCount = 0;
 			Func<MainLoop, bool> abortCallback = (MainLoop loop) => {
 				abortCount++;
+				output.WriteLine ($"'Generic' abortCount {abortCount}");
 				Application.RequestStop ();
 				return false;
 			};
@@ -125,22 +156,15 @@ namespace UICatalog.Tests {
 				Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key);
 			};
 
-			generic.Init (Colors.Base);
+			generic.Init ();
 			generic.Setup ();
-			// There is no need to call Application.Begin because Init already creates the Application.Top
-			// If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run.
-			//var rs = Application.Begin (Application.Top);
 			generic.Run ();
 
-			//Application.End (rs);
-
 			Assert.Equal (0, abortCount);
 			// # of key up events should match # of iterations
 			Assert.Equal (1, iterations);
-			// Using variable in the left side of Assert.Equal/NotEqual give error. Must be used literals values.
-			//Assert.Equal (stackSize, iterations);
 
-			generic.Dispose();
+			generic.Dispose ();
 
 			// Shutdown must be called to safely clean up Application if Init has been called
 			Application.Shutdown ();
@@ -303,7 +327,7 @@ namespace UICatalog.Tests {
 				_curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
 			};
 
-			_computedCheckBox.Toggled += (s,e) => {
+			_computedCheckBox.Toggled += (s, e) => {
 				if (_curView != null) {
 					_curView.LayoutStyle = e.OldValue == true ? LayoutStyle.Absolute : LayoutStyle.Computed;
 					_hostPane.LayoutSubviews ();
@@ -321,7 +345,7 @@ namespace UICatalog.Tests {
 				}
 			};
 
-			_yText.TextChanged += (s,e) => {
+			_yText.TextChanged += (s, e) => {
 				try {
 					_yVal = int.Parse (_yText.Text.ToString ());
 					DimPosChanged (_curView);
@@ -330,7 +354,7 @@ namespace UICatalog.Tests {
 				}
 			};
 
-			_yRadioGroup.SelectedItemChanged += (s,selected) => DimPosChanged (_curView);
+			_yRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
 
 			_wRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
 
@@ -563,7 +587,7 @@ namespace UICatalog.Tests {
 				return view;
 			}
 
-			void LayoutCompleteHandler (object sender, View.LayoutEventArgs args)
+			void LayoutCompleteHandler (object sender, LayoutEventArgs args)
 			{
 				UpdateTitle (_curView);
 			}

+ 17 - 12
UnitTests/Views/StatusBarTests.cs

@@ -14,11 +14,11 @@ namespace Terminal.Gui.ViewTests {
 		[Fact]
 		public void StatusItem_Constructor ()
 		{
-			var si = new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null);
+			var si = new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit", null);
 			Assert.Equal (Key.CtrlMask | Key.Q, si.Shortcut);
-			Assert.Equal ("~^Q~ Quit", si.Title);
+			Assert.Equal ($"{Application.QuitKey} to Quit", si.Title);
 			Assert.Null (si.Action);
-			si = new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", () => { });
+			si = new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit", () => { });
 			Assert.NotNull (si.Action);
 		}
 
@@ -72,7 +72,7 @@ namespace Terminal.Gui.ViewTests {
 		public void Run_Action_With_Key_And_Mouse ()
 		{
 			var msg = "";
-			var sb = new StatusBar (new StatusItem [] { new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", () => msg = "Quiting...") });
+			var sb = new StatusBar (new StatusItem [] { new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit", () => msg = "Quiting...") });
 			Application.Top.Add (sb);
 
 			var iteration = 0;
@@ -101,26 +101,31 @@ namespace Terminal.Gui.ViewTests {
 		public void Redraw_Output ()
 		{
 			var sb = new StatusBar (new StatusItem [] {
-				new StatusItem (Key.CtrlMask | Key.Q, "~^O~ Open", null),
-				new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null)
+				new StatusItem (Key.CtrlMask | Key.O, "~^O~ Open", null),
+				new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit!", null)
 			});
 			Application.Top.Add (sb);
 
 			sb.Redraw (sb.Bounds);
 
 			string expected = @$"
-^O Open {Application.Driver.VLine} ^Q Quit
+^O Open {Application.Driver.VLine} Q, CtrlMask to Quit!
 ";
-
 			TestHelpers.AssertDriverContentsAre (expected, output);
-
-			sb = new StatusBar (new StatusItem [] {
-				new StatusItem (Key.CtrlMask | Key.Q, "~CTRL-O~ Open", null),
+		}
+		
+		[Fact]
+		[AutoInitShutdown]
+		public void Redraw_Output_CTRLQ ()
+		{
+			var sb = new StatusBar (new StatusItem [] {
+				new StatusItem (Key.CtrlMask | Key.O, "~CTRL-O~ Open", null),
 				new StatusItem (Key.CtrlMask | Key.Q, "~CTRL-Q~ Quit", null)
 			});
+			Application.Top.Add (sb);
 			sb.Redraw (sb.Bounds);
 
-			expected = @$"
+			string expected = @$"
 CTRL-O Open {Application.Driver.VLine} CTRL-Q Quit
 ";