Преглед на файлове

Robustness improvements in prep for implementing Virtual Terminal Sequences (#3094)

* Fixes #2616. Support combining sequences that don't normalize

* Decouples Application from ConsoleDriver in TestHelpers

* Updates driver tests to match new arch

* Start on making all driver tests test all drivers

* Improves handling if combining marks.

* Fix unit tests fails.

* Fix unit tests fails.

* Handling combining mask.

* Tying to fix this unit test that sometimes fail.

* Add support for combining mask on NetDriver.

* Enable CombiningMarks as List<Rune>.

* Prevents combining marks on invalid runes default and space.

* Formatting for CI tests.

* Fix non-normalized combining mark to add 1 to Col.

* Reformatting for retest the CI.

* Forces non-normalized CMs to be ignored.

* Initial experiment

* Created ANSiDriver. Updated UI Catalog command line handling

* Fixed ForceDriver logic

* Fixed ForceDriver logic

* Updating P/Invoke

* Force16 colors WIP

* Fixed 16 colo mode

* Updated unit tests

* UI catalog tweak

* Added chinese scenario from bdisp

* Disabled AnsiDriver unit tests for now.

* Code cleanup

* Initial commit (fork from v2_fixes_2610_WT_VTS)

* Code cleanup

* Removed nativemethods.txt

* Removed not needed native stuff

* Code cleanup

* Ensures command line handler doesn't eat exceptions

---------

Co-authored-by: BDisp <[email protected]>
Tig преди 1 година
родител
ревизия
7af54f369d

+ 66 - 33
Terminal.Gui/Application.cs

@@ -33,15 +33,20 @@ namespace Terminal.Gui;
 /// </remarks>
 public static partial class Application {
 	/// <summary>
-	/// Gets the <see cref="ConsoleDriver"/> that has been selected. See also <see cref="UseSystemConsole"/>.
+	/// Gets the <see cref="ConsoleDriver"/> that has been selected. See also <see cref="ForceDriver"/>.
 	/// </summary>
 	public static ConsoleDriver Driver { get; internal set; }
 
 	/// <summary>
-	/// If <see langword="true"/>, forces the use of the System.Console-based (see <see cref="NetDriver"/>) driver. The default is <see langword="false"/>.
+	/// Forces the use of the specified driver (one of "fake", "ansi", "curses", "net", or "windows"). If
+	/// not specified, the driver is selected based on the platform.
 	/// </summary>
+	/// <remarks>
+	/// Note, <see cref="Application.Init(ConsoleDriver, string)"/> will override this configuration setting if
+	/// called with either `driver` or `driverName` specified.
+	/// </remarks>
 	[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-	public static bool UseSystemConsole { get; set; } = false;
+	public static string ForceDriver { get; set; } = string.Empty;
 
 	/// <summary>
 	/// Gets or sets whether <see cref="Application.Driver"/> will be forced to output only the 16 colors defined in <see cref="ColorName"/>.
@@ -98,14 +103,13 @@ public static partial class Application {
 	/// </para>
 	/// <para>
 	/// The <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> function 
-	/// combines <see cref="Init(ConsoleDriver)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
+	/// combines <see cref="Init(ConsoleDriver, string)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
 	/// into a single call. An application cam use <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> 
-	/// without explicitly calling <see cref="Init(ConsoleDriver)"/>.
+	/// without explicitly calling <see cref="Init(ConsoleDriver, string)"/>.
 	/// </para>
-	/// <param name="driver">
-	/// The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
-	/// platform will be used (see <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, and <see cref="NetDriver"/>).</param>
-	public static void Init (ConsoleDriver driver = null) => InternalInit (() => Toplevel.Create (), driver);
+	/// <param name="driver">The <see cref="ConsoleDriver"/> to use. If neither <paramref name="driver"/> or <paramref name="driverName"/> are specified the default driver for the platform will be used.</param>
+	/// <param name="driverName">The short name (e.g. "net", "windows", "ansi", "fake", or "curses") of the <see cref="ConsoleDriver"/> to use. If neither <paramref name="driver"/> or <paramref name="driverName"/> are specified the default driver for the platform will be used.</param>
+	public static void Init (ConsoleDriver driver = null, string driverName = null) => InternalInit (Toplevel.Create, driver, driverName);
 
 	internal static bool _initialized = false;
 	internal static int _mainThreadId = -1;
@@ -119,7 +123,7 @@ public static partial class Application {
 	// Unit Tests - To initialize the app with a custom Toplevel, using the FakeDriver. calledViaRunT will be false, causing all state to be reset.
 	// 
 	// calledViaRunT: If false (default) all state will be reset. If true the state will not be reset.
-	internal static void InternalInit (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, bool calledViaRunT = false)
+	internal static void InternalInit (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, string driverName = null, bool calledViaRunT = false)
 	{
 		if (_initialized && driver == null) {
 			return;
@@ -147,15 +151,28 @@ public static partial class Application {
 		Load (true);
 		Apply ();
 
-		Driver ??= Environment.OSVersion.Platform switch {
-			_ when _forceFakeConsole => new FakeDriver (), // for unit testing only
-			_ when UseSystemConsole => new NetDriver (),
-			PlatformID.Win32NT or PlatformID.Win32S or PlatformID.Win32Windows => new WindowsDriver (),
-			_ => new CursesDriver ()
-		};
+		// Ignore Configuration for ForceDriver if driverName is specified
+		if (!string.IsNullOrEmpty (driverName)) {
+			ForceDriver = driverName;
+		}
 
 		if (Driver == null) {
-			throw new InvalidOperationException ("Init could not determine the ConsoleDriver to use.");
+			var p = Environment.OSVersion.Platform;
+			if (string.IsNullOrEmpty (ForceDriver)) {
+				if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
+					Driver = new WindowsDriver ();
+				} else {
+					Driver = new CursesDriver ();
+				}
+			} else {
+				var drivers = GetDriverTypes ();
+				var driverType = drivers.FirstOrDefault (t => t.Name.ToLower () == ForceDriver.ToLower ());
+				if (driverType != null) {
+					Driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+				} else {
+					throw new ArgumentException ($"Invalid driver name: {ForceDriver}. Valid names are {string.Join (", ", drivers.Select (t => t.Name))}");
+				}
+			}
 		}
 
 		try {
@@ -168,10 +185,10 @@ public static partial class Application {
 			throw new InvalidOperationException ("Unable to initialize the console. This can happen if the console is already in use by another process or in unit tests.", ex);
 		}
 
-		Driver.SizeChanged += Driver_SizeChanged;
-		Driver.KeyDown += Driver_KeyDown;
-		Driver.KeyUp += Driver_KeyUp;
-		Driver.MouseEvent += Driver_MouseEvent;
+		Driver.SizeChanged += (s, args) => OnSizeChanging (args);
+		Driver.KeyDown += (s, args) => OnKeyDown (args);
+		Driver.KeyUp += (s, args) => OnKeyUp (args);
+		Driver.MouseEvent += (s, args) => OnMouseEvent (args);
 
 		SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext ());
 
@@ -190,12 +207,29 @@ public static partial class Application {
 
 	static void Driver_MouseEvent (object sender, MouseEventEventArgs e) => OnMouseEvent (e);
 
+	/// <summary>
+	/// Gets of list of <see cref="ConsoleDriver"/> types that are available.
+	/// </summary>
+	/// <returns></returns>
+	public static List<Type> GetDriverTypes ()
+	{
+		// use reflection to get the list of drivers
+		var driverTypes = new List<Type> ();
+		foreach (var asm in AppDomain.CurrentDomain.GetAssemblies ()) {
+			foreach (var type in asm.GetTypes ()) {
+				if (type.IsSubclassOf (typeof (ConsoleDriver)) && !type.IsAbstract) {
+					driverTypes.Add (type);
+				}
+			}
+		}
+		return driverTypes;
+	}
 
 	/// <summary>
-	/// Shutdown an application initialized with <see cref="Init(ConsoleDriver)"/>.
+	/// Shutdown an application initialized with <see cref="Init"/>.
 	/// </summary>
 	/// <remarks>
-	/// Shutdown must be called for every call to <see cref="Init(ConsoleDriver)"/> or <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>
+	/// Shutdown must be called for every call to <see cref="Init"/> or <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>
 	/// to ensure all resources are cleaned up (Disposed) and terminal settings are restored.
 	/// </remarks>
 	public static void Shutdown ()
@@ -394,7 +428,7 @@ public static partial class Application {
 	/// Runs the application by calling <see cref="Run(Toplevel, Func{Exception, bool})"/> 
 	/// with a new instance of the specified <see cref="Toplevel"/>-derived class.
 	/// <para>
-	/// Calling <see cref="Init(ConsoleDriver)"/> first is not needed as this function will initialize the application.
+	/// Calling <see cref="Init"/> first is not needed as this function will initialize the application.
 	/// </para>
 	/// <para>
 	/// <see cref="Shutdown"/> must be called when the application is closing (typically after Run> has 
@@ -407,7 +441,7 @@ public static partial class Application {
 	/// <param name="errorHandler"></param>
 	/// <param name="driver">The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
 	/// platform will be used (<see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>).
-	/// Must be <see langword="null"/> if <see cref="Init(ConsoleDriver)"/> has already been called. 
+	/// Must be <see langword="null"/> if <see cref="Init"/> has already been called. 
 	/// </param>
 	public static void Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null) where T : Toplevel, new ()
 	{
@@ -429,7 +463,7 @@ public static partial class Application {
 			}
 		} else {
 			// Init() has NOT been called.
-			InternalInit (() => new T (), driver, true);
+			InternalInit (() => new T (), driver, null, true);
 			Run (Top, errorHandler);
 		}
 	}
@@ -838,13 +872,12 @@ public static partial class Application {
 	#endregion Run (Begin, Run, End)
 
 	#region Toplevel handling
-
 	/// <summary>
 	/// Holds the stack of TopLevel views.
 	/// </summary>
 	// BUGBUG: Techncally, this is not the full lst of TopLevels. THere be dragons hwre. E.g. see how Toplevel.Id is used. What
 	// about TopLevels that are just a SubView of another View?
-	static readonly Stack<Toplevel> _topLevels = new ();
+	static readonly Stack<Toplevel> _topLevels = new Stack<Toplevel> ();
 
 	/// <summary>
 	/// The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Application.Top"/>)
@@ -1296,7 +1329,7 @@ public static partial class Application {
 	#endregion Mouse handling
 
 	#region Keyboard handling
-	static Key _alternateForwardKey = new (KeyCode.PageDown | KeyCode.CtrlMask);
+	static Key _alternateForwardKey = new Key (KeyCode.PageDown | KeyCode.CtrlMask);
 
 	/// <summary>
 	/// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.
@@ -1320,7 +1353,7 @@ public static partial class Application {
 		}
 	}
 
-	static Key _alternateBackwardKey = new (KeyCode.PageUp | KeyCode.CtrlMask);
+	static Key _alternateBackwardKey = new Key (KeyCode.PageUp | KeyCode.CtrlMask);
 
 	/// <summary>
 	/// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.
@@ -1344,7 +1377,7 @@ public static partial class Application {
 		}
 	}
 
-	static Key _quitKey = new (KeyCode.Q | KeyCode.CtrlMask);
+	static Key _quitKey = new Key (KeyCode.Q | KeyCode.CtrlMask);
 
 	/// <summary>
 	/// Gets or sets the key to quit the application.
@@ -1481,8 +1514,8 @@ public static partial class Application {
 	}
 	#endregion Keyboard handling
 }
-
 /// <summary>
 /// Event arguments for the <see cref="Application.Iteration"/> event.
 /// </summary>
-public class IterationEventArgs { }
+public class IterationEventArgs {
+}

+ 4 - 2
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -257,7 +257,7 @@ public static partial class ConfigurationManager {
 
 	/// <summary>
 	/// Resets the state of <see cref="ConfigurationManager"/>. Should be called whenever a new app session
-	/// (e.g. in <see cref="Application.Init(ConsoleDriver)"/> starts. Called by <see cref="Load"/>
+	/// (e.g. in <see cref="Application.Init"/> starts. Called by <see cref="Load"/>
 	/// if the <c>reset</c> parameter is <see langword="true"/>.
 	/// </summary>
 	/// <remarks>
@@ -412,7 +412,9 @@ public static partial class ConfigurationManager {
 	{
 		Debug.WriteLine ($"ConfigurationManager.Load()");
 
-		if (reset) Reset ();
+		if (reset) {
+			Reset ();
+		}
 
 		// LibraryResources is always loaded by Reset
 		if (Locations == ConfigLocations.All) {

+ 27 - 5
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -58,12 +58,24 @@ public abstract class ConsoleDriver {
 	/// <summary>
 	/// The number of columns visible in the terminal.
 	/// </summary>
-	public virtual int Cols { get; internal set; }
+	public virtual int Cols {
+		get => _cols;
+		internal set {
+			_cols = value;
+			ClearContents();
+		}
+	}
 
 	/// <summary>
 	/// The number of rows visible in the terminal.
 	/// </summary>
-	public virtual int Rows { get; internal set; }
+	public virtual int Rows {
+		get => _rows;
+		internal set {
+			_rows = value;
+			ClearContents();
+		}
+	}
 
 	/// <summary>
 	/// The leftmost column in the terminal.
@@ -152,11 +164,19 @@ public abstract class ConsoleDriver {
 			rune = rune.MakePrintable ();
 			runeWidth = rune.GetColumns ();
 			if (runeWidth == 0 && rune.IsCombiningMark ()) {
+				// AtlasEngine does not support NON-NORMALIZED combining marks in a way
+				// compatible with the driver architecture. Any CMs (except in the first col)
+				// are correctly combined with the base char, but are ALSO treated as 1 column
+				// width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é  ]`.
+				// 
+				// Until this is addressed (see Issue #), we do our best by 
+				// a) Attempting to normalize any CM with the base char to it's left
+				// b) Ignoring any CMs that don't normalize
 				if (Col > 0) {
 					if (Contents [Row, Col - 1].CombiningMarks.Count > 0) {
 						// Just add this mark to the list
 						Contents [Row, Col - 1].CombiningMarks.Add (rune);
-						// Don't move to next column (let the driver figure out what to do).
+						// Ignore. Don't move to next column (let the driver figure out what to do).
 					} else {
 						// Attempt to normalize the cell to our left combined with this mark
 						string combined = Contents [Row, Col - 1].Rune + rune.ToString ();
@@ -167,11 +187,11 @@ public abstract class ConsoleDriver {
 							// It normalized! We can just set the Cell to the left with the
 							// normalized codepoint 
 							Contents [Row, Col - 1].Rune = (Rune)normalized [0];
-							// Don't move to next column because we're already there
+							// Ignore. Don't move to next column because we're already there
 						} else {
 							// It didn't normalize. Add it to the Cell to left's CM list
 							Contents [Row, Col - 1].CombiningMarks.Add (rune);
-							// Don't move to next column (let the driver figure out what to do).
+							// Ignore. Don't move to next column (let the driver figure out what to do).
 						}
 					}
 					Contents [Row, Col - 1].Attribute = CurrentAttribute;
@@ -398,6 +418,8 @@ public abstract class ConsoleDriver {
 	}
 
 	Attribute _currentAttribute;
+	int _cols;
+	int _rows;
 
 	/// <summary>
 	/// The <see cref="Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or <see cref="AddStr"/> call.

+ 4 - 0
Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs

@@ -90,6 +90,10 @@ public static class ConsoleKeyMapping {
 	[DllImport ("user32.dll")]
 	extern static bool GetKeyboardLayoutName ([Out] StringBuilder pwszKLID);
 
+	/// <summary>
+	/// Retrieves the name of the active input locale identifier (formerly called the keyboard layout) for the calling thread.
+	/// </summary>
+	/// <returns></returns>
 	public static string GetKeyboardLayoutName ()
 	{
 		if (Environment.OSVersion.Platform != PlatformID.Win32NT) {

+ 8 - 2
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -17,12 +17,18 @@ namespace Terminal.Gui;
 class CursesDriver : ConsoleDriver {
 	public override int Cols {
 		get => Curses.Cols;
-		internal set => Curses.Cols = value;
+		internal set {
+			Curses.Cols = value;
+			ClearContents();
+		}
 	}
 
 	public override int Rows {
 		get => Curses.Lines;
-		internal set => Curses.Lines = value;
+		internal set {
+			Curses.Lines = value;
+			ClearContents();
+		}
 	}
 
 	CursorVisibility? _initialCursorVisibility = null;

+ 19 - 5
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -170,10 +170,10 @@ public static class EscSeqUtils {
 	/// <summary>
 	/// ESC [ y ; x H - CUP Cursor Position - Cursor moves to x ; y coordinate within the viewport, where x is the column of the y line
 	/// </summary>
-	/// <param name="x"></param>
-	/// <param name="y"></param>
+	/// <param name="row">Origin is (1,1).</param>
+	/// <param name="col">Origin is (1,1).</param>
 	/// <returns></returns>
-	public static string CSI_SetCursorPosition (int y, int x) => $"{CSI}{y};{x}H";
+	public static string CSI_SetCursorPosition (int row, int col) => $"{CSI}{row};{col}H";
 
 
 	//ESC [ <y> ; <x> f - HVP     Horizontal Vertical Position* Cursor moves to<x>; <y> coordinate within the viewport, where <x> is the column of the<y> line
@@ -248,15 +248,29 @@ public static class EscSeqUtils {
 	/// </summary>
 	public static string CSI_SetGraphicsRendition (params int [] parameters) => $"{CSI}{string.Join (";", parameters)}m";
 
+	/// <summary>
+	/// ESC [ (n) m - Uses <see cref="CSI_SetGraphicsRendition(int[])" /> to set the foreground color.
+	/// </summary>
+	/// <param name="code">One of the 16 color codes.</param>
+	/// <returns></returns>
+	public static string CSI_SetForegroundColor (AnsiColorCode code) => CSI_SetGraphicsRendition ((int)code);
+
+	/// <summary>
+	/// ESC [ (n) m - Uses <see cref="CSI_SetGraphicsRendition(int[])" /> to set the background color.
+	/// </summary>
+	/// <param name="code">One of the 16 color codes.</param>
+	/// <returns></returns>
+	public static string CSI_SetBackgroundColor (AnsiColorCode code) => CSI_SetGraphicsRendition ((int)code+10);
+
 	/// <summary>
 	/// ESC[38;5;{id}m - Set foreground color (256 colors)
 	/// </summary>
-	public static string CSI_SetForegroundColor (int id) => $"{CSI}38;5;{id}m";
+	public static string CSI_SetForegroundColor256 (int color) => $"{CSI}38;5;{color}m";
 
 	/// <summary>
 	/// ESC[48;5;{id}m - Set background color (256 colors)
 	/// </summary>
-	public static string CSI_SetBackgroundColor (int id) => $"{CSI}48;5;{id}m";
+	public static string CSI_SetBackgroundColor256 (int color) => $"{CSI}48;5;{color}m";
 
 	/// <summary>
 	/// ESC[38;2;{r};{g};{b}m	Set foreground color as RGB.

+ 1 - 6
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -837,9 +837,8 @@ class NetDriver : ConsoleDriver {
 						} else if (lastCol == -1) {
 							lastCol = col;
 						}
-						if (lastCol + 1 < cols) {
+						if (lastCol + 1 < cols)
 							lastCol++;
-						}
 						continue;
 					}
 
@@ -1167,10 +1166,6 @@ class NetDriver : ConsoleDriver {
 				// and passing on Shift would be redundant.
 				return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
 			}
-			break;
-
-
-			return (KeyCode)(uint)keyInfo.KeyChar;
 		}
 
 		var key = keyInfo.Key;

+ 226 - 192
Terminal.Gui/Drawing/Color.cs

@@ -9,6 +9,7 @@ using System.Text.Json.Serialization;
 using System.Text.RegularExpressions;
 
 namespace Terminal.Gui;
+
 /// <summary>
 /// Defines the 16 legacy color names and values that can be used to set the
 /// foreground and background colors in Terminal.Gui apps. Used with <see cref="Color"/>.
@@ -88,14 +89,82 @@ public enum ColorName {
 	/// </summary>
 	White
 }
+/// <summary>
+/// The 16 foreground color codes used by ANSI Esc sequences for 256 color terminals. Add 10 to these values for background color.
+/// </summary>
+public enum AnsiColorCode {
+	/// <summary>
+	/// The ANSI color code for Black.
+	/// </summary>
+	BLACK = 30,
 
+	/// <summary>
+	/// The ANSI color code for Red.
+	/// </summary>
+	RED = 31,
+	/// <summary>
+	/// The ANSI color code for Green.
+	/// </summary>
+	GREEN = 32,
+	/// <summary>
+	/// The ANSI color code for Yellow.
+	/// </summary>
+	YELLOW = 33,
+	/// <summary>
+	/// The ANSI color code for Blue.
+	/// </summary>
+	BLUE = 34,
+	/// <summary>
+	/// The ANSI color code for Magenta.
+	/// </summary>
+	MAGENTA = 35,
+	/// <summary>
+	/// The ANSI color code for Cyan.
+	/// </summary>
+	CYAN = 36,
+	/// <summary>
+	/// The ANSI color code for White.
+	/// </summary>
+	WHITE = 37,
+	/// <summary>
+	/// The ANSI color code for Bright Black.
+	/// </summary>
+	BRIGHT_BLACK = 90,
+	/// <summary>
+	/// The ANSI color code for Bright Red.
+	/// </summary>
+	BRIGHT_RED = 91,
+	/// <summary>
+	/// The ANSI color code for Bright Green.
+	/// </summary>
+	BRIGHT_GREEN = 92,
+	/// <summary>
+	/// The ANSI color code for Bright Yellow.
+	/// </summary>
+	BRIGHT_YELLOW = 93,
+	/// <summary>
+	/// The ANSI color code for Bright Blue.
+	/// </summary>
+	BRIGHT_BLUE = 94,
+	/// <summary>
+	/// The ANSI color code for Bright Magenta.
+	/// </summary>
+	BRIGHT_MAGENTA = 95,
+	/// <summary>
+	/// The ANSI color code for Bright Cyan.
+	/// </summary>
+	BRIGHT_CYAN = 96,
+	/// <summary>
+	/// The ANSI color code for Bright White.
+	/// </summary>
+	BRIGHT_WHITE = 97
+}
 /// <summary>
 /// Represents a 24-bit color. Provides automatic mapping between the legacy 4-bit (16 color) system and 24-bit colors (see <see cref="ColorName"/>).
 /// Used with <see cref="Attribute"/>. 
 /// </summary>
 [JsonConverter (typeof (ColorJsonConverter))]
 public class Color : IEquatable<Color> {
-
 	/// <summary>
 	/// Initializes a new instance of the <see cref="Color"/> class.
 	/// </summary>
@@ -115,10 +184,7 @@ public class Color : IEquatable<Color> {
 	/// Initializes a new instance of the <see cref="Color"/> class with an encoded 24-bit color value.
 	/// </summary>
 	/// <param name="rgba">The encoded 24-bit color value (see <see cref="Rgba"/>).</param>
-	public Color (int rgba)
-	{
-		Rgba = rgba;
-	}
+	public Color (int rgba) => Rgba = rgba;
 
 	/// <summary>
 	/// Initializes a new instance of the <see cref="Color"/> color from a legacy 16-color value.
@@ -126,7 +192,7 @@ public class Color : IEquatable<Color> {
 	/// <param name="colorName">The 16-color value.</param>
 	public Color (ColorName colorName)
 	{
-		var c = Color.FromColorName (colorName);
+		var c = FromColorName (colorName);
 		R = c.R;
 		G = c.G;
 		B = c.B;
@@ -188,11 +254,11 @@ public class Color : IEquatable<Color> {
 	/// </code>
 	/// </summary>
 	public int Rgba {
-		get => (A << 24) | (R << 16) | (G << 8) | B;
+		get => A << 24 | R << 16 | G << 8 | B;
 		set {
-			A = (byte)((value >> 24) & 0xFF);
-			R = (byte)((value >> 16) & 0xFF);
-			G = (byte)((value >> 8) & 0xFF);
+			A = (byte)(value >> 24 & 0xFF);
+			R = (byte)(value >> 16 & 0xFF);
+			G = (byte)(value >> 8 & 0xFF);
 			B = (byte)(value & 0xFF);
 		}
 	}
@@ -203,39 +269,61 @@ public class Color : IEquatable<Color> {
 	/// Maps legacy 16-color values to the corresponding 24-bit RGB value.
 	/// </summary>
 	internal static ImmutableDictionary<Color, ColorName> _colorToNameMap = new Dictionary<Color, ColorName> () {
-			// using "Windows 10 Console/PowerShell 6" here: https://i.stack.imgur.com/9UVnC.png
-			// See also: https://en.wikipedia.org/wiki/ANSI_escape_code
-			{ new Color (12, 12, 12),Gui.ColorName.Black },
-			{ new Color (0, 55, 218),Gui.ColorName.Blue },
-			{ new Color (19, 161, 14),Gui.ColorName.Green},
-			{ new Color (58, 150, 221),Gui.ColorName.Cyan},
-			{ new Color (197, 15, 31),Gui.ColorName.Red},
-			{ new Color (136, 23, 152),Gui.ColorName.Magenta},
-			{ new Color (128, 64, 32),Gui.ColorName.Yellow},
-			{ new Color (204, 204, 204),Gui.ColorName.Gray},
-			{ new Color (118, 118, 118),Gui.ColorName.DarkGray},
-			{ new Color (59, 120, 255),Gui.ColorName.BrightBlue},
-			{ new Color (22, 198, 12),Gui.ColorName.BrightGreen},
-			{ new Color (97, 214, 214),Gui.ColorName.BrightCyan},
-			{ new Color (231, 72, 86),Gui.ColorName.BrightRed},
-			{ new Color (180, 0, 158),Gui.ColorName.BrightMagenta },
-			{ new Color (249, 241, 165),Gui.ColorName.BrightYellow},
-			{ new Color (242, 242, 242),Gui.ColorName.White},
-		}.ToImmutableDictionary ();
+		// using "Windows 10 Console/PowerShell 6" here: https://i.stack.imgur.com/9UVnC.png
+		// See also: https://en.wikipedia.org/wiki/ANSI_escape_code
+		{ new Color (12, 12, 12), ColorName.Black },
+		{ new Color (0, 55, 218), ColorName.Blue },
+		{ new Color (19, 161, 14), ColorName.Green },
+		{ new Color (58, 150, 221), ColorName.Cyan },
+		{ new Color (197, 15, 31), ColorName.Red },
+		{ new Color (136, 23, 152), ColorName.Magenta },
+		{ new Color (128, 64, 32), ColorName.Yellow },
+		{ new Color (204, 204, 204), ColorName.Gray },
+		{ new Color (118, 118, 118), ColorName.DarkGray },
+		{ new Color (59, 120, 255), ColorName.BrightBlue },
+		{ new Color (22, 198, 12), ColorName.BrightGreen },
+		{ new Color (97, 214, 214), ColorName.BrightCyan },
+		{ new Color (231, 72, 86), ColorName.BrightRed },
+		{ new Color (180, 0, 158), ColorName.BrightMagenta },
+		{ new Color (249, 241, 165), ColorName.BrightYellow },
+		{ new Color (242, 242, 242), ColorName.White }
+	}.ToImmutableDictionary ();
+
+
+	/// <summary>
+	/// Defines the 16 legacy color names and values that can be used to set the
+	/// </summary>
+	internal static ImmutableDictionary<ColorName, AnsiColorCode> _colorNameToAnsiColorMap = new Dictionary<ColorName, AnsiColorCode> {
+		{ ColorName.Black, AnsiColorCode.BLACK },
+		{ ColorName.Blue, AnsiColorCode.BLUE },
+		{ ColorName.Green, AnsiColorCode.GREEN },
+		{ ColorName.Cyan, AnsiColorCode.CYAN },
+		{ ColorName.Red, AnsiColorCode.RED },
+		{ ColorName.Magenta, AnsiColorCode.MAGENTA },
+		{ ColorName.Yellow, AnsiColorCode.YELLOW },
+		{ ColorName.Gray, AnsiColorCode.WHITE },
+		{ ColorName.DarkGray, AnsiColorCode.BRIGHT_BLACK },
+		{ ColorName.BrightBlue, AnsiColorCode.BRIGHT_BLUE },
+		{ ColorName.BrightGreen, AnsiColorCode.BRIGHT_GREEN },
+		{ ColorName.BrightCyan, AnsiColorCode.BRIGHT_CYAN },
+		{ ColorName.BrightRed, AnsiColorCode.BRIGHT_RED },
+		{ ColorName.BrightMagenta, AnsiColorCode.BRIGHT_MAGENTA },
+		{ ColorName.BrightYellow, AnsiColorCode.BRIGHT_YELLOW },
+		{ ColorName.White, AnsiColorCode.BRIGHT_WHITE }
+	}.ToImmutableDictionary ();
 
 	/// <summary>
 	/// Gets or sets the 24-bit color value for each of the legacy 16-color values.
 	/// </summary>
 	[SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
 	public static Dictionary<ColorName, string> Colors {
-		get {
+		get =>
 			// Transform _colorToNameMap into a Dictionary<ColorNames,string>
-			return _colorToNameMap.ToDictionary (kvp => kvp.Value, kvp => $"#{kvp.Key.R:X2}{kvp.Key.G:X2}{kvp.Key.B:X2}");
-		}
+			_colorToNameMap.ToDictionary (kvp => kvp.Value, kvp => $"#{kvp.Key.R:X2}{kvp.Key.G:X2}{kvp.Key.B:X2}");
 		set {
 			// Transform Dictionary<ColorNames,string> into _colorToNameMap
 			var newMap = value.ToDictionary (kvp => new Color (kvp.Value), kvp => {
-				if (Enum.TryParse<ColorName> (kvp.Key.ToString (), ignoreCase: true, out ColorName colorName)) {
+				if (Enum.TryParse<ColorName> (kvp.Key.ToString (), true, out var colorName)) {
 					return colorName;
 				}
 				throw new ArgumentException ($"Invalid color name: {kvp.Key}");
@@ -247,9 +335,9 @@ public class Color : IEquatable<Color> {
 	/// <summary>
 	/// Converts a legacy <see cref="Gui.ColorName"/> to a 24-bit <see cref="Color"/>.
 	/// </summary>
-	/// <param name="consoleColor">The <see cref="Color"/> to convert.</param>
+	/// <param name="colorName">The <see cref="Color"/> to convert.</param>
 	/// <returns></returns>
-	private static Color FromColorName (ColorName consoleColor) => _colorToNameMap.FirstOrDefault (x => x.Value == consoleColor).Key;
+	static Color FromColorName (ColorName colorName) => _colorToNameMap.FirstOrDefault (x => x.Value == colorName).Key;
 
 	// Iterates through the entries in the _colorNames dictionary, calculates the
 	// Euclidean distance between the input color and each dictionary color in RGB space,
@@ -257,11 +345,11 @@ public class Color : IEquatable<Color> {
 	// representing the closest color entry and its associated color name.
 	internal static ColorName FindClosestColor (Color inputColor)
 	{
-		ColorName closestColor = Gui.ColorName.Black; // Default to Black
+		var closestColor = ColorName.Black; // Default to Black
 		double closestDistance = double.MaxValue;
 
 		foreach (var colorEntry in _colorToNameMap) {
-			var distance = CalculateColorDistance (inputColor, colorEntry.Key);
+			double distance = CalculateColorDistance (inputColor, colorEntry.Key);
 			if (distance < closestDistance) {
 				closestDistance = distance;
 				closestColor = colorEntry.Value;
@@ -271,12 +359,12 @@ public class Color : IEquatable<Color> {
 		return closestColor;
 	}
 
-	private static double CalculateColorDistance (Color color1, Color color2)
+	static double CalculateColorDistance (Color color1, Color color2)
 	{
 		// Calculate the Euclidean distance between two colors
-		var deltaR = (double)color1.R - (double)color2.R;
-		var deltaG = (double)color1.G - (double)color2.G;
-		var deltaB = (double)color1.B - (double)color2.B;
+		double deltaR = (double)color1.R - (double)color2.R;
+		double deltaG = (double)color1.G - (double)color2.G;
+		double deltaB = (double)color1.B - (double)color2.B;
 
 		return Math.Sqrt (deltaR * deltaR + deltaG * deltaG + deltaB * deltaB);
 	}
@@ -300,6 +388,16 @@ public class Color : IEquatable<Color> {
 		}
 	}
 
+	/// <summary>
+	/// Gets or sets the <see cref="Color"/> using a legacy 16-color <see cref="Gui.ColorName"/> value.
+	/// <see langword="get"/> will return the closest 16 color match to the true color when no exact value is found.
+	/// </summary>
+	/// <remarks>
+	/// Get returns the <see cref="ColorName"/> of the closest 24-bit color value. Set sets the RGB value using a hard-coded map.
+	/// </remarks>
+	[JsonIgnore]
+	public AnsiColorCode AnsiColorCode => _colorNameToAnsiColorMap [ColorName];
+
 	#region Legacy Color Names
 	/// <summary>
 	/// The black color.
@@ -382,49 +480,49 @@ public class Color : IEquatable<Color> {
 	public static bool TryParse (string text, [NotNullWhen (true)] out Color color)
 	{
 		// empty color
-		if ((text == null) || (text.Length == 0)) {
+		if (text == null || text.Length == 0) {
 			color = null;
 			return false;
 		}
 
 		// #RRGGBB, #RGB
-		if ((text [0] == '#') && text.Length is 7 or 4) {
+		if (text [0] == '#' && text.Length is 7 or 4) {
 			if (text.Length == 7) {
-				var r = Convert.ToInt32 (text.Substring (1, 2), 16);
-				var g = Convert.ToInt32 (text.Substring (3, 2), 16);
-				var b = Convert.ToInt32 (text.Substring (5, 2), 16);
+				int r = Convert.ToInt32 (text.Substring (1, 2), 16);
+				int g = Convert.ToInt32 (text.Substring (3, 2), 16);
+				int b = Convert.ToInt32 (text.Substring (5, 2), 16);
 				color = new Color (r, g, b);
 			} else {
-				var rText = char.ToString (text [1]);
-				var gText = char.ToString (text [2]);
-				var bText = char.ToString (text [3]);
+				string rText = char.ToString (text [1]);
+				string gText = char.ToString (text [2]);
+				string bText = char.ToString (text [3]);
 
-				var r = Convert.ToInt32 (rText + rText, 16);
-				var g = Convert.ToInt32 (gText + gText, 16);
-				var b = Convert.ToInt32 (bText + bText, 16);
+				int r = Convert.ToInt32 (rText + rText, 16);
+				int g = Convert.ToInt32 (gText + gText, 16);
+				int b = Convert.ToInt32 (bText + bText, 16);
 				color = new Color (r, g, b);
 			}
 			return true;
 		}
 
 		// #RRGGBB, #RGBA
-		if ((text [0] == '#') && text.Length is 8 or 5) {
+		if (text [0] == '#' && text.Length is 8 or 5) {
 			if (text.Length == 7) {
-				var r = Convert.ToInt32 (text.Substring (1, 2), 16);
-				var g = Convert.ToInt32 (text.Substring (3, 2), 16);
-				var b = Convert.ToInt32 (text.Substring (5, 2), 16);
-				var a = Convert.ToInt32 (text.Substring (7, 2), 16);
+				int r = Convert.ToInt32 (text.Substring (1, 2), 16);
+				int g = Convert.ToInt32 (text.Substring (3, 2), 16);
+				int b = Convert.ToInt32 (text.Substring (5, 2), 16);
+				int a = Convert.ToInt32 (text.Substring (7, 2), 16);
 				color = new Color (a, r, g, b);
 			} else {
-				var rText = char.ToString (text [1]);
-				var gText = char.ToString (text [2]);
-				var bText = char.ToString (text [3]);
-				var aText = char.ToString (text [4]);
-
-				var r = Convert.ToInt32 (aText + aText, 16);
-				var g = Convert.ToInt32 (rText + rText, 16);
-				var b = Convert.ToInt32 (gText + gText, 16);
-				var a = Convert.ToInt32 (bText + bText, 16);
+				string rText = char.ToString (text [1]);
+				string gText = char.ToString (text [2]);
+				string bText = char.ToString (text [3]);
+				string aText = char.ToString (text [4]);
+
+				int r = Convert.ToInt32 (aText + aText, 16);
+				int g = Convert.ToInt32 (rText + rText, 16);
+				int b = Convert.ToInt32 (gText + gText, 16);
+				int a = Convert.ToInt32 (bText + bText, 16);
 				color = new Color (r, g, b, a);
 			}
 			return true;
@@ -433,9 +531,9 @@ public class Color : IEquatable<Color> {
 		// rgb(r,g,b)
 		var match = Regex.Match (text, @"rgb\((\d+),(\d+),(\d+)\)");
 		if (match.Success) {
-			var r = int.Parse (match.Groups [1].Value);
-			var g = int.Parse (match.Groups [2].Value);
-			var b = int.Parse (match.Groups [3].Value);
+			int r = int.Parse (match.Groups [1].Value);
+			int g = int.Parse (match.Groups [2].Value);
+			int b = int.Parse (match.Groups [3].Value);
 			color = new Color (r, g, b);
 			return true;
 		}
@@ -443,15 +541,15 @@ public class Color : IEquatable<Color> {
 		// rgb(r,g,b,a)
 		match = Regex.Match (text, @"rgb\((\d+),(\d+),(\d+),(\d+)\)");
 		if (match.Success) {
-			var r = int.Parse (match.Groups [1].Value);
-			var g = int.Parse (match.Groups [2].Value);
-			var b = int.Parse (match.Groups [3].Value);
-			var a = int.Parse (match.Groups [4].Value);
+			int r = int.Parse (match.Groups [1].Value);
+			int g = int.Parse (match.Groups [2].Value);
+			int b = int.Parse (match.Groups [3].Value);
+			int a = int.Parse (match.Groups [4].Value);
 			color = new Color (r, g, b, a);
 			return true;
 		}
 
-		if (Enum.TryParse<ColorName> (text, ignoreCase: true, out ColorName colorName)) {
+		if (Enum.TryParse<ColorName> (text, true, out var colorName)) {
 			color = new Color (colorName);
 			return true;
 		}
@@ -465,37 +563,25 @@ public class Color : IEquatable<Color> {
 	/// Cast from int.
 	/// </summary>
 	/// <param name="rgba"></param>
-	public static implicit operator Color (int rgba)
-	{
-		return new Color (rgba);
-	}
+	public static implicit operator Color (int rgba) => new Color (rgba);
 
 	/// <summary>
 	/// Cast to int.
 	/// </summary>
 	/// <param name="color"></param>
-	public static explicit operator int (Color color)
-	{
-		return color.Rgba;
-	}
+	public static explicit operator int (Color color) => color.Rgba;
 
 	/// <summary>
 	/// Cast from <see cref="Gui.ColorName"/>.
 	/// </summary>
 	/// <param name="colorName"></param>
-	public static explicit operator Color (ColorName colorName)
-	{
-		return new Color (colorName);
-	}
+	public static explicit operator Color (ColorName colorName) => new Color (colorName);
 
 	/// <summary>
 	/// Cast to <see cref="Gui.ColorName"/>.
 	/// </summary>
 	/// <param name="color"></param>
-	public static explicit operator ColorName (Color color)
-	{
-		return color.ColorName;
-	}
+	public static explicit operator ColorName (Color color) => color.ColorName;
 
 
 	/// <summary>
@@ -506,11 +592,13 @@ public class Color : IEquatable<Color> {
 	/// <returns></returns>
 	public static bool operator == (Color left, Color right)
 	{
-		if (left is null && right is null)
+		if (left is null && right is null) {
 			return true;
+		}
 
-		if (left is null || right is null)
+		if (left is null || right is null) {
 			return false;
+		}
 
 		return left.Equals (right);
 	}
@@ -524,11 +612,13 @@ public class Color : IEquatable<Color> {
 	/// <returns></returns>
 	public static bool operator != (Color left, Color right)
 	{
-		if (left is null && right is null)
+		if (left is null && right is null) {
 			return false;
+		}
 
-		if (left is null || right is null)
+		if (left is null || right is null) {
 			return true;
+		}
 
 		return !left.Equals (right);
 	}
@@ -539,10 +629,7 @@ public class Color : IEquatable<Color> {
 	/// <param name="left"></param>
 	/// <param name="right"></param>
 	/// <returns></returns>
-	public static bool operator == (ColorName left, Color right)
-	{
-		return left == right.ColorName;
-	}
+	public static bool operator == (ColorName left, Color right) => left == right.ColorName;
 
 	/// <summary>
 	/// Inequality operator for <see cref="Color"/> and <see cref="Gui.ColorName"/> objects.
@@ -550,10 +637,7 @@ public class Color : IEquatable<Color> {
 	/// <param name="left"></param>
 	/// <param name="right"></param>
 	/// <returns></returns>
-	public static bool operator != (ColorName left, Color right)
-	{
-		return left != right.ColorName;
-	}
+	public static bool operator != (ColorName left, Color right) => left != right.ColorName;
 
 	/// <summary>
 	/// Equality operator for <see cref="Color"/> and <see cref="Gui.ColorName"/> objects.
@@ -561,10 +645,7 @@ public class Color : IEquatable<Color> {
 	/// <param name="left"></param>
 	/// <param name="right"></param>
 	/// <returns></returns>
-	public static bool operator == (Color left, ColorName right)
-	{
-		return left.ColorName == right;
-	}
+	public static bool operator == (Color left, ColorName right) => left.ColorName == right;
 
 	/// <summary>
 	/// Inequality operator for <see cref="Color"/> and <see cref="Gui.ColorName"/> objects.
@@ -572,33 +653,20 @@ public class Color : IEquatable<Color> {
 	/// <param name="left"></param>
 	/// <param name="right"></param>
 	/// <returns></returns>
-	public static bool operator != (Color left, ColorName right)
-	{
-		return left.ColorName != right;
-	}
+	public static bool operator != (Color left, ColorName right) => left.ColorName != right;
 
 
 	/// <inheritdoc/>
-	public override bool Equals (object obj)
-	{
-		return obj is Color other && Equals (other);
-	}
+	public override bool Equals (object obj) => obj is Color other && Equals (other);
 
 	/// <inheritdoc/>
-	public bool Equals (Color other)
-	{
-		return
-			R == other.R &&
-			G == other.G &&
-			B == other.B &&
-			A == other.A;
-	}
+	public bool Equals (Color other) => R == other.R &&
+					G == other.G &&
+					B == other.B &&
+					A == other.A;
 
 	/// <inheritdoc/>
-	public override int GetHashCode ()
-	{
-		return HashCode.Combine (R, G, B, A);
-	}
+	public override int GetHashCode () => HashCode.Combine (R, G, B, A);
 	#endregion
 
 	/// <summary>
@@ -616,14 +684,13 @@ public class Color : IEquatable<Color> {
 	public override string ToString ()
 	{
 		// If Values has an exact match with a named color (in _colorNames), use that.
-		if (_colorToNameMap.TryGetValue (this, out ColorName colorName)) {
+		if (_colorToNameMap.TryGetValue (this, out var colorName)) {
 			return Enum.GetName (typeof (ColorName), colorName);
 		}
 		// Otherwise return as an RGB hex value.
 		return $"#{R:X2}{G:X2}{B:X2}";
 	}
 }
-
 /// <summary>
 /// Attributes represent how text is styled when displayed in the terminal. 
 /// </summary>
@@ -634,7 +701,6 @@ public class Color : IEquatable<Color> {
 /// </remarks>
 [JsonConverter (typeof (AttributeJsonConverter))]
 public readonly struct Attribute : IEquatable<Attribute> {
-
 	/// <summary>
 	/// Default empty attribute.
 	/// </summary>
@@ -665,19 +731,20 @@ public readonly struct Attribute : IEquatable<Attribute> {
 	{
 		PlatformColor = -1;
 		var d = Default;
-		Foreground = new (d.Foreground.ColorName);
-		Background = new (d.Background.ColorName);
+		Foreground = new Color (d.Foreground.ColorName);
+		Background = new Color (d.Background.ColorName);
 	}
 
 	/// <summary>
 	/// Initializes a new instance with platform specific color value.
 	/// </summary>
 	/// <param name="platformColor">Value.</param>
-	internal Attribute (int platformColor) {
+	internal Attribute (int platformColor)
+	{
 		PlatformColor = platformColor;
 		var d = Default;
-		Foreground = new (d.Foreground.ColorName);
-		Background = new (d.Background.ColorName);
+		Foreground = new Color (d.Foreground.ColorName);
+		Background = new Color (d.Background.ColorName);
 	}
 
 	/// <summary>
@@ -775,30 +842,21 @@ public readonly struct Attribute : IEquatable<Attribute> {
 	public static bool operator != (Attribute left, Attribute right) => !(left == right);
 
 	/// <inheritdoc />
-	public override bool Equals (object obj)
-	{
-		return obj is Attribute other && Equals (other);
-	}
+	public override bool Equals (object obj) => obj is Attribute other && Equals (other);
 
 	/// <inheritdoc />
-	public bool Equals (Attribute other)
-	{
-		return PlatformColor == other.PlatformColor &&
-			Foreground == other.Foreground &&
-			Background == other.Background;
-	}
+	public bool Equals (Attribute other) => PlatformColor == other.PlatformColor &&
+						Foreground == other.Foreground &&
+						Background == other.Background;
 
 	/// <inheritdoc />
 	public override int GetHashCode () => HashCode.Combine (PlatformColor, Foreground, Background);
 
 	/// <inheritdoc />
-	public override string ToString ()
-	{
+	public override string ToString () =>
 		// Note, Unit tests are dependent on this format
-		return $"{Foreground},{Background}";
-	}
+		$"{Foreground},{Background}";
 }
-
 /// <summary>
 /// Defines the <see cref="Attribute"/>s for common visible elements in a <see cref="View"/>. 
 /// Containers such as <see cref="Window"/> and <see cref="FrameView"/> use <see cref="ColorScheme"/> to determine
@@ -899,25 +957,19 @@ public class ColorScheme : IEquatable<ColorScheme> {
 	/// </summary>
 	/// <param name="obj"></param>
 	/// <returns>true if the two objects are equal</returns>
-	public override bool Equals (object obj)
-	{
-		return Equals (obj as ColorScheme);
-	}
+	public override bool Equals (object obj) => Equals (obj as ColorScheme);
 
 	/// <summary>
 	/// Compares two <see cref="ColorScheme"/> objects for equality.
 	/// </summary>
 	/// <param name="other"></param>
 	/// <returns>true if the two objects are equal</returns>
-	public bool Equals (ColorScheme other)
-	{
-		return other != null &&
-	       EqualityComparer<Attribute>.Default.Equals (_normal, other._normal) &&
-	       EqualityComparer<Attribute>.Default.Equals (_focus, other._focus) &&
-	       EqualityComparer<Attribute>.Default.Equals (_hotNormal, other._hotNormal) &&
-	       EqualityComparer<Attribute>.Default.Equals (_hotFocus, other._hotFocus) &&
-	       EqualityComparer<Attribute>.Default.Equals (_disabled, other._disabled);
-	}
+	public bool Equals (ColorScheme other) => other != null &&
+						EqualityComparer<Attribute>.Default.Equals (_normal, other._normal) &&
+						EqualityComparer<Attribute>.Default.Equals (_focus, other._focus) &&
+						EqualityComparer<Attribute>.Default.Equals (_hotNormal, other._hotNormal) &&
+						EqualityComparer<Attribute>.Default.Equals (_hotFocus, other._hotFocus) &&
+						EqualityComparer<Attribute>.Default.Equals (_disabled, other._disabled);
 
 	/// <summary>
 	/// Returns a hashcode for this instance.
@@ -940,10 +992,7 @@ public class ColorScheme : IEquatable<ColorScheme> {
 	/// <param name="left"></param>
 	/// <param name="right"></param>
 	/// <returns><c>true</c> if the two objects are equivalent</returns>
-	public static bool operator == (ColorScheme left, ColorScheme right)
-	{
-		return EqualityComparer<ColorScheme>.Default.Equals (left, right);
-	}
+	public static bool operator == (ColorScheme left, ColorScheme right) => EqualityComparer<ColorScheme>.Default.Equals (left, right);
 
 	/// <summary>
 	/// Compares two <see cref="ColorScheme"/> objects for inequality.
@@ -951,12 +1000,8 @@ public class ColorScheme : IEquatable<ColorScheme> {
 	/// <param name="left"></param>
 	/// <param name="right"></param>
 	/// <returns><c>true</c> if the two objects are not equivalent</returns>
-	public static bool operator != (ColorScheme left, ColorScheme right)
-	{
-		return !(left == right);
-	}
+	public static bool operator != (ColorScheme left, ColorScheme right) => !(left == right);
 }
-
 /// <summary>
 /// The default <see cref="ColorScheme"/>s for the application.
 /// </summary>
@@ -964,7 +1009,7 @@ public class ColorScheme : IEquatable<ColorScheme> {
 /// This property can be set in a Theme to change the default <see cref="Colors"/> for the application.
 /// </remarks>
 public static class Colors {
-	private class SchemeNameComparerIgnoreCase : IEqualityComparer<string> {
+	class SchemeNameComparerIgnoreCase : IEqualityComparer<string> {
 		public bool Equals (string x, string y)
 		{
 			if (x != null && y != null) {
@@ -973,29 +1018,21 @@ public static class Colors {
 			return false;
 		}
 
-		public int GetHashCode (string obj)
-		{
-			return obj.ToLowerInvariant ().GetHashCode ();
-		}
+		public int GetHashCode (string obj) => obj.ToLowerInvariant ().GetHashCode ();
 	}
 
-	static Colors ()
-	{
-		ColorSchemes = Create ();
-	}
+	static Colors () => ColorSchemes = Create ();
 
 	/// <summary>
 	/// Creates a new dictionary of new <see cref="ColorScheme"/> objects.
 	/// </summary>
-	public static Dictionary<string, ColorScheme> Create ()
-	{
+	public static Dictionary<string, ColorScheme> Create () =>
 		// Use reflection to dynamically create the default set of ColorSchemes from the list defined 
 		// by the class. 
-		return typeof (Colors).GetProperties ()
-			.Where (p => p.PropertyType == typeof (ColorScheme))
-			.Select (p => new KeyValuePair<string, ColorScheme> (p.Name, new ColorScheme ()))
-			.ToDictionary (t => t.Key, t => t.Value, comparer: new SchemeNameComparerIgnoreCase ());
-	}
+		typeof (Colors).GetProperties ()
+				.Where (p => p.PropertyType == typeof (ColorScheme))
+				.Select (p => new KeyValuePair<string, ColorScheme> (p.Name, new ColorScheme ()))
+				.ToDictionary (t => t.Key, t => t.Value, new SchemeNameComparerIgnoreCase ());
 
 	/// <summary>
 	/// The application Toplevel color scheme, for the default Toplevel views.
@@ -1047,10 +1084,7 @@ public static class Colors {
 	/// </remarks>
 	public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); }
 
-	static ColorScheme GetColorScheme ([CallerMemberName] string schemeBeingSet = null)
-	{
-		return ColorSchemes [schemeBeingSet];
-	}
+	static ColorScheme GetColorScheme ([CallerMemberName] string schemeBeingSet = null) => ColorSchemes [schemeBeingSet];
 
 	static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string schemeBeingSet = null)
 	{
@@ -1064,4 +1098,4 @@ public static class Colors {
 	[SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)]
 	[JsonConverter (typeof (DictionaryJsonConverter<ColorScheme>))]
 	public static Dictionary<string, ColorScheme> ColorSchemes { get; private set; }
-}
+}

+ 844 - 842
Terminal.Gui/Views/Toplevel.cs

@@ -3,238 +3,252 @@ using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
 
-namespace Terminal.Gui {
+namespace Terminal.Gui; 
+
+/// <summary>
+/// Toplevel views can be modally executed. They are used for both an application's main view (filling the entire screeN and
+/// for pop-up views such as <see cref="Dialog"/>, <see cref="MessageBox"/>, and <see cref="Wizard"/>.
+/// </summary>
+/// <remarks>
+///   <para>
+///     Toplevels can be modally executing views, started by calling <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>. 
+///     They return control to the caller when <see cref="Application.RequestStop(Toplevel)"/> has 
+///     been called (which sets the <see cref="Toplevel.Running"/> property to <c>false</c>). 
+///   </para>
+///   <para>
+///     A Toplevel is created when an application initializes Terminal.Gui by calling <see cref="Application.Init"/>.
+///     The application Toplevel can be accessed via <see cref="Application.Top"/>. Additional Toplevels can be created 
+///     and run (e.g. <see cref="Dialog"/>s. To run a Toplevel, create the <see cref="Toplevel"/> and 
+///     call <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>.
+///   </para>
+/// </remarks>
+public partial class Toplevel : View {
 	/// <summary>
-	/// Toplevel views can be modally executed. They are used for both an application's main view (filling the entire screeN and
-	/// for pop-up views such as <see cref="Dialog"/>, <see cref="MessageBox"/>, and <see cref="Wizard"/>.
+	/// Gets or sets whether the main loop for this <see cref="Toplevel"/> is running or not. 
 	/// </summary>
 	/// <remarks>
-	///   <para>
-	///     Toplevels can be modally executing views, started by calling <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>. 
-	///     They return control to the caller when <see cref="Application.RequestStop(Toplevel)"/> has 
-	///     been called (which sets the <see cref="Toplevel.Running"/> property to <c>false</c>). 
-	///   </para>
-	///   <para>
-	///     A Toplevel is created when an application initializes Terminal.Gui by calling <see cref="Application.Init(ConsoleDriver)"/>.
-	///     The application Toplevel can be accessed via <see cref="Application.Top"/>. Additional Toplevels can be created 
-	///     and run (e.g. <see cref="Dialog"/>s. To run a Toplevel, create the <see cref="Toplevel"/> and 
-	///     call <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>.
-	///   </para>
+	///    Setting this property directly is discouraged. Use <see cref="Application.RequestStop"/> instead. 
 	/// </remarks>
-	public partial class Toplevel : View {
-		/// <summary>
-		/// Gets or sets whether the main loop for this <see cref="Toplevel"/> is running or not. 
-		/// </summary>
-		/// <remarks>
-		///    Setting this property directly is discouraged. Use <see cref="Application.RequestStop"/> instead. 
-		/// </remarks>
-		public bool Running { get; set; }
-
-		/// <summary>
-		/// Invoked when the <see cref="Toplevel"/> <see cref="RunState"/> has begun to be loaded.
-		/// A Loaded event handler is a good place to finalize initialization before calling 
-		/// <see cref="Application.RunLoop(RunState)"/>.
-		/// </summary>
-		public event EventHandler Loaded;
-
-		/// <summary>
-		/// Invoked when the <see cref="Toplevel"/> main loop has started it's first iteration.
-		/// Subscribe to this event to perform tasks when the <see cref="Toplevel"/> has been laid out and focus has been set.
-		/// changes. 
-		/// <para>A Ready event handler is a good place to finalize initialization after calling 
-		/// <see cref="Application.Run(Func{Exception, bool})"/> on this <see cref="Toplevel"/>.</para>
-		/// </summary>
-		public event EventHandler Ready;
-
-		/// <summary>
-		/// Invoked when the Toplevel <see cref="RunState"/> has been unloaded.
-		/// A Unloaded event handler is a good place to dispose objects after calling <see cref="Application.End(RunState)"/>.
-		/// </summary>
-		public event EventHandler Unloaded;
-
-		/// <summary>
-		/// Invoked when the Toplevel <see cref="RunState"/> becomes the <see cref="Application.Current"/> Toplevel.
-		/// </summary>
-		public event EventHandler<ToplevelEventArgs> Activate;
-
-		/// <summary>
-		/// Invoked when the Toplevel<see cref="RunState"/> ceases to be the <see cref="Application.Current"/> Toplevel.
-		/// </summary>
-		public event EventHandler<ToplevelEventArgs> Deactivate;
-
-		/// <summary>
-		/// Invoked when a child of the Toplevel <see cref="RunState"/> is closed by  
-		/// <see cref="Application.End(RunState)"/>.
-		/// </summary>
-		public event EventHandler<ToplevelEventArgs> ChildClosed;
-
-		/// <summary>
-		/// Invoked when the last child of the Toplevel <see cref="RunState"/> is closed from 
-		/// by <see cref="Application.End(RunState)"/>.
-		/// </summary>
-		public event EventHandler AllChildClosed;
-
-		/// <summary>
-		/// Invoked when the Toplevel's <see cref="RunState"/> is being closed by  
-		/// <see cref="Application.RequestStop(Toplevel)"/>.
-		/// </summary>
-		public event EventHandler<ToplevelClosingEventArgs> Closing;
-
-		/// <summary>
-		/// Invoked when the Toplevel's <see cref="RunState"/> is closed by <see cref="Application.End(RunState)"/>.
-		/// </summary>
-		public event EventHandler<ToplevelEventArgs> Closed;
-
-		/// <summary>
-		/// Invoked when a child Toplevel's <see cref="RunState"/> has been loaded.
-		/// </summary>
-		public event EventHandler<ToplevelEventArgs> ChildLoaded;
-
-		/// <summary>
-		/// Invoked when a cjhild Toplevel's <see cref="RunState"/> has been unloaded.
-		/// </summary>
-		public event EventHandler<ToplevelEventArgs> ChildUnloaded;
-
-		/// <summary>
-		/// Invoked when the terminal has been resized. The new <see cref="Size"/> of the terminal is provided.
-		/// </summary>
-		public event EventHandler<SizeChangedEventArgs> SizeChanging;
-
-		// TODO: Make cancelable?
-		internal virtual void OnSizeChanging (SizeChangedEventArgs size) => SizeChanging?.Invoke (this, size);
-
-		internal virtual void OnChildUnloaded (Toplevel top) => ChildUnloaded?.Invoke (this, new ToplevelEventArgs (top));
-
-		internal virtual void OnChildLoaded (Toplevel top) => ChildLoaded?.Invoke (this, new ToplevelEventArgs (top));
-
-		internal virtual void OnClosed (Toplevel top) => Closed?.Invoke (this, new ToplevelEventArgs (top));
-
-		internal virtual bool OnClosing (ToplevelClosingEventArgs ev)
-		{
-			Closing?.Invoke (this, ev);
-			return ev.Cancel;
-		}
-
-		internal virtual void OnAllChildClosed () => AllChildClosed?.Invoke (this, EventArgs.Empty);
-
-		internal virtual void OnChildClosed (Toplevel top)
-		{
-			if (IsOverlappedContainer) {
-				SetSubViewNeedsDisplay ();
-			}
-			ChildClosed?.Invoke (this, new ToplevelEventArgs (top));
-		}
+	public bool Running { get; set; }
+
+	/// <summary>
+	/// Invoked when the <see cref="Toplevel"/> <see cref="RunState"/> has begun to be loaded.
+	/// A Loaded event handler is a good place to finalize initialization before calling 
+	/// <see cref="Application.RunLoop(RunState)"/>.
+	/// </summary>
+	public event EventHandler Loaded;
+
+	/// <summary>
+	/// Invoked when the <see cref="Toplevel"/> main loop has started it's first iteration.
+	/// Subscribe to this event to perform tasks when the <see cref="Toplevel"/> has been laid out and focus has been set.
+	/// changes. 
+	/// <para>A Ready event handler is a good place to finalize initialization after calling 
+	/// <see cref="Application.Run(Func{Exception, bool})"/> on this <see cref="Toplevel"/>.</para>
+	/// </summary>
+	public event EventHandler Ready;
+
+	/// <summary>
+	/// Invoked when the Toplevel <see cref="RunState"/> has been unloaded.
+	/// A Unloaded event handler is a good place to dispose objects after calling <see cref="Application.End(RunState)"/>.
+	/// </summary>
+	public event EventHandler Unloaded;
+
+	/// <summary>
+	/// Invoked when the Toplevel <see cref="RunState"/> becomes the <see cref="Application.Current"/> Toplevel.
+	/// </summary>
+	public event EventHandler<ToplevelEventArgs> Activate;
+
+	/// <summary>
+	/// Invoked when the Toplevel<see cref="RunState"/> ceases to be the <see cref="Application.Current"/> Toplevel.
+	/// </summary>
+	public event EventHandler<ToplevelEventArgs> Deactivate;
 
-		internal virtual void OnDeactivate (Toplevel activated)
-		{
-			Deactivate?.Invoke (this, new ToplevelEventArgs (activated));
+	/// <summary>
+	/// Invoked when a child of the Toplevel <see cref="RunState"/> is closed by  
+	/// <see cref="Application.End(RunState)"/>.
+	/// </summary>
+	public event EventHandler<ToplevelEventArgs> ChildClosed;
+
+	/// <summary>
+	/// Invoked when the last child of the Toplevel <see cref="RunState"/> is closed from 
+	/// by <see cref="Application.End(RunState)"/>.
+	/// </summary>
+	public event EventHandler AllChildClosed;
+
+	/// <summary>
+	/// Invoked when the Toplevel's <see cref="RunState"/> is being closed by  
+	/// <see cref="Application.RequestStop(Toplevel)"/>.
+	/// </summary>
+	public event EventHandler<ToplevelClosingEventArgs> Closing;
+
+	/// <summary>
+	/// Invoked when the Toplevel's <see cref="RunState"/> is closed by <see cref="Application.End(RunState)"/>.
+	/// </summary>
+	public event EventHandler<ToplevelEventArgs> Closed;
+
+	/// <summary>
+	/// Invoked when a child Toplevel's <see cref="RunState"/> has been loaded.
+	/// </summary>
+	public event EventHandler<ToplevelEventArgs> ChildLoaded;
+
+	/// <summary>
+	/// Invoked when a cjhild Toplevel's <see cref="RunState"/> has been unloaded.
+	/// </summary>
+	public event EventHandler<ToplevelEventArgs> ChildUnloaded;
+
+	/// <summary>
+	/// Invoked when the terminal has been resized. The new <see cref="Size"/> of the terminal is provided.
+	/// </summary>
+	public event EventHandler<SizeChangedEventArgs> SizeChanging;
+
+	// TODO: Make cancelable?
+	internal virtual void OnSizeChanging (SizeChangedEventArgs size) => SizeChanging?.Invoke (this, size);
+
+	internal virtual void OnChildUnloaded (Toplevel top) => ChildUnloaded?.Invoke (this, new ToplevelEventArgs (top));
+
+	internal virtual void OnChildLoaded (Toplevel top) => ChildLoaded?.Invoke (this, new ToplevelEventArgs (top));
+
+	internal virtual void OnClosed (Toplevel top) => Closed?.Invoke (this, new ToplevelEventArgs (top));
+
+	internal virtual bool OnClosing (ToplevelClosingEventArgs ev)
+	{
+		Closing?.Invoke (this, ev);
+		return ev.Cancel;
+	}
+
+	internal virtual void OnAllChildClosed () => AllChildClosed?.Invoke (this, EventArgs.Empty);
+
+	internal virtual void OnChildClosed (Toplevel top)
+	{
+		if (IsOverlappedContainer) {
+			SetSubViewNeedsDisplay ();
 		}
+		ChildClosed?.Invoke (this, new ToplevelEventArgs (top));
+	}
+
+	internal virtual void OnDeactivate (Toplevel activated) => Deactivate?.Invoke (this, new ToplevelEventArgs (activated));
 
-		internal virtual void OnActivate (Toplevel deactivated)
-		{
-			Activate?.Invoke (this, new ToplevelEventArgs (deactivated));
+	internal virtual void OnActivate (Toplevel deactivated) => Activate?.Invoke (this, new ToplevelEventArgs (deactivated));
+
+	/// <summary>
+	/// Called from <see cref="Application.Begin(Toplevel)"/> before the <see cref="Toplevel"/> redraws for the first time. 
+	/// </summary>
+	public virtual void OnLoaded ()
+	{
+		IsLoaded = true;
+		foreach (Toplevel tl in Subviews.Where (v => v is Toplevel)) {
+			tl.OnLoaded ();
 		}
+		Loaded?.Invoke (this, EventArgs.Empty);
+	}
 
-		/// <summary>
-		/// Called from <see cref="Application.Begin(Toplevel)"/> before the <see cref="Toplevel"/> redraws for the first time. 
-		/// </summary>
-		public virtual void OnLoaded ()
-		{
-			IsLoaded = true;
-			foreach (Toplevel tl in Subviews.Where (v => v is Toplevel)) {
-				tl.OnLoaded ();
-			}
-			Loaded?.Invoke (this, EventArgs.Empty);
+	/// <summary>
+	/// Called from <see cref="Application.RunLoop"/> after the <see cref="Toplevel"/> has entered the 
+	/// first iteration of the loop.
+	/// </summary>
+	internal virtual void OnReady ()
+	{
+		foreach (Toplevel tl in Subviews.Where (v => v is Toplevel)) {
+			tl.OnReady ();
 		}
+		Ready?.Invoke (this, EventArgs.Empty);
+	}
 
-		/// <summary>
-		/// Called from <see cref="Application.RunLoop"/> after the <see cref="Toplevel"/> has entered the 
-		/// first iteration of the loop.
-		/// </summary>
-		internal virtual void OnReady ()
-		{
-			foreach (Toplevel tl in Subviews.Where (v => v is Toplevel)) {
-				tl.OnReady ();
-			}
-			Ready?.Invoke (this, EventArgs.Empty);
+	/// <summary>
+	/// Called from <see cref="Application.End(RunState)"/> before the <see cref="Toplevel"/> is disposed.
+	/// </summary>
+	internal virtual void OnUnloaded ()
+	{
+		foreach (Toplevel tl in Subviews.Where (v => v is Toplevel)) {
+			tl.OnUnloaded ();
 		}
+		Unloaded?.Invoke (this, EventArgs.Empty);
+	}
+
+	/// <summary>
+	/// Initializes a new instance of the <see cref="Toplevel"/> class with the specified <see cref="LayoutStyle.Absolute"/> layout.
+	/// </summary>
+	/// <param name="frame">A Superview-relative rectangle specifying the location and size for the new Toplevel</param>
+	public Toplevel (Rect frame) : base (frame) => SetInitialProperties ();
 
-		/// <summary>
-		/// Called from <see cref="Application.End(RunState)"/> before the <see cref="Toplevel"/> is disposed.
-		/// </summary>
-		internal virtual void OnUnloaded ()
-		{
-			foreach (Toplevel tl in Subviews.Where (v => v is Toplevel)) {
-				tl.OnUnloaded ();
+	/// <summary>
+	/// Initializes a new instance of the <see cref="Toplevel"/> class with <see cref="LayoutStyle.Computed"/> layout, 
+	/// defaulting to full screen.
+	/// </summary>
+	public Toplevel () : base ()
+	{
+		SetInitialProperties ();
+		Width = Dim.Fill ();
+		Height = Dim.Fill ();
+	}
+
+	void SetInitialProperties ()
+	{
+		ColorScheme = Colors.TopLevel;
+
+		Application.GrabbingMouse += Application_GrabbingMouse;
+		Application.UnGrabbingMouse += Application_UnGrabbingMouse;
+
+		// TODO: v2 - ALL Views (Responders??!?!) should support the commands related to 
+		//    - Focus
+		//  Move the appropriate AddCommand calls to `Responder`
+
+		// Things this view knows how to do
+		AddCommand (Command.QuitToplevel, () => {
+			QuitToplevel ();
+			return true;
+		});
+		AddCommand (Command.Suspend, () => {
+			Driver.Suspend ();
+			;
+			return true;
+		});
+		AddCommand (Command.NextView, () => {
+			MoveNextView ();
+			return true;
+		});
+		AddCommand (Command.PreviousView, () => {
+			MovePreviousView ();
+			return true;
+		});
+		AddCommand (Command.NextViewOrTop, () => {
+			MoveNextViewOrTop ();
+			return true;
+		});
+		AddCommand (Command.PreviousViewOrTop, () => {
+			MovePreviousViewOrTop ();
+			return true;
+		});
+		AddCommand (Command.Refresh, () => {
+			Application.Refresh ();
+			return true;
+		});
+		AddCommand (Command.Accept, () => {
+			// TODO: Perhaps all views should support the concept of being default?
+			// TODO: It's bad that Toplevel is tightly coupled with Button
+			if (Subviews.FirstOrDefault (v => v is Button && ((Button)v).IsDefault && ((Button)v).Enabled) is Button defaultBtn) {
+				defaultBtn.InvokeCommand (Command.Accept);
+				return true;
 			}
-			Unloaded?.Invoke (this, EventArgs.Empty);
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Toplevel"/> class with the specified <see cref="LayoutStyle.Absolute"/> layout.
-		/// </summary>
-		/// <param name="frame">A Superview-relative rectangle specifying the location and size for the new Toplevel</param>
-		public Toplevel (Rect frame) : base (frame)
-		{
-			SetInitialProperties ();
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Toplevel"/> class with <see cref="LayoutStyle.Computed"/> layout, 
-		/// defaulting to full screen.
-		/// </summary>
-		public Toplevel () : base ()
-		{
-			SetInitialProperties ();
-			Width = Dim.Fill ();
-			Height = Dim.Fill ();
-		}
-
-		void SetInitialProperties ()
-		{
-			ColorScheme = Colors.TopLevel;
-
-			Application.GrabbingMouse += Application_GrabbingMouse;
-			Application.UnGrabbingMouse += Application_UnGrabbingMouse;
-
-			// TODO: v2 - ALL Views (Responders??!?!) should support the commands related to 
-			//    - Focus
-			//  Move the appropriate AddCommand calls to `Responder`
-
-			// Things this view knows how to do
-			AddCommand (Command.QuitToplevel, () => { QuitToplevel (); return true; });
-			AddCommand (Command.Suspend, () => { Driver.Suspend (); ; return true; });
-			AddCommand (Command.NextView, () => { MoveNextView (); return true; });
-			AddCommand (Command.PreviousView, () => { MovePreviousView (); return true; });
-			AddCommand (Command.NextViewOrTop, () => { MoveNextViewOrTop (); return true; });
-			AddCommand (Command.PreviousViewOrTop, () => { MovePreviousViewOrTop (); return true; });
-			AddCommand (Command.Refresh, () => { Application.Refresh (); return true; });
-			AddCommand (Command.Accept, () => {
-				// TODO: Perhaps all views should support the concept of being default?
-				// TODO: It's bad that Toplevel is tightly coupled with Button
-				if (Subviews.FirstOrDefault(v => v is Button && ((Button)v).IsDefault && ((Button)v).Enabled) is Button defaultBtn) {
-					defaultBtn.InvokeCommand (Command.Accept);
-					return true;
-				}
-				return false;
-			});
+			return false;
+		});
 
-			// Default keybindings for this view
-			KeyBindings.Add ((KeyCode)Application.QuitKey, Command.QuitToplevel);
+		// Default keybindings for this view
+		KeyBindings.Add ((KeyCode)Application.QuitKey, Command.QuitToplevel);
 
-			KeyBindings.Add (KeyCode.CursorRight, Command.NextView);
-			KeyBindings.Add (KeyCode.CursorDown, Command.NextView);
-			KeyBindings.Add (KeyCode.CursorLeft, Command.PreviousView);
-			KeyBindings.Add (KeyCode.CursorUp, Command.PreviousView);
+		KeyBindings.Add (KeyCode.CursorRight, Command.NextView);
+		KeyBindings.Add (KeyCode.CursorDown, Command.NextView);
+		KeyBindings.Add (KeyCode.CursorLeft, Command.PreviousView);
+		KeyBindings.Add (KeyCode.CursorUp, Command.PreviousView);
 
-			KeyBindings.Add (KeyCode.Tab, Command.NextView);
-			KeyBindings.Add (KeyCode.Tab | KeyCode.ShiftMask, Command.PreviousView);
-			KeyBindings.Add (KeyCode.Tab | KeyCode.CtrlMask, Command.NextViewOrTop);
-			KeyBindings.Add (KeyCode.Tab | KeyCode.ShiftMask | KeyCode.CtrlMask, Command.PreviousViewOrTop);
+		KeyBindings.Add (KeyCode.Tab, Command.NextView);
+		KeyBindings.Add (KeyCode.Tab | KeyCode.ShiftMask, Command.PreviousView);
+		KeyBindings.Add (KeyCode.Tab | KeyCode.CtrlMask, Command.NextViewOrTop);
+		KeyBindings.Add (KeyCode.Tab | KeyCode.ShiftMask | KeyCode.CtrlMask, Command.PreviousViewOrTop);
 
-			KeyBindings.Add (KeyCode.F5, Command.Refresh);
-			KeyBindings.Add ((KeyCode)Application.AlternateForwardKey, Command.NextViewOrTop); // Needed on Unix
-			KeyBindings.Add ((KeyCode)Application.AlternateBackwardKey, Command.PreviousViewOrTop); // Needed on Unix
+		KeyBindings.Add (KeyCode.F5, Command.Refresh);
+		KeyBindings.Add ((KeyCode)Application.AlternateForwardKey, Command.NextViewOrTop); // Needed on Unix
+		KeyBindings.Add ((KeyCode)Application.AlternateBackwardKey, Command.PreviousViewOrTop); // Needed on Unix
 
 #if UNIX_KEY_BINDINGS
 			KeyBindings.Add (Key.Z | Key.CtrlMask, Command.Suspend);
@@ -243,726 +257,714 @@ namespace Terminal.Gui {
 			KeyBindings.Add (Key.I | Key.CtrlMask, Command.NextView); // Unix
 			KeyBindings.Add (Key.B | Key.CtrlMask, Command.PreviousView);// Unix
 #endif
-			// This enables the default button to be activated by the Enter key.
-			KeyBindings.Add (KeyCode.Enter, Command.Accept);
-		}
+		// This enables the default button to be activated by the Enter key.
+		KeyBindings.Add (KeyCode.Enter, Command.Accept);
+	}
 
-		private void Application_UnGrabbingMouse (object sender, GrabMouseEventArgs e)
-		{
-			if (Application.MouseGrabView == this && _dragPosition.HasValue) {
-				e.Cancel = true;
-			}
+	void Application_UnGrabbingMouse (object sender, GrabMouseEventArgs e)
+	{
+		if (Application.MouseGrabView == this && _dragPosition.HasValue) {
+			e.Cancel = true;
 		}
+	}
 
-		private void Application_GrabbingMouse (object sender, GrabMouseEventArgs e)
-		{
-			if (Application.MouseGrabView == this && _dragPosition.HasValue) {
-				e.Cancel = true;
-			}
+	void Application_GrabbingMouse (object sender, GrabMouseEventArgs e)
+	{
+		if (Application.MouseGrabView == this && _dragPosition.HasValue) {
+			e.Cancel = true;
 		}
+	}
+
+	/// <summary>
+	/// Invoked when the <see cref="Application.AlternateForwardKey"/> is changed.
+	/// </summary>
+	public event EventHandler<KeyChangedEventArgs> AlternateForwardKeyChanged;
+
+	/// <summary>
+	/// Virtual method to invoke the <see cref="AlternateForwardKeyChanged"/> event.
+	/// </summary>
+	/// <param name="e"></param>
+	public virtual void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
+	{
+		KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey);
+		AlternateForwardKeyChanged?.Invoke (this, e);
+	}
+
+	/// <summary>
+	/// Invoked when the <see cref="Application.AlternateBackwardKey"/> is changed.
+	/// </summary>
+	public event EventHandler<KeyChangedEventArgs> AlternateBackwardKeyChanged;
+
+	/// <summary>
+	/// Virtual method to invoke the <see cref="AlternateBackwardKeyChanged"/> event.
+	/// </summary>
+	/// <param name="e"></param>
+	public virtual void OnAlternateBackwardKeyChanged (KeyChangedEventArgs e)
+	{
+		KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey);
+		AlternateBackwardKeyChanged?.Invoke (this, e);
+	}
+
+	/// <summary>
+	/// Invoked when the <see cref="Application.QuitKey"/> is changed.
+	/// </summary>
+	public event EventHandler<KeyChangedEventArgs> QuitKeyChanged;
+
+	/// <summary>
+	/// Virtual method to invoke the <see cref="QuitKeyChanged"/> event.
+	/// </summary>
+	/// <param name="e"></param>
+	public virtual void OnQuitKeyChanged (KeyChangedEventArgs e)
+	{
+		KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey);
+		QuitKeyChanged?.Invoke (this, e);
+	}
+
+	/// <summary>
+	/// Convenience factory method that creates a new Toplevel with the current terminal dimensions.
+	/// </summary>
+	/// <returns>The created Toplevel.</returns>
+	public static Toplevel Create () => new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
+
+	/// <summary>
+	/// Gets or sets a value indicating whether this <see cref="Toplevel"/> can focus.
+	/// </summary>
+	/// <value><c>true</c> if can focus; otherwise, <c>false</c>.</value>
+	public override bool CanFocus => SuperView == null ? true : base.CanFocus;
+
+	/// <summary>
+	/// Determines whether the <see cref="Toplevel"/> is modal or not. 
+	/// If set to <c>false</c> (the default):
+	/// 
+	/// <list type="bullet">
+	///   <item>
+	///		<description><see cref="View.OnKeyDown"/> events will propagate keys upwards.</description>
+	///   </item>
+	///   <item>
+	///		<description>The Toplevel will act as an embedded view (not a modal/pop-up).</description>
+	///   </item>
+	/// </list>
+	///
+	/// If set to <c>true</c>:
+	/// 
+	/// <list type="bullet">
+	///   <item>
+	///		<description><see cref="View.OnKeyDown"/> events will NOT propagate keys upwards.</description>
+	///	  </item>
+	///   <item>
+	///		<description>The Toplevel will and look like a modal (pop-up) (e.g. see <see cref="Dialog"/>.</description>
+	///   </item>
+	/// </list>
+	/// </summary>
+	public bool Modal { get; set; }
 
-		/// <summary>
-		/// Invoked when the <see cref="Application.AlternateForwardKey"/> is changed.
-		/// </summary>
-		public event EventHandler<KeyChangedEventArgs> AlternateForwardKeyChanged;
-
-		/// <summary>
-		/// Virtual method to invoke the <see cref="AlternateForwardKeyChanged"/> event.
-		/// </summary>
-		/// <param name="e"></param>
-		public virtual void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
-		{
-			KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey);
-			AlternateForwardKeyChanged?.Invoke (this, e);
-		}
-
-		/// <summary>
-		/// Invoked when the <see cref="Application.AlternateBackwardKey"/> is changed.
-		/// </summary>
-		public event EventHandler<KeyChangedEventArgs> AlternateBackwardKeyChanged;
-
-		/// <summary>
-		/// Virtual method to invoke the <see cref="AlternateBackwardKeyChanged"/> event.
-		/// </summary>
-		/// <param name="e"></param>
-		public virtual void OnAlternateBackwardKeyChanged (KeyChangedEventArgs e)
-		{
-			KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey);
-			AlternateBackwardKeyChanged?.Invoke (this, e);
-		}
-
-		/// <summary>
-		/// Invoked when the <see cref="Application.QuitKey"/> is changed.
-		/// </summary>
-		public event EventHandler<KeyChangedEventArgs> QuitKeyChanged;
-
-		/// <summary>
-		/// Virtual method to invoke the <see cref="QuitKeyChanged"/> event.
-		/// </summary>
-		/// <param name="e"></param>
-		public virtual void OnQuitKeyChanged (KeyChangedEventArgs e)
-		{
-			KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey);
-			QuitKeyChanged?.Invoke (this, e);
-		}
-
-		/// <summary>
-		/// Convenience factory method that creates a new Toplevel with the current terminal dimensions.
-		/// </summary>
-		/// <returns>The created Toplevel.</returns>
-		public static Toplevel Create ()
-		{
-			return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
-		}
-
-		/// <summary>
-		/// Gets or sets a value indicating whether this <see cref="Toplevel"/> can focus.
-		/// </summary>
-		/// <value><c>true</c> if can focus; otherwise, <c>false</c>.</value>
-		public override bool CanFocus {
-			get => SuperView == null ? true : base.CanFocus;
-		}
-
-		/// <summary>
-		/// Determines whether the <see cref="Toplevel"/> is modal or not. 
-		/// If set to <c>false</c> (the default):
-		/// 
-		/// <list type="bullet">
-		///   <item>
-		///		<description><see cref="View.OnKeyDown"/> events will propagate keys upwards.</description>
-		///   </item>
-		///   <item>
-		///		<description>The Toplevel will act as an embedded view (not a modal/pop-up).</description>
-		///   </item>
-		/// </list>
-		///
-		/// If set to <c>true</c>:
-		/// 
-		/// <list type="bullet">
-		///   <item>
-		///		<description><see cref="View.OnKeyDown"/> events will NOT propagate keys upwards.</description>
-		///	  </item>
-		///   <item>
-		///		<description>The Toplevel will and look like a modal (pop-up) (e.g. see <see cref="Dialog"/>.</description>
-		///   </item>
-		/// </list>
-		/// </summary>
-		public bool Modal { get; set; }
-
-		/// <summary>
-		/// Gets or sets the menu for this Toplevel.
-		/// </summary>
-		public virtual MenuBar MenuBar { get; set; }
-
-		/// <summary>
-		/// Gets or sets the status bar for this Toplevel.
-		/// </summary>
-		public virtual StatusBar StatusBar { get; set; }
-
-		/// <summary>
-		/// <see langword="true"/> if was already loaded by the <see cref="Application.Begin(Toplevel)"/>
-		/// <see langword="false"/>, otherwise.
-		/// </summary>
-		public bool IsLoaded { get; private set; }
-
-		private void MovePreviousViewOrTop ()
-		{
-			if (Application.OverlappedTop == null) {
-				var top = Modal ? this : Application.Top;
+	/// <summary>
+	/// Gets or sets the menu for this Toplevel.
+	/// </summary>
+	public virtual MenuBar MenuBar { get; set; }
+
+	/// <summary>
+	/// Gets or sets the status bar for this Toplevel.
+	/// </summary>
+	public virtual StatusBar StatusBar { get; set; }
+
+	/// <summary>
+	/// <see langword="true"/> if was already loaded by the <see cref="Application.Begin(Toplevel)"/>
+	/// <see langword="false"/>, otherwise.
+	/// </summary>
+	public bool IsLoaded { get; private set; }
+
+	void MovePreviousViewOrTop ()
+	{
+		if (Application.OverlappedTop == null) {
+			var top = Modal ? this : Application.Top;
+			top.FocusPrev ();
+			if (top.Focused == null) {
 				top.FocusPrev ();
-				if (top.Focused == null) {
-					top.FocusPrev ();
-				}
-				top.SetNeedsDisplay ();
-				Application.BringOverlappedTopToFront ();
-			} else {
-				Application.OverlappedMovePrevious ();
 			}
+			top.SetNeedsDisplay ();
+			Application.BringOverlappedTopToFront ();
+		} else {
+			Application.OverlappedMovePrevious ();
 		}
+	}
 
-		private void MoveNextViewOrTop ()
-		{
-			if (Application.OverlappedTop == null) {
-				var top = Modal ? this : Application.Top;
+	void MoveNextViewOrTop ()
+	{
+		if (Application.OverlappedTop == null) {
+			var top = Modal ? this : Application.Top;
+			top.FocusNext ();
+			if (top.Focused == null) {
 				top.FocusNext ();
-				if (top.Focused == null) {
-					top.FocusNext ();
-				}
-				top.SetNeedsDisplay ();
-				Application.BringOverlappedTopToFront ();
-			} else {
-				Application.OverlappedMoveNext ();
 			}
+			top.SetNeedsDisplay ();
+			Application.BringOverlappedTopToFront ();
+		} else {
+			Application.OverlappedMoveNext ();
 		}
+	}
 
-		private void MovePreviousView ()
-		{
-			var old = GetDeepestFocusedSubview (Focused);
-			if (!FocusPrev ())
-				FocusPrev ();
-			if (old != Focused && old != Focused?.Focused) {
-				old?.SetNeedsDisplay ();
-				Focused?.SetNeedsDisplay ();
-			} else {
-				FocusNearestView (SuperView?.TabIndexes?.Reverse (), Direction.Backward);
-			}
+	void MovePreviousView ()
+	{
+		var old = GetDeepestFocusedSubview (Focused);
+		if (!FocusPrev ()) {
+			FocusPrev ();
+		}
+		if (old != Focused && old != Focused?.Focused) {
+			old?.SetNeedsDisplay ();
+			Focused?.SetNeedsDisplay ();
+		} else {
+			FocusNearestView (SuperView?.TabIndexes?.Reverse (), Direction.Backward);
 		}
+	}
 
-		private void MoveNextView ()
-		{
-			var old = GetDeepestFocusedSubview (Focused);
-			if (!FocusNext ())
-				FocusNext ();
-			if (old != Focused && old != Focused?.Focused) {
-				old?.SetNeedsDisplay ();
-				Focused?.SetNeedsDisplay ();
-			} else {
-				FocusNearestView (SuperView?.TabIndexes, Direction.Forward);
-			}
+	void MoveNextView ()
+	{
+		var old = GetDeepestFocusedSubview (Focused);
+		if (!FocusNext ()) {
+			FocusNext ();
 		}
+		if (old != Focused && old != Focused?.Focused) {
+			old?.SetNeedsDisplay ();
+			Focused?.SetNeedsDisplay ();
+		} else {
+			FocusNearestView (SuperView?.TabIndexes, Direction.Forward);
+		}
+	}
 
-		private void QuitToplevel ()
-		{
-			if (Application.OverlappedTop != null) {
-				Application.OverlappedTop.RequestStop ();
-			} else {
-				Application.RequestStop ();
-			}
+	void QuitToplevel ()
+	{
+		if (Application.OverlappedTop != null) {
+			Application.OverlappedTop.RequestStop ();
+		} else {
+			Application.RequestStop ();
 		}
+	}
 
-		View GetDeepestFocusedSubview (View view)
-		{
-			if (view == null) {
-				return null;
-			}
+	View GetDeepestFocusedSubview (View view)
+	{
+		if (view == null) {
+			return null;
+		}
 
-			foreach (var v in view.Subviews) {
-				if (v.HasFocus) {
-					return GetDeepestFocusedSubview (v);
-				}
+		foreach (var v in view.Subviews) {
+			if (v.HasFocus) {
+				return GetDeepestFocusedSubview (v);
 			}
-			return view;
 		}
+		return view;
+	}
 
-		void FocusNearestView (IEnumerable<View> views, Direction direction)
-		{
-			if (views == null) {
-				return;
-			}
+	void FocusNearestView (IEnumerable<View> views, Direction direction)
+	{
+		if (views == null) {
+			return;
+		}
 
-			bool found = false;
-			bool focusProcessed = false;
-			int idx = 0;
+		bool found = false;
+		bool focusProcessed = false;
+		int idx = 0;
 
-			foreach (var v in views) {
-				if (v == this) {
-					found = true;
+		foreach (var v in views) {
+			if (v == this) {
+				found = true;
+			}
+			if (found && v != this) {
+				if (direction == Direction.Forward) {
+					SuperView?.FocusNext ();
+				} else {
+					SuperView?.FocusPrev ();
 				}
-				if (found && v != this) {
-					if (direction == Direction.Forward) {
-						SuperView?.FocusNext ();
-					} else {
-						SuperView?.FocusPrev ();
-					}
-					focusProcessed = true;
-					if (SuperView.Focused != null && SuperView.Focused != this) {
-						return;
-					}
-				} else if (found && !focusProcessed && idx == views.Count () - 1) {
-					views.ToList () [0].SetFocus ();
+				focusProcessed = true;
+				if (SuperView.Focused != null && SuperView.Focused != this) {
+					return;
 				}
-				idx++;
+			} else if (found && !focusProcessed && idx == views.Count () - 1) {
+				views.ToList () [0].SetFocus ();
 			}
+			idx++;
 		}
+	}
 
-		///<inheritdoc/>
-		public override void Add (View view)
-		{
-			CanFocus = true;
-			AddMenuStatusBar (view);
-			base.Add (view);
-		}
+	///<inheritdoc/>
+	public override void Add (View view)
+	{
+		CanFocus = true;
+		AddMenuStatusBar (view);
+		base.Add (view);
+	}
 
-		internal void AddMenuStatusBar (View view)
-		{
-			if (view is MenuBar) {
-				MenuBar = view as MenuBar;
-			}
-			if (view is StatusBar) {
-				StatusBar = view as StatusBar;
-			}
+	internal void AddMenuStatusBar (View view)
+	{
+		if (view is MenuBar) {
+			MenuBar = view as MenuBar;
 		}
+		if (view is StatusBar) {
+			StatusBar = view as StatusBar;
+		}
+	}
 
-		///<inheritdoc/>
-		public override void Remove (View view)
-		{
-			if (this is Toplevel Toplevel && Toplevel.MenuBar != null) {
-				RemoveMenuStatusBar (view);
-			}
-			base.Remove (view);
+	///<inheritdoc/>
+	public override void Remove (View view)
+	{
+		if (this is Toplevel Toplevel && Toplevel.MenuBar != null) {
+			RemoveMenuStatusBar (view);
 		}
+		base.Remove (view);
+	}
 
-		///<inheritdoc/>
-		public override void RemoveAll ()
-		{
-			if (this == Application.Top) {
-				MenuBar?.Dispose ();
-				MenuBar = null;
-				StatusBar?.Dispose ();
-				StatusBar = null;
-			}
-			base.RemoveAll ();
+	///<inheritdoc/>
+	public override void RemoveAll ()
+	{
+		if (this == Application.Top) {
+			MenuBar?.Dispose ();
+			MenuBar = null;
+			StatusBar?.Dispose ();
+			StatusBar = null;
 		}
+		base.RemoveAll ();
+	}
 
-		internal void RemoveMenuStatusBar (View view)
-		{
-			if (view is MenuBar) {
-				MenuBar?.Dispose ();
-				MenuBar = null;
-			}
-			if (view is StatusBar) {
-				StatusBar?.Dispose ();
-				StatusBar = null;
-			}
+	internal void RemoveMenuStatusBar (View view)
+	{
+		if (view is MenuBar) {
+			MenuBar?.Dispose ();
+			MenuBar = null;
 		}
+		if (view is StatusBar) {
+			StatusBar?.Dispose ();
+			StatusBar = null;
+		}
+	}
 
-		/// <summary>
-		///  Gets a new location of the <see cref="Toplevel"/> that is within the Bounds of the <paramref name="top"/>'s 
-		///  <see cref="View.SuperView"/> (e.g. for dragging a Window).
-		///  The `out` parameters are the new X and Y coordinates.
-		/// </summary>
-		/// <remarks>
-		/// If <paramref name="top"/> does not have a <see cref="View.SuperView"/> or it's SuperView is not <see cref="Application.Top"/>
-		/// the position will be bound by the <see cref="ConsoleDriver.Cols"/> and <see cref="ConsoleDriver.Rows"/>.
-		/// </remarks>
-		/// <param name="top">The Toplevel that is to be moved.</param>
-		/// <param name="targetX">The target x location.</param>
-		/// <param name="targetY">The target y location.</param>
-		/// <param name="nx">The x location that will ensure <paramref name="top"/> will be visible.</param>
-		/// <param name="ny">The y location that will ensure <paramref name="top"/> will be visible.</param>
-		/// <param name="menuBar">The new top most menuBar</param>
-		/// <param name="statusBar">The new top most statusBar</param>
-		/// <returns>
-		///  Either <see cref="Application.Top"/> (if <paramref name="top"/> does not have a Super View) or
-		///  <paramref name="top"/>'s SuperView. This can be used to ensure LayoutSubviews is called on the correct View.
-		///  </returns>
-		internal View GetLocationThatFits (Toplevel top, int targetX, int targetY,
+	/// <summary>
+	///  Gets a new location of the <see cref="Toplevel"/> that is within the Bounds of the <paramref name="top"/>'s 
+	///  <see cref="View.SuperView"/> (e.g. for dragging a Window).
+	///  The `out` parameters are the new X and Y coordinates.
+	/// </summary>
+	/// <remarks>
+	/// If <paramref name="top"/> does not have a <see cref="View.SuperView"/> or it's SuperView is not <see cref="Application.Top"/>
+	/// the position will be bound by the <see cref="ConsoleDriver.Cols"/> and <see cref="ConsoleDriver.Rows"/>.
+	/// </remarks>
+	/// <param name="top">The Toplevel that is to be moved.</param>
+	/// <param name="targetX">The target x location.</param>
+	/// <param name="targetY">The target y location.</param>
+	/// <param name="nx">The x location that will ensure <paramref name="top"/> will be visible.</param>
+	/// <param name="ny">The y location that will ensure <paramref name="top"/> will be visible.</param>
+	/// <param name="menuBar">The new top most menuBar</param>
+	/// <param name="statusBar">The new top most statusBar</param>
+	/// <returns>
+	///  Either <see cref="Application.Top"/> (if <paramref name="top"/> does not have a Super View) or
+	///  <paramref name="top"/>'s SuperView. This can be used to ensure LayoutSubviews is called on the correct View.
+	///  </returns>
+	internal View GetLocationThatFits (Toplevel top, int targetX, int targetY,
 					out int nx, out int ny, out MenuBar menuBar, out StatusBar statusBar)
-		{
-			int maxWidth;
-			View superView;
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				maxWidth = Driver.Cols;
-				superView = Application.Top;
-			} else {
-				// Use the SuperView's Bounds, not Frame
-				maxWidth = top.SuperView.Bounds.Width;
-				superView = top.SuperView;
-			}
-			if (superView.Margin != null && superView == top.SuperView) {
-				maxWidth -= superView.GetFramesThickness ().Left + superView.GetFramesThickness ().Right;
-			}
-			if (top.Frame.Width <= maxWidth) {
-				nx = Math.Max (targetX, 0);
-				nx = nx + top.Frame.Width > maxWidth ? Math.Max (maxWidth - top.Frame.Width, 0) : nx;
-				if (nx > top.Frame.X + top.Frame.Width) {
-					nx = Math.Max (top.Frame.Right, 0);
-				}
-			} else {
-				nx = targetX;
-			}
-			//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
-			bool menuVisible, statusVisible;
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				menuVisible = Application.Top.MenuBar?.Visible == true;
-				menuBar = Application.Top.MenuBar;
-			} else {
-				var t = top.SuperView;
-				while (t is not Toplevel) {
-					t = t.SuperView;
-				}
-				menuVisible = ((Toplevel)t).MenuBar?.Visible == true;
-				menuBar = ((Toplevel)t).MenuBar;
-			}
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				maxWidth = menuVisible ? 1 : 0;
-			} else {
-				maxWidth = 0;
-			}
-			ny = Math.Max (targetY, maxWidth);
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				statusVisible = Application.Top.StatusBar?.Visible == true;
-				statusBar = Application.Top.StatusBar;
-			} else {
-				var t = top.SuperView;
-				while (t is not Toplevel) {
-					t = t.SuperView;
-				}
-				statusVisible = ((Toplevel)t).StatusBar?.Visible == true;
-				statusBar = ((Toplevel)t).StatusBar;
-			}
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				maxWidth = statusVisible ? Driver.Rows - 1 : Driver.Rows;
-			} else {
-				maxWidth = statusVisible ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
-			}
-			if (superView.Margin != null && superView == top.SuperView) {
-				maxWidth -= superView.GetFramesThickness ().Top + superView.GetFramesThickness ().Bottom;
-			}
-			ny = Math.Min (ny, maxWidth);
-			if (top.Frame.Height <= maxWidth) {
-				ny = ny + top.Frame.Height > maxWidth ? Math.Max (maxWidth - top.Frame.Height, menuVisible ? 1 : 0) : ny;
-				if (ny > top.Frame.Y + top.Frame.Height) {
-					ny = Math.Max (top.Frame.Bottom, 0);
-				}
-			}
-			//System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
-
-			return superView;
-		}
+	{
+		int maxWidth;
+		View superView;
+		if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+			maxWidth = Driver.Cols;
+			superView = Application.Top;
+		} else {
+			// Use the SuperView's Bounds, not Frame
+			maxWidth = top.SuperView.Bounds.Width;
+			superView = top.SuperView;
+		}
+		if (superView.Margin != null && superView == top.SuperView) {
+			maxWidth -= superView.GetFramesThickness ().Left + superView.GetFramesThickness ().Right;
+		}
+		if (top.Frame.Width <= maxWidth) {
+			nx = Math.Max (targetX, 0);
+			nx = nx + top.Frame.Width > maxWidth ? Math.Max (maxWidth - top.Frame.Width, 0) : nx;
+			if (nx > top.Frame.X + top.Frame.Width) {
+				nx = Math.Max (top.Frame.Right, 0);
+			}
+		} else {
+			nx = targetX;
+		}
+		//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
+		bool menuVisible, statusVisible;
+		if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+			menuVisible = Application.Top.MenuBar?.Visible == true;
+			menuBar = Application.Top.MenuBar;
+		} else {
+			var t = top.SuperView;
+			while (t is not Toplevel) {
+				t = t.SuperView;
+			}
+			menuVisible = ((Toplevel)t).MenuBar?.Visible == true;
+			menuBar = ((Toplevel)t).MenuBar;
+		}
+		if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+			maxWidth = menuVisible ? 1 : 0;
+		} else {
+			maxWidth = 0;
+		}
+		ny = Math.Max (targetY, maxWidth);
+		if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+			statusVisible = Application.Top.StatusBar?.Visible == true;
+			statusBar = Application.Top.StatusBar;
+		} else {
+			var t = top.SuperView;
+			while (t is not Toplevel) {
+				t = t.SuperView;
+			}
+			statusVisible = ((Toplevel)t).StatusBar?.Visible == true;
+			statusBar = ((Toplevel)t).StatusBar;
+		}
+		if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+			maxWidth = statusVisible ? Driver.Rows - 1 : Driver.Rows;
+		} else {
+			maxWidth = statusVisible ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
+		}
+		if (superView.Margin != null && superView == top.SuperView) {
+			maxWidth -= superView.GetFramesThickness ().Top + superView.GetFramesThickness ().Bottom;
+		}
+		ny = Math.Min (ny, maxWidth);
+		if (top.Frame.Height <= maxWidth) {
+			ny = ny + top.Frame.Height > maxWidth ? Math.Max (maxWidth - top.Frame.Height, menuVisible ? 1 : 0) : ny;
+			if (ny > top.Frame.Y + top.Frame.Height) {
+				ny = Math.Max (top.Frame.Bottom, 0);
+			}
+		}
+		//System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
+
+		return superView;
+	}
 
-		// TODO: v2 - Not sure this is needed anymore.
-		internal void PositionToplevels ()
-		{
-			PositionToplevel (this);
-			foreach (var top in Subviews) {
-				if (top is Toplevel) {
-					PositionToplevel ((Toplevel)top);
-				}
+	// TODO: v2 - Not sure this is needed anymore.
+	internal void PositionToplevels ()
+	{
+		PositionToplevel (this);
+		foreach (var top in Subviews) {
+			if (top is Toplevel) {
+				PositionToplevel ((Toplevel)top);
 			}
 		}
+	}
 
-		/// <summary>
-		/// Adjusts the location and size of <paramref name="top"/> within this Toplevel.
-		/// Virtual method enabling implementation of specific positions for inherited <see cref="Toplevel"/> views.
-		/// </summary>
-		/// <param name="top">The Toplevel to adjust.</param>
-		public virtual void PositionToplevel (Toplevel top)
-		{
-			var superView = GetLocationThatFits (top, top.Frame.X, top.Frame.Y,
-				out int nx, out int ny, out _, out StatusBar sb);
-			bool layoutSubviews = false;
-			var maxWidth = 0;
-			if (superView.Margin != null && superView == top.SuperView) {
-				maxWidth -= superView.GetFramesThickness ().Left + superView.GetFramesThickness ().Right;
-			}
-			if ((superView != top || top?.SuperView != null || (top != Application.Top && top.Modal)
-				|| (top?.SuperView == null && top.IsOverlapped))
-				&& (top.Frame.X + top.Frame.Width > maxWidth || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
-
-				if ((top.X == null || top.X is Pos.PosAbsolute) && top.Frame.X != nx) {
-					top.X = nx;
-					layoutSubviews = true;
-				}
-				if ((top.Y == null || top.Y is Pos.PosAbsolute) && top.Frame.Y != ny) {
-					top.Y = ny;
-					layoutSubviews = true;
-				}
+	/// <summary>
+	/// Adjusts the location and size of <paramref name="top"/> within this Toplevel.
+	/// Virtual method enabling implementation of specific positions for inherited <see cref="Toplevel"/> views.
+	/// </summary>
+	/// <param name="top">The Toplevel to adjust.</param>
+	public virtual void PositionToplevel (Toplevel top)
+	{
+		var superView = GetLocationThatFits (top, top.Frame.X, top.Frame.Y,
+			out int nx, out int ny, out _, out var sb);
+		bool layoutSubviews = false;
+		int maxWidth = 0;
+		if (superView.Margin != null && superView == top.SuperView) {
+			maxWidth -= superView.GetFramesThickness ().Left + superView.GetFramesThickness ().Right;
+		}
+		if ((superView != top || top?.SuperView != null || top != Application.Top && top.Modal
+			|| top?.SuperView == null && top.IsOverlapped)
+		&& (top.Frame.X + top.Frame.Width > maxWidth || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
+
+			if ((top.X == null || top.X is Pos.PosAbsolute) && top.Frame.X != nx) {
+				top.X = nx;
+				layoutSubviews = true;
 			}
-
-			// TODO: v2 - This is a hack to get the StatusBar to be positioned correctly.
-			if (sb != null && !top.Subviews.Contains (sb) && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0)
-				&& top.Height is Dim.DimFill && -top.Height.Anchor (0) < 1) {
-
-				top.Height = Dim.Fill (sb.Visible ? 1 : 0);
+			if ((top.Y == null || top.Y is Pos.PosAbsolute) && top.Frame.Y != ny) {
+				top.Y = ny;
 				layoutSubviews = true;
 			}
+		}
 
-			if (superView.LayoutNeeded || layoutSubviews) {
-				superView.LayoutSubviews ();
-			}
-			if (LayoutNeeded) {
-				LayoutSubviews ();
-			}
+		// TODO: v2 - This is a hack to get the StatusBar to be positioned correctly.
+		if (sb != null && !top.Subviews.Contains (sb) && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0)
+		&& top.Height is Dim.DimFill && -top.Height.Anchor (0) < 1) {
+
+			top.Height = Dim.Fill (sb.Visible ? 1 : 0);
+			layoutSubviews = true;
 		}
 
-		///<inheritdoc/>
-		public override void OnDrawContent (Rect contentArea)
-		{
-			if (!Visible) {
-				return;
-			}
+		if (superView.LayoutNeeded || layoutSubviews) {
+			superView.LayoutSubviews ();
+		}
+		if (LayoutNeeded) {
+			LayoutSubviews ();
+		}
+	}
 
-			if (NeedsDisplay || SubViewNeedsDisplay || LayoutNeeded) {
-				//Driver.SetAttribute (GetNormalColor ());
-				// TODO: It's bad practice for views to always clear. Defeats the purpose of clipping etc...
-				Clear ();
-				LayoutSubviews ();
-				PositionToplevels ();
-
-				if (this == Application.OverlappedTop) {
-					foreach (var top in Application.OverlappedChildren.AsEnumerable ().Reverse ()) {
-						if (top.Frame.IntersectsWith (Bounds)) {
-							if (top != this && !top.IsCurrentTop && !OutsideTopFrame (top) && top.Visible) {
-								top.SetNeedsLayout ();
-								top.SetNeedsDisplay (top.Bounds);
-								top.Draw ();
-								top.OnRenderLineCanvas ();
-							}
+	///<inheritdoc/>
+	public override void OnDrawContent (Rect contentArea)
+	{
+		if (!Visible) {
+			return;
+		}
+
+		if (NeedsDisplay || SubViewNeedsDisplay || LayoutNeeded) {
+			//Driver.SetAttribute (GetNormalColor ());
+			// TODO: It's bad practice for views to always clear. Defeats the purpose of clipping etc...
+			Clear ();
+			LayoutSubviews ();
+			PositionToplevels ();
+
+			if (this == Application.OverlappedTop) {
+				foreach (var top in Application.OverlappedChildren.AsEnumerable ().Reverse ()) {
+					if (top.Frame.IntersectsWith (Bounds)) {
+						if (top != this && !top.IsCurrentTop && !OutsideTopFrame (top) && top.Visible) {
+							top.SetNeedsLayout ();
+							top.SetNeedsDisplay (top.Bounds);
+							top.Draw ();
+							top.OnRenderLineCanvas ();
 						}
 					}
 				}
+			}
 
-				// This should not be here, but in base
-				foreach (var view in Subviews) {
-					if (view.Frame.IntersectsWith (Bounds) && !OutsideTopFrame (this)) {
-						//view.SetNeedsLayout ();
-						view.SetNeedsDisplay (view.Bounds);
-						view.SetSubViewNeedsDisplay ();
-					}
+			// This should not be here, but in base
+			foreach (var view in Subviews) {
+				if (view.Frame.IntersectsWith (Bounds) && !OutsideTopFrame (this)) {
+					//view.SetNeedsLayout ();
+					view.SetNeedsDisplay (view.Bounds);
+					view.SetSubViewNeedsDisplay ();
 				}
+			}
 
-				base.OnDrawContent (contentArea);
+			base.OnDrawContent (contentArea);
 
-				// This is causing the menus drawn incorrectly if UseSubMenusSingleFrame is true
-				//if (this.MenuBar != null && this.MenuBar.IsMenuOpen && this.MenuBar.openMenu != null) {
-				//	// TODO: Hack until we can get compositing working right.
-				//	this.MenuBar.openMenu.Redraw (this.MenuBar.openMenu.Bounds);
-				//}
-			}
+			// This is causing the menus drawn incorrectly if UseSubMenusSingleFrame is true
+			//if (this.MenuBar != null && this.MenuBar.IsMenuOpen && this.MenuBar.openMenu != null) {
+			//	// TODO: Hack until we can get compositing working right.
+			//	this.MenuBar.openMenu.Redraw (this.MenuBar.openMenu.Bounds);
+			//}
 		}
+	}
 
-		bool OutsideTopFrame (Toplevel top)
-		{
-			if (top.Frame.X > Driver.Cols || top.Frame.Y > Driver.Rows) {
-				return true;
-			}
-			return false;
+	bool OutsideTopFrame (Toplevel top)
+	{
+		if (top.Frame.X > Driver.Cols || top.Frame.Y > Driver.Rows) {
+			return true;
 		}
+		return false;
+	}
 
-		internal static Point? _dragPosition;
-		Point _startGrabPoint;
-
-		///<inheritdoc/>
-		public override bool MouseEvent (MouseEvent mouseEvent)
-		{
-			if (!CanFocus) {
-				return true;
-			}
-
-			//System.Diagnostics.Debug.WriteLine ($"dragPosition before: {dragPosition.HasValue}");
-
-			int nx, ny;
-			if (!_dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed
-				|| mouseEvent.Flags == MouseFlags.Button2Pressed
-				|| mouseEvent.Flags == MouseFlags.Button3Pressed)) {
-
-				SetFocus ();
-				Application.BringOverlappedTopToFront ();
-
-				// Only start grabbing if the user clicks on the title bar.
-				// BUGBUG: Assumes Frame == Border and Title is always at Y == 0
-				if (mouseEvent.Y == 0 && mouseEvent.Flags == MouseFlags.Button1Pressed) {
-					_startGrabPoint = new Point (mouseEvent.X, mouseEvent.Y);
-					_dragPosition = new Point ();
-					nx = mouseEvent.X - mouseEvent.OfX;
-					ny = mouseEvent.Y - mouseEvent.OfY;
-					_dragPosition = new Point (nx, ny);
-					Application.GrabMouse (this);
+	internal static Point? _dragPosition;
+	Point _startGrabPoint;
+
+	///<inheritdoc/>
+	public override bool MouseEvent (MouseEvent mouseEvent)
+	{
+		if (!CanFocus) {
+			return true;
+		}
+
+		//System.Diagnostics.Debug.WriteLine ($"dragPosition before: {dragPosition.HasValue}");
+
+		int nx, ny;
+		if (!_dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed
+						|| mouseEvent.Flags == MouseFlags.Button2Pressed
+						|| mouseEvent.Flags == MouseFlags.Button3Pressed)) {
+
+			SetFocus ();
+			Application.BringOverlappedTopToFront ();
+
+			// Only start grabbing if the user clicks on the title bar.
+			// BUGBUG: Assumes Frame == Border and Title is always at Y == 0
+			if (mouseEvent.Y == 0 && mouseEvent.Flags == MouseFlags.Button1Pressed) {
+				_startGrabPoint = new Point (mouseEvent.X, mouseEvent.Y);
+				_dragPosition = new Point ();
+				nx = mouseEvent.X - mouseEvent.OfX;
+				ny = mouseEvent.Y - mouseEvent.OfY;
+				_dragPosition = new Point (nx, ny);
+				Application.GrabMouse (this);
+			}
+
+			//System.Diagnostics.Debug.WriteLine ($"Starting at {dragPosition}");
+			return true;
+		} else if (mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
+			mouseEvent.Flags == MouseFlags.Button3Pressed) {
+			if (_dragPosition.HasValue) {
+				if (SuperView == null) {
+					// Redraw the entire app window using just our Frame. Since we are 
+					// Application.Top, and our Frame always == our Bounds (Location is always (0,0))
+					// our Frame is actually view-relative (which is what Redraw takes).
+					// We need to pass all the view bounds because since the windows was 
+					// moved around, we don't know exactly what was the affected region.
+					Application.Top.SetNeedsDisplay ();
+				} else {
+					SuperView.SetNeedsDisplay ();
 				}
+				// BUGBUG: Assumes Frame == Border?
+				GetLocationThatFits (this, mouseEvent.X + (SuperView == null ? mouseEvent.OfX - _startGrabPoint.X : Frame.X - _startGrabPoint.X),
+					mouseEvent.Y + (SuperView == null ? mouseEvent.OfY - _startGrabPoint.Y : Frame.Y - _startGrabPoint.Y),
+					out nx, out ny, out _, out _);
 
-				//System.Diagnostics.Debug.WriteLine ($"Starting at {dragPosition}");
-				return true;
-			} else if (mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
-				mouseEvent.Flags == MouseFlags.Button3Pressed) {
-				if (_dragPosition.HasValue) {
-					if (SuperView == null) {
-						// Redraw the entire app window using just our Frame. Since we are 
-						// Application.Top, and our Frame always == our Bounds (Location is always (0,0))
-						// our Frame is actually view-relative (which is what Redraw takes).
-						// We need to pass all the view bounds because since the windows was 
-						// moved around, we don't know exactly what was the affected region.
-						Application.Top.SetNeedsDisplay ();
-					} else {
-						SuperView.SetNeedsDisplay ();
-					}
-					// BUGBUG: Assumes Frame == Border?
-					GetLocationThatFits (this, mouseEvent.X + (SuperView == null ? mouseEvent.OfX - _startGrabPoint.X : Frame.X - _startGrabPoint.X),
-						mouseEvent.Y + (SuperView == null ? mouseEvent.OfY - _startGrabPoint.Y : Frame.Y - _startGrabPoint.Y),
-						out nx, out ny, out _, out _);
-
-					_dragPosition = new Point (nx, ny);
-					X = nx;
-					Y = ny;
-					//System.Diagnostics.Debug.WriteLine ($"Drag: nx:{nx},ny:{ny}");
-
-					SetNeedsDisplay ();
-					return true;
-				}
-			}
+				_dragPosition = new Point (nx, ny);
+				X = nx;
+				Y = ny;
+				//System.Diagnostics.Debug.WriteLine ($"Drag: nx:{nx},ny:{ny}");
 
-			if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && _dragPosition.HasValue) {
-				_dragPosition = null;
-				Application.UngrabMouse ();
+				SetNeedsDisplay ();
+				return true;
 			}
+		}
 
-			//System.Diagnostics.Debug.WriteLine ($"dragPosition after: {dragPosition.HasValue}");
-			//System.Diagnostics.Debug.WriteLine ($"Toplevel: {mouseEvent}");
-			return false;
+		if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && _dragPosition.HasValue) {
+			_dragPosition = null;
+			Application.UngrabMouse ();
 		}
 
-		/// <summary>
-		/// Stops and closes this <see cref="Toplevel"/>. If this Toplevel is the top-most Toplevel, 
-		/// <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to exit.
-		/// </summary>
-		public virtual void RequestStop ()
-		{
-			if (IsOverlappedContainer && Running
-				&& (Application.Current == this
-				|| Application.Current?.Modal == false
-				|| Application.Current?.Modal == true && Application.Current?.Running == false)) {
-
-				foreach (var child in Application.OverlappedChildren) {
-					var ev = new ToplevelClosingEventArgs (this);
-					if (child.OnClosing (ev)) {
-						return;
-					}
-					child.Running = false;
-					Application.RequestStop (child);
-				}
-				Running = false;
-				Application.RequestStop (this);
-			} else if (IsOverlappedContainer && Running && Application.Current?.Modal == true && Application.Current?.Running == true) {
-				var ev = new ToplevelClosingEventArgs (Application.Current);
-				if (OnClosing (ev)) {
-					return;
-				}
-				Application.RequestStop (Application.Current);
-			} else if (!IsOverlappedContainer && Running && (!Modal || (Modal && Application.Current != this))) {
+		//System.Diagnostics.Debug.WriteLine ($"dragPosition after: {dragPosition.HasValue}");
+		//System.Diagnostics.Debug.WriteLine ($"Toplevel: {mouseEvent}");
+		return false;
+	}
+
+	/// <summary>
+	/// Stops and closes this <see cref="Toplevel"/>. If this Toplevel is the top-most Toplevel, 
+	/// <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to exit.
+	/// </summary>
+	public virtual void RequestStop ()
+	{
+		if (IsOverlappedContainer && Running
+					&& (Application.Current == this
+					|| Application.Current?.Modal == false
+					|| Application.Current?.Modal == true && Application.Current?.Running == false)) {
+
+			foreach (var child in Application.OverlappedChildren) {
 				var ev = new ToplevelClosingEventArgs (this);
-				if (OnClosing (ev)) {
+				if (child.OnClosing (ev)) {
 					return;
 				}
-				Running = false;
-				Application.RequestStop (this);
-			} else {
-				Application.RequestStop (Application.Current);
+				child.Running = false;
+				Application.RequestStop (child);
+			}
+			Running = false;
+			Application.RequestStop (this);
+		} else if (IsOverlappedContainer && Running && Application.Current?.Modal == true && Application.Current?.Running == true) {
+			var ev = new ToplevelClosingEventArgs (Application.Current);
+			if (OnClosing (ev)) {
+				return;
 			}
+			Application.RequestStop (Application.Current);
+		} else if (!IsOverlappedContainer && Running && (!Modal || Modal && Application.Current != this)) {
+			var ev = new ToplevelClosingEventArgs (this);
+			if (OnClosing (ev)) {
+				return;
+			}
+			Running = false;
+			Application.RequestStop (this);
+		} else {
+			Application.RequestStop (Application.Current);
 		}
+	}
 
-		/// <summary>
-		/// Stops and closes the <see cref="Toplevel"/> specified by <paramref name="top"/>. If <paramref name="top"/> is the top-most Toplevel, 
-		/// <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to exit.
-		/// </summary>
-		/// <param name="top">The Toplevel to request stop.</param>
-		public virtual void RequestStop (Toplevel top)
-		{
-			top.RequestStop ();
-		}
+	/// <summary>
+	/// Stops and closes the <see cref="Toplevel"/> specified by <paramref name="top"/>. If <paramref name="top"/> is the top-most Toplevel, 
+	/// <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to exit.
+	/// </summary>
+	/// <param name="top">The Toplevel to request stop.</param>
+	public virtual void RequestStop (Toplevel top) => top.RequestStop ();
 
-		///<inheritdoc/>
-		public override void PositionCursor ()
-		{
-			if (!IsOverlappedContainer) {
-				base.PositionCursor ();
+	///<inheritdoc/>
+	public override void PositionCursor ()
+	{
+		if (!IsOverlappedContainer) {
+			base.PositionCursor ();
+			if (Focused == null) {
+				EnsureFocus ();
 				if (Focused == null) {
-					EnsureFocus ();
-					if (Focused == null) {
-						Driver.SetCursorVisibility (CursorVisibility.Invisible);
-					}
+					Driver.SetCursorVisibility (CursorVisibility.Invisible);
 				}
-				return;
 			}
+			return;
+		}
 
-			if (Focused == null) {
-				foreach (var top in Application.OverlappedChildren) {
-					if (top != this && top.Visible) {
-						top.SetFocus ();
-						return;
-					}
+		if (Focused == null) {
+			foreach (var top in Application.OverlappedChildren) {
+				if (top != this && top.Visible) {
+					top.SetFocus ();
+					return;
 				}
 			}
-			base.PositionCursor ();
-			if (Focused == null) {
-				Driver.SetCursorVisibility (CursorVisibility.Invisible);
-			}
 		}
-
-		///<inheritdoc/>
-		public override bool OnEnter (View view)
-		{
-			return MostFocused?.OnEnter (view) ?? base.OnEnter (view);
+		base.PositionCursor ();
+		if (Focused == null) {
+			Driver.SetCursorVisibility (CursorVisibility.Invisible);
 		}
+	}
 
-		///<inheritdoc/>
-		public override bool OnLeave (View view)
-		{
-			return MostFocused?.OnLeave (view) ?? base.OnLeave (view);
-		}
+	///<inheritdoc/>
+	public override bool OnEnter (View view) => MostFocused?.OnEnter (view) ?? base.OnEnter (view);
 
-		///<inheritdoc/>
-		protected override void Dispose (bool disposing)
-		{
-			Application.GrabbingMouse -= Application_GrabbingMouse;
-			Application.UnGrabbingMouse -= Application_UnGrabbingMouse;
+	///<inheritdoc/>
+	public override bool OnLeave (View view) => MostFocused?.OnLeave (view) ?? base.OnLeave (view);
 
-			_dragPosition = null;
-			base.Dispose (disposing);
+	///<inheritdoc/>
+	protected override void Dispose (bool disposing)
+	{
+		Application.GrabbingMouse -= Application_GrabbingMouse;
+		Application.UnGrabbingMouse -= Application_UnGrabbingMouse;
+
+		_dragPosition = null;
+		base.Dispose (disposing);
+	}
+}
+/// <summary>
+/// Implements the <see cref="IEqualityComparer{T}"/> for comparing two <see cref="Toplevel"/>s
+/// used by <see cref="StackExtensions"/>.
+/// </summary>
+public class ToplevelEqualityComparer : IEqualityComparer<Toplevel> {
+	/// <summary>Determines whether the specified objects are equal.</summary>
+	/// <param name="x">The first object of type <see cref="Toplevel" /> to compare.</param>
+	/// <param name="y">The second object of type <see cref="Toplevel" /> to compare.</param>
+	/// <returns>
+	///     <see langword="true" /> if the specified objects are equal; otherwise, <see langword="false" />.</returns>
+	public bool Equals (Toplevel x, Toplevel y)
+	{
+		if (y == null && x == null) {
+			return true;
+		} else if (x == null || y == null) {
+			return false;
+		} else if (x.Id == y.Id) {
+			return true;
+		} else {
+			return false;
 		}
 	}
 
-	/// <summary>
-	/// Implements the <see cref="IEqualityComparer{T}"/> for comparing two <see cref="Toplevel"/>s
-	/// used by <see cref="StackExtensions"/>.
-	/// </summary>
-	public class ToplevelEqualityComparer : IEqualityComparer<Toplevel> {
-		/// <summary>Determines whether the specified objects are equal.</summary>
-		/// <param name="x">The first object of type <see cref="Toplevel" /> to compare.</param>
-		/// <param name="y">The second object of type <see cref="Toplevel" /> to compare.</param>
-		/// <returns>
-		///     <see langword="true" /> if the specified objects are equal; otherwise, <see langword="false" />.</returns>
-		public bool Equals (Toplevel x, Toplevel y)
-		{
-			if (y == null && x == null)
-				return true;
-			else if (x == null || y == null)
-				return false;
-			else if (x.Id == y.Id)
-				return true;
-			else
-				return false;
-		}
-
-		/// <summary>Returns a hash code for the specified object.</summary>
-		/// <param name="obj">The <see cref="Toplevel" /> for which a hash code is to be returned.</param>
-		/// <returns>A hash code for the specified object.</returns>
-		/// <exception cref="ArgumentNullException">The type of <paramref name="obj" /> 
-		/// is a reference type and <paramref name="obj" /> is <see langword="null" />.</exception>
-		public int GetHashCode (Toplevel obj)
-		{
-			if (obj == null)
-				throw new ArgumentNullException ();
-
-			int hCode = 0;
-			if (int.TryParse (obj.Id, out int result)) {
-				hCode = result;
-			}
-			return hCode.GetHashCode ();
+	/// <summary>Returns a hash code for the specified object.</summary>
+	/// <param name="obj">The <see cref="Toplevel" /> for which a hash code is to be returned.</param>
+	/// <returns>A hash code for the specified object.</returns>
+	/// <exception cref="ArgumentNullException">The type of <paramref name="obj" /> 
+	/// is a reference type and <paramref name="obj" /> is <see langword="null" />.</exception>
+	public int GetHashCode (Toplevel obj)
+	{
+		if (obj == null) {
+			throw new ArgumentNullException ();
 		}
-	}
 
-	/// <summary>
-	/// Implements the <see cref="IComparer{T}"/> to sort the <see cref="Toplevel"/> 
-	/// from the <see cref="Application.OverlappedChildren"/> if needed.
-	/// </summary>
-	public sealed class ToplevelComparer : IComparer<Toplevel> {
-		/// <summary>Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.</summary>
-		/// <param name="x">The first object to compare.</param>
-		/// <param name="y">The second object to compare.</param>
-		/// <returns>A signed integer that indicates the relative values of <paramref name="x" /> and <paramref name="y" />, as shown in the following table.Value Meaning Less than zero
-		///             <paramref name="x" /> is less than <paramref name="y" />.Zero
-		///             <paramref name="x" /> equals <paramref name="y" />.Greater than zero
-		///             <paramref name="x" /> is greater than <paramref name="y" />.</returns>
-		public int Compare (Toplevel x, Toplevel y)
-		{
-			if (ReferenceEquals (x, y))
-				return 0;
-			else if (x == null)
-				return -1;
-			else if (y == null)
-				return 1;
-			else
-				return string.Compare (x.Id, y.Id);
+		int hCode = 0;
+		if (int.TryParse (obj.Id, out int result)) {
+			hCode = result;
 		}
+		return hCode.GetHashCode ();
 	}
 }
+/// <summary>
+/// Implements the <see cref="IComparer{T}"/> to sort the <see cref="Toplevel"/> 
+/// from the <see cref="Application.OverlappedChildren"/> if needed.
+/// </summary>
+public sealed class ToplevelComparer : IComparer<Toplevel> {
+	/// <summary>Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.</summary>
+	/// <param name="x">The first object to compare.</param>
+	/// <param name="y">The second object to compare.</param>
+	/// <returns>A signed integer that indicates the relative values of <paramref name="x" /> and <paramref name="y" />, as shown in the following table.Value Meaning Less than zero
+	///             <paramref name="x" /> is less than <paramref name="y" />.Zero
+	///             <paramref name="x" /> equals <paramref name="y" />.Greater than zero
+	///             <paramref name="x" /> is greater than <paramref name="y" />.</returns>
+	public int Compare (Toplevel x, Toplevel y)
+	{
+		if (ReferenceEquals (x, y)) {
+			return 0;
+		} else if (x == null) {
+			return -1;
+		} else if (y == null) {
+			return 1;
+		} else {
+			return string.Compare (x.Id, y.Id);
+		}
+	}
+}

+ 10 - 9
UICatalog/Properties/launchSettings.json

@@ -1,10 +1,15 @@
 {
   "profiles": {
     "UICatalog": {
+      "commandName": "Project"
+    },
+    "UICatalog --driver NetDriver": {
+      "commandName": "Project",
+      "commandLineArgs": "--driver NetDriver"
+    },
+    "UICatalog --driver WindowsDriver": {
       "commandName": "Project",
-      "environmentVariables": {
-        "WT_SESSION": "yes"
-      }
+      "commandLineArgs": "--driver WindowsDriver"
     },
     "WSL : UICatalog": {
       "commandName": "Executable",
@@ -12,14 +17,10 @@
       "commandLineArgs": "dotnet UICatalog.dll",
       "distributionName": ""
     },
-    "UICatalog -usc": {
-      "commandName": "Project",
-      "commandLineArgs": "-usc"
-    },
-    "WSL: UICatalog -usc": {
+    "WSL: UICatalog --driver NetDriver": {
       "commandName": "Executable",
       "executablePath": "wsl",
-      "commandLineArgs": "dotnet UICatalog.dll -usc",
+      "commandLineArgs": "dotnet UICatalog.dll --driver NetDriver",
       "distributionName": ""
     },
     "Sliders": {

+ 7 - 6
UICatalog/Scenarios/CharacterMap.cs

@@ -230,6 +230,7 @@ public class CharacterMap : Scenario {
 }
 
 class CharMap : ScrollView {
+	const CursorVisibility _cursor = CursorVisibility.Default;
 	/// <summary>
 	/// Specifies the starting offset for the character map. The default is 0x2500 
 	/// which is the Box Drawing characters.
@@ -296,11 +297,11 @@ class CharMap : ScrollView {
 	public override void PositionCursor ()
 	{
 		if (HasFocus &&
-		Cursor.X >= RowLabelWidth &&
-		Cursor.X < Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0) &&
-		Cursor.Y > 0 &&
-		Cursor.Y < Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0)) {
-			Driver.SetCursorVisibility (CursorVisibility.Default);
+			Cursor.X >= RowLabelWidth &&
+			Cursor.X < Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0) &&
+			Cursor.Y > 0 &&
+			Cursor.Y < Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0)) {
+			Driver.SetCursorVisibility (_cursor);
 			Move (Cursor.X, Cursor.Y);
 		} else {
 			Driver.SetCursorVisibility (CursorVisibility.Invisible);
@@ -807,7 +808,7 @@ class CharMap : ScrollView {
 	public override bool OnEnter (View view)
 	{
 		if (IsInitialized) {
-			Application.Driver.SetCursorVisibility (CursorVisibility.Default);
+			Application.Driver.SetCursorVisibility (_cursor);
 		}
 		return base.OnEnter (view);
 	}

+ 53 - 0
UICatalog/Scenarios/ChineseUI.cs

@@ -0,0 +1,53 @@
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios; 
+
+[ScenarioMetadata ("ChineseUI", "Chinese UI")]
+[ScenarioCategory ("Unicode")]
+public class ChineseUI : Scenario {
+	public override void Init ()
+	{
+		Application.Init ();
+		var top = Application.Top;
+
+		var win = new Window () {
+			Title = "Test",
+			X = 0,
+			Y = 0,
+			Width = Dim.Fill (),
+			Height = Dim.Fill ()
+		};
+		top.Add (win);
+
+		var buttonPanel = new FrameView () {
+			Title = "Command",
+			X = 0,
+			Y = 1,
+			Width = Dim.Fill (),
+			Height = 5
+		};
+		win.Add (buttonPanel);
+
+		var btn = new Button (1, 1, "你", true); // v1: A
+		btn.Clicked += (s, e) => {
+			int result = MessageBox.Query ("Confirm",
+				"Are you sure you want to quit ui?", 0,
+				"Yes", "No");
+			if (result == 0) {
+				RequestStop ();
+			}
+		};
+
+		buttonPanel.Add (
+			btn,
+			new Button (12, 1, "好"), // v1: B
+			new Button (22, 1, "呀") // v1: C
+		);
+
+		Application.Run ();
+	}
+
+	public override void Run ()
+	{
+	}
+}

+ 111 - 83
UICatalog/UICatalog.cs

@@ -1,24 +1,23 @@
-global using CM = Terminal.Gui.ConfigurationManager;
 global using Attribute = Terminal.Gui.Attribute;
+global using CM = Terminal.Gui.ConfigurationManager;
 using System;
 using System.Collections.Generic;
+using System.CommandLine;
 using System.Diagnostics;
 using System.Globalization;
+using System.IO;
 using System.Linq;
+using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
 using Terminal.Gui;
-using System.IO;
-using System.Reflection;
-using System.Threading;
 using static Terminal.Gui.ConfigurationManager;
-using System.Text.Json.Serialization;
-using static Terminal.Gui.TableView;
-
 
 #nullable enable
 
-namespace UICatalog; 
+namespace UICatalog;
 
 /// <summary>
 /// UI Catalog is a comprehensive sample library for Terminal.Gui. It provides a simple UI for adding to the catalog of scenarios.
@@ -51,13 +50,20 @@ namespace UICatalog;
 /// </para>	
 /// </remarks>
 class UICatalogApp {
-	[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 ();
-	static readonly FileSystemWatcher _homeDirWatcher = new ();
+	static readonly FileSystemWatcher _currentDirWatcher = new FileSystemWatcher ();
+	static readonly FileSystemWatcher _homeDirWatcher = new FileSystemWatcher ();
+
+	struct Options {
+		public string Driver;
+		public string Scenario;
+		/* etc. */
+	}
 
-	static void Main (string [] args)
+	static int Main (string [] args)
 	{
 		Console.OutputEncoding = Encoding.Default;
 
@@ -68,22 +74,68 @@ class UICatalogApp {
 		_scenarios = Scenario.GetScenarios ();
 		_categories = Scenario.GetAllCategories ();
 
-		if (args.Length > 0 && args.Contains ("-usc")) {
-			_useSystemConsole = true;
-			args = args.Where (val => val != "-usc").ToArray ();
+		// Process command line args
+		// "UICatalog [-driver <driver>] [scenario name]"
+		// If no driver is provided, the default driver is used.
+		var driverOption = new Option<string> (
+			"--driver",
+			"The ConsoleDriver to use."
+		).FromAmong (Application.GetDriverTypes ().Select (d => d.Name).ToArray ());
+		driverOption.AddAlias ("-d");
+		driverOption.AddAlias ("--d");
+
+		var scenarioArgument = new Argument<string> (
+			"scenario",
+			description: "The name of the scenario to run.",
+			getDefaultValue: () => "none"
+		).FromAmong (_scenarios.Select (s => s.GetName ()).Append ("none").ToArray ());
+
+		var rootCommand = new RootCommand (description: "A comprehensive sample library for Terminal.Gui") {
+			scenarioArgument,
+			driverOption
+		};
+
+		rootCommand.SetHandler ((context) => {
+			Options options = new Options () {
+				Driver = context.ParseResult.GetValueForOption (driverOption),
+				Scenario = context.ParseResult.GetValueForArgument (scenarioArgument),
+				/* etc. */
+			};
+			context.ExitCode = CommandWrapper (options);
+		});
+
+		return rootCommand.Invoke (args);
+	}
+
+	// See https://github.com/dotnet/command-line-api/issues/796 for the rationale behind this hackery
+	static int CommandWrapper (Options options)
+	{
+		try {
+			UICatalogMain (options);
+		} catch (Exception e) {
+			Console.WriteLine (e.ToString());
+			return 1;
 		}
+		return 0;
+	}
 
+	static void UICatalogMain (Options options)
+	{
 		StartConfigFileWatcher ();
 
+		// By setting _forceDriver we ensure that if the user has specified a driver on the command line, it will be used
+		// regardless of what's in a config file.
+		Application.ForceDriver = _forceDriver = options.Driver;
+
 		// If a Scenario name has been provided on the commandline
 		// run it and exit when done.
-		if (args.Length > 0) {
+		if (options.Scenario != "none") {
 			_topLevelColorScheme = "Base";
 
-			int item = _scenarios.FindIndex (s => s.GetName ().Equals (args [0], StringComparison.OrdinalIgnoreCase));
+			int item = _scenarios!.FindIndex (s => s.GetName ().Equals (options.Scenario, StringComparison.OrdinalIgnoreCase));
 			_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ())!;
-			Application.UseSystemConsole = _useSystemConsole;
-			Application.Init ();
+
+			Application.Init (driverName: _forceDriver);
 			_selectedScenario.Theme = _cachedTheme;
 			_selectedScenario.TopLevelColorScheme = _topLevelColorScheme;
 			_selectedScenario.Init ();
@@ -148,14 +200,14 @@ class UICatalogApp {
 		// Setup a file system watcher for `./.tui/`
 		_currentDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
 
-		var assemblyLocation = Assembly.GetExecutingAssembly ().Location;
+		string assemblyLocation = Assembly.GetExecutingAssembly ().Location;
 		string tuiDir;
 
 		if (!string.IsNullOrEmpty (assemblyLocation)) {
 			var assemblyFile = new FileInfo (assemblyLocation);
 			tuiDir = Path.Combine (assemblyFile.Directory!.FullName, ".tui");
 		} else {
-			tuiDir = Path.Combine (System.AppContext.BaseDirectory, ".tui");
+			tuiDir = Path.Combine (AppContext.BaseDirectory, ".tui");
 		}
 
 
@@ -206,11 +258,14 @@ class UICatalogApp {
 	/// <returns></returns>
 	static Scenario RunUICatalogTopLevel ()
 	{
-		Application.UseSystemConsole = _useSystemConsole;
 
 		// Run UI Catalog UI. When it exits, if _selectedScenario is != null then
 		// a Scenario was selected. Otherwise, the user wants to quit UI Catalog.
-		Application.Init ();
+
+		// If the user specified a driver on the command line then use it,
+		// ignoring Config files.
+
+		Application.Init (driverName: _forceDriver);
 
 		if (_cachedTheme is null) {
 			_cachedTheme = Themes?.Theme;
@@ -240,7 +295,7 @@ class UICatalogApp {
 	// If set, holds the scenario the user selected
 	static Scenario? _selectedScenario = null;
 
-	static bool _useSystemConsole = false;
+	static string _forceDriver = string.Empty;
 	static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
 	static bool _isFirstRunning = true;
 	static string _topLevelColorScheme = string.Empty;
@@ -264,7 +319,7 @@ class UICatalogApp {
 		// TableView works. There's no real reason not to use ListView. Because we use TableView, and TableView
 		// doesn't (currently) have CollectionNavigator support built in, we implement it here, within the app.
 		public TableView ScenarioList;
-		CollectionNavigator _scenarioCollectionNav = new ();
+		CollectionNavigator _scenarioCollectionNav = new CollectionNavigator ();
 
 		public StatusItem DriverName;
 		public StatusItem OS;
@@ -274,16 +329,15 @@ class UICatalogApp {
 			_themeMenuItems = CreateThemeMenuItems ();
 			_themeMenuBarItem = new MenuBarItem ("_Themes", _themeMenuItems);
 			MenuBar = new MenuBar (new MenuBarItem [] {
-				new ("_File", new MenuItem [] {
-					new ("_Quit", "Quit UI Catalog", RequestStop, null, null)
+				new MenuBarItem ("_File", new MenuItem [] {
+					new MenuItem ("_Quit", "Quit UI Catalog", RequestStop, null, null)
 				}),
 				_themeMenuBarItem,
-				new ("Diag_nostics", CreateDiagnosticMenuItems ()),
-				new ("_Help", new MenuItem [] {
-					new ("_Documentation", "", () => OpenUrl ("https://gui-cs.github.io/Terminal.GuiV2Docs"), null, null, (KeyCode)Key.F1),
-					new ("_README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, (KeyCode)Key.F2),
-					new ("_About...",
-						"About UI Catalog", () => MessageBox.Query ("About UI Catalog", _aboutMessage!.ToString (), 0, false, "_Ok"), null, null, (KeyCode)Key.A.WithCtrl)
+				new MenuBarItem ("Diag_nostics", CreateDiagnosticMenuItems ()),
+				new MenuBarItem ("_Help", new MenuItem [] {
+					new MenuItem ("_Documentation", "", () => OpenUrl ("https://gui-cs.github.io/Terminal.GuiV2Docs"), null, null, (KeyCode)Key.F1),
+					new MenuItem ("_README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, (KeyCode)Key.F2),
+					new MenuItem ("_About...", "About UI Catalog", () => MessageBox.Query ("About UI Catalog", _aboutMessage!.ToString (), 0, false, "_Ok"), null, null, (KeyCode)Key.A.WithCtrl)
 				})
 			});
 
@@ -295,7 +349,7 @@ class UICatalogApp {
 			};
 
 			StatusBar.Items = new StatusItem [] {
-				new (Application.QuitKey, $"~{Application.QuitKey} to quit", () => {
+				new StatusItem (Application.QuitKey, $"~{Application.QuitKey} to quit", () => {
 					if (_selectedScenario is null) {
 						// This causes GetScenarioToRun to return null
 						_selectedScenario = null;
@@ -304,7 +358,7 @@ class UICatalogApp {
 						_selectedScenario.RequestStop ();
 					}
 				}),
-				new (Key.F10, "~F10~ Status Bar", () => {
+				new StatusItem (Key.F10, "~F10~ Status Bar", () => {
 					StatusBar.Visible = !StatusBar.Visible;
 					//ContentPane!.Height = Dim.Fill(StatusBar.Visible ? 1 : 0);
 					LayoutSubviews ();
@@ -390,8 +444,8 @@ class UICatalogApp {
 			ScenarioList.CellActivated += ScenarioView_OpenSelectedItem;
 
 			// TableView typically is a grid where nav keys are biased for moving left/right.
-			ScenarioList.KeyBindings.Add (Key.Home, Command.TopHome);
-			ScenarioList.KeyBindings.Add (Key.End, Command.BottomEnd);
+			ScenarioList.KeyBindings.Add (Key.Home, Terminal.Gui.Command.TopHome);
+			ScenarioList.KeyBindings.Add (Key.End, Terminal.Gui.Command.BottomEnd);
 
 			// Ideally, TableView.MultiSelect = false would turn off any keybindings for
 			// multi-select options. But it currently does not. UI Catalog uses Ctrl-A for
@@ -453,10 +507,7 @@ class UICatalogApp {
 			Unloaded -= UnloadedHandler;
 		}
 
-		void ConfigAppliedHandler (object? sender, ConfigurationManagerEventArgs? a)
-		{
-			ConfigChanged ();
-		}
+		void ConfigAppliedHandler (object? sender, ConfigurationManagerEventArgs? a) => ConfigChanged ();
 
 		/// <summary>
 		/// Launches the selected scenario, setting the global _selectedScenario
@@ -516,7 +567,7 @@ class UICatalogApp {
 			miIsMenuBorderDisabled = new MenuItem {
 				Title = "Disable Menu _Border"
 			};
-			miIsMenuBorderDisabled.Shortcut = (KeyCode)(new Key (miIsMenuBorderDisabled!.Title!.Substring (14, 1) [0])).WithAlt.WithCtrl;
+			miIsMenuBorderDisabled.Shortcut = (KeyCode)new Key (miIsMenuBorderDisabled!.Title!.Substring (14, 1) [0]).WithAlt.WithCtrl;
 			miIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked;
 			miIsMenuBorderDisabled.Action += () => {
 				miIsMenuBorderDisabled.Checked = (bool)!miIsMenuBorderDisabled.Checked!;
@@ -553,7 +604,7 @@ class UICatalogApp {
 			miIsMouseDisabled = new MenuItem {
 				Title = "_Disable Mouse"
 			};
-			miIsMouseDisabled.Shortcut = (KeyCode)(new Key (miIsMouseDisabled!.Title!.Substring (1, 1) [0])).WithAlt.WithCtrl;
+			miIsMouseDisabled.Shortcut = (KeyCode)new Key (miIsMouseDisabled!.Title!.Substring (1, 1) [0]).WithAlt.WithCtrl;
 			miIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked;
 			miIsMouseDisabled.Action += () => {
 				miIsMouseDisabled.Checked = Application.IsMouseDisabled = (bool)!miIsMouseDisabled.Checked!;
@@ -592,7 +643,7 @@ class UICatalogApp {
 			foreach (Enum diag in Enum.GetValues (_diagnosticFlags.GetType ())) {
 				var item = new MenuItem {
 					Title = GetDiagnosticsTitle (diag),
-					Shortcut = (KeyCode)(new Key(index.ToString () [0])).WithAlt
+					Shortcut = (KeyCode)new Key (index.ToString () [0]).WithAlt
 				};
 				index++;
 				item.CheckType |= MenuItemCheckStyle.Checked;
@@ -633,24 +684,18 @@ class UICatalogApp {
 			}
 			return menuItems.ToArray ();
 
-			string GetDiagnosticsTitle (Enum diag)
-			{
-				return Enum.GetName (_diagnosticFlags.GetType (), diag) switch {
-					"Off" => OFF,
-					"FrameRuler" => FRAME_RULER,
-					"FramePadding" => FRAME_PADDING,
-					_ => ""
-				};
-			}
+			string GetDiagnosticsTitle (Enum diag) => Enum.GetName (_diagnosticFlags.GetType (), diag) switch {
+				"Off" => OFF,
+				"FrameRuler" => FRAME_RULER,
+				"FramePadding" => FRAME_PADDING,
+				_ => ""
+			};
 
-			Enum GetDiagnosticsEnumValue (string title)
-			{
-				return title switch {
-					FRAME_RULER => ConsoleDriver.DiagnosticFlags.FrameRuler,
-					FRAME_PADDING => ConsoleDriver.DiagnosticFlags.FramePadding,
-					_ => null!
-				};
-			}
+			Enum GetDiagnosticsEnumValue (string title) => title switch {
+				FRAME_RULER => ConsoleDriver.DiagnosticFlags.FrameRuler,
+				FRAME_PADDING => ConsoleDriver.DiagnosticFlags.FramePadding,
+				_ => null!
+			};
 
 			void SetDiagnosticsFlag (Enum diag, bool add)
 			{
@@ -685,7 +730,7 @@ class UICatalogApp {
 			foreach (var theme in Themes!) {
 				var item = new MenuItem {
 					Title = $"_{theme.Key}",
-					Shortcut = (KeyCode)(new Key ((KeyCode)((uint)KeyCode.D1 + (schemeCount++))).WithCtrl)
+					Shortcut = (KeyCode)new Key ((KeyCode)((uint)KeyCode.D1 + schemeCount++)).WithCtrl
 				};
 				item.CheckType |= MenuItemCheckStyle.Checked;
 				item.Checked = theme.Key == _cachedTheme; // CM.Themes.Theme;
@@ -723,13 +768,13 @@ class UICatalogApp {
 
 		public void ConfigChanged ()
 		{
-			miForce16Colors!.Checked = Application.Force16Colors;
-
 			if (_topLevelColorScheme == null || !Colors.ColorSchemes.ContainsKey (_topLevelColorScheme)) {
 				_topLevelColorScheme = "Base";
 			}
 
-			_themeMenuItems = ((UICatalogTopLevel)Application.Top).CreateThemeMenuItems ();
+			_cachedTheme = Themes?.Theme;
+
+			_themeMenuItems = CreateThemeMenuItems ();
 			_themeMenuBarItem!.Children = _themeMenuItems;
 			foreach (var mi in _themeMenuItems!) {
 				if (mi is { Parent: null }) {
@@ -737,25 +782,8 @@ class UICatalogApp {
 				}
 			}
 
-			var checkedThemeMenu = _themeMenuItems?.Where (m => m?.Checked ?? false).FirstOrDefault ();
-			if (checkedThemeMenu != null) {
-				checkedThemeMenu.Checked = false;
-			}
-			checkedThemeMenu = _themeMenuItems?.Where (m => m != null && m.Title == Themes?.Theme).FirstOrDefault ();
-			if (checkedThemeMenu != null) {
-				Themes!.Theme = checkedThemeMenu.Title!;
-				checkedThemeMenu.Checked = true;
-			}
-
-			var schemeMenuItems = ((MenuBarItem)_themeMenuItems?.Where (i => i is MenuBarItem)!.FirstOrDefault ()!)!.Children;
-			foreach (var schemeMenuItem in schemeMenuItems) {
-				schemeMenuItem.Checked = (string)schemeMenuItem.Data == _topLevelColorScheme;
-			}
-
 			ColorScheme = Colors.ColorSchemes [_topLevelColorScheme];
 
-			//ContentPane.LineStyle = FrameView.DefaultBorderStyle;
-
 			MenuBar.Menus [0].Children [0].Shortcut = (KeyCode)Application.QuitKey;
 			StatusBar.Items [0].Shortcut = Application.QuitKey;
 			StatusBar.Items [0].Title = $"~{Application.QuitKey} to quit";
@@ -763,7 +791,7 @@ class UICatalogApp {
 			miIsMouseDisabled!.Checked = Application.IsMouseDisabled;
 
 			int height = ShowStatusBar ? 1 : 0; // + (MenuBar.Visible ? 1 : 0);
-			//ContentPane.Height = Dim.Fill (height);
+							    //ContentPane.Height = Dim.Fill (height);
 
 			StatusBar.Visible = ShowStatusBar;
 

+ 1 - 0
UICatalog/UICatalog.csproj

@@ -32,6 +32,7 @@
     <PackageReference Include="SixLabors.ImageSharp" Version="3.1.1" />
     <PackageReference Include="CsvHelper" Version="30.0.1" />
     <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
+    <PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />

+ 37 - 27
UnitTests/Application/ApplicationTests.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Diagnostics;
+using System.IO;
 using System.Linq;
 using System.Runtime.InteropServices;
 using System.Threading;
@@ -17,7 +18,9 @@ public class ApplicationTests {
 
 	public ApplicationTests (ITestOutputHelper output)
 	{
-		this._output = output;
+		_output = output;
+		ConsoleDriver.RunningUnitTests = true;
+
 #if DEBUG_IDISPOSABLE
 		Responder.Instances.Clear ();
 		RunState.Instances.Clear ();
@@ -52,10 +55,7 @@ public class ApplicationTests {
 		Assert.NotNull (SynchronizationContext.Current);
 	}
 
-	void Shutdown ()
-	{
-		Application.Shutdown ();
-	}
+	void Shutdown () => Application.Shutdown ();
 
 	[Fact]
 	public void Init_Shutdown_Cleans_Up ()
@@ -73,10 +73,10 @@ public class ApplicationTests {
 		// Verify state is back to initial
 		//Pre_Init_State ();
 #if DEBUG_IDISPOSABLE
-			// Validate there are no outstanding Responder-based instances 
-			// after a scenario was selected to run. This proves the main UI Catalog
-			// 'app' closed cleanly.
-			Assert.Empty (Responder.Instances);
+		// Validate there are no outstanding Responder-based instances 
+		// after a scenario was selected to run. This proves the main UI Catalog
+		// 'app' closed cleanly.
+		Assert.Empty (Responder.Instances);
 #endif
 	}
 
@@ -106,10 +106,7 @@ public class ApplicationTests {
 	}
 
 	class TestToplevel : Toplevel {
-		public TestToplevel ()
-		{
-			IsOverlappedContainer = false;
-		}
+		public TestToplevel () => IsOverlappedContainer = false;
 	}
 
 	[Fact]
@@ -122,6 +119,21 @@ public class ApplicationTests {
 		Shutdown ();
 	}
 
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void Init_DriverName_Should_Pick_Correct_Driver (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		Application.Init (driverName: driverType.Name);
+		Assert.NotNull (Application.Driver);
+		Assert.Equal (driverType, Application.Driver.GetType ());
+		Shutdown ();
+	}
+
 	[Fact]
 	public void Init_Begin_End_Cleans_Up ()
 	{
@@ -140,7 +152,7 @@ public class ApplicationTests {
 		};
 		Application.NotifyNewRunState += NewRunStateFn;
 
-		Toplevel topLevel = new Toplevel ();
+		var topLevel = new Toplevel ();
 		var rs = Application.Begin (topLevel);
 		Assert.NotNull (rs);
 		Assert.NotNull (runstate);
@@ -225,7 +237,6 @@ public class ApplicationTests {
 	}
 
 	#region RunTests
-
 	[Fact]
 	public void Run_T_After_InitWithDriver_with_TopLevel_Throws ()
 	{
@@ -249,7 +260,7 @@ public class ApplicationTests {
 		Init ();
 
 		// Run<Toplevel> when already initialized with a Driver will throw (because Toplevel is not dervied from TopLevel)
-		Assert.Throws<ArgumentException> (() => Application.Run<Toplevel> (errorHandler: null, new FakeDriver ()));
+		Assert.Throws<ArgumentException> (() => Application.Run<Toplevel> (null, new FakeDriver ()));
 
 		Shutdown ();
 
@@ -281,7 +292,7 @@ public class ApplicationTests {
 	[Fact]
 	public void Run_T_After_InitNullDriver_with_TestTopLevel_Throws ()
 	{
-		Application._forceFakeConsole = true;
+		Application.ForceDriver = "FakeDriver";
 
 		Application.Init (null);
 		Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
@@ -324,7 +335,7 @@ public class ApplicationTests {
 	[Fact]
 	public void Run_T_NoInit_DoesNotThrow ()
 	{
-		Application._forceFakeConsole = true;
+		Application.ForceDriver = "FakeDriver";
 
 		Application.Iteration += (s, a) => {
 			Application.RequestStop ();
@@ -348,7 +359,7 @@ public class ApplicationTests {
 		};
 
 		// Init has NOT been called and we're passing a valid driver to Run<TestTopLevel>. This is ok.
-		Application.Run<TestToplevel> (errorHandler: null, new FakeDriver ());
+		Application.Run<TestToplevel> (null, new FakeDriver ());
 
 		Shutdown ();
 
@@ -410,7 +421,7 @@ public class ApplicationTests {
 	{
 		Init ();
 		var top = Application.Top;
-		var count = 0;
+		int count = 0;
 		top.Loaded += (s, e) => count++;
 		top.Ready += (s, e) => count++;
 		top.Unloaded += (s, e) => count++;
@@ -425,12 +436,12 @@ public class ApplicationTests {
 	public void Run_Toplevel_With_Modal_View_Does_Not_Refresh_If_Not_Dirty ()
 	{
 		Init ();
-		var count = 0;
+		int count = 0;
 		// Don't use Dialog here as it has more layout logic. Use Window instead.
 		Dialog d = null;
 		var top = Application.Top;
 		top.DrawContent += (s, a) => count++;
-		var iteration = -1;
+		int iteration = -1;
 		Application.Iteration += (s, a) => {
 			iteration++;
 			if (iteration == 0) {
@@ -439,7 +450,7 @@ public class ApplicationTests {
 				d.DrawContent += (s, a) => count++;
 				Application.Run (d);
 			} else if (iteration < 3) {
-				Application.OnMouseEvent (new (new () { X = 0, Y = 0, Flags = MouseFlags.ReportMousePosition }));
+				Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.ReportMousePosition }));
 				Assert.False (top.NeedsDisplay);
 				Assert.False (top.SubViewNeedsDisplay);
 				Assert.False (top.LayoutNeeded);
@@ -521,7 +532,6 @@ public class ApplicationTests {
 	}
 
 	// TODO: Add tests for Run that test errorHandler
-
 	#endregion
 
 	#region ShutdownTests
@@ -556,7 +566,7 @@ public class ApplicationTests {
 	}
 	#endregion
 
-	[Fact, AutoInitShutdown]
+	[Fact] [AutoInitShutdown]
 	public void Begin_Sets_Application_Top_To_Console_Size ()
 	{
 		Assert.Equal (new Rect (0, 0, 80, 25), Application.Top.Frame);
@@ -580,7 +590,7 @@ public class ApplicationTests {
 		var t4 = new Toplevel ();
 
 		// t1, t2, t3, d, t4
-		var iterations = 5;
+		int iterations = 5;
 
 		t1.Ready += (s, e) => {
 			Assert.Equal (t1, Application.Top);
@@ -667,7 +677,7 @@ public class ApplicationTests {
 		var rs = Application.Begin (top);
 		bool firstIteration = false;
 
-		var actionCalled = 0;
+		int actionCalled = 0;
 		Application.Invoke (() => { actionCalled++; });
 		Application.MainLoop.Running = true;
 		Application.RunIteration (ref rs, ref firstIteration);

+ 12 - 10
UnitTests/ConsoleDrivers/AddRuneTests.cs

@@ -7,20 +7,22 @@ using Xunit.Abstractions;
 // Alias Console to MockConsole so we don't accidentally use Console
 
 namespace Terminal.Gui.DriverTests;
+
 public class AddRuneTests {
 	readonly ITestOutputHelper _output;
 
 	public AddRuneTests (ITestOutputHelper output)
 	{
 		ConsoleDriver.RunningUnitTests = true;
-		this._output = output;
+		_output = output;
 	}
 
 	[Theory]
 	[InlineData (typeof (FakeDriver))]
 	[InlineData (typeof (NetDriver))]
-	[InlineData (typeof (CursesDriver))]
+	//[InlineData (typeof (ANSIDriver))]
 	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
 	public void AddRune (Type driverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
@@ -44,8 +46,8 @@ public class AddRuneTests {
 		driver.Move (driver.Cols, driver.Rows);
 		driver.AddRune ('a');
 
-		for (var col = 0; col < driver.Cols; col++) {
-			for (var row = 0; row < driver.Rows; row++) {
+		for (int col = 0; col < driver.Cols; col++) {
+			for (int row = 0; row < driver.Rows; row++) {
 				Assert.Equal ((Rune)' ', driver.Contents [row, col].Rune);
 			}
 		}
@@ -70,7 +72,7 @@ public class AddRuneTests {
 		Assert.Equal (2, driver.Col);
 
 		// Move to the last column of the first row
-		var lastCol = driver.Cols - 1;
+		int lastCol = driver.Cols - 1;
 		driver.Move (lastCol, 0);
 		Assert.Equal (0, driver.Row);
 		Assert.Equal (lastCol, driver.Col);
@@ -83,8 +85,8 @@ public class AddRuneTests {
 		// Add a rune; should succeed but do nothing as it's outside of Contents
 		driver.AddRune ('d');
 		Assert.Equal (lastCol + 2, driver.Col);
-		for (var col = 0; col < driver.Cols; col++) {
-			for (var row = 0; row < driver.Rows; row++) {
+		for (int col = 0; col < driver.Cols; col++) {
+			for (int row = 0; row < driver.Rows; row++) {
 				Assert.NotEqual ((Rune)'d', driver.Contents [row, col].Rune);
 			}
 		}
@@ -99,7 +101,7 @@ public class AddRuneTests {
 		driver.Init ();
 
 		// 🍕 Slice of Pizza "\U0001F355"
-		var operationStatus = Rune.DecodeFromUtf16 ("\U0001F355", out Rune rune, out int charsConsumed);
+		var operationStatus = Rune.DecodeFromUtf16 ("\U0001F355", out var rune, out int charsConsumed);
 		Assert.Equal (OperationStatus.Done, operationStatus);
 		Assert.Equal (charsConsumed, rune.Utf16SequenceLength);
 		Assert.Equal (2, rune.GetColumns ());
@@ -145,7 +147,7 @@ public class AddRuneTests {
 
 		var expected = new Rune ('ắ');
 
-		var text = "\u1eaf";
+		string text = "\u1eaf";
 		driver.AddStr (text);
 		Assert.Equal (expected, driver.Contents [0, 0].Rune);
 		Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune);
@@ -188,4 +190,4 @@ public class AddRuneTests {
 		//ắ", output);
 		driver.End ();
 	}
-}
+}

+ 121 - 104
UnitTests/ConsoleDrivers/ClipRegionTests.cs

@@ -9,108 +9,125 @@ using Xunit.Abstractions;
 // Alias Console to MockConsole so we don't accidentally use Console
 using Console = Terminal.Gui.FakeConsole;
 
-namespace Terminal.Gui.DriverTests {
-	public class ClipRegionTests {
-		readonly ITestOutputHelper output;
-
-		public ClipRegionTests (ITestOutputHelper output)
-		{
-			this.output = output;
-		}
-		
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void IsValidLocation (Type driverType)
-		{
-			var driver = (FakeDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			// positive
-			Assert.True (driver.IsValidLocation (0, 0));
-			Assert.True (driver.IsValidLocation (1, 1));
-			Assert.True (driver.IsValidLocation (driver.Cols - 1, driver.Rows - 1));
-			// negative
-			Assert.False (driver.IsValidLocation (-1, 0));
-			Assert.False (driver.IsValidLocation (0, -1));
-			Assert.False (driver.IsValidLocation (-1, -1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows));
-
-			// Define a clip rectangle
-			driver.Clip = new Rect (5, 5, 5, 5);
-			// positive
-			Assert.True (driver.IsValidLocation (5, 5));
-			Assert.True (driver.IsValidLocation (9, 9));
-			// negative
-			Assert.False (driver.IsValidLocation (4, 5));
-			Assert.False (driver.IsValidLocation (5, 4));
-			Assert.False (driver.IsValidLocation (10, 9));
-			Assert.False (driver.IsValidLocation (9, 10));
-			Assert.False (driver.IsValidLocation (-1, 0));
-			Assert.False (driver.IsValidLocation (0, -1));
-			Assert.False (driver.IsValidLocation (-1, -1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows));
-
-			Application.Shutdown ();
-		}
-
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void Clip_Set_To_Empty_AllInvalid (Type driverType)
-		{
-			var driver = (FakeDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			// Define a clip rectangle
-			driver.Clip = Rect.Empty;
-
-			// negative
-			Assert.False (driver.IsValidLocation (4, 5));
-			Assert.False (driver.IsValidLocation (5, 4));
-			Assert.False (driver.IsValidLocation (10, 9));
-			Assert.False (driver.IsValidLocation (9, 10));
-			Assert.False (driver.IsValidLocation (-1, 0));
-			Assert.False (driver.IsValidLocation (0, -1));
-			Assert.False (driver.IsValidLocation (-1, -1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
-			Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows));
-
-			Application.Shutdown ();
-		}
-
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void AddRune_Is_Clipped (Type driverType)
-		{
-			var driver = (FakeDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			driver.Move (0, 0);
-			driver.AddRune ('x');
-			Assert.Equal ((Rune)'x', driver.Contents [0, 0].Rune);
-
-			driver.Move (5, 5);
-			driver.AddRune ('x');
-			Assert.Equal ((Rune)'x', driver.Contents [5, 5].Rune);
-
-			// Clear the contents
-			driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), ' ');
-			Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune);
-
-			// Setup the region with a single rectangle, fill screen with 'x'
-			driver.Clip = new Rect (5, 5, 5, 5);
-			driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), 'x');
-			Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune);
-			Assert.Equal ((Rune)' ', driver.Contents [4, 9].Rune);
-			Assert.Equal ((Rune)'x', driver.Contents [5, 5].Rune);
-			Assert.Equal ((Rune)'x', driver.Contents [9, 9].Rune);
-			Assert.Equal ((Rune)' ', driver.Contents [10, 10].Rune);
-
-			Application.Shutdown ();
-		}
+namespace Terminal.Gui.DriverTests; 
+
+public class ClipRegionTests {
+	readonly ITestOutputHelper output;
+
+	public ClipRegionTests (ITestOutputHelper output)
+	{
+		ConsoleDriver.RunningUnitTests = true;
+		this.output = output;
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void IsValidLocation (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		Application.Init (driver);
+		Application.Driver.Rows = 10;
+		Application.Driver.Cols = 10;
+
+		// positive
+		Assert.True (driver.IsValidLocation (0, 0));
+		Assert.True (driver.IsValidLocation (1, 1));
+		Assert.True (driver.IsValidLocation (driver.Cols - 1, driver.Rows - 1));
+		// negative
+		Assert.False (driver.IsValidLocation (-1, 0));
+		Assert.False (driver.IsValidLocation (0, -1));
+		Assert.False (driver.IsValidLocation (-1, -1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows));
+
+		// Define a clip rectangle
+		driver.Clip = new Rect (5, 5, 5, 5);
+		// positive
+		Assert.True (driver.IsValidLocation (5, 5));
+		Assert.True (driver.IsValidLocation (9, 9));
+		// negative
+		Assert.False (driver.IsValidLocation (4, 5));
+		Assert.False (driver.IsValidLocation (5, 4));
+		Assert.False (driver.IsValidLocation (10, 9));
+		Assert.False (driver.IsValidLocation (9, 10));
+		Assert.False (driver.IsValidLocation (-1, 0));
+		Assert.False (driver.IsValidLocation (0, -1));
+		Assert.False (driver.IsValidLocation (-1, -1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows));
+
+		Application.Shutdown ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void Clip_Set_To_Empty_AllInvalid (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		Application.Init (driver);
+
+		// Define a clip rectangle
+		driver.Clip = Rect.Empty;
+
+		// negative
+		Assert.False (driver.IsValidLocation (4, 5));
+		Assert.False (driver.IsValidLocation (5, 4));
+		Assert.False (driver.IsValidLocation (10, 9));
+		Assert.False (driver.IsValidLocation (9, 10));
+		Assert.False (driver.IsValidLocation (-1, 0));
+		Assert.False (driver.IsValidLocation (0, -1));
+		Assert.False (driver.IsValidLocation (-1, -1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1));
+		Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows));
+
+		Application.Shutdown ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void AddRune_Is_Clipped (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		Application.Init (driver);
+		Application.Driver.Rows = 25;
+		Application.Driver.Cols = 80;
+
+		driver.Move (0, 0);
+		driver.AddRune ('x');
+		Assert.Equal ((Rune)'x', driver.Contents [0, 0].Rune);
+
+		driver.Move (5, 5);
+		driver.AddRune ('x');
+		Assert.Equal ((Rune)'x', driver.Contents [5, 5].Rune);
+
+		// Clear the contents
+		driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), ' ');
+		Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune);
+
+		// Setup the region with a single rectangle, fill screen with 'x'
+		driver.Clip = new Rect (5, 5, 5, 5);
+		driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), 'x');
+		Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune);
+		Assert.Equal ((Rune)' ', driver.Contents [4, 9].Rune);
+		Assert.Equal ((Rune)'x', driver.Contents [5, 5].Rune);
+		Assert.Equal ((Rune)'x', driver.Contents [9, 9].Rune);
+		Assert.Equal ((Rune)' ', driver.Contents [10, 10].Rune);
+
+		Application.Shutdown ();
 	}
-}
+}

+ 254 - 247
UnitTests/ConsoleDrivers/ConsoleDriverTests.cs

@@ -8,264 +8,271 @@ using Xunit.Abstractions;
 // Alias Console to MockConsole so we don't accidentally use Console
 using Console = Terminal.Gui.FakeConsole;
 
-namespace Terminal.Gui.DriverTests {
-	public class ConsoleDriverTests {
-		readonly ITestOutputHelper output;
-
-		public ConsoleDriverTests (ITestOutputHelper output)
-		{
-			ConsoleDriver.RunningUnitTests = true;
-			this.output = output;
-		}
+namespace Terminal.Gui.DriverTests; 
 
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		[InlineData (typeof (NetDriver))]
-		[InlineData (typeof (CursesDriver))]
-		[InlineData (typeof (WindowsDriver))]
-		public void Init_Inits (Type driverType)
-		{
-			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			var ml = driver.Init ();
-			Assert.NotNull (ml);
-			Assert.NotNull (driver.Clipboard);
-			Console.ForegroundColor = ConsoleColor.Red;
-			Assert.Equal (ConsoleColor.Red, Console.ForegroundColor);
-			Console.BackgroundColor = ConsoleColor.Green;
-			Assert.Equal (ConsoleColor.Green, Console.BackgroundColor);
-
-			driver.End ();
-		}
+public class ConsoleDriverTests {
+	readonly ITestOutputHelper output;
 
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		[InlineData (typeof (NetDriver))]
-		[InlineData (typeof (CursesDriver))]
-		[InlineData (typeof (WindowsDriver))]
-		public void End_Cleans_Up (Type driverType)
-		{
-			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			driver.Init ();
-			driver.End ();
-		}
+	public ConsoleDriverTests (ITestOutputHelper output)
+	{
+		ConsoleDriver.RunningUnitTests = true;
+		this.output = output;
+	}
 
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void FakeDriver_Only_Sends_Keystrokes_Through_MockKeyPresses (Type driverType)
-		{
-			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void Init_Inits (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var ml = driver.Init ();
+		Assert.NotNull (ml);
+		Assert.NotNull (driver.Clipboard);
+		Console.ForegroundColor = ConsoleColor.Red;
+		Assert.Equal (ConsoleColor.Red, Console.ForegroundColor);
+		Console.BackgroundColor = ConsoleColor.Green;
+		Assert.Equal (ConsoleColor.Green, Console.BackgroundColor);
+
+		driver.End ();
+	}
 
-			var top = Application.Top;
-			var view = new View () {
-				CanFocus = true
-			};
-			var count = 0;
-			var wasKeyPressed = false;
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void End_Cleans_Up (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		driver.Init ();
+		driver.End ();
+	}
 
-			view.KeyDown += (s, e) => {
-				wasKeyPressed = true;
-			};
-			top.Add (view);
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	public void FakeDriver_Only_Sends_Keystrokes_Through_MockKeyPresses (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		Application.Init (driver);
+
+		var top = Application.Top;
+		var view = new View () {
+			CanFocus = true
+		};
+		int count = 0;
+		bool wasKeyPressed = false;
+
+		view.KeyDown += (s, e) => {
+			wasKeyPressed = true;
+		};
+		top.Add (view);
+
+		Application.Iteration += (s, a) => {
+			count++;
+			if (count == 10) {
+				Application.RequestStop ();
+			}
+		};
 
-			Application.Iteration += (s, a) => {
-				count++;
-				if (count == 10) Application.RequestStop ();
-			};
+		Application.Run ();
 
-			Application.Run ();
+		Assert.False (wasKeyPressed);
 
-			Assert.False (wasKeyPressed);
+		// Shutdown must be called to safely clean up Application if Init has been called
+		Application.Shutdown ();
+	}
 
-			// Shutdown must be called to safely clean up Application if Init has been called
-			Application.Shutdown ();
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	public void FakeDriver_MockKeyPresses (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		Application.Init (driver);
+
+		string text = "MockKeyPresses";
+		var mKeys = new Stack<ConsoleKeyInfo> ();
+		foreach (char r in text.Reverse ()) {
+			var ck = char.IsLetter (r) ? (ConsoleKey)char.ToUpper (r) : (ConsoleKey)r;
+			var cki = new ConsoleKeyInfo (r, ck, false, false, false);
+			mKeys.Push (cki);
 		}
-
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void FakeDriver_MockKeyPresses (Type driverType)
-		{
-			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			var text = "MockKeyPresses";
-			var mKeys = new Stack<ConsoleKeyInfo> ();
-			foreach (var r in text.Reverse ()) {
-				var ck = char.IsLetter (r) ? (ConsoleKey)char.ToUpper (r) : (ConsoleKey)r;
-				var cki = new ConsoleKeyInfo (r, ck, false, false, false);
-				mKeys.Push (cki);
+		Console.MockKeyPresses = mKeys;
+
+		var top = Application.Top;
+		var view = new View () {
+			CanFocus = true
+		};
+		string rText = "";
+		int idx = 0;
+
+		view.KeyDown += (s, e) => {
+			Assert.Equal (text [idx], (char)e.KeyCode);
+			rText += (char)e.KeyCode;
+			Assert.Equal (rText, text.Substring (0, idx + 1));
+			e.Handled = true;
+			idx++;
+		};
+		top.Add (view);
+
+		Application.Iteration += (s, a) => {
+			if (mKeys.Count == 0) {
+				Application.RequestStop ();
 			}
-			Console.MockKeyPresses = mKeys;
-
-			var top = Application.Top;
-			var view = new View () {
-				CanFocus = true
-			};
-			var rText = "";
-			var idx = 0;
-
-			view.KeyDown += (s, e) => {
-				Assert.Equal (text [idx], (char)e.KeyCode);
-				rText += (char)e.KeyCode;
-				Assert.Equal (rText, text.Substring (0, idx + 1));
-				e.Handled = true;
-				idx++;
-			};
-			top.Add (view);
-
-			Application.Iteration += (s, a) => {
-				if (mKeys.Count == 0) Application.RequestStop ();
-			};
-
-			Application.Run ();
-
-			Assert.Equal ("MockKeyPresses", rText);
-
-			// Shutdown must be called to safely clean up Application if Init has been called
-			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.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.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
-
-		//	Key key = Key.Unknown;
-			
-		//	Application.Top.KeyPress += (e) => {
-		//		key = e.Key;
-		//		output.WriteLine ($"  Application.Top.KeyPress: {key}");
-		//		e.Handled = true;
-				
-		//	};
-
-		//	int iterations = 0;
-		//	Application.Iteration += (s, a) => {
-		//		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))]
-		[InlineData (typeof (NetDriver))]
-		[InlineData (typeof (CursesDriver))]
-		[InlineData (typeof (WindowsDriver))]
-		public void TerminalResized_Simulation (Type driverType)
-		{
-			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			driver?.Init ();
-			driver.Cols = 80;
-			driver.Rows = 25;
-			
-			var wasTerminalResized = false;
-			driver.SizeChanged += (s, e) => {
-				wasTerminalResized = true;
-				Assert.Equal (120, e.Size.Width);
-				Assert.Equal (40, e.Size.Height);
-			};
-
-			Assert.Equal (80, driver.Cols);
-			Assert.Equal (25, driver.Rows);
-			Assert.False (wasTerminalResized);
-
-			driver.Cols = 120;
-			driver.Rows = 40;
-			driver.OnSizeChanged (new SizeChangedEventArgs(new Size(driver.Cols, driver.Rows)));
-			Assert.Equal (120, driver.Cols);
-			Assert.Equal (40, driver.Rows);
-			Assert.True (wasTerminalResized);
-			driver.End ();
-		}
+		Application.Run ();
+
+		Assert.Equal ("MockKeyPresses", rText);
 
-		// Disabled due to test error - Change Task.Delay to an await
-		//		[Fact, AutoInitShutdown]
-		//		public void Write_Do_Not_Change_On_ProcessKey ()
-		//		{
-		//			var win = new Window ();
-		//			Application.Begin (win);
-		//			((FakeDriver)Application.Driver).SetBufferSize (20, 8);
-
-		//			System.Threading.Tasks.Task.Run (() => {
-		//				System.Threading.Tasks.Task.Delay (500).Wait ();
-		//				Application.Invoke (() => {
-		//					var lbl = new Label ("Hello World") { X = Pos.Center () };
-		//					var dlg = new Dialog ();
-		//					dlg.Add (lbl);
-		//					Application.Begin (dlg);
-
-		//					var expected = @"
-		//┌──────────────────┐
-		//│┌───────────────┐ │
-		//││  Hello World  │ │
-		//││               │ │
-		//││               │ │
-		//││               │ │
-		//│└───────────────┘ │
-		//└──────────────────┘
-		//";
-
-		//					var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-		//					Assert.Equal (new Rect (0, 0, 20, 8), pos);
-
-		//					Assert.True (dlg.ProcessKey (new (Key.Tab)));
-		//					dlg.Draw ();
-
-		//					expected = @"
-		//┌──────────────────┐
-		//│┌───────────────┐ │
-		//││  Hello World  │ │
-		//││               │ │
-		//││               │ │
-		//││               │ │
-		//│└───────────────┘ │
-		//└──────────────────┘
-		//";
-
-		//					pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-		//					Assert.Equal (new Rect (0, 0, 20, 8), pos);
-
-		//					win.RequestStop ();
-		//				});
-		//			});
-
-		//			Application.Run (win);
-		//			Application.Shutdown ();
-		//		}
+		// Shutdown must be called to safely clean up Application if Init has been called
+		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.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.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
+
+	//	Key key = Key.Unknown;
+
+	//	Application.Top.KeyPress += (e) => {
+	//		key = e.Key;
+	//		output.WriteLine ($"  Application.Top.KeyPress: {key}");
+	//		e.Handled = true;
+
+	//	};
+
+	//	int iterations = 0;
+	//	Application.Iteration += (s, a) => {
+	//		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))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void TerminalResized_Simulation (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		driver?.Init ();
+		driver.Cols = 80;
+		driver.Rows = 25;
+
+		bool wasTerminalResized = false;
+		driver.SizeChanged += (s, e) => {
+			wasTerminalResized = true;
+			Assert.Equal (120, e.Size.Width);
+			Assert.Equal (40, e.Size.Height);
+		};
+
+		Assert.Equal (80, driver.Cols);
+		Assert.Equal (25, driver.Rows);
+		Assert.False (wasTerminalResized);
+
+		driver.Cols = 120;
+		driver.Rows = 40;
+		driver.OnSizeChanged (new SizeChangedEventArgs (new Size (driver.Cols, driver.Rows)));
+		Assert.Equal (120, driver.Cols);
+		Assert.Equal (40, driver.Rows);
+		Assert.True (wasTerminalResized);
+		driver.End ();
+	}
+
+	// Disabled due to test error - Change Task.Delay to an await
+	//		[Fact, AutoInitShutdown]
+	//		public void Write_Do_Not_Change_On_ProcessKey ()
+	//		{
+	//			var win = new Window ();
+	//			Application.Begin (win);
+	//			((FakeDriver)Application.Driver).SetBufferSize (20, 8);
+
+	//			System.Threading.Tasks.Task.Run (() => {
+	//				System.Threading.Tasks.Task.Delay (500).Wait ();
+	//				Application.Invoke (() => {
+	//					var lbl = new Label ("Hello World") { X = Pos.Center () };
+	//					var dlg = new Dialog ();
+	//					dlg.Add (lbl);
+	//					Application.Begin (dlg);
+
+	//					var expected = @"
+	//┌──────────────────┐
+	//│┌───────────────┐ │
+	//││  Hello World  │ │
+	//││               │ │
+	//││               │ │
+	//││               │ │
+	//│└───────────────┘ │
+	//└──────────────────┘
+	//";
+
+	//					var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+	//					Assert.Equal (new Rect (0, 0, 20, 8), pos);
+
+	//					Assert.True (dlg.ProcessKey (new (Key.Tab)));
+	//					dlg.Draw ();
+
+	//					expected = @"
+	//┌──────────────────┐
+	//│┌───────────────┐ │
+	//││  Hello World  │ │
+	//││               │ │
+	//││               │ │
+	//││               │ │
+	//│└───────────────┘ │
+	//└──────────────────┘
+	//";
+
+	//					pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+	//					Assert.Equal (new Rect (0, 0, 20, 8), pos);
+
+	//					win.RequestStop ();
+	//				});
+	//			});
+
+	//			Application.Run (win);
+	//			Application.Shutdown ();
+	//		}
+}

+ 31 - 27
UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs

@@ -8,31 +8,35 @@ using Xunit.Abstractions;
 // Alias Console to MockConsole so we don't accidentally use Console
 using Console = Terminal.Gui.FakeConsole;
 
-namespace Terminal.Gui.DriverTests {
-	public class ConsoleScrollingTests {
-		readonly ITestOutputHelper output;
-
-		public ConsoleScrollingTests (ITestOutputHelper output)
-		{
-			this.output = output;
-		}
-
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void Left_And_Top_Is_Always_Zero (Type driverType)
-		{
-			var driver = (FakeDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-
-			driver.SetWindowPosition (5, 5);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-
-			Application.Shutdown ();
-		}
-		
+namespace Terminal.Gui.DriverTests; 
+
+public class ConsoleScrollingTests {
+	readonly ITestOutputHelper output;
+
+	public ConsoleScrollingTests (ITestOutputHelper output)
+	{
+		ConsoleDriver.RunningUnitTests = true;
+		this.output = output;
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	//[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	//[InlineData (typeof (WindowsDriver))]
+	//[InlineData (typeof (CursesDriver))]
+	public void Left_And_Top_Is_Always_Zero (Type driverType)
+	{
+		var driver = (FakeDriver)Activator.CreateInstance (driverType);
+		Application.Init (driver);
+
+		Assert.Equal (0, Console.WindowLeft);
+		Assert.Equal (0, Console.WindowTop);
+
+		driver.SetWindowPosition (5, 5);
+		Assert.Equal (0, Console.WindowLeft);
+		Assert.Equal (0, Console.WindowTop);
+
+		Application.Shutdown ();
 	}
-}
+}

+ 12 - 9
UnitTests/ConsoleDrivers/ContentsTests.cs

@@ -10,6 +10,7 @@ using Xunit.Abstractions;
 using Console = Terminal.Gui.FakeConsole;
 
 namespace Terminal.Gui.DriverTests;
+
 public class ContentsTests {
 	readonly ITestOutputHelper output;
 
@@ -23,13 +24,14 @@ public class ContentsTests {
 	[Theory]
 	[InlineData (typeof (FakeDriver))]
 	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
 	//[InlineData (typeof (CursesDriver))] // TODO: Uncomment when #2796 and #2615 are fixed
 	//[InlineData (typeof (WindowsDriver))] // TODO: Uncomment when #2610 is fixed
 	public void AddStr_Combining_Character_1st_Column (Type driverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		driver.Init ();
-		var expected = "\u0301!";
+		string expected = "\u0301!";
 		driver.AddStr ("\u0301!"); // acute accent + exclamation mark
 		TestHelpers.AssertDriverContentsAre (expected, output, driver);
 
@@ -39,6 +41,7 @@ public class ContentsTests {
 	[Theory]
 	[InlineData (typeof (FakeDriver))]
 	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
 	//[InlineData (typeof (CursesDriver))] // TODO: Uncomment when #2796 and #2615 are fixed
 	//[InlineData (typeof (WindowsDriver))] // TODO: Uncomment when #2610 is fixed
 	public void AddStr_With_Combining_Characters (Type driverType)
@@ -46,18 +49,18 @@ public class ContentsTests {
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		driver.Init ();
 
-		var acuteaccent = new System.Text.Rune (0x0301); // Combining acute accent (é)
-		var combined = "e" + acuteaccent;
-		var expected = "é";
+		var acuteaccent = new Rune (0x0301); // Combining acute accent (é)
+		string combined = "e" + acuteaccent;
+		string expected = "é";
 
 		driver.AddStr (combined);
 		TestHelpers.AssertDriverContentsAre (expected, output, driver);
 
 		// 3 char combine
 		// a + ogonek + acute = <U+0061, U+0328, U+0301> ( ą́ )
-		var ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape)
+		var ogonek = new Rune (0x0328); // Combining ogonek (a small hook or comma shape)
 		combined = "a" + ogonek + acuteaccent;
-		expected = ("a" + ogonek).Normalize(NormalizationForm.FormC); // See Issue #2616
+		expected = ("a" + ogonek).Normalize (NormalizationForm.FormC); // See Issue #2616
 
 		driver.Move (0, 0);
 		driver.AddStr (combined);
@@ -93,8 +96,9 @@ public class ContentsTests {
 	[Theory]
 	[InlineData (typeof (FakeDriver))]
 	[InlineData (typeof (NetDriver))]
-	[InlineData (typeof (CursesDriver))]
+	//[InlineData (typeof (ANSIDriver))]
 	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
 	public void Move_Bad_Coordinates (Type driverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
@@ -160,5 +164,4 @@ public class ContentsTests {
 	// Refresh works correctly
 
 	// IsDirty tests
-}
-
+}

+ 64 - 64
UnitTests/ConsoleDrivers/DriverColorTests.cs

@@ -4,69 +4,69 @@ using Xunit;
 // Alias Console to MockConsole so we don't accidentally use Console
 using Console = Terminal.Gui.FakeConsole;
 
-namespace Terminal.Gui.DriverTests {
-	public class DriverColorTests {
-		public DriverColorTests ()
-		{
-			ConsoleDriver.RunningUnitTests = true;
-		}
-		
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		[InlineData (typeof (NetDriver))]
-		[InlineData (typeof (CursesDriver))]
-		[InlineData (typeof (WindowsDriver))]
-		public void SetColors_Changes_Colors (Type driverType)
-		{
-			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			driver.Init ();
-
-			Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor);
-			Assert.Equal (ConsoleColor.Black, Console.BackgroundColor);
-
-			Console.ForegroundColor = ConsoleColor.Red;
-			Assert.Equal (ConsoleColor.Red, Console.ForegroundColor);
-
-			Console.BackgroundColor = ConsoleColor.Green;
-			Assert.Equal (ConsoleColor.Green, Console.BackgroundColor);
-
-			Console.ResetColor ();
-			Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor);
-			Assert.Equal (ConsoleColor.Black, Console.BackgroundColor);
-
-			driver.End ();
-		}
-
-
-		[Theory]
-		[InlineData (typeof (FakeDriver), false)]
-		[InlineData (typeof (NetDriver), true)]
-		[InlineData (typeof (CursesDriver), false)]
-		[InlineData (typeof (WindowsDriver), true)] // Because we're not Windows Terminal
-		public void SupportsTrueColor_Defaults (Type driverType, bool expectedSetting)
-		{
-			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			driver.Init ();
-
-			Assert.Equal (expectedSetting, driver.SupportsTrueColor);
-
-			driver.End ();
-		}
-
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		[InlineData (typeof (NetDriver))]
-		[InlineData (typeof (CursesDriver))]
-		[InlineData (typeof (WindowsDriver))]
-		public void Force16Colors_Sets (Type driverType)
-		{
-			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			driver.Init ();
-
-			driver.Force16Colors = true;
-			Assert.True (driver.Force16Colors);
-
-			driver.End ();
-		}
+namespace Terminal.Gui.DriverTests; 
+
+public class DriverColorTests {
+	public DriverColorTests () => ConsoleDriver.RunningUnitTests = true;
+
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void SetColors_Changes_Colors (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		driver.Init ();
+
+		Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor);
+		Assert.Equal (ConsoleColor.Black, Console.BackgroundColor);
+
+		Console.ForegroundColor = ConsoleColor.Red;
+		Assert.Equal (ConsoleColor.Red, Console.ForegroundColor);
+
+		Console.BackgroundColor = ConsoleColor.Green;
+		Assert.Equal (ConsoleColor.Green, Console.BackgroundColor);
+
+		Console.ResetColor ();
+		Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor);
+		Assert.Equal (ConsoleColor.Black, Console.BackgroundColor);
+
+		driver.End ();
+	}
+
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), false)]
+	[InlineData (typeof (NetDriver), true)]
+	//[InlineData (typeof (ANSIDriver), true)]
+	[InlineData (typeof (WindowsDriver), true)]
+	[InlineData (typeof (CursesDriver), false)]
+	public void SupportsTrueColor_Defaults (Type driverType, bool expectedSetting)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		driver.Init ();
+
+		Assert.Equal (expectedSetting, driver.SupportsTrueColor);
+
+		driver.End ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver))]
+	[InlineData (typeof (NetDriver))]
+	//[InlineData (typeof (ANSIDriver))]
+	[InlineData (typeof (WindowsDriver))]
+	[InlineData (typeof (CursesDriver))]
+	public void Force16Colors_Sets (Type driverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		driver.Init ();
+
+		driver.Force16Colors = true;
+		Assert.True (driver.Force16Colors);
+
+		driver.End ();
 	}
 }

+ 29 - 22
UnitTests/ConsoleDrivers/MainLoopDriverTests.cs

@@ -12,17 +12,14 @@ using Console = Terminal.Gui.FakeConsole;
 namespace Terminal.Gui.DriverTests;
 
 public class MainLoopDriverTests {
-
-	public MainLoopDriverTests (ITestOutputHelper output)
-	{
-		ConsoleDriver.RunningUnitTests = true;
-	}
+	public MainLoopDriverTests (ITestOutputHelper output) => ConsoleDriver.RunningUnitTests = true;
 
 	[Theory]
 	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_Constructs_Disposes (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
@@ -45,20 +42,21 @@ public class MainLoopDriverTests {
 		Assert.Empty (mainLoop.Timeouts);
 		Assert.False (mainLoop.Running);
 	}
-	
+
 	[Theory]
 	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_AddTimeout_ValidParameters_ReturnsToken (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
 		var mainLoop = new MainLoop (mainLoopDriver);
-		var callbackInvoked = false;
+		bool callbackInvoked = false;
 
-		var token = mainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), () => {
+		object token = mainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), () => {
 			callbackInvoked = true;
 			return false;
 		});
@@ -77,14 +75,15 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_RemoveTimeout_ValidToken_ReturnsTrue (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
 		var mainLoop = new MainLoop (mainLoopDriver);
 
-		var token = mainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), () => false);
-		var result = mainLoop.RemoveTimeout (token);
+		object token = mainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), () => false);
+		bool result = mainLoop.RemoveTimeout (token);
 
 		Assert.True (result);
 		mainLoop.Dispose ();
@@ -95,13 +94,14 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_RemoveTimeout_InvalidToken_ReturnsFalse (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
 		var mainLoop = new MainLoop (mainLoopDriver);
 
-		var result = mainLoop.RemoveTimeout (new object ());
+		bool result = mainLoop.RemoveTimeout (new object ());
 
 		Assert.False (result);
 	}
@@ -111,12 +111,13 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_AddIdle_ValidIdleHandler_ReturnsToken (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
 		var mainLoop = new MainLoop (mainLoopDriver);
-		var idleHandlerInvoked = false;
+		bool idleHandlerInvoked = false;
 
 		bool IdleHandler ()
 		{
@@ -124,7 +125,7 @@ public class MainLoopDriverTests {
 			return false;
 		}
 
-		Func<bool> token = mainLoop.AddIdle (IdleHandler);
+		var token = mainLoop.AddIdle (IdleHandler);
 
 		Assert.NotNull (token);
 		Assert.False (idleHandlerInvoked); // Idle handler should not be invoked immediately
@@ -138,6 +139,7 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_RemoveIdle_ValidToken_ReturnsTrue (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
@@ -145,8 +147,8 @@ public class MainLoopDriverTests {
 		var mainLoop = new MainLoop (mainLoopDriver);
 
 		bool IdleHandler () => false;
-		Func<bool> token = mainLoop.AddIdle (IdleHandler);
-		var result = mainLoop.RemoveIdle (token);
+		var token = mainLoop.AddIdle (IdleHandler);
+		bool result = mainLoop.RemoveIdle (token);
 
 		Assert.True (result);
 		mainLoop.Dispose ();
@@ -157,13 +159,14 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_RemoveIdle_InvalidToken_ReturnsFalse (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
 		var mainLoop = new MainLoop (mainLoopDriver);
 
-		var result = mainLoop.RemoveIdle (() => false);
+		bool result = mainLoop.RemoveIdle (() => false);
 
 		Assert.False (result);
 		mainLoop.Dispose ();
@@ -174,14 +177,15 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_RunIteration_ValidIdleHandler_CallsIdleHandler (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
 		var mainLoop = new MainLoop (mainLoopDriver);
-		var idleHandlerInvoked = false;
+		bool idleHandlerInvoked = false;
 
-		Func<bool> idleHandler = () => {
+		var idleHandler = () => {
 			idleHandlerInvoked = true;
 			return false;
 		};
@@ -198,13 +202,14 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_CheckTimersAndIdleHandlers_NoTimersOrIdleHandlers_ReturnsFalse (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
 		var mainLoop = new MainLoop (mainLoopDriver);
 
-		var result = mainLoop.CheckTimersAndIdleHandlers (out var waitTimeout);
+		bool result = mainLoop.CheckTimersAndIdleHandlers (out int waitTimeout);
 
 		Assert.False (result);
 		Assert.Equal (-1, waitTimeout);
@@ -216,6 +221,7 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_CheckTimersAndIdleHandlers_TimersActive_ReturnsTrue (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
@@ -223,7 +229,7 @@ public class MainLoopDriverTests {
 		var mainLoop = new MainLoop (mainLoopDriver);
 
 		mainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), () => false);
-		var result = mainLoop.CheckTimersAndIdleHandlers (out var waitTimeout);
+		bool result = mainLoop.CheckTimersAndIdleHandlers (out int waitTimeout);
 
 		Assert.True (result);
 		Assert.True (waitTimeout >= 0);
@@ -235,6 +241,7 @@ public class MainLoopDriverTests {
 	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
 	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
 	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
 	public void MainLoop_CheckTimersAndIdleHandlers_IdleHandlersActive_ReturnsTrue (Type driverType, Type mainLoopDriverType)
 	{
 		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
@@ -242,7 +249,7 @@ public class MainLoopDriverTests {
 		var mainLoop = new MainLoop (mainLoopDriver);
 
 		mainLoop.AddIdle (() => false);
-		var result = mainLoop.CheckTimersAndIdleHandlers (out var waitTimeout);
+		bool result = mainLoop.CheckTimersAndIdleHandlers (out int waitTimeout);
 
 		Assert.True (result);
 		Assert.Equal (-1, waitTimeout);
@@ -267,4 +274,4 @@ public class MainLoopDriverTests {
 	//	Assert.True (actionInvoked);
 	//	mainLoop.Dispose ();
 	//}
-}
+}