Browse Source

Makes UI Catalog Scenarios use Theme and top-level color scheme (#2401)

* lots of clean up

* scenarios now get themes from uicatalog
Tig 2 years ago
parent
commit
c94f9165f4
52 changed files with 302 additions and 157 deletions
  1. 1 1
      Example/Example.cs
  2. 2 2
      README.md
  3. 15 1
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs
  4. 2 0
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  5. 4 4
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs
  6. 1 1
      Terminal.Gui/Core/Application.cs
  7. 21 1
      Terminal.Gui/Core/ConsoleKeyMapping.cs
  8. 10 23
      UICatalog/Scenario.cs
  9. 2 2
      UICatalog/Scenarios/ASCIICustomButton.cs
  10. 8 3
      UICatalog/Scenarios/AllViewsTester.cs
  11. 4 4
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  12. 1 1
      UICatalog/Scenarios/BordersComparisons.cs
  13. 2 2
      UICatalog/Scenarios/BordersOnFrameView.cs
  14. 2 2
      UICatalog/Scenarios/BordersOnToplevel.cs
  15. 2 2
      UICatalog/Scenarios/BordersOnWindow.cs
  16. 1 1
      UICatalog/Scenarios/Clipping.cs
  17. 3 3
      UICatalog/Scenarios/CollectionNavigatorTester.cs
  18. 5 2
      UICatalog/Scenarios/ComputedLayout.cs
  19. 4 2
      UICatalog/Scenarios/ConfigurationEditor.cs
  20. 1 1
      UICatalog/Scenarios/CsvEditor.cs
  21. 2 2
      UICatalog/Scenarios/DynamicMenuBar.cs
  22. 2 2
      UICatalog/Scenarios/DynamicStatusBar.cs
  23. 9 6
      UICatalog/Scenarios/Editor.cs
  24. 8 7
      UICatalog/Scenarios/Generic.cs
  25. 1 1
      UICatalog/Scenarios/GraphViewExample.cs
  26. 1 1
      UICatalog/Scenarios/HexEditor.cs
  27. 1 1
      UICatalog/Scenarios/InteractiveTree.cs
  28. 6 3
      UICatalog/Scenarios/Keys.cs
  29. 1 1
      UICatalog/Scenarios/LineViewExample.cs
  30. 1 1
      UICatalog/Scenarios/MultiColouredTable.cs
  31. 2 2
      UICatalog/Scenarios/Notepad.cs
  32. 1 1
      UICatalog/Scenarios/RunTExample.cs
  33. 1 1
      UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  34. 2 2
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  35. 1 1
      UICatalog/Scenarios/SyntaxHighlighting.cs
  36. 1 1
      UICatalog/Scenarios/TabViewExample.cs
  37. 1 1
      UICatalog/Scenarios/TableEditor.cs
  38. 1 1
      UICatalog/Scenarios/TextViewAutocompletePopup.cs
  39. 1 1
      UICatalog/Scenarios/TileViewExperiment.cs
  40. 1 1
      UICatalog/Scenarios/TreeUseCases.cs
  41. 1 1
      UICatalog/Scenarios/TreeViewFileSystem.cs
  42. 1 1
      UICatalog/Scenarios/Unicode.cs
  43. 1 1
      UICatalog/Scenarios/VkeyPacketSimulator.cs
  44. 1 1
      UICatalog/Scenarios/WizardAsView.cs
  45. 23 6
      UICatalog/UICatalog.cs
  46. 4 4
      UnitTests/Application/ApplicationTests.cs
  47. 60 3
      UnitTests/Drivers/ConsoleDriverTests.cs
  48. 1 1
      UnitTests/Drivers/KeyTests.cs
  49. 2 2
      UnitTests/Menus/ContextMenuTests.cs
  50. 1 1
      UnitTests/TopLevels/ToplevelTests.cs
  51. 55 29
      UnitTests/UICatalog/ScenarioTests.cs
  52. 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 () { 

+ 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 ();
 		}
 

+ 1 - 1
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),

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

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

+ 2 - 2
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);

+ 8 - 3
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 ();

+ 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/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 += (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 += (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);
 		}

+ 1 - 1
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);
 

+ 1 - 1
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)
 			});

+ 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);

+ 1 - 1
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},

+ 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/VkeyPacketSimulator.cs

@@ -125,7 +125,7 @@ namespace UICatalog.Scenarios {
 			View.KeyEventEventArgs unknownChar = null;
 
 			tvInput.KeyPress += (e) => {
-				if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) {
+				if (e.KeyEvent.Key == Application.QuitKey) {
 					Application.RequestStop ();
 					return;
 				}

+ 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 = () => {

+ 60 - 3
UnitTests/Drivers/ConsoleDriverTests.cs

@@ -121,7 +121,7 @@ namespace Terminal.Gui.DriverTests {
 			var rText = "";
 			var idx = 0;
 
-			view.KeyPress += (e) => {
+			top.KeyPress += (e) => {
 				Assert.Equal (text [idx], (char)e.KeyEvent.Key);
 				rText += (char)e.KeyEvent.Key;
 				Assert.Equal (rText, text.Substring (0, idx + 1));
@@ -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);

+ 1 - 1
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);
 

+ 55 - 29
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,47 @@ namespace UICatalog.Tests {
 			Assert.NotEmpty (scenarios);
 
 			foreach (var scenario in scenarios) {
+				output.WriteLine ($"Running Scenario '{scenario.GetName ()}'");
+				
+				Application.Init (new FakeDriver ());
 
-				output.WriteLine ($"Running Scenario '{scenario}'");
+				// 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 += (View.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);
+
+				int iterations = 0;
+				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,51 +124,49 @@ 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;
 			};
 			var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback);
 
 			Application.Top.KeyPress += (View.KeyEventEventArgs args) => {
-				Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key);
+				output.WriteLine ($"'Generic' KeyPress {args.KeyEvent.Key}");
+				Assert.Equal (Application.QuitKey, 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 ();

+ 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
 ";