Parcourir la source

merged v2_develop

Tig Kindel il y a 2 ans
Parent
commit
27ca37eed0
56 fichiers modifiés avec 519 ajouts et 211 suppressions
  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. 15 4
      Terminal.Gui/Views/Menu.cs
  9. 37 1
      Terminal.Gui/Windows/Dialog.cs
  10. 43 28
      Terminal.Gui/Windows/MessageBox.cs
  11. 10 23
      UICatalog/Scenario.cs
  12. 2 2
      UICatalog/Scenarios/ASCIICustomButton.cs
  13. 8 3
      UICatalog/Scenarios/AllViewsTester.cs
  14. 4 4
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  15. 1 1
      UICatalog/Scenarios/BordersComparisons.cs
  16. 2 2
      UICatalog/Scenarios/BordersOnFrameView.cs
  17. 2 2
      UICatalog/Scenarios/BordersOnWindow.cs
  18. 1 1
      UICatalog/Scenarios/Clipping.cs
  19. 3 3
      UICatalog/Scenarios/CollectionNavigatorTester.cs
  20. 5 2
      UICatalog/Scenarios/ComputedLayout.cs
  21. 4 2
      UICatalog/Scenarios/ConfigurationEditor.cs
  22. 1 1
      UICatalog/Scenarios/CsvEditor.cs
  23. 2 2
      UICatalog/Scenarios/DynamicMenuBar.cs
  24. 2 2
      UICatalog/Scenarios/DynamicStatusBar.cs
  25. 9 6
      UICatalog/Scenarios/Editor.cs
  26. 8 7
      UICatalog/Scenarios/Generic.cs
  27. 1 1
      UICatalog/Scenarios/GraphViewExample.cs
  28. 1 1
      UICatalog/Scenarios/HexEditor.cs
  29. 1 1
      UICatalog/Scenarios/InteractiveTree.cs
  30. 6 3
      UICatalog/Scenarios/Keys.cs
  31. 1 1
      UICatalog/Scenarios/LineViewExample.cs
  32. 14 7
      UICatalog/Scenarios/MessageBoxes.cs
  33. 1 1
      UICatalog/Scenarios/MultiColouredTable.cs
  34. 2 2
      UICatalog/Scenarios/Notepad.cs
  35. 1 1
      UICatalog/Scenarios/RunTExample.cs
  36. 1 1
      UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  37. 2 2
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  38. 1 1
      UICatalog/Scenarios/SyntaxHighlighting.cs
  39. 1 1
      UICatalog/Scenarios/TabViewExample.cs
  40. 1 1
      UICatalog/Scenarios/TableEditor.cs
  41. 1 1
      UICatalog/Scenarios/TextViewAutocompletePopup.cs
  42. 1 1
      UICatalog/Scenarios/TileViewExperiment.cs
  43. 1 1
      UICatalog/Scenarios/TreeUseCases.cs
  44. 1 1
      UICatalog/Scenarios/TreeViewFileSystem.cs
  45. 1 1
      UICatalog/Scenarios/Unicode.cs
  46. 1 1
      UICatalog/Scenarios/VkeyPacketSimulator.cs
  47. 1 1
      UICatalog/Scenarios/WizardAsView.cs
  48. 34 16
      UICatalog/UICatalog.cs
  49. 4 4
      UnitTests/Application/ApplicationTests.cs
  50. 60 3
      UnitTests/Drivers/ConsoleDriverTests.cs
  51. 1 1
      UnitTests/Drivers/KeyTests.cs
  52. 2 2
      UnitTests/Menus/ContextMenuTests.cs
  53. 99 6
      UnitTests/TopLevels/MessageBoxTests.cs
  54. 1 1
      UnitTests/TopLevels/ToplevelTests.cs
  55. 55 29
      UnitTests/UICatalog/ScenarioTests.cs
  56. 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>

+ 15 - 4
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 (View obj)
+		{
+			_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 ();
 			}
@@ -1509,6 +1518,8 @@ namespace Terminal.Gui {
 						selected = -1;
 					}
 					LastFocused.SetFocus ();
+				} else if (openSubMenu == null || openSubMenu.Count == 0) {
+					CloseAllMenus ();
 				} else {
 					SetFocus ();
 					PositionCursor ();

+ 37 - 1
Terminal.Gui/Windows/Dialog.cs

@@ -62,7 +62,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		/// Use the constructor that does not take a <c>width</c> and <c>height</c> instead.
 		/// </remarks>
-		public Dialog (ustring title, int width, int height, params Button [] buttons) : base (title, padding: padding)
+		public Dialog (ustring title, int width, int height, params Button [] buttons) : base (title: title, padding: padding)
 		{
 			X = Pos.Center ();
 			Y = Pos.Center ();
@@ -119,6 +119,42 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public Dialog (ustring title, params Button [] buttons) : this (title: title, width: 0, height: 0, buttons: buttons) { }
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Dialog"/> class using <see cref="LayoutStyle.Computed"/> positioning, 
+		/// with a <see cref="Border"/> and with an optional set of <see cref="Button"/>s to display
+		/// </summary>
+		/// <param name="title">Title for the dialog.</param>
+		/// <param name="border">The border.</param>
+		/// <param name="buttons">Optional buttons to lay out at the bottom of the dialog.</param>
+		public Dialog (ustring title, Border border, params Button [] buttons)
+			: this (title: title, width: 0, height: 0, buttons: buttons)
+		{
+			Initialize (title, border);
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Dialog"/> class using <see cref="LayoutStyle.Computed"/> positioning, 
+		/// with a <see cref="Border"/> and with an optional set of <see cref="Button"/>s to display
+		/// </summary>
+		/// <param name="title">Title for the dialog.</param>
+		/// <param name="width">Width for the dialog.</param>
+		/// <param name="height">Height for the dialog.</param>
+		/// <param name="border">The border.</param>
+		/// <param name="buttons">Optional buttons to lay out at the bottom of the dialog.</param>
+		public Dialog (ustring title, int width, int height, Border border, params Button [] buttons)
+			: this (title: title, width: width, height: height, buttons: buttons)
+		{
+			Initialize (title, border);
+		}
+
+		void Initialize (ustring title, Border border)
+		{
+			if (border != null) {
+				Border = border;
+				Border.Title = title;
+			}
+		}
+
 		/// <summary>
 		/// Adds a <see cref="Button"/> to the <see cref="Dialog"/>, its layout will be controlled by the <see cref="Dialog"/>
 		/// </summary>

+ 43 - 28
Terminal.Gui/Windows/MessageBox.cs

@@ -38,7 +38,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static int Query (int width, int height, ustring title, ustring message, params ustring [] buttons)
 		{
-			return QueryFull (false, width, height, title, message, 0, null, buttons);
+			return QueryFull (false, width, height, title, message, 0, null, true, buttons);
 		}
 
 		/// <summary>
@@ -54,7 +54,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static int Query (ustring title, ustring message, params ustring [] buttons)
 		{
-			return QueryFull (false, 0, 0, title, message, 0, null, buttons);
+			return QueryFull (false, 0, 0, title, message, 0, null, true, buttons);
 		}
 
 		/// <summary>
@@ -71,7 +71,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static int ErrorQuery (int width, int height, ustring title, ustring message, params ustring [] buttons)
 		{
-			return QueryFull (true, width, height, title, message, 0, null, buttons);
+			return QueryFull (true, width, height, title, message, 0, null, true, buttons);
 		}
 
 		/// <summary>
@@ -87,7 +87,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static int ErrorQuery (ustring title, ustring message, params ustring [] buttons)
 		{
-			return QueryFull (true, 0, 0, title, message, 0, null, buttons);
+			return QueryFull (true, 0, 0, title, message, 0, null, true, buttons);
 		}
 
 		/// <summary>
@@ -105,7 +105,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static int Query (int width, int height, ustring title, ustring message, int defaultButton = 0, params ustring [] buttons)
 		{
-			return QueryFull (false, width, height, title, message, defaultButton, null, buttons);
+			return QueryFull (false, width, height, title, message, defaultButton, null, true, buttons);
 		}
 
 		/// <summary>
@@ -122,7 +122,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static int Query (ustring title, ustring message, int defaultButton = 0, params ustring [] buttons)
 		{
-			return QueryFull (false, 0, 0, title, message, defaultButton, null, buttons);
+			return QueryFull (false, 0, 0, title, message, defaultButton, null, true, buttons);
 		}
 
 		/// <summary>
@@ -135,13 +135,14 @@ namespace Terminal.Gui {
 		/// <param name="message">Message to display, might contain multiple lines.</param>
 		/// <param name="defaultButton">Index of the default button.</param>
 		/// <param name="border">The border settings.</param>
+		/// <param name="wrapMessagge">If wrap the message or not.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
 		/// Use <see cref="Query(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
 		/// </remarks>
-		public static int Query (int width, int height, ustring title, ustring message, int defaultButton = 0, Border border = null, params ustring [] buttons)
+		public static int Query (int width, int height, ustring title, ustring message, int defaultButton = 0, Border border = null, bool wrapMessagge = true, params ustring [] buttons)
 		{
-			return QueryFull (false, width, height, title, message, defaultButton, border, buttons);
+			return QueryFull (false, width, height, title, message, defaultButton, border, wrapMessagge, buttons);
 		}
 
 		/// <summary>
@@ -152,14 +153,15 @@ namespace Terminal.Gui {
 		/// <param name="message">Message to display, might contain multiple lines.</param>
 		/// <param name="defaultButton">Index of the default button.</param>
 		/// <param name="border">The border settings.</param>
+		/// <param name="wrapMessagge">If wrap the message or not.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
 		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
 		/// from the size of the message and buttons.
 		/// </remarks>
-		public static int Query (ustring title, ustring message, int defaultButton = 0, Border border = null, params ustring [] buttons)
+		public static int Query (ustring title, ustring message, int defaultButton = 0, Border border = null, bool wrapMessagge = true, params ustring [] buttons)
 		{
-			return QueryFull (false, 0, 0, title, message, defaultButton, border, buttons);
+			return QueryFull (false, 0, 0, title, message, defaultButton, border, wrapMessagge, buttons);
 		}
 
 
@@ -178,7 +180,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static int ErrorQuery (int width, int height, ustring title, ustring message, int defaultButton = 0, params ustring [] buttons)
 		{
-			return QueryFull (true, width, height, title, message, defaultButton, null, buttons);
+			return QueryFull (true, width, height, title, message, defaultButton, null, true, buttons);
 		}
 
 		/// <summary>
@@ -195,7 +197,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static int ErrorQuery (ustring title, ustring message, int defaultButton = 0, params ustring [] buttons)
 		{
-			return QueryFull (true, 0, 0, title, message, defaultButton, null, buttons);
+			return QueryFull (true, 0, 0, title, message, defaultButton, null, true, buttons);
 		}
 
 		/// <summary>
@@ -208,13 +210,14 @@ namespace Terminal.Gui {
 		/// <param name="message">Message to display, might contain multiple lines.</param>
 		/// <param name="defaultButton">Index of the default button.</param>
 		/// <param name="border">The border settings.</param>
+		/// <param name="wrapMessagge">If wrap the message or not.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
 		/// Use <see cref="ErrorQuery(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
 		/// </remarks>
-		public static int ErrorQuery (int width, int height, ustring title, ustring message, int defaultButton = 0, Border border = null, params ustring [] buttons)
+		public static int ErrorQuery (int width, int height, ustring title, ustring message, int defaultButton = 0, Border border = null, bool wrapMessagge = true, params ustring [] buttons)
 		{
-			return QueryFull (true, width, height, title, message, defaultButton, border, buttons);
+			return QueryFull (true, width, height, title, message, defaultButton, border, wrapMessagge, buttons);
 		}
 
 		/// <summary>
@@ -225,25 +228,26 @@ namespace Terminal.Gui {
 		/// <param name="message">Message to display, might contain multiple lines.</param>
 		/// <param name="defaultButton">Index of the default button.</param>
 		/// <param name="border">The border settings.</param>
+		/// <param name="wrapMessagge">If wrap the message or not.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
 		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
 		/// from the size of the title, message. and buttons.
 		/// </remarks>
-		public static int ErrorQuery (ustring title, ustring message, int defaultButton = 0, Border border = null, params ustring [] buttons)
+		public static int ErrorQuery (ustring title, ustring message, int defaultButton = 0, Border border = null, bool wrapMessagge = true, params ustring [] buttons)
 		{
-			return QueryFull (true, 0, 0, title, message, defaultButton, border, buttons);
+			return QueryFull (true, 0, 0, title, message, defaultButton, border, wrapMessagge, buttons);
 		}
 
 		static int QueryFull (bool useErrorColors, int width, int height, ustring title, ustring message,
-			int defaultButton = 0, Border border = null, params ustring [] buttons)
+			int defaultButton = 0, Border border = null, bool wrapMessagge = true, params ustring [] buttons)
 		{
 			int defaultWidth = 50;
 			if (defaultWidth > Application.Driver.Cols / 2) {
 				defaultWidth = (int)(Application.Driver.Cols * 0.60f);
 			}
 			int maxWidthLine = TextFormatter.MaxWidthLine (message);
-			if (maxWidthLine > Application.Driver.Cols) {
+			if (wrapMessagge && maxWidthLine > Application.Driver.Cols) {
 				maxWidthLine = Application.Driver.Cols;
 			}
 			if (width == 0) {
@@ -251,10 +255,14 @@ namespace Terminal.Gui {
 			} else {
 				maxWidthLine = width;
 			}
-			int textWidth = Math.Min (TextFormatter.MaxWidth (message, maxWidthLine), Application.Driver.Cols);
+			int textWidth = TextFormatter.MaxWidth (message, maxWidthLine);
 			int textHeight = TextFormatter.MaxLines (message, textWidth); // message.Count (ustring.Make ('\n')) + 1;
-			int msgboxHeight = Math.Min (Math.Max (1, textHeight) + 4, Application.Driver.Rows); // textHeight + (top + top padding + buttons + bottom)
+			int msgboxHeight = Math.Max (1, textHeight) + 4; // textHeight + (top + top padding + buttons + bottom)
 
+			if (wrapMessagge) {
+				textWidth = Math.Min (textWidth, Application.Driver.Cols);
+				msgboxHeight = Math.Min (msgboxHeight, Application.Driver.Rows);
+			}
 			// Create button array for Dialog
 			int count = 0;
 			List<Button> buttonList = new List<Button> ();
@@ -273,22 +281,24 @@ namespace Terminal.Gui {
 			// Create Dialog (retain backwards compat by supporting specifying height/width)
 			Dialog d;
 			if (width == 0 & height == 0) {
-				d = new Dialog (title, buttonList.ToArray ()) {
+				d = new Dialog (title, border, buttonList.ToArray ()) {
 					Height = msgboxHeight
 				};
 			} else {
-				d = new Dialog (title, width, Math.Max (height, 4), buttonList.ToArray ());
-			}
-
-			if (border != null) {
-				d.Border = border;
+				d = new Dialog (title, width, Math.Max (height, 4), border, buttonList.ToArray ());
 			}
 
 			if (useErrorColors) {
 				d.ColorScheme = Colors.Error;
+				d.Border.BorderBrush = Colors.Error.Normal.Foreground;
+				d.Border.Background = Colors.Error.Normal.Background;
+			} else {
+				d.ColorScheme = Colors.Dialog;
+				d.Border.BorderBrush = Colors.Dialog.Normal.Foreground;
+				d.Border.Background = Colors.Dialog.Normal.Background;
 			}
 
-			if (message != null) {
+			if (!ustring.IsNullOrEmpty (message)) {
 				var l = new Label (message) {
 					LayoutStyle = LayoutStyle.Computed,
 					TextAlignment = TextAlignment.Centered,
@@ -303,7 +313,12 @@ namespace Terminal.Gui {
 
 			if (width == 0 & height == 0) {
 				// Dynamically size Width
-				d.Width = Math.Min (Math.Max (maxWidthLine, Math.Max (title.ConsoleWidth, Math.Max (textWidth + 2, d.GetButtonsWidth () + d.buttons.Count + 2))), Application.Driver.Cols); // textWidth + (left + padding + padding + right)
+				var dWidth = Math.Max (maxWidthLine, Math.Max (title.ConsoleWidth, Math.Max (textWidth + 2, d.GetButtonsWidth () + d.buttons.Count + 2))); // textWidth + (left + padding + padding + right)
+				if (wrapMessagge) {
+					d.Width = Math.Min (dWidth, Application.Driver.Cols);
+				} else {
+					d.Width = dWidth;
+				}
 			}
 
 			// Setup actions

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

+ 14 - 7
UICatalog/Scenarios/MessageBoxes.cs

@@ -125,6 +125,11 @@ namespace UICatalog.Scenarios {
 			};
 			frame.Add (defaultButtonEdit);
 
+			var border = new Border () {
+				Effect3D = true,
+				BorderStyle = BorderStyle.Single
+			};
+
 			label = new Label ("Style:") {
 				X = 0,
 				Y = Pos.Bottom (label),
@@ -133,16 +138,13 @@ namespace UICatalog.Scenarios {
 				TextAlignment = Terminal.Gui.TextAlignment.Right,
 			};
 			frame.Add (label);
+
 			var styleRadioGroup = new RadioGroup (new ustring [] { "_Query", "_Error" }) {
 				X = Pos.Right (label) + 1,
 				Y = Pos.Top (label),
 			};
 			frame.Add (styleRadioGroup);
 
-			var border = new Border () {
-				Effect3D = true,
-				BorderStyle = BorderStyle.Single
-			};
 			var ckbEffect3D = new CheckBox ("Effect3D", true) {
 				X = Pos.Right (label) + 1,
 				Y = Pos.Top (label) + 2
@@ -151,11 +153,16 @@ namespace UICatalog.Scenarios {
 				border.Effect3D = (bool)!e;
 			};
 			frame.Add (ckbEffect3D);
+			var ckbWrapMessage = new CheckBox ("Wrap Message", true) {
+				X = Pos.Right (label) + 1,
+				Y = Pos.Top (label) + 3
+			};
+			frame.Add (ckbWrapMessage);
 
 			void Top_Loaded ()
 			{
 				frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit) + Dim.Height (messageEdit)
-				+ Dim.Height (numButtonsEdit) + Dim.Height (defaultButtonEdit) + Dim.Height (styleRadioGroup) + 2 + Dim.Height (ckbEffect3D);
+				+ Dim.Height (numButtonsEdit) + Dim.Height (defaultButtonEdit) + Dim.Height (styleRadioGroup) + 2 + Dim.Height (ckbEffect3D) + Dim.Height (ckbWrapMessage);
 				Application.Top.Loaded -= Top_Loaded;
 			}
 			Application.Top.Loaded += Top_Loaded;
@@ -196,9 +203,9 @@ namespace UICatalog.Scenarios {
 						btns.Add (NumberToWords.Convert (i));
 					}
 					if (styleRadioGroup.SelectedItem == 0) {
-						buttonPressedLabel.Text = $"{MessageBox.Query (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), defaultButton, border, btns.ToArray ())}";
+						buttonPressedLabel.Text = $"{MessageBox.Query (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), defaultButton, border, (bool)ckbWrapMessage.Checked, btns.ToArray ())}";
 					} else {
-						buttonPressedLabel.Text = $"{MessageBox.ErrorQuery (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), defaultButton, border, btns.ToArray ())}";
+						buttonPressedLabel.Text = $"{MessageBox.ErrorQuery (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), defaultButton, border, (bool)ckbWrapMessage.Checked, btns.ToArray ())}";
 					}
 				} catch (FormatException) {
 					buttonPressedLabel.Text = "Invalid Options";

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

+ 34 - 16
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>
@@ -52,7 +54,7 @@ namespace UICatalog {
 		//[SerializableConfigurationProperty (Scope = typeof (AppScope), OmitClassName = true), JsonPropertyName ("UICatalog.StatusBar")]
 		//public static bool ShowStatusBar { get; set; } = true;
 
-		[SerializableConfigurationProperty (Scope = typeof (AppScope), OmitClassName = true), JsonPropertyName("UICatalog.StatusBar")]
+		[SerializableConfigurationProperty (Scope = typeof (AppScope), OmitClassName = true), JsonPropertyName ("UICatalog.StatusBar")]
 		public static bool ShowStatusBar { get; set; } = true;
 
 		static readonly FileSystemWatcher _currentDirWatcher = new FileSystemWatcher ();
@@ -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 ();
@@ -127,7 +135,8 @@ namespace UICatalog {
 			VerifyObjectsWereDisposed ();
 		}
 
-		private static void StopConfigFileWatcher() {
+		private static void StopConfigFileWatcher ()
+		{
 			_currentDirWatcher.EnableRaisingEvents = false;
 			_currentDirWatcher.Changed -= ConfigFileChanged;
 			_currentDirWatcher.Created -= ConfigFileChanged;
@@ -137,7 +146,7 @@ namespace UICatalog {
 			_homeDirWatcher.Created -= ConfigFileChanged;
 		}
 
-		private static void StartConfigFileWatcher()
+		private static void StartConfigFileWatcher ()
 		{
 			// Setup a file system watcher for `./.tui/`
 			_currentDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
@@ -193,10 +202,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 +228,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
@@ -258,7 +276,7 @@ namespace UICatalog {
 						new MenuItem ("_gui.cs API Overview", "", () => OpenUrl ("https://gui-cs.github.io/Terminal.Gui/articles/overview.html"), null, null, Key.F1),
 						new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, Key.F2),
 						new MenuItem ("_About...",
-							"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", _aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
+							"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", _aboutMessage.ToString(), 0, null, false, "_Ok"), null, null, Key.CtrlMask | Key.A),
 					}),
 				});
 
@@ -269,7 +287,7 @@ namespace UICatalog {
 				OS = new StatusItem (Key.CharMask, "OS:", null);
 
 				StatusBar = new StatusBar () {
-					Visible = UICatalogApp.ShowStatusBar					
+					Visible = UICatalogApp.ShowStatusBar
 				};
 
 				StatusBar.Items = new StatusItem [] {
@@ -303,7 +321,7 @@ namespace UICatalog {
 				ContentPane.Border.BorderStyle = BorderStyle.Single;
 				ContentPane.SetSplitterPos (0, 25);
 				ContentPane.ShortcutAction = () => ContentPane.SetFocus ();
-					
+
 				CategoryListView = new ListView (_categories) {
 					X = 0,
 					Y = 0,
@@ -351,7 +369,7 @@ namespace UICatalog {
 
 				ConfigurationManager.Applied += ConfigAppliedHandler;
 			}
- 
+
 			void LoadedHandler ()
 			{
 				ConfigChanged ();
@@ -386,7 +404,7 @@ namespace UICatalog {
 				ConfigurationManager.Applied -= ConfigAppliedHandler;
 				Unloaded -= UnloadedHandler;
 			}
-			
+
 			void ConfigAppliedHandler (ConfigurationManagerEventArgs a)
 			{
 				ConfigChanged ();
@@ -575,9 +593,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);
@@ -614,7 +632,7 @@ namespace UICatalog {
 				if (_topLevelColorScheme == null || !Colors.ColorSchemes.ContainsKey (_topLevelColorScheme)) {
 					_topLevelColorScheme = "Base";
 				}
-				
+
 				_themeMenuItems = ((UICatalogTopLevel)Application.Top).CreateThemeMenuItems ();
 				_themeMenuBarItem.Children = _themeMenuItems;
 
@@ -700,7 +718,7 @@ namespace UICatalog {
 			// after a scenario was selected to run. This proves the main UI Catalog
 			// 'app' closed cleanly.
 			foreach (var inst in Responder.Instances) {
-				
+
 				Debug.Assert (inst.WasDisposed);
 			}
 			Responder.Instances.Clear ();

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

+ 99 - 6
UnitTests/TopLevels/MessageBoxTests.cs

@@ -157,7 +157,7 @@ namespace Terminal.Gui.TopLevelTests {
 		}
 
 		[Fact, AutoInitShutdown]
-		public void MessageBox_With_A_Label_Without_Spaces ()
+		public void MessageBox_With_A_Label_Without_Spaces_WrapMessagge_True ()
 		{
 			var iterations = -1;
 			Application.Begin (Application.Top);
@@ -196,8 +196,7 @@ namespace Terminal.Gui.TopLevelTests {
 │ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff│
 │ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff│
 │                                   [◦ ok ◦]                                   │
-└──────────────────────────────────────────────────────────────────────────────┘
-", output);
+└──────────────────────────────────────────────────────────────────────────────┘", output);
 
 					Application.RequestStop ();
 				}
@@ -207,7 +206,7 @@ namespace Terminal.Gui.TopLevelTests {
 		}
 
 		[Fact, AutoInitShutdown]
-		public void MessageBox_With_A_Label_With_Spaces ()
+		public void MessageBox_With_A_Label_With_Spaces_WrapMessagge_True ()
 		{
 			var iterations = -1;
 			Application.Begin (Application.Top);
@@ -250,8 +249,102 @@ namespace Terminal.Gui.TopLevelTests {
 │ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff │
 │ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff │
 │                                   [◦ ok ◦]                                   │
-└──────────────────────────────────────────────────────────────────────────────┘
-", output);
+└──────────────────────────────────────────────────────────────────────────────┘", output);
+
+					Application.RequestStop ();
+				}
+			};
+
+			Application.Run ();
+		}
+
+		[Fact, AutoInitShutdown]
+		public void MessageBox_With_A_Label_Without_Spaces_WrapMessagge_False ()
+		{
+			var iterations = -1;
+			Application.Begin (Application.Top);
+
+			Application.Iteration += () => {
+				iterations++;
+
+				if (iterations == 0) {
+					MessageBox.Query ("mywindow", new string ('f', 2000), 0, null, false, "ok");
+
+					Application.RequestStop ();
+				} else if (iterations == 1) {
+					Application.Refresh ();
+					TestHelpers.AssertDriverContentsWithFrameAre (@"
+────────────────────────────────────────────────────────────────────────────────
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+                                                                                
+                                    [◦ ok ◦]                                    
+────────────────────────────────────────────────────────────────────────────────", output);
+
+					Application.RequestStop ();
+				}
+			};
+
+			Application.Run ();
+		}
+
+		[Fact, AutoInitShutdown]
+		public void MessageBox_With_A_Label_With_Spaces_WrapMessagge_False ()
+		{
+			var iterations = -1;
+			Application.Begin (Application.Top);
+
+			Application.Iteration += () => {
+				iterations++;
+
+				if (iterations == 0) {
+					var sb = new StringBuilder ();
+					for (int i = 0; i < 1000; i++)
+						sb.Append ("ff ");
+
+					MessageBox.Query ("mywindow", sb.ToString (), 0, null, false, "ok");
+
+					Application.RequestStop ();
+				} else if (iterations == 1) {
+					Application.Refresh ();
+					TestHelpers.AssertDriverContentsWithFrameAre (@"
+────────────────────────────────────────────────────────────────────────────────
+ ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff f
+                                                                                
+                                    [◦ ok ◦]                                    
+────────────────────────────────────────────────────────────────────────────────", output);
+
+					Application.RequestStop ();
+				}
+			};
+
+			Application.Run ();
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData ("", true)]
+		[InlineData ("", false)]
+		[InlineData ("\n", true)]
+		[InlineData ("\n", false)]
+		public void MessageBox_With_A_Empty_Message_Or_A_NewLline_WrapMessagge_True_Or_False (string message, bool wrapMessage)
+		{
+			var iterations = -1;
+			Application.Begin (Application.Top);
+
+			Application.Iteration += () => {
+				iterations++;
+
+				if (iterations == 0) {
+					MessageBox.Query ("mywindow", message, 0, null, wrapMessage, "ok");
+
+					Application.RequestStop ();
+				} else if (iterations == 1) {
+					Application.Refresh ();
+					TestHelpers.AssertDriverContentsWithFrameAre (@"
+                ┌ mywindow ────────────────────────────────────┐
+                │                                              │
+                │                                              │
+                │                   [◦ ok ◦]                   │
+                └──────────────────────────────────────────────┘", output);
 
 					Application.RequestStop ();
 				}

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