Explorar el Código

merged ; pretty broken

Tigger Kindel hace 2 años
padre
commit
9a18e30c1a
Se han modificado 66 ficheros con 4924 adiciones y 3286 borrados
  1. 2 2
      .github/workflows/dotnet-core.yml
  2. 4 4
      Example/Example.csproj
  3. 3 1
      README.md
  4. 6 6
      ReactiveExample/ReactiveExample.csproj
  5. 16 17
      Terminal.Gui/Configuration/ConfigurationManager.cs
  6. 10 2
      Terminal.Gui/Configuration/Scope.cs
  7. 3 0
      Terminal.Gui/Configuration/SettingsScope.cs
  8. 7 7
      Terminal.Gui/Configuration/ThemeScope.cs
  9. 79 395
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  10. 3 27
      Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs
  11. 14 21
      Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs
  12. 0 5
      Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
  13. 2 2
      Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs
  14. 284 289
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs
  15. 9 12
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  16. 258 828
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  17. 51 36
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  18. 30 17
      Terminal.Gui/Core/Application.cs
  19. 2 1
      Terminal.Gui/Core/Autocomplete/Autocomplete.cs
  20. 96 178
      Terminal.Gui/Core/Border.cs
  21. 19 72
      Terminal.Gui/Core/ConsoleDriver.cs
  22. 19 0
      Terminal.Gui/Core/ConsoleKeyMapping.cs
  23. 109 0
      Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs
  24. 907 0
      Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs
  25. 1 1
      Terminal.Gui/Core/Event.cs
  26. 2 2
      Terminal.Gui/Core/PosDim.cs
  27. 20 9
      Terminal.Gui/Core/TextFormatter.cs
  28. 8 4
      Terminal.Gui/Core/Toplevel.cs
  29. 78 71
      Terminal.Gui/Core/View.cs
  30. 87 15
      Terminal.Gui/Core/Window.cs
  31. 1 1
      Terminal.Gui/Resources/config.json
  32. 9 10
      Terminal.Gui/Terminal.Gui.csproj
  33. 73 2
      Terminal.Gui/Views/FrameView.cs
  34. 209 132
      Terminal.Gui/Views/TableView.cs
  35. 33 17
      Terminal.Gui/Views/TileView.cs
  36. 1 0
      Terminal.Gui/Windows/Dialog.cs
  37. 6 14
      UICatalog/Properties/launchSettings.json
  38. 313 0
      UICatalog/Scenarios/ASCIICustomButton.cs
  39. 6 5
      UICatalog/Scenarios/BordersComparisons.cs
  40. 52 55
      UICatalog/Scenarios/BordersOnContainers.cs
  41. 12 376
      UICatalog/Scenarios/BordersOnFrameView.cs
  42. 12 375
      UICatalog/Scenarios/BordersOnWindow.cs
  43. 2 0
      UICatalog/Scenarios/TableEditor.cs
  44. 2 2
      UICatalog/Scenarios/TileViewExperiment.cs
  45. 3 0
      UICatalog/Scenarios/TileViewNesting.cs
  46. 27 20
      UICatalog/UICatalog.cs
  47. 5 5
      UICatalog/UICatalog.csproj
  48. 6 7
      UnitTests/Application/ApplicationTests.cs
  49. 13 13
      UnitTests/Configuration/ConfigurationMangerTests.cs
  50. 6 6
      UnitTests/Configuration/SettingsScopeTests.cs
  51. 141 67
      UnitTests/Core/BorderTests.cs
  52. 78 0
      UnitTests/Core/EscSeqReqTests.cs
  53. 870 0
      UnitTests/Core/EscSeqUtilsTests.cs
  54. 16 4
      UnitTests/Core/LayoutTests.cs
  55. 141 45
      UnitTests/Core/ViewTests.cs
  56. 11 8
      UnitTests/Drivers/ClipboardTests.cs
  57. 15 15
      UnitTests/Drivers/ConsoleDriverTests.cs
  58. 184 6
      UnitTests/Text/TextFormatterTests.cs
  59. 6 6
      UnitTests/TopLevels/MessageBoxTests.cs
  60. 50 0
      UnitTests/TopLevels/ToplevelTests.cs
  61. 15 14
      UnitTests/Types/DimTests.cs
  62. 2 2
      UnitTests/UnitTests.csproj
  63. 10 10
      UnitTests/Views/ScrollBarViewTests.cs
  64. 223 5
      UnitTests/Views/ScrollViewTests.cs
  65. 181 0
      UnitTests/Views/TableViewTests.cs
  66. 61 40
      UnitTests/Views/TileViewTests.cs

+ 2 - 2
.github/workflows/dotnet-core.yml

@@ -2,9 +2,9 @@ name: Build & Test Terminal.Gui with .NET Core
 
 on:
   push:
-    branches: [ main, develop ]
+    branches: [ main, develop, v2_develop ]
   pull_request:
-    branches: [ main, develop ]
+    branches: [ main, develop, v2_develop ]
 
 jobs:
   build:

+ 4 - 4
Example/Example.csproj

@@ -5,10 +5,10 @@
     <!-- Version numbers are automatically updated by gitversion when a release is released -->
     <!-- In the source tree the version will always be 1.0 for all projects. -->
     <!-- Do not modify these. -->
-    <AssemblyVersion>1.0</AssemblyVersion>
-    <FileVersion>1.0</FileVersion>
-    <Version>1.0</Version>
-    <InformationalVersion>1.0</InformationalVersion>
+    <AssemblyVersion>2.0</AssemblyVersion>
+    <FileVersion>2.0</FileVersion>
+    <Version>2.0</Version>
+    <InformationalVersion>2.0</InformationalVersion>
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />

+ 3 - 1
README.md

@@ -6,10 +6,12 @@
 [![License](https://img.shields.io/github/license/gui-cs/gui.cs.svg)](LICENSE)
 ![Bugs](https://img.shields.io/github/issues/gui-cs/gui.cs/bug)
 
-# Terminal.Gui - Cross Platform Terminal UI toolkit for .NET
+# Terminal.Gui v2.x - Cross Platform Terminal UI toolkit for .NET
 
 A toolkit for building rich console apps for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
 
+NOTE: This is the WORK IN PROGRESS `v2.x` branch. The `main` branch is the stable `v1.x` branch.
+
 ![Sample app](docfx/images/sample.gif)
 
 ## Quick Start

+ 6 - 6
ReactiveExample/ReactiveExample.csproj

@@ -5,14 +5,14 @@
     <!-- Version numbers are automatically updated by gitversion when a release is released -->
     <!-- In the source tree the version will always be 2.0 for all projects. -->
     <!-- Do not modify these. -->
-    <AssemblyVersion>1.0</AssemblyVersion>
-    <FileVersion>1.0</FileVersion>
-    <Version>1.0</Version>
-    <InformationalVersion>1.0</InformationalVersion>
+    <AssemblyVersion>2.0</AssemblyVersion>
+    <FileVersion>2.0</FileVersion>
+    <Version>2.0</Version>
+    <InformationalVersion>2.0</InformationalVersion>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="ReactiveUI.Fody" Version="18.4.1" />
-    <PackageReference Include="ReactiveUI" Version="18.4.1" />
+    <PackageReference Include="ReactiveUI.Fody" Version="18.4.22" />
+    <PackageReference Include="ReactiveUI" Version="18.4.22" />
     <PackageReference Include="ReactiveMarbles.ObservableEvents.SourceGenerator" Version="1.2.3" PrivateAssets="all" />
   </ItemGroup>
   <ItemGroup>

+ 16 - 17
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -44,7 +44,7 @@ namespace Terminal.Gui.Configuration {
 	///	3. Application configuration found in the applications's resources (<c>Resources/config.json</c>). 
 	/// </para>
 	/// <para>
-	///	4. Global configuration found in the the user's home directory (<c>~/.tui/config.json</c>).
+	///	4. Global configuration found in the user's home directory (<c>~/.tui/config.json</c>).
 	/// </para>
 	/// <para>
 	///	5. Global configuration found in the directory the app was launched from (<c>./.tui/config.json</c>).
@@ -63,7 +63,7 @@ namespace Terminal.Gui.Configuration {
 			DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
 			WriteIndented = true,
 			Converters = {
-				// No need to set converterss - the ConfigRootConverter uses property attributes apply the correct
+				// No need to set converters - the ConfigRootConverter uses property attributes apply the correct
 				// Converter.
 			},
 		};
@@ -196,7 +196,7 @@ namespace Terminal.Gui.Configuration {
 		/// </summary>
 		/// <remarks>
 		/// Is <see langword="null"/> until <see cref="Reset"/> is called. Gets set to a new instance by
-		/// deserializtion (see <see cref="Load"/>).
+		/// deserialization (see <see cref="Load"/>).
 		/// </remarks>
 		private static SettingsScope? _settings;
 
@@ -223,14 +223,14 @@ namespace Terminal.Gui.Configuration {
 		public static ThemeManager? Themes => ThemeManager.Instance;
 
 		/// <summary>
-		/// Aplication-specific configuration settings scope.
+		/// Application-specific configuration settings scope.
 		/// </summary>
 		[SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true), JsonPropertyName ("AppSettings")]
 		public static AppScope? AppSettings { get; set; }
 
 		/// <summary>
-		/// Initializes the internal state of ConfiguraitonManager. Nominally called once as part of application
-		/// startup to initilaize global state. Also called from some Unit Tests to ensure correctness (e.g. Reset()).
+		/// Initializes the internal state of ConfigurationManager. Nominally called once as part of application
+		/// startup to initialize global state. Also called from some Unit Tests to ensure correctness (e.g. Reset()).
 		/// </summary>
 		internal static void Initialize ()
 		{
@@ -260,7 +260,7 @@ namespace Terminal.Gui.Configuration {
 					  select p) {
 				if (p.GetCustomAttribute (typeof (SerializableConfigurationProperty)) is SerializableConfigurationProperty scp) {
 					if (p.GetGetMethod (true)!.IsStatic) {
-						// If the class name is ommited, JsonPropertyName is allowed. 
+						// If the class name is omitted, JsonPropertyName is allowed. 
 						_allConfigProperties!.Add (scp.OmitClassName ? ConfigProperty.GetJsonPropertyName (p) : $"{p.DeclaringType?.Name}.{p.Name}", new ConfigProperty {
 							PropertyInfo = p,
 							PropertyValue = null
@@ -357,7 +357,7 @@ namespace Terminal.Gui.Configuration {
 		}
 
 		/// <summary>
-		/// Event fired when the configuration has been upddated from a configuration source.  
+		/// Event fired when the configuration has been updated from a configuration source.  
 		/// application.
 		/// </summary>
 		public static event Action<ConfigurationManagerEventArgs>? Updated;
@@ -378,7 +378,7 @@ namespace Terminal.Gui.Configuration {
 			}
 
 			ClearJsonErrors ();
-			
+
 			Settings = new SettingsScope ();
 			ThemeManager.Reset ();
 			AppSettings = new AppScope ();
@@ -402,7 +402,7 @@ namespace Terminal.Gui.Configuration {
 		/// to generate the JSON doc that is embedded into Terminal.Gui (during development). 
 		/// </para>
 		/// <para>
-		/// WARNING: The <c>Terminal.Gui.Resources.config.json</c> resource has setting defintions (Themes)
+		/// WARNING: The <c>Terminal.Gui.Resources.config.json</c> resource has setting definitions (Themes)
 		/// that are NOT generated by this function. If you use this function to regenerate <c>Terminal.Gui.Resources.config.json</c>,
 		/// make sure you copy the Theme definitions from the existing <c>Terminal.Gui.Resources.config.json</c> file.
 		/// </para>		
@@ -455,7 +455,7 @@ namespace Terminal.Gui.Configuration {
 		public static string AppName { get; set; } = Assembly.GetEntryAssembly ()?.FullName?.Split (',') [0]?.Trim ()!;
 
 		/// <summary>
-		/// Describes the location of the configuration files. The constancts can be
+		/// Describes the location of the configuration files. The constants can be
 		/// combined (bitwise) to specify multiple locations.
 		/// </summary>
 		[Flags]
@@ -488,27 +488,26 @@ namespace Terminal.Gui.Configuration {
 		public static ConfigLocations Locations { get; set; } = ConfigLocations.All;
 
 		/// <summary>
-		/// Loads all settings found in the various configuraiton storage locations to 
+		/// Loads all settings found in the various configuration storage locations to 
 		/// the <see cref="ConfigurationManager"/>. Optionally,
-		/// resets all settings attributed with <see cref="SerializableConfigurationProperty"/> to the defaults 
-		/// defined in <see cref="LoadAppResources"/>.
+		/// resets all settings attributed with <see cref="SerializableConfigurationProperty"/> to the defaults.
 		/// </summary>
 		/// <remarks>
 		/// Use <see cref="Apply"/> to cause the loaded settings to be applied to the running application.
 		/// </remarks>
 		/// <param name="reset">If <see langword="true"/> the state of <see cref="ConfigurationManager"/> will
-		/// be reset to the defaults defined in <see cref="LoadAppResources"/>.</param>
+		/// be reset to the defaults.</param>
 		public static void Load (bool reset = false)
 		{
 			Debug.WriteLine ($"ConfigurationManager.Load()");
 
 			if (reset) Reset ();
 
-			// LibraryResoruces is always loaded by Reset
+			// LibraryResources is always loaded by Reset
 			if (Locations == ConfigLocations.All) {
 				var embeddedStylesResourceName = Assembly.GetEntryAssembly ()?
 					.GetManifestResourceNames ().FirstOrDefault (x => x.EndsWith (_configFilename));
-				if (string.IsNullOrEmpty(embeddedStylesResourceName)) {
+				if (string.IsNullOrEmpty (embeddedStylesResourceName)) {
 					embeddedStylesResourceName = _configFilename;
 				}
 

+ 10 - 2
Terminal.Gui/Configuration/Scope.cs

@@ -129,9 +129,17 @@ namespace Terminal.Gui.Configuration {
 								}
 							}
 							var readHelper = Activator.CreateInstance ((Type?)typeof (ReadHelper<>).MakeGenericType (typeof (scopeT), propertyType!)!, converter) as ReadHelper;
-							scope! [propertyName].PropertyValue = readHelper?.Read (ref reader, propertyType!, options);
+							try {
+								scope! [propertyName].PropertyValue = readHelper?.Read (ref reader, propertyType!, options);
+							} catch (NotSupportedException e) {
+								throw new JsonException ($"Error reading property \"{propertyName}\" of type \"{propertyType?.Name}\".", e);
+							}
 						} else {
-							scope! [propertyName].PropertyValue = JsonSerializer.Deserialize (ref reader, propertyType!, options);
+							try {
+								scope! [propertyName].PropertyValue = JsonSerializer.Deserialize (ref reader, propertyType!, options);
+							} catch (Exception ex) {
+								System.Diagnostics.Debug.WriteLine ($"scopeT Read: {ex}");
+							}
 						}
 					} else {
 						// It is not a config property. Maybe it's just a property on the Scope with [JsonInclude]

+ 3 - 0
Terminal.Gui/Configuration/SettingsScope.cs

@@ -34,6 +34,9 @@ namespace Terminal.Gui.Configuration {
 			[JsonInclude, JsonPropertyName ("$schema")]
 			public string Schema { get; set; } = "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json";
 
+			/// <summary>
+			/// The list of paths to the configuration files.
+			/// </summary>
 			public List<string> Sources = new List<string> ();
 
 			/// <summary>

+ 7 - 7
Terminal.Gui/Configuration/ThemeScope.cs

@@ -120,21 +120,21 @@ namespace Terminal.Gui.Configuration {
 			/// </summary>
 			public static ThemeManager Instance { get { return _instance; } }
 
-			private static string theme = string.Empty;
+			private static string _theme = string.Empty;
 
 			/// <summary>
 			/// The currently selected theme. This is the internal version; see <see cref="Theme"/>.
 			/// </summary>
 			[JsonInclude, SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true), JsonPropertyName ("Theme")]
 			internal static string SelectedTheme {
-				get => theme;
+				get => _theme;
 				set {
-					var oldTheme = theme;
-					theme = value;
-					if (oldTheme != theme &&
+					var oldTheme = _theme;
+					_theme = value;
+					if (oldTheme != _theme &&
 						ConfigurationManager.Settings! ["Themes"]?.PropertyValue is Dictionary<string, ThemeScope> themes &&
-						themes.ContainsKey (theme)) {
-						ConfigurationManager.Settings! ["Theme"].PropertyValue = theme;
+						themes.ContainsKey (_theme)) {
+						ConfigurationManager.Settings! ["Theme"].PropertyValue = _theme;
 						Instance.OnThemeChanged (oldTheme);
 					}
 				}

+ 79 - 395
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -1,9 +1,6 @@
 //
 // Driver.cs: Curses-based Driver
 //
-// Authors:
-//   Miguel de Icaza ([email protected])
-//
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -23,7 +20,7 @@ namespace Terminal.Gui {
 		public override int Rows => Curses.Lines;
 		public override int Left => 0;
 		public override int Top => 0;
-		public override bool HeightAsBuffer { get; set; }
+		public override bool EnableConsoleScrolling { get; set; }
 		public override IClipboard Clipboard { get => clipboard; }
 
 		CursorVisibility? initialCursorVisibility = null;
@@ -116,7 +113,7 @@ namespace Terminal.Gui {
 			if (runeWidth < 0 || runeWidth > 0) {
 				ccol++;
 			}
-			
+
 			if (runeWidth > 1) {
 				if (validClip && ccol < Clip.Right) {
 					contents [crow, ccol, 1] = CurrentAttribute;
@@ -158,26 +155,10 @@ namespace Terminal.Gui {
 
 		public override void End ()
 		{
-			if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) {
-				StopReportingMouseMoves ();
-			}
-
+			StopReportingMouseMoves ();
 			SetCursorVisibility (CursorVisibility.Default);
 
 			Curses.endwin ();
-
-			// I'm commenting this because was used in a trying to fix the Linux hanging and forgot to exclude it.
-			// Clear and reset entire screen.
-			//Console.Out.Write ("\x1b[2J");
-			//Console.Out.Flush ();
-
-			// Set top and bottom lines of a window.
-			//Console.Out.Write ("\x1b[1;25r");
-			//Console.Out.Flush ();
-
-			//Set cursor key to cursor.
-			//Console.Out.Write ("\x1b[?1l");
-			//Console.Out.Flush ();
 		}
 
 		public override void UpdateScreen () => window.redrawwin ();
@@ -324,305 +305,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-		Curses.Event? lastMouseButtonPressed;
-		bool isButtonPressed;
-		bool cancelButtonClicked;
-		bool isReportMousePosition;
-		Point point;
-		int buttonPressedCount;
-
-		MouseEvent ToDriverMouse (Curses.MouseEvent cev)
-		{
-			MouseFlags mouseFlag = MouseFlags.AllEvents;
-
-			if (lastMouseButtonPressed != null && cev.ButtonState != Curses.Event.ReportMousePosition) {
-				lastMouseButtonPressed = null;
-				isButtonPressed = false;
-			}
-
-			if (cev.ButtonState == Curses.Event.Button1Pressed
-				|| cev.ButtonState == Curses.Event.Button2Pressed
-				|| cev.ButtonState == Curses.Event.Button3Pressed) {
-
-				isButtonPressed = true;
-				buttonPressedCount++;
-			} else {
-				buttonPressedCount = 0;
-			}
-			//System.Diagnostics.Debug.WriteLine ($"buttonPressedCount: {buttonPressedCount}");
-
-			if (buttonPressedCount == 2
-				&& (cev.ButtonState == Curses.Event.Button1Pressed
-				|| cev.ButtonState == Curses.Event.Button2Pressed
-				|| cev.ButtonState == Curses.Event.Button3Pressed)) {
-
-				switch (cev.ButtonState) {
-				case Curses.Event.Button1Pressed:
-					mouseFlag = MouseFlags.Button1DoubleClicked;
-					break;
-
-				case Curses.Event.Button2Pressed:
-					mouseFlag = MouseFlags.Button2DoubleClicked;
-					break;
-
-				case Curses.Event.Button3Pressed:
-					mouseFlag = MouseFlags.Button3DoubleClicked;
-					break;
-				}
-				cancelButtonClicked = true;
-
-			} else if (buttonPressedCount == 3
-			       && (cev.ButtonState == Curses.Event.Button1Pressed
-			       || cev.ButtonState == Curses.Event.Button2Pressed
-			       || cev.ButtonState == Curses.Event.Button3Pressed)) {
-
-				switch (cev.ButtonState) {
-				case Curses.Event.Button1Pressed:
-					mouseFlag = MouseFlags.Button1TripleClicked;
-					break;
-
-				case Curses.Event.Button2Pressed:
-					mouseFlag = MouseFlags.Button2TripleClicked;
-					break;
-
-				case Curses.Event.Button3Pressed:
-					mouseFlag = MouseFlags.Button3TripleClicked;
-					break;
-				}
-				buttonPressedCount = 0;
-
-			} else if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked ||
-			       cev.ButtonState == Curses.Event.Button3Clicked) &&
-			       lastMouseButtonPressed == null) {
-
-				isButtonPressed = false;
-				mouseFlag = ProcessButtonClickedEvent (cev);
-
-			} else if (((cev.ButtonState == Curses.Event.Button1Pressed || cev.ButtonState == Curses.Event.Button2Pressed ||
-				cev.ButtonState == Curses.Event.Button3Pressed) && lastMouseButtonPressed == null) ||
-				isButtonPressed && lastMouseButtonPressed != null && cev.ButtonState == Curses.Event.ReportMousePosition) {
-
-				mouseFlag = MapCursesButton (cev.ButtonState);
-				if (cev.ButtonState != Curses.Event.ReportMousePosition)
-					lastMouseButtonPressed = cev.ButtonState;
-				isButtonPressed = true;
-				isReportMousePosition = false;
-
-				if (cev.ButtonState == Curses.Event.ReportMousePosition) {
-					mouseFlag = MapCursesButton ((Curses.Event)lastMouseButtonPressed) | MouseFlags.ReportMousePosition;
-					cancelButtonClicked = true;
-				}
-				point = new Point () {
-					X = cev.X,
-					Y = cev.Y
-				};
-
-				if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) {
-					Application.MainLoop.AddIdle (() => {
-						Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
-						return false;
-					});
-				}
-
-
-			} else if ((cev.ButtonState == Curses.Event.Button1Released || cev.ButtonState == Curses.Event.Button2Released ||
-				cev.ButtonState == Curses.Event.Button3Released)) {
-
-				mouseFlag = ProcessButtonReleasedEvent (cev);
-				isButtonPressed = false;
-
-			} else if (cev.ButtonState == Curses.Event.ButtonWheeledUp) {
-
-				mouseFlag = MouseFlags.WheeledUp;
-
-			} else if (cev.ButtonState == Curses.Event.ButtonWheeledDown) {
-
-				mouseFlag = MouseFlags.WheeledDown;
-
-			} else if ((cev.ButtonState & (Curses.Event.ButtonWheeledUp & Curses.Event.ButtonShift)) != 0) {
-
-				mouseFlag = MouseFlags.WheeledLeft;
-
-			} else if ((cev.ButtonState & (Curses.Event.ButtonWheeledDown & Curses.Event.ButtonShift)) != 0) {
-
-				mouseFlag = MouseFlags.WheeledRight;
-
-			} else if (cev.ButtonState == Curses.Event.ReportMousePosition) {
-				if (cev.X != point.X || cev.Y != point.Y) {
-					mouseFlag = MouseFlags.ReportMousePosition;
-					isReportMousePosition = true;
-					point = new Point ();
-				} else {
-					mouseFlag = 0;
-				}
-
-			} else {
-				mouseFlag = 0;
-				var eFlags = cev.ButtonState;
-				foreach (Enum value in Enum.GetValues (eFlags.GetType ())) {
-					if (eFlags.HasFlag (value)) {
-						mouseFlag |= MapCursesButton ((Curses.Event)value);
-					}
-				}
-			}
-
-			mouseFlag = SetControlKeyStates (cev, mouseFlag);
-
-			return new MouseEvent () {
-				X = cev.X,
-				Y = cev.Y,
-				//Flags = MapCursesButton (cev.ButtonState)
-				Flags = mouseFlag
-			};
-		}
-
-		MouseFlags ProcessButtonClickedEvent (Curses.MouseEvent cev)
-		{
-			lastMouseButtonPressed = cev.ButtonState;
-			var mf = GetButtonState (cev, true);
-			mouseHandler (ProcessButtonState (cev, mf));
-			if (lastMouseButtonPressed != null && lastMouseButtonPressed == cev.ButtonState) {
-				mf = GetButtonState (cev, false);
-				mouseHandler (ProcessButtonState (cev, mf));
-				if (lastMouseButtonPressed != null && lastMouseButtonPressed == cev.ButtonState) {
-					mf = MapCursesButton (cev.ButtonState);
-				}
-			}
-			lastMouseButtonPressed = null;
-			isButtonPressed = false;
-			return mf;
-		}
-
-		MouseFlags ProcessButtonReleasedEvent (Curses.MouseEvent cev)
-		{
-			var mf = MapCursesButton (cev.ButtonState);
-			if (!cancelButtonClicked && lastMouseButtonPressed == null && !isReportMousePosition) {
-				mouseHandler (ProcessButtonState (cev, mf));
-				mf = GetButtonState (cev);
-			} else if (isReportMousePosition) {
-				mf = MouseFlags.ReportMousePosition;
-			}
-			cancelButtonClicked = false;
-			return mf;
-		}
-
-		async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag)
-		{
-			while (isButtonPressed) {
-				await Task.Delay (100);
-				var me = new MouseEvent () {
-					X = point.X,
-					Y = point.Y,
-					Flags = mouseFlag
-				};
-
-				var view = Application.WantContinuousButtonPressedView;
-				if (view == null)
-					break;
-				if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
-					Application.MainLoop.Invoke (() => mouseHandler (me));
-				}
-			}
-		}
-
-		MouseFlags GetButtonState (Curses.MouseEvent cev, bool pressed = false)
-		{
-			MouseFlags mf = default;
-			switch (cev.ButtonState) {
-			case Curses.Event.Button1Clicked:
-				if (pressed)
-					mf = MouseFlags.Button1Pressed;
-				else
-					mf = MouseFlags.Button1Released;
-				break;
-
-			case Curses.Event.Button2Clicked:
-				if (pressed)
-					mf = MouseFlags.Button2Pressed;
-				else
-					mf = MouseFlags.Button2Released;
-				break;
-
-			case Curses.Event.Button3Clicked:
-				if (pressed)
-					mf = MouseFlags.Button3Pressed;
-				else
-					mf = MouseFlags.Button3Released;
-				break;
-
-			case Curses.Event.Button1Released:
-				mf = MouseFlags.Button1Clicked;
-				break;
-
-			case Curses.Event.Button2Released:
-				mf = MouseFlags.Button2Clicked;
-				break;
-
-			case Curses.Event.Button3Released:
-				mf = MouseFlags.Button3Clicked;
-				break;
-
-			}
-			return mf;
-		}
-
-		MouseEvent ProcessButtonState (Curses.MouseEvent cev, MouseFlags mf)
-		{
-			return new MouseEvent () {
-				X = cev.X,
-				Y = cev.Y,
-				Flags = mf
-			};
-		}
-
-		MouseFlags MapCursesButton (Curses.Event cursesButton)
-		{
-			switch (cursesButton) {
-			case Curses.Event.Button1Pressed: return MouseFlags.Button1Pressed;
-			case Curses.Event.Button1Released: return MouseFlags.Button1Released;
-			case Curses.Event.Button1Clicked: return MouseFlags.Button1Clicked;
-			case Curses.Event.Button1DoubleClicked: return MouseFlags.Button1DoubleClicked;
-			case Curses.Event.Button1TripleClicked: return MouseFlags.Button1TripleClicked;
-			case Curses.Event.Button2Pressed: return MouseFlags.Button2Pressed;
-			case Curses.Event.Button2Released: return MouseFlags.Button2Released;
-			case Curses.Event.Button2Clicked: return MouseFlags.Button2Clicked;
-			case Curses.Event.Button2DoubleClicked: return MouseFlags.Button2DoubleClicked;
-			case Curses.Event.Button2TrippleClicked: return MouseFlags.Button2TripleClicked;
-			case Curses.Event.Button3Pressed: return MouseFlags.Button3Pressed;
-			case Curses.Event.Button3Released: return MouseFlags.Button3Released;
-			case Curses.Event.Button3Clicked: return MouseFlags.Button3Clicked;
-			case Curses.Event.Button3DoubleClicked: return MouseFlags.Button3DoubleClicked;
-			case Curses.Event.Button3TripleClicked: return MouseFlags.Button3TripleClicked;
-			case Curses.Event.ButtonWheeledUp: return MouseFlags.WheeledUp;
-			case Curses.Event.ButtonWheeledDown: return MouseFlags.WheeledDown;
-			case Curses.Event.Button4Pressed: return MouseFlags.Button4Pressed;
-			case Curses.Event.Button4Released: return MouseFlags.Button4Released;
-			case Curses.Event.Button4Clicked: return MouseFlags.Button4Clicked;
-			case Curses.Event.Button4DoubleClicked: return MouseFlags.Button4DoubleClicked;
-			case Curses.Event.Button4TripleClicked: return MouseFlags.Button4TripleClicked;
-			case Curses.Event.ButtonShift: return MouseFlags.ButtonShift;
-			case Curses.Event.ButtonCtrl: return MouseFlags.ButtonCtrl;
-			case Curses.Event.ButtonAlt: return MouseFlags.ButtonAlt;
-			case Curses.Event.ReportMousePosition: return MouseFlags.ReportMousePosition;
-			case Curses.Event.AllEvents: return MouseFlags.AllEvents;
-			default: return 0;
-			}
-		}
-
-		static MouseFlags SetControlKeyStates (Curses.MouseEvent cev, MouseFlags mouseFlag)
-		{
-			if ((cev.ButtonState & Curses.Event.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0)
-				mouseFlag |= MouseFlags.ButtonCtrl;
-
-			if ((cev.ButtonState & Curses.Event.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0)
-				mouseFlag |= MouseFlags.ButtonShift;
-
-			if ((cev.ButtonState & Curses.Event.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0)
-				mouseFlag |= MouseFlags.ButtonAlt;
-			return mouseFlag;
-		}
-
-
 		KeyModifiers keyModifiers;
 
 		KeyModifiers MapKeyModifiers (Key key)
@@ -656,9 +338,18 @@ namespace Terminal.Gui {
 					ProcessWinChange ();
 				}
 				if (wch == Curses.KeyMouse) {
-					Curses.getmouse (out Curses.MouseEvent ev);
-					//System.Diagnostics.Debug.WriteLine ($"ButtonState: {ev.ButtonState}; ID: {ev.ID}; X: {ev.X}; Y: {ev.Y}; Z: {ev.Z}");
-					mouseHandler (ToDriverMouse (ev));
+					int wch2 = wch;
+
+					while (wch2 == Curses.KeyMouse) {
+						KeyEvent key = null;
+						ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] {
+							new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false),
+							new ConsoleKeyInfo ('[', 0, false, false, false),
+							new ConsoleKeyInfo ('<', 0, false, false, false)
+						};
+						code = 0;
+						GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki);
+					}
 					return;
 				}
 				k = MapCursesKey (wch);
@@ -694,7 +385,7 @@ namespace Terminal.Gui {
 					k = Key.AltMask | MapCursesKey (wch);
 				}
 				if (code == 0) {
-					KeyEvent key;
+					KeyEvent key = null;
 
 					// The ESC-number handling, debatable.
 					// Simulates the AltMask itself by pressing Alt + Space.
@@ -706,55 +397,13 @@ namespace Terminal.Gui {
 						k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + (wch2 + 64));
 					} else if (wch2 >= (uint)Key.D0 && wch2 <= (uint)Key.D9) {
 						k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - (uint)Key.D0));
-					} else if (wch2 == 27) {
-						k = (Key)wch2;
-					} else if (wch2 == Curses.KEY_CODE_SEQ) {
-						int [] c = null;
-						while (code == 0) {
-							code = Curses.get_wch (out wch2);
-							if (wch2 > 0) {
-								Array.Resize (ref c, c == null ? 1 : c.Length + 1);
-								c [c.Length - 1] = wch2;
-							}
-						}
-						if (c [0] == 49 && c [1] == 59 && c [2] == 55 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Alt+(F1 - F4)
-							wch2 = c [3] + 185;
-							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 49 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Alt+(F5 - F8)
-							wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215;
-							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 50 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Ctrl+Alt+(F9 - F12)
-							wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224;
-							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Shift+Alt+(F1 - F4)
-							wch2 = c [3] + 185;
-							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 49 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Shift+Alt+(F5 - F8)
-							wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215;
-							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 50 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) {  // Ctrl+Shift+Alt+(F9 - F12)
-							wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224;
-							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 49 && c [1] == 59 && c [2] == 52 && c [3] == 83) {  // Shift+Alt+(F4)
-							wch2 = 268;
-							k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 49 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) {  // Shift+Alt+(F5 - F8)
-							wch2 = c [1] < 55 ? c [1] + 216 : c [1] + 215;
-							k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 50 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) {  // Shift+Alt+(F9 - F12)
-							wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224;
-							k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 54 && c [1] == 59 && c [2] == 56 && c [3] == 126) {  // Shift+Ctrl+Alt+KeyNPage
-							k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageDown;
-						} else if (c [0] == 53 && c [1] == 59 && c [2] == 56 && c [3] == 126) {  // Shift+Ctrl+Alt+KeyPPage
-							k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageUp;
-						} else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 72) {  // Shift+Ctrl+Alt+KeyHome
-							k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.Home;
-						} else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 70) {  // Shift+Ctrl+Alt+KeyEnd
-							k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.End;
-						} else {
-							k = MapCursesKey (wch2);
-						}
+					} else if (wch2 == Curses.KeyCSI) {
+						ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] {
+							new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false),
+							new ConsoleKeyInfo ('[', 0, false, false, false)
+						};
+						GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki);
+						return;
 					} else {
 						// Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
 						if (((Key)wch2 & Key.CtrlMask) != 0) {
@@ -809,6 +458,52 @@ namespace Terminal.Gui {
 			//}
 		}
 
+		void GetEscSeq (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref ConsoleKeyInfo [] cki)
+		{
+			ConsoleKey ck = 0;
+			ConsoleModifiers mod = 0;
+			while (code == 0) {
+				code = Curses.get_wch (out wch2);
+				var consoleKeyInfo = new ConsoleKeyInfo ((char)wch2, 0, false, false, false);
+				if (wch2 == 0 || wch2 == 27 || wch2 == Curses.KeyMouse) {
+					EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out List<MouseFlags> mouseFlags, out Point pos, out _, ProcessContinuousButtonPressed);
+					if (isKeyMouse) {
+						foreach (var mf in mouseFlags) {
+							ProcessMouseEvent (mf, pos);
+						}
+						cki = null;
+						if (wch2 == 27) {
+							cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0,
+								false, false, false), cki);
+						}
+					} else {
+						k = ConsoleKeyMapping.MapConsoleKeyToKey (consoleKeyInfo.Key, out _);
+						k = ConsoleKeyMapping.MapKeyModifiers (consoleKeyInfo, k);
+						key = new KeyEvent (k, MapKeyModifiers (k));
+						keyDownHandler (key);
+						keyHandler (key);
+					}
+				} else {
+					cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki);
+				}
+			}
+		}
+
+		void ProcessMouseEvent (MouseFlags mouseFlag, Point pos)
+		{
+			var me = new MouseEvent () {
+				Flags = mouseFlag,
+				X = pos.X,
+				Y = pos.Y
+			};
+			mouseHandler (me);
+		}
+
+		void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos)
+		{
+			ProcessMouseEvent (mouseFlag, pos);
+		}
+
 		Action<KeyEvent> keyHandler;
 		Action<KeyEvent> keyDownHandler;
 		Action<KeyEvent> keyUpHandler;
@@ -835,17 +530,12 @@ namespace Terminal.Gui {
 			};
 		}
 
-		Curses.Event oldMouseEvents, reportableMouseEvents;
 		public override void Init (Action terminalResized)
 		{
 			if (window != null)
 				return;
 
 			try {
-				//Set cursor key to application.
-				//Console.Out.Write ("\x1b[?1h");
-				//Console.Out.Flush ();
-
 				window = Curses.initscr ();
 				Curses.set_escdelay (10);
 			} catch (Exception e) {
@@ -892,10 +582,8 @@ namespace Terminal.Gui {
 			Curses.noecho ();
 
 			Curses.Window.Standard.keypad (true);
-			reportableMouseEvents = Curses.mousemask (Curses.Event.AllEvents | Curses.Event.ReportMousePosition, out oldMouseEvents);
 			TerminalResized = terminalResized;
-			if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition))
-				StartReportingMouseMoves ();
+			StartReportingMouseMoves ();
 
 			CurrentAttribute = MakeColor (Color.White, Color.Black);
 
@@ -944,8 +632,7 @@ namespace Terminal.Gui {
 		public override void ResizeScreen ()
 		{
 			Clip = new Rect (0, 0, Cols, Rows);
-			Console.Out.Write ("\x1b[3J");
-			Console.Out.Flush ();
+			Curses.refresh ();
 		}
 
 		public override void UpdateOffScreen ()
@@ -1065,25 +752,21 @@ namespace Terminal.Gui {
 
 		public override void Suspend ()
 		{
-			if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition))
-				StopReportingMouseMoves ();
+			StopReportingMouseMoves ();
 			Platform.Suspend ();
 			Curses.Window.Standard.redrawwin ();
 			Curses.refresh ();
-			if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition))
-				StartReportingMouseMoves ();
+			StartReportingMouseMoves ();
 		}
 
 		public override void StartReportingMouseMoves ()
 		{
-			Console.Out.Write ("\x1b[?1003h");
-			Console.Out.Flush ();
+			Console.Out.Write (EscSeqUtils.EnableMouseEvents);
 		}
 
 		public override void StopReportingMouseMoves ()
 		{
-			Console.Out.Write ("\x1b[?1003l");
-			Console.Out.Flush ();
+			Console.Out.Write (EscSeqUtils.DisableMouseEvents);
 		}
 
 		//int lastMouseInterval;
@@ -1126,7 +809,6 @@ namespace Terminal.Gui {
 
 			if (visibility != CursorVisibility.Invisible) {
 				Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF);
-				Console.Out.Flush ();
 			}
 
 			currentCursorVisibility = visibility;
@@ -1191,8 +873,8 @@ namespace Terminal.Gui {
 			background = default;
 			int back = -1;
 			IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
-			      .OfType<ConsoleColor> ()
-			      .Select (s => (int)s);
+				.OfType<ConsoleColor> ()
+				.Select (s => (int)s);
 			if (values.Contains ((value >> 12) & 0xffff)) {
 				hasColor = true;
 				back = (value >> 12) & 0xffff;
@@ -1285,6 +967,7 @@ namespace Terminal.Gui {
 
 		bool CheckSupport ()
 		{
+#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception.
 			try {
 				var (exitCode, result) = ClipboardProcessRunner.Bash ("which xclip", waitForOutput: true);
 				if (exitCode == 0 && result.FileExists ()) {
@@ -1294,6 +977,7 @@ namespace Terminal.Gui {
 			} catch (Exception) {
 				// Permissions issue.
 			}
+#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception.
 			return false;
 		}
 

+ 3 - 27
Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs

@@ -1,30 +1,6 @@
 //
 // mainloop.cs: Simple managed mainloop implementation.
 //
-// Authors:
-//   Miguel de Icaza ([email protected])
-//
-// Copyright (C) 2011 Novell (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
 using System;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
@@ -52,7 +28,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Condition on which to wake up from file descriptor activity.  These match the Linux/BSD poll definitions.
+		///	Condition on which to wake up from file descriptor activity.  These match the Linux/BSD poll definitions.
 		/// </summary>
 		[Flags]
 		public enum Condition : short {
@@ -127,10 +103,10 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Removes an active watch from the mainloop.
+		///	Removes an active watch from the mainloop.
 		/// </summary>
 		/// <remarks>
-		///   The token parameter is the value returned from AddWatch
+		///	The token parameter is the value returned from AddWatch
 		/// </remarks>
 		public void RemoveWatch (object token)
 		{

+ 14 - 21
Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs

@@ -6,7 +6,7 @@
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
-//     http://www.apache.org/licenses/LICENSE-2.0
+//	 http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
@@ -21,8 +21,6 @@ using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Threading;
 
-
-
 namespace Unix.Terminal {
 	/// <summary>
 	/// Represents a dynamically loaded unmanaged library in a (partially) platform independent manner.
@@ -45,7 +43,7 @@ namespace Unix.Terminal {
 		static bool IsNetCore;
 
 		public static bool IsMacOSPlatform => IsMacOS;
-		
+
 		[DllImport ("libc")]
 		static extern int uname (IntPtr buf);
 
@@ -105,11 +103,11 @@ namespace Unix.Terminal {
 		//
 		public UnmanagedLibrary (string [] libraryPathAlternatives, bool isFullPath)
 		{
-			if (isFullPath){
+			if (isFullPath) {
 				this.libraryPath = FirstValidLibraryPath (libraryPathAlternatives);
 				this.handle = PlatformSpecificLoadLibrary (this.libraryPath);
 			} else {
-				foreach (var lib in libraryPathAlternatives){
+				foreach (var lib in libraryPathAlternatives) {
 					this.handle = PlatformSpecificLoadLibrary (lib);
 					if (this.handle != IntPtr.Zero)
 						break;
@@ -164,13 +162,13 @@ namespace Unix.Terminal {
 		}
 
 		public T GetNativeMethodDelegate<T> (string methodName)
-		    where T : class
+			where T : class
 		{
 			var ptr = LoadSymbol (methodName);
 			if (ptr == IntPtr.Zero) {
 				throw new MissingMethodException (string.Format ("The native method \"{0}\" does not exist", methodName));
 			}
-			return Marshal.GetDelegateForFunctionPointer<T>(ptr);  // non-generic version is obsolete
+			return Marshal.GetDelegateForFunctionPointer<T> (ptr);  // non-generic version is obsolete
 		}
 
 		/// <summary>
@@ -209,12 +207,11 @@ namespace Unix.Terminal {
 				}
 			}
 			throw new FileNotFoundException (
-			    String.Format ("Error loading native library. Not found in any of the possible locations: {0}",
+				String.Format ("Error loading native library. Not found in any of the possible locations: {0}",
 				string.Join (",", libraryPathAlternatives)));
 		}
 
-		static class Windows
-		{
+		static class Windows {
 			[DllImport ("kernel32.dll")]
 			internal static extern IntPtr LoadLibrary (string filename);
 
@@ -222,8 +219,7 @@ namespace Unix.Terminal {
 			internal static extern IntPtr GetProcAddress (IntPtr hModule, string procName);
 		}
 
-		static class Linux
-		{
+		static class Linux {
 			[DllImport ("libdl.so")]
 			internal static extern IntPtr dlopen (string filename, int flags);
 
@@ -231,8 +227,7 @@ namespace Unix.Terminal {
 			internal static extern IntPtr dlsym (IntPtr handle, string symbol);
 		}
 
-		static class MacOSX
-		{
+		static class MacOSX {
 			[DllImport ("libSystem.dylib")]
 			internal static extern IntPtr dlopen (string filename, int flags);
 
@@ -247,8 +242,7 @@ namespace Unix.Terminal {
 		/// dlopen and dlsym from the current process as on Linux
 		/// Mono sure is linked against these symbols.
 		/// </summary>
-		static class Mono
-		{
+		static class Mono {
 			[DllImport ("__Internal")]
 			internal static extern IntPtr dlopen (string filename, int flags);
 
@@ -261,13 +255,12 @@ namespace Unix.Terminal {
 		/// dlopen and dlsym from the "libcoreclr.so",
 		/// to avoid the dependency on libc-dev Linux.
 		/// </summary>
-		static class CoreCLR
-		{
+		static class CoreCLR {
 #if NET6_0
 			// Custom resolver to support true single-file apps
 			// (those which run directly from bundle; in-memory).
-			//     -1 on Unix means self-referencing binary (libcoreclr.so)
-			//     0 means fallback to CoreCLR's internal resolution
+			//	 -1 on Unix means self-referencing binary (libcoreclr.so)
+			//	 0 means fallback to CoreCLR's internal resolution
 			// Note: meaning of -1 stay the same even for non-single-file form factors.
 			static CoreCLR() =>  NativeLibrary.SetDllImportResolver(typeof(CoreCLR).Assembly,
 				(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) =>

+ 0 - 5
Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs

@@ -145,11 +145,6 @@ namespace Unix.Terminal {
 			if (l == 1 || l != lines || c != cols) {
 				lines = l;
 				cols = c;
-				//if (l <= 0 || c <= 0) {
-				//	Console.Out.Write ($"\x1b[8;50;{c}t");
-				//	Console.Out.Flush ();
-				//	return false;
-				//}
 				return true;
 			}
 			return false;

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

@@ -53,7 +53,6 @@ namespace Unix.Terminal {
 		public const int COLOR_WHITE = unchecked((int)0x7);
 		public const int COLOR_GRAY = unchecked((int)0x8);
 		public const int KEY_CODE_YES = unchecked((int)0x100);
-		public const int KEY_CODE_SEQ = unchecked((int)0x5b);
 		public const int ERR = unchecked((int)0xffffffff);
 		public const int TIOCGWINSZ  = unchecked((int)0x5413);
 		public const int TIOCGWINSZ_MAC  = unchecked((int)0x40087468);
@@ -69,7 +68,7 @@ namespace Unix.Terminal {
 			Button2Released = unchecked((int)0x20),
 			Button2Clicked = unchecked((int)0x80),
 			Button2DoubleClicked = unchecked((int)0x100),
-			Button2TrippleClicked = unchecked((int)0x200),
+			Button2TripleClicked = unchecked((int)0x200),
 			Button3Pressed = unchecked((int)0x800),
 			Button3Released = unchecked((int)0x400),
 			Button3Clicked = unchecked((int)0x1000),
@@ -106,6 +105,7 @@ namespace Unix.Terminal {
 		public const int KeyPPage = unchecked((int)0x153);
 		public const int KeyHome = unchecked((int)0x106);
 		public const int KeyMouse = unchecked((int)0x199);
+		public const int KeyCSI = unchecked((int)0x5b);
 		public const int KeyEnd = unchecked((int)0x168);
 		public const int KeyDeleteChar = unchecked((int)0x14a);
 		public const int KeyInsertChar = unchecked((int)0x14b);

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 284 - 289
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs


+ 9 - 12
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -1,9 +1,6 @@
 //
 // FakeDriver.cs: A fake ConsoleDriver for unit tests. 
 //
-// Authors:
-//   Charlie Kindel (github.com/tig)
-//
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -48,7 +45,7 @@ namespace Terminal.Gui {
 		// Only handling left here because not all terminals has a horizontal scroll bar.
 		public override int Left => 0;
 		public override int Top => 0;
-		public override bool HeightAsBuffer { get; set; }
+		public override bool EnableConsoleScrolling { get; set; }
 		private IClipboard clipboard = null;
 		public override IClipboard Clipboard => clipboard;
 
@@ -245,8 +242,8 @@ namespace Terminal.Gui {
 		{
 			redrawColor = color;
 			IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
-			      .OfType<ConsoleColor> ()
-			      .Select (s => (int)s);
+				.OfType<ConsoleColor> ()
+				.Select (s => (int)s);
 			if (values.Contains (color & 0xffff)) {
 				FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff);
 			}
@@ -535,7 +532,7 @@ namespace Terminal.Gui {
 			FakeConsole.SetBufferSize (width, height);
 			cols = width;
 			rows = height;
-			if (!HeightAsBuffer) {
+			if (!EnableConsoleScrolling) {
 				SetWindowSize (width, height);
 			}
 			ProcessResize ();
@@ -544,7 +541,7 @@ namespace Terminal.Gui {
 		public void SetWindowSize (int width, int height)
 		{
 			FakeConsole.SetWindowSize (width, height);
-			if (!HeightAsBuffer) {
+			if (!EnableConsoleScrolling) {
 				if (width != cols || height != rows) {
 					SetBufferSize (width, height);
 					cols = width;
@@ -556,7 +553,7 @@ namespace Terminal.Gui {
 
 		public void SetWindowPosition (int left, int top)
 		{
-			if (HeightAsBuffer) {
+			if (EnableConsoleScrolling) {
 				this.left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0);
 				this.top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0);
 			} else if (this.left > 0 || this.top > 0) {
@@ -575,7 +572,7 @@ namespace Terminal.Gui {
 
 		public override void ResizeScreen ()
 		{
-			if (!HeightAsBuffer) {
+			if (!EnableConsoleScrolling) {
 				if (FakeConsole.WindowHeight > 0) {
 					// Can raise an exception while is still resizing.
 					try {
@@ -629,8 +626,8 @@ namespace Terminal.Gui {
 			foreground = default;
 			background = default;
 			IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
-			      .OfType<ConsoleColor> ()
-			      .Select (s => (int)s);
+				.OfType<ConsoleColor> ()
+				.Select (s => (int)s);
 			if (values.Contains (value & 0xffff)) {
 				hasColor = true;
 				background = (Color)(ConsoleColor)(value & 0xffff);

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 258 - 828
Terminal.Gui/ConsoleDrivers/NetDriver.cs


+ 51 - 36
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -1,30 +1,6 @@
 //
 // WindowsDriver.cs: Windows specific driver
 //
-// Authors:
-//   Miguel de Icaza ([email protected])
-//   Nick Van Dyck ([email protected])
-//
-// Copyright (c) 2018
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
 using NStack;
 using System;
 using System.Collections.Generic;
@@ -646,7 +622,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-#if false      // Not needed on the constructor. Perhaps could be used on resizing. To study.
+#if false      // Not needed on the constructor. Perhaps could be used on resizing. To study.                                                                                     
 		[DllImport ("kernel32.dll", ExactSpelling = true)]
 		static extern IntPtr GetConsoleWindow ();
 
@@ -739,7 +715,7 @@ namespace Terminal.Gui {
 		public override int Rows => rows;
 		public override int Left => left;
 		public override int Top => top;
-		public override bool HeightAsBuffer { get; set; }
+		public override bool EnableConsoleScrolling { get; set; }
 		public override IClipboard Clipboard => clipboard;
 		public override int [,,] Contents => contents;
 
@@ -774,7 +750,7 @@ namespace Terminal.Gui {
 
 		private void ChangeWin (Size e)
 		{
-			if (!HeightAsBuffer) {
+			if (!EnableConsoleScrolling) {
 				var w = e.Width;
 				if (w == cols - 3 && e.Height < rows) {
 					w += 3;
@@ -915,8 +891,12 @@ namespace Terminal.Gui {
 				left = pos.X;
 				top = pos.Y;
 				cols = inputEvent.WindowBufferSizeEvent.size.X;
-				rows = inputEvent.WindowBufferSizeEvent.size.Y;
-				//System.Diagnostics.Debug.WriteLine ($"{HeightAsBuffer},{cols},{rows}");
+				if (EnableConsoleScrolling) {
+					rows = Math.Max (inputEvent.WindowBufferSizeEvent.size.Y, rows);
+				} else {
+					rows = inputEvent.WindowBufferSizeEvent.size.Y;
+				}
+				//System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}");
 				ResizeScreen ();
 				UpdateOffScreen ();
 				TerminalResized?.Invoke ();
@@ -1459,6 +1439,19 @@ namespace Terminal.Gui {
 			TerminalResized = terminalResized;
 
 			try {
+				// Needed for Windows Terminal
+				// ESC [ ? 1047 h  Activate xterm alternative buffer (no backscroll)
+				// ESC [ ? 1047 l  Restore xterm working buffer (with backscroll)
+				// ESC [ ? 1048 h  Save cursor position
+				// ESC [ ? 1048 l  Restore cursor position
+				// ESC [ ? 1049 h  Save cursor position and activate xterm alternative buffer (no backscroll)
+				// ESC [ ? 1049 l  Restore cursor position and restore xterm working buffer (with backscroll)
+				// Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not 
+				// wipe out the backscroll buffer when the application exits.
+				Console.Out.Write ("\x1b[?1047h");
+
+				// Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526
+
 				var winSize = WinConsole.GetConsoleOutputWindow (out Point pos);
 				cols = winSize.Width;
 				rows = winSize.Height;
@@ -1467,6 +1460,9 @@ namespace Terminal.Gui {
 				CurrentAttribute = MakeColor (Color.White, Color.Black);
 				InitalizeColorSchemes ();
 
+				CurrentAttribute = MakeColor (Color.White, Color.Black);
+				InitalizeColorSchemes ();
+
 				ResizeScreen ();
 				UpdateOffScreen ();
 			} catch (Win32Exception e) {
@@ -1485,8 +1481,15 @@ namespace Terminal.Gui {
 				Right = (short)Cols
 			};
 			WinConsole.ForceRefreshCursorVisibility ();
-			Console.Out.Write ("\x1b[3J");
-			Console.Out.Flush ();
+			if (!EnableConsoleScrolling) {
+				// ANSI ESC "[xJ" Clears part of the screen.
+				// If n is 0 (or missing), clear from cursor to end of screen.
+				// If n is 1, clear from cursor to beginning of the screen.
+				// If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS).
+				// If n is 3, clear entire screen and delete all lines saved in the scrollback buffer
+				// DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer
+				Console.Out.Write ("\x1b[3J");
+			}
 		}
 
 		public override void UpdateOffScreen ()
@@ -1653,7 +1656,7 @@ namespace Terminal.Gui {
 			if (damageRegion.Left == -1)
 				return;
 
-			if (!HeightAsBuffer) {
+			if (!EnableConsoleScrolling) {
 				var windowSize = WinConsole.GetConsoleBufferWindow (out _);
 				if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows))
 					return;
@@ -1700,6 +1703,18 @@ namespace Terminal.Gui {
 		{
 			WinConsole.Cleanup ();
 			WinConsole = null;
+
+			// Needed for Windows Terminal
+			// Clear the alternative screen buffer from the cursor to the
+			// end of the screen.
+			// Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE
+			// backbuffer! So we need to use [0J instead.
+			Console.Out.Write ("\x1b[0J");
+
+			// Disable alternative screen buffer.
+			Console.Out.Write ("\x1b[?1047l");
+
+			// Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526
 		}
 
 		/// <inheritdoc/>
@@ -1780,8 +1795,8 @@ namespace Terminal.Gui {
 			foreground = default;
 			background = default;
 			IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
-			      .OfType<ConsoleColor> ()
-			      .Select (s => (int)s);
+				.OfType<ConsoleColor> ()
+				.Select (s => (int)s);
 			if (values.Contains ((value >> 4) & 0xffff)) {
 				hasColor = true;
 				background = (Color)(ConsoleColor)((value >> 4) & 0xffff);
@@ -1897,9 +1912,9 @@ namespace Terminal.Gui {
 		{
 			while (true) {
 				Thread.Sleep (100);
-				if (!consoleDriver.HeightAsBuffer) {
+				if (!consoleDriver.EnableConsoleScrolling) {
 					windowSize = winConsole.GetConsoleBufferWindow (out _);
-					//System.Diagnostics.Debug.WriteLine ($"{consoleDriver.HeightAsBuffer},{windowSize.Width},{windowSize.Height}");
+					//System.Diagnostics.Debug.WriteLine ($"{consoleDriver.EnableConsoleScrolling},{windowSize.Width},{windowSize.Height}");
 					if (windowSize != Size.Empty && windowSize.Width != consoleDriver.Cols
 						|| windowSize.Height != consoleDriver.Rows) {
 						return;

+ 30 - 17
Terminal.Gui/Core/Application.cs

@@ -114,27 +114,39 @@ namespace Terminal.Gui {
 		/// </summary>
 		public static View WantContinuousButtonPressedView { get; private set; }
 
-		private static bool? _heightAsBuffer;
+		private static bool? _enableConsoleScrolling;
 
 		/// <summary>
-		/// The current <see cref="ConsoleDriver.HeightAsBuffer"/> used in the terminal.
+		/// The current <see cref="ConsoleDriver.EnableConsoleScrolling"/> used in the terminal.
 		/// </summary>
-		/// 
-		[SerializableConfigurationProperty (Scope = typeof(SettingsScope))]
-		public static bool HeightAsBuffer {
+		/// <remarks>
+		/// <para>
+		/// If <see langword="false"/> (the default) the height of the Terminal.Gui application (<see cref="ConsoleDriver.Rows"/>) 
+		/// tracks to the height of the visible console view when the console is resized. In this case 
+		/// scrolling in the console will be disabled and all <see cref="ConsoleDriver.Rows"/> will remain visible.
+		/// </para>
+		/// <para>
+		/// If <see langword="true"/> then height of the Terminal.Gui application <see cref="ConsoleDriver.Rows"/> only tracks 
+		/// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). 
+		/// In this case console scrolling is enabled and the contents (<see cref="ConsoleDriver.Rows"/> high) will scroll
+		/// as the console scrolls. 
+		/// </para>
+		/// This API was previously named 'HeightAsBuffer` but was renamed to make its purpose more clear.
+		/// </remarks>
+		[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+		public static bool EnableConsoleScrolling {
 			get {
 				if (Driver == null) {
-					return _heightAsBuffer.HasValue && _heightAsBuffer.Value;
+					return _enableConsoleScrolling.HasValue && _enableConsoleScrolling.Value;
 				}
-				return Driver.HeightAsBuffer;
+				return Driver.EnableConsoleScrolling;
 			}
 			set {
-				_heightAsBuffer = value;
+				_enableConsoleScrolling = value;
 				if (Driver == null) {
 					return;
 				}
-
-				Driver.HeightAsBuffer = _heightAsBuffer.Value;
+				Driver.EnableConsoleScrolling = value;
 			}
 		}
 
@@ -143,7 +155,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.
 		/// </summary>
-		[SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter(typeof(KeyJsonConverter))]
+		[SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
 		public static Key AlternateForwardKey {
 			get => alternateForwardKey;
 			set {
@@ -157,7 +169,7 @@ namespace Terminal.Gui {
 
 		static void OnAlternateForwardKeyChanged (Key oldKey)
 		{
-			foreach (var top in toplevels.ToArray()) {
+			foreach (var top in toplevels.ToArray ()) {
 				top.OnAlternateForwardKeyChanged (oldKey);
 			}
 		}
@@ -167,7 +179,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.
 		/// </summary>
-		[SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
+		[SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
 		public static Key AlternateBackwardKey {
 			get => alternateBackwardKey;
 			set {
@@ -181,7 +193,7 @@ namespace Terminal.Gui {
 
 		static void OnAlternateBackwardKeyChanged (Key oldKey)
 		{
-			foreach (var top in toplevels.ToArray()) {
+			foreach (var top in toplevels.ToArray ()) {
 				top.OnAlternateBackwardKeyChanged (oldKey);
 			}
 		}
@@ -191,7 +203,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Gets or sets the key to quit the application.
 		/// </summary>
-		[SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
+		[SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
 		public static Key QuitKey {
 			get => quitKey;
 			set {
@@ -213,7 +225,7 @@ namespace Terminal.Gui {
 		static void OnQuitKeyChanged (Key oldKey)
 		{
 			// Duplicate the list so if it changes during enumeration we're safe
-			foreach (var top in toplevels.ToArray()) {
+			foreach (var top in toplevels.ToArray ()) {
 				top.OnQuitKeyChanged (oldKey);
 			}
 		}
@@ -445,7 +457,7 @@ namespace Terminal.Gui {
 			MainLoop = new MainLoop (mainLoopDriver);
 
 			try {
-				Driver.HeightAsBuffer = HeightAsBuffer;
+				Driver.EnableConsoleScrolling = EnableConsoleScrolling;
 				Driver.Init (TerminalResized);
 			} catch (InvalidOperationException ex) {
 				// This is a case where the driver is unable to initialize the console.
@@ -1121,6 +1133,7 @@ namespace Terminal.Gui {
 			NotifyStopRunState = null;
 			_initialized = false;
 			mouseGrabView = null;
+			_enableConsoleScrolling = false;
 
 			// Reset synchronization context to allow the user to run async/await,
 			// as the main loop has been ended, the synchronization context from 

+ 2 - 1
Terminal.Gui/Core/Autocomplete/Autocomplete.cs

@@ -106,7 +106,7 @@ namespace Terminal.Gui {
 			}
 
 			if (!Visible && popup != null) {
-				top.Remove (popup);
+				top?.Remove (popup);
 				popup.Dispose ();
 				popup = null;
 			}
@@ -323,6 +323,7 @@ namespace Terminal.Gui {
 		{
 			if (IsWordChar ((char)kb.Key)) {
 				Visible = true;
+				ManipulatePopup ();
 				closed = false;
 				return false;
 			}

+ 96 - 178
Terminal.Gui/Core/Border.cs

@@ -513,6 +513,7 @@ namespace Terminal.Gui {
 		private Point effect3DOffset = new Point (1, 1);
 		private Attribute? effect3DBrush;
 		private ustring title = ustring.Empty;
+		//private View child;
 
 		/// <summary>
 		/// Specifies the <see cref="Gui.BorderStyle"/> for a view.
@@ -628,7 +629,47 @@ namespace Terminal.Gui {
 		///// Gets or sets the single child element of a <see cref="View"/>.
 		///// </summary>
 		//[JsonIgnore]
-		//public View Child { get; set; }
+		//public View Child {
+		//	get => child;
+		//	set {
+		//		child = value;
+		//		if (child != null && Parent != null) {
+		//			Parent.Initialized += Parent_Initialized;
+		//			Parent.Removed += Parent_Removed;
+		//		}
+		//	}
+		//}
+
+		//private void Parent_Removed (View obj)
+		//{
+		//	BorderBrush = default;
+		//	Background = default;
+		//	child.Removed -= Parent_Removed;
+		//}
+
+		//private void Parent_Initialized (object s, EventArgs e)
+		//{
+		//	SetMarginFrameTitleBrush ();
+		//	child.Initialized -= Parent_Initialized;
+		//}
+
+		//private void SetMarginFrameTitleBrush ()
+		//{
+		//	if (child != null) {
+		//		var view = Parent?.Border != null ? Parent : child;
+		//		if (view.ColorScheme != null) {
+		//			if (borderBrush == default) {
+		//				BorderBrush = view.GetNormalColor ().Foreground;
+		//			}
+		//			if (background == default) {
+		//				Background = view.GetNormalColor ().Background;
+		//			}
+		//			return;
+		//		}
+		//	}
+		//	BorderBrush = default;
+		//	Background = default;
+		//}
 
 		///// <summary>
 		///// Gets the parent <see cref="Child"/> parent if any.
@@ -791,7 +832,7 @@ namespace Terminal.Gui {
 		//		Child.Clear (borderRect);
 		//	}
 
-		//	driver.SetAttribute (savedAttribute);
+		//	driver.SetAttribute (new Attribute (BorderBrush, Background));
 
 		//	// Draw margin frame
 		//	if (DrawMarginFrame) {
@@ -815,6 +856,7 @@ namespace Terminal.Gui {
 		//			driver.DrawWindowFrame (borderRect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill: true, this);
 		//		}
 		//	}
+		//	driver.SetAttribute (savedAttribute);
 		//}
 
 		//private void DrawChildBorder (Rect frame, bool fill = true)
@@ -829,35 +871,47 @@ namespace Terminal.Gui {
 
 		//	driver.SetAttribute (new Attribute (BorderBrush));
 
-		//	// Draw the upper BorderThickness
+			// Draw the upper BorderThickness
 		//	for (int r = frame.Y - drawMarginFrame - sumThickness.Top;
 		//		r < frame.Y - drawMarginFrame - padding.Top; r++) {
+
+		//		if (r < 0) {
+		//			continue;
+		//		}
 		//		for (int c = frame.X - drawMarginFrame - sumThickness.Left;
 		//			c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
 
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
+		////			AddRuneAt (driver, c, r, ' ');
+		////		}
+		////	}
 
 		//	// Draw the left BorderThickness
 		//	for (int r = frame.Y - drawMarginFrame - padding.Top;
 		//		r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) {
+
+		//		if (r < 0) {
+		//			continue;
+		//		}
 		//		for (int c = frame.X - drawMarginFrame - sumThickness.Left;
 		//			c < frame.X - drawMarginFrame - padding.Left; c++) {
 
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
+		////			AddRuneAt (driver, c, r, ' ');
+		////		}
+		////	}
 
 		//	// Draw the right BorderThickness
 		//	for (int r = frame.Y - drawMarginFrame - padding.Top;
 		//		r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) {
+
+		//		if (r < 0) {
+		//			continue;
+		//		}
 		//		for (int c = frame.Right + drawMarginFrame + padding.Right;
 		//			c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
 
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
+		////			AddRuneAt (driver, c, r, ' ');
+		////		}
+		////	}
 
 		//	// Draw the lower BorderThickness
 		//	for (int r = frame.Bottom + drawMarginFrame + padding.Bottom;
@@ -865,21 +919,25 @@ namespace Terminal.Gui {
 		//		for (int c = frame.X - drawMarginFrame - sumThickness.Left;
 		//			c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
 
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
+		////			AddRuneAt (driver, c, r, ' ');
+		////		}
+		////	}
 
-		//	driver.SetAttribute (new Attribute (Background));
+		////	driver.SetAttribute (new Attribute (Background));
 
 		//	// Draw the upper Padding
 		//	for (int r = frame.Y - drawMarginFrame - padding.Top;
 		//		r < frame.Y - drawMarginFrame; r++) {
+
+		//		if (r < 0) {
+		//			continue;
+		//		}
 		//		for (int c = frame.X - drawMarginFrame - padding.Left;
 		//			c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
 
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
+		////			AddRuneAt (driver, c, r, ' ');
+		////		}
+		////	}
 
 		//	// Draw the left Padding
 		//	for (int r = frame.Y - drawMarginFrame;
@@ -887,9 +945,9 @@ namespace Terminal.Gui {
 		//		for (int c = frame.X - drawMarginFrame - padding.Left;
 		//			c < frame.X - drawMarginFrame; c++) {
 
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
+		////			AddRuneAt (driver, c, r, ' ');
+		////		}
+		////	}
 
 		//	// Draw the right Padding
 		//	for (int r = frame.Y - drawMarginFrame;
@@ -897,9 +955,9 @@ namespace Terminal.Gui {
 		//		for (int c = frame.Right + drawMarginFrame;
 		//			c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
 
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
+		////			AddRuneAt (driver, c, r, ' ');
+		////		}
+		////	}
 
 		//	// Draw the lower Padding
 		//	for (int r = frame.Bottom + drawMarginFrame;
@@ -907,11 +965,11 @@ namespace Terminal.Gui {
 		//		for (int c = frame.X - drawMarginFrame - padding.Left;
 		//			c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
 
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
+		////			AddRuneAt (driver, c, r, ' ');
+		////		}
+		////	}
 
-		//	driver.SetAttribute (savedAttribute);
+		//	driver.SetAttribute (new Attribute (BorderBrush, Background));
 
 		//	// Draw the MarginFrame
 		//	if (DrawMarginFrame) {
@@ -1003,148 +1061,7 @@ namespace Terminal.Gui {
 
 		//	driver.SetAttribute (new Attribute (BorderBrush));
 
-		//	// Draw the upper BorderThickness
-		//	for (int r = frame.Y;
-		//		r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) {
-		//		for (int c = frame.X;
-		//			c < Math.Min (frame.Right, driver.Cols); c++) {
-
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
-
-		//	// Draw the left BorderThickness
-		//	for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
-		//		r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
-		//		for (int c = frame.X;
-		//			c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) {
-
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
-
-		//	// Draw the right BorderThickness
-		//	for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
-		//		r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
-		//		for (int c = Math.Max (frame.Right - borderThickness.Right, frame.X);
-		//			c < Math.Min (frame.Right, driver.Cols); c++) {
-
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
-
-		//	// Draw the lower BorderThickness
-		//	for (int r = Math.Max (frame.Bottom - borderThickness.Bottom, frame.Y);
-		//		r < Math.Min (frame.Bottom, driver.Rows); r++) {
-		//		for (int c = frame.X;
-		//			c < Math.Min (frame.Right, driver.Cols); c++) {
-
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
-
-		//	driver.SetAttribute (new Attribute (Background));
-
-		//	// Draw the upper Padding
-		//	for (int r = frame.Y + borderThickness.Top;
-		//		r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) {
-		//		for (int c = frame.X + borderThickness.Left;
-		//			c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) {
-
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
-
-		//	// Draw the left Padding
-		//	for (int r = frame.Y + sumThickness.Top;
-		//		r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
-		//		for (int c = frame.X + borderThickness.Left;
-		//			c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) {
-
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
-
-		//	// Draw the right Padding
-		//	for (int r = frame.Y + sumThickness.Top;
-		//		r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
-		//		for (int c = Math.Max (frame.Right - sumThickness.Right, frame.X + sumThickness.Left);
-		//			c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) {
-
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
-
-		//	// Draw the lower Padding
-		//	for (int r = Math.Max (frame.Bottom - sumThickness.Bottom, frame.Y + borderThickness.Top);
-		//		r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
-		//		for (int c = frame.X + borderThickness.Left;
-		//			c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) {
-
-		//			AddRuneAt (driver, c, r, ' ');
-		//		}
-		//	}
-
-		//	driver.SetAttribute (savedAttribute);
-
-		//	// Draw the MarginFrame
-		//	if (DrawMarginFrame) {
-		//		var rect = new Rect () {
-		//			X = frame.X + sumThickness.Left,
-		//			Y = frame.Y + sumThickness.Top,
-		//			Width = Math.Max (frame.Width - sumThickness.Right - sumThickness.Left, 0),
-		//			Height = Math.Max (frame.Height - sumThickness.Bottom - sumThickness.Top, 0)
-		//		};
-		//		if (rect.Width > 0 && rect.Height > 0) {
-		//			driver.DrawWindowFrame (rect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill, this);
-		//			DrawTitle (Parent);
-		//		}
-		//	}
-
-		//	if (Effect3D) {
-		//		driver.SetAttribute ((Attribute)Effect3DBrush);
-
-		//		// Draw the upper Effect3D
-		//		for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0);
-		//			r < frame.Y; r++) {
-		//			for (int c = Math.Max (frame.X + effect3DOffset.X, 0);
-		//				c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
-
-		//				AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]);
-		//			}
-		//		}
-
-		//		// Draw the left Effect3D
-		//		for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0);
-		//			r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
-		//			for (int c = Math.Max (frame.X + effect3DOffset.X, 0);
-		//				c < frame.X; c++) {
-
-		//				AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]);
-		//			}
-		//		}
-
-		//		// Draw the right Effect3D
-		//		for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0);
-		//			r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
-		//			for (int c = frame.Right;
-		//				c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
-
-		//				AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]);
-		//			}
-		//		}
-
-		//		// Draw the lower Effect3D
-		//		for (int r = frame.Bottom;
-		//			r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
-		//			for (int c = Math.Max (frame.X + effect3DOffset.X, 0);
-		//				c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
-
-		//				AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]);
-		//			}
-		//		}
-		//	}
-		//	driver.SetAttribute (savedAttribute);
+			// Draw the upper BorderThickness
 		//}
 
 		//private void AddRuneAt (ConsoleDriver driver, int col, int row, Rune ch)
@@ -1167,9 +1084,10 @@ namespace Terminal.Gui {
 		//{
 		//	var driver = Application.Driver;
 		//	if (DrawMarginFrame) {
-		//		driver.SetAttribute (Child.GetNormalColor ());
-		//		if (Child.HasFocus)
-		//			driver.SetAttribute (Child.ColorScheme.HotNormal);
+		//		driver.SetAttribute (new Attribute (BorderBrush, Background));
+		//		if (view.HasFocus) {
+		//			driver.SetAttribute (new Attribute (Child.ColorScheme.HotNormal.Foreground, Background));
+		//		}
 		//		var padding = view.Border.GetSumThickness ();
 		//		Rect scrRect;
 		//		if (view == Child) {
@@ -1178,7 +1096,7 @@ namespace Terminal.Gui {
 		//			driver.DrawWindowTitle (scrRect, Title, 0, 0, 0, 0);
 		//		} else {
 		//			scrRect = view.ViewToScreen (new Rect (0, 0, view.Frame.Width, view.Frame.Height));
-		//			driver.DrawWindowTitle (scrRect, Title,
+		//			driver.DrawWindowTitle (scrRect, Parent.Border.Title,
 		//				padding.Left, padding.Top, padding.Right, padding.Bottom);
 		//		}
 		//	}
@@ -1194,9 +1112,9 @@ namespace Terminal.Gui {
 		//{
 		//	var driver = Application.Driver;
 		//	if (DrawMarginFrame) {
-		//		driver.SetAttribute (view.GetNormalColor ());
+		//		driver.SetAttribute (new Attribute (BorderBrush, Background));
 		//		if (view.HasFocus) {
-		//			driver.SetAttribute (view.ColorScheme.HotNormal);
+		//			driver.SetAttribute (new Attribute (view.ColorScheme.HotNormal.Foreground, Background));
 		//		}
 		//		var padding = Parent.Border.GetSumThickness ();
 		//		var scrRect = Parent.ViewToScreen (new Rect (0, 0, rect.Width, rect.Height));
@@ -1214,4 +1132,4 @@ namespace Terminal.Gui {
 			BorderChanged?.Invoke (this);
 		}
 	}
-}
+}

+ 19 - 72
Terminal.Gui/Core/ConsoleDriver.cs

@@ -89,7 +89,7 @@ namespace Terminal.Gui {
 	}
 
 	/// <summary>
-	/// 
+	/// Indicates the RGB for true colors.
 	/// </summary>
 	public class TrueColor {
 		/// <summary>
@@ -119,7 +119,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// 
+		/// Converts true color to console color.
 		/// </summary>
 		/// <returns></returns>
 		public Color ToConsoleColor ()
@@ -504,7 +504,7 @@ namespace Terminal.Gui {
 			public bool Equals (string x, string y)
 			{
 				if (x != null && y != null) {
-					return x.ToLowerInvariant () == y.ToLowerInvariant ();
+					return string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase);
 				}
 				return false;
 			}
@@ -659,72 +659,7 @@ namespace Terminal.Gui {
 		/// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Block"/></remarks>
 		BoxFix = 0x02020164,
 	}
-
-	///// <summary>
-	///// Special characters that can be drawn with 
-	///// </summary>
-	//public enum SpecialChar {
-	//	/// <summary>
-	//	/// Horizontal line character.
-	//	/// </summary>
-	//	HLine,
-
-	//	/// <summary>
-	//	/// Vertical line character.
-	//	/// </summary>
-	//	VLine,
-
-	//	/// <summary>
-	//	/// Stipple pattern
-	//	/// </summary>
-	//	Stipple,
-
-	//	/// <summary>
-	//	/// Diamond character
-	//	/// </summary>
-	//	Diamond,
-
-	//	/// <summary>
-	//	/// Upper left corner
-	//	/// </summary>
-	//	ULCorner,
-
-	//	/// <summary>
-	//	/// Lower left corner
-	//	/// </summary>
-	//	LLCorner,
-
-	//	/// <summary>
-	//	/// Upper right corner
-	//	/// </summary>
-	//	URCorner,
-
-	//	/// <summary>
-	//	/// Lower right corner
-	//	/// </summary>
-	//	LRCorner,
-
-	//	/// <summary>
-	//	/// Left tee
-	//	/// </summary>
-	//	LeftTee,
-
-	//	/// <summary>
-	//	/// Right tee
-	//	/// </summary>
-	//	RightTee,
-
-	//	/// <summary>
-	//	/// Top tee
-	//	/// </summary>
-	//	TopTee,
-
-	//	/// <summary>
-	//	/// The bottom tee.
-	//	/// </summary>
-	//	BottomTee,
-	//}
-
+	
 	/// <summary>
 	/// ConsoleDriver is an abstract class that defines the requirements for a console driver.  
 	/// There are currently three implementations: <see cref="CursesDriver"/> (for Unix and Mac), <see cref="WindowsDriver"/>, and <see cref="NetDriver"/> that uses the .NET Console API.
@@ -761,10 +696,22 @@ namespace Terminal.Gui {
 		public abstract IClipboard Clipboard { get; }
 
 		/// <summary>
-		/// If false height is measured by the window height and thus no scrolling.
-		/// If true then height is measured by the buffer height, enabling scrolling.
+		/// <para>
+		/// If <see langword="false"/> (the default) the height of the Terminal.Gui application (<see cref="Rows"/>) 
+		/// tracks to the height of the visible console view when the console is resized. In this case 
+		/// scrolling in the console will be disabled and all <see cref="Rows"/> will remain visible.
+		/// </para>
+		/// <para>
+		/// If <see langword="true"/> then height of the Terminal.Gui application <see cref="Rows"/> only tracks 
+		/// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). 
+		/// In this case console scrolling is enabled and the contents (<see cref="Rows"/> high) will scroll
+		/// as the console scrolls. 
+		/// </para>
 		/// </summary>
-		public abstract bool HeightAsBuffer { get; set; }
+		/// <remarks>
+		/// NOTE: This functionaliy is currently broken on Windows Terminal.
+		/// </remarks>
+		public abstract bool EnableConsoleScrolling { get; set; }
 
 		/// <summary>
 		/// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag

+ 19 - 0
Terminal.Gui/Core/ConsoleKeyMapping.cs

@@ -334,6 +334,25 @@ namespace Terminal.Gui {
 			return (Key)consoleKey;
 		}
 
+		/// <summary>
+		/// Maps a <see cref="ConsoleKeyInfo"/> to a <see cref="Key"/>.
+		/// </summary>
+		/// <param name="keyInfo">The console key info.</param>
+		/// <param name="key">The key.</param>
+		/// <returns>The <see cref="Key"/> with <see cref="ConsoleModifiers"/> or the <paramref name="key"/></returns>
+		public static Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+		{
+			Key keyMod = new Key ();
+			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
+				keyMod = Key.ShiftMask;
+			if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0)
+				keyMod |= Key.CtrlMask;
+			if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0)
+				keyMod |= Key.AltMask;
+
+			return keyMod != Key.Null ? keyMod | key : key;
+		}
+
 		private static HashSet<ScanCodeMapping> scanCodes = new HashSet<ScanCodeMapping> {
 			new ScanCodeMapping (1,27,0,27),	// Escape
 			new ScanCodeMapping (1,27,ConsoleModifiers.Shift,27),

+ 109 - 0
Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs

@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Represents the state of an ANSI escape sequence request.
+	/// </summary>
+	/// <remarks>
+	/// This is needed because there are some escape sequence requests responses that are equal
+	/// with some normal escape sequences and thus, will be only considered the responses to the
+	/// requests that were registered with this object.
+	/// </remarks>
+	public class EscSeqReqStatus {
+		/// <summary>
+		/// Gets the terminating.
+		/// </summary>
+		public string Terminating { get; }
+		/// <summary>
+		/// Gets the number of requests.
+		/// </summary>
+		public int NumRequests { get; }
+		/// <summary>
+		/// Gets information about unfinished requests.
+		/// </summary>
+		public int NumOutstanding { get; set; }
+
+		/// <summary>
+		/// Creates a new state of escape sequence request.
+		/// </summary>
+		/// <param name="terminating">The terminating.</param>
+		/// <param name="numOfReq">The number of requests.</param>
+		public EscSeqReqStatus (string terminating, int numOfReq)
+		{
+			Terminating = terminating;
+			NumRequests = NumOutstanding = numOfReq;
+		}
+	}
+
+	/// <summary>
+	/// Manages a list of <see cref="EscSeqReqStatus"/>.
+	/// </summary>
+	public class EscSeqReqProc {
+		/// <summary>
+		/// Gets the <see cref="EscSeqReqStatus"/> list.
+		/// </summary>
+		public List<EscSeqReqStatus> EscSeqReqStats { get; } = new List<EscSeqReqStatus> ();
+
+		/// <summary>
+		/// Adds a new <see cref="EscSeqReqStatus"/> instance to the <see cref="EscSeqReqStats"/> list.
+		/// </summary>
+		/// <param name="terminating">The terminating.</param>
+		/// <param name="numOfReq">The number of requests.</param>
+		public void Add (string terminating, int numOfReq = 1)
+		{
+			lock (EscSeqReqStats) {
+				var found = EscSeqReqStats.Find (x => x.Terminating == terminating);
+				if (found == null) {
+					EscSeqReqStats.Add (new EscSeqReqStatus (terminating, numOfReq));
+				} else if (found != null && found.NumOutstanding < found.NumRequests) {
+					found.NumOutstanding = Math.Min (found.NumOutstanding + numOfReq, found.NumRequests);
+				}
+			}
+		}
+
+		/// <summary>
+		/// Removes a <see cref="EscSeqReqStatus"/> instance from the <see cref="EscSeqReqStats"/> list.
+		/// </summary>
+		/// <param name="terminating">The terminating string.</param>
+		public void Remove (string terminating)
+		{
+			lock (EscSeqReqStats) {
+				var found = EscSeqReqStats.Find (x => x.Terminating == terminating);
+				if (found == null) {
+					return;
+				}
+				if (found != null && found.NumOutstanding == 0) {
+					EscSeqReqStats.Remove (found);
+				} else if (found != null && found.NumOutstanding > 0) {
+					found.NumOutstanding--;
+					if (found.NumOutstanding == 0) {
+						EscSeqReqStats.Remove (found);
+					}
+				}
+			}
+		}
+
+		/// <summary>
+		/// Indicates if a <see cref="EscSeqReqStatus"/> with the <paramref name="terminating"/> exist
+		/// in the <see cref="EscSeqReqStats"/> list.
+		/// </summary>
+		/// <param name="terminating"></param>
+		/// <returns><see langword="true"/> if exist, <see langword="false"/> otherwise.</returns>
+		public bool Requested (string terminating)
+		{
+			lock (EscSeqReqStats) {
+				var found = EscSeqReqStats.Find (x => x.Terminating == terminating);
+				if (found == null) {
+					return false;
+				}
+				if (found != null && found.NumOutstanding > 0) {
+					return true;
+				} else {
+					EscSeqReqStats.Remove (found);
+				}
+				return false;
+			}
+		}
+	}
+}

+ 907 - 0
Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs

@@ -0,0 +1,907 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Management;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Provides a platform-independent API for managing ANSI escape sequence codes.
+	/// </summary>
+	public static class EscSeqUtils {
+		/// <summary>
+		/// Represents the escape key.
+		/// </summary>
+		public static readonly char KeyEsc = (char)Key.Esc;
+		/// <summary>
+		/// Represents the CSI (Control Sequence Introducer).
+		/// </summary>
+		public static readonly string KeyCSI = $"{KeyEsc}[";
+		/// <summary>
+		/// Represents the CSI for enable any mouse event tracking.
+		/// </summary>
+		public static readonly string CSI_EnableAnyEventMouse = KeyCSI + "?1003h";
+		/// <summary>
+		/// Represents the CSI for enable SGR (Select Graphic Rendition).
+		/// </summary>
+		public static readonly string CSI_EnableSgrExtModeMouse = KeyCSI + "?1006h";
+		/// <summary>
+		/// Represents the CSI for enable URXVT (Unicode Extended Virtual Terminal).
+		/// </summary>
+		public static readonly string CSI_EnableUrxvtExtModeMouse = KeyCSI + "?1015h";
+		/// <summary>
+		/// Represents the CSI for disable any mouse event tracking.
+		/// </summary>
+		public static readonly string CSI_DisableAnyEventMouse = KeyCSI + "?1003l";
+		/// <summary>
+		/// Represents the CSI for disable SGR (Select Graphic Rendition).
+		/// </summary>
+		public static readonly string CSI_DisableSgrExtModeMouse = KeyCSI + "?1006l";
+		/// <summary>
+		/// Represents the CSI for disable URXVT (Unicode Extended Virtual Terminal).
+		/// </summary>
+		public static readonly string CSI_DisableUrxvtExtModeMouse = KeyCSI + "?1015l";
+
+		/// <summary>
+		/// Control sequence for enable mouse events.
+		/// </summary>
+		public static string EnableMouseEvents { get; set; } =
+			CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse;
+		/// <summary>
+		/// Control sequence for disable mouse events.
+		/// </summary>
+		public static string DisableMouseEvents { get; set; } =
+			CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse;
+
+		/// <summary>
+		/// Ensures a console key is mapped to one that works correctly with ANSI escape sequences.
+		/// </summary>
+		/// <param name="consoleKeyInfo">The <see cref="ConsoleKeyInfo"/>.</param>
+		/// <returns>The <see cref="ConsoleKeyInfo"/> modified.</returns>
+		public static ConsoleKeyInfo GetConsoleInputKey (ConsoleKeyInfo consoleKeyInfo)
+		{
+			ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo;
+			ConsoleKey key;
+			var keyChar = consoleKeyInfo.KeyChar;
+			switch ((uint)keyChar) {
+			case 0:
+				if (consoleKeyInfo.Key == (ConsoleKey)64) {    // Ctrl+Space in Windows.
+					newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar,
+						(consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+						(consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+						(consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+				}
+				break;
+			case uint n when (n >= '\u0001' && n <= '\u001a'):
+				if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') {
+					key = ConsoleKey.Enter;
+					newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar,
+						key,
+						(consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+						(consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+						(consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+				} else if (consoleKeyInfo.Key == 0) {
+					key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1);
+					newConsoleKeyInfo = new ConsoleKeyInfo ((char)key,
+						key,
+						(consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+						(consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+						true);
+				}
+				break;
+			case 127:
+				newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace,
+					(consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+					(consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+					(consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+				break;
+			default:
+				newConsoleKeyInfo = consoleKeyInfo;
+				break;
+			}
+
+			return newConsoleKeyInfo;
+		}
+
+		/// <summary>
+		/// A helper to resize the <see cref="ConsoleKeyInfo"/> as needed.
+		/// </summary>
+		/// <param name="consoleKeyInfo">The <see cref="ConsoleKeyInfo"/>.</param>
+		/// <param name="cki">The <see cref="ConsoleKeyInfo"/> array to resize.</param>
+		/// <returns>The <see cref="ConsoleKeyInfo"/> resized.</returns>
+		public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, ConsoleKeyInfo [] cki)
+		{
+			Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1);
+			cki [cki.Length - 1] = consoleKeyInfo;
+			return cki;
+		}
+
+		/// <summary>
+		/// Decodes a escape sequence to been processed in the appropriate manner.
+		/// </summary>
+		/// <param name="escSeqReqProc">The <see cref="EscSeqReqProc"/> which may contain a request.</param>
+		/// <param name="newConsoleKeyInfo">The <see cref="ConsoleKeyInfo"/> which may changes.</param>
+		/// <param name="key">The <see cref="ConsoleKey"/> which may changes.</param>
+		/// <param name="cki">The <see cref="ConsoleKeyInfo"/> array.</param>
+		/// <param name="mod">The <see cref="ConsoleModifiers"/> which may changes.</param>
+		/// <param name="c1Control">The control returned by the <see cref="GetC1ControlChar(char)"/> method.</param>
+		/// <param name="code">The code returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
+		/// <param name="values">The values returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
+		/// <param name="terminating">The terminating returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
+		/// <param name="isKeyMouse">Indicates if the escape sequence is a mouse key.</param>
+		/// <param name="buttonState">The <see cref="MouseFlags"/> button state.</param>
+		/// <param name="pos">The <see cref="MouseFlags"/> position.</param>
+		/// <param name="isReq">Indicates if the escape sequence is a response to a request.</param>
+		/// <param name="continuousButtonPressedHandler">The handler that will process the event.</param>
+		public static void DecodeEscSeq (EscSeqReqProc escSeqReqProc, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminating, out bool isKeyMouse, out List<MouseFlags> buttonState, out Point pos, out bool isReq, Action<MouseFlags, Point> continuousButtonPressedHandler)
+		{
+			char [] kChars = GetKeyCharArray (cki);
+			(c1Control, code, values, terminating) = GetEscapeResult (kChars);
+			isKeyMouse = false;
+			buttonState = new List<MouseFlags> () { 0 };
+			pos = default;
+			isReq = false;
+			switch (c1Control) {
+			case "ESC":
+				if (values == null && string.IsNullOrEmpty (terminating)) {
+					key = ConsoleKey.Escape;
+					newConsoleKeyInfo = new ConsoleKeyInfo (cki [0].KeyChar, key,
+						(mod & ConsoleModifiers.Shift) != 0,
+						(mod & ConsoleModifiers.Alt) != 0,
+						(mod & ConsoleModifiers.Control) != 0);
+				} else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) {
+					key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1);
+					newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar,
+						key,
+						false,
+						true,
+						true);
+				} else {
+					if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) {
+						key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0];
+					} else {
+						key = (ConsoleKey)cki [1].KeyChar;
+					}
+					newConsoleKeyInfo = new ConsoleKeyInfo ((char)key,
+						(ConsoleKey)Math.Min ((uint)key, 255),
+						false,
+						true,
+						false);
+				}
+				break;
+			case "SS3":
+				key = GetConsoleKey (terminating [0], values [0], ref mod);
+				newConsoleKeyInfo = new ConsoleKeyInfo ('\0',
+					key,
+					(mod & ConsoleModifiers.Shift) != 0,
+					(mod & ConsoleModifiers.Alt) != 0,
+					(mod & ConsoleModifiers.Control) != 0);
+				break;
+			case "CSI":
+				if (!string.IsNullOrEmpty (code) && code == "<") {
+					GetMouse (cki, out buttonState, out pos, continuousButtonPressedHandler);
+					isKeyMouse = true;
+					return;
+				} else if (escSeqReqProc != null && escSeqReqProc.Requested (terminating)) {
+					isReq = true;
+					escSeqReqProc.Remove (terminating);
+					return;
+				}
+				key = GetConsoleKey (terminating [0], values [0], ref mod);
+				if (key != 0 && values.Length > 1) {
+					mod |= GetConsoleModifiers (values [1]);
+				}
+				newConsoleKeyInfo = new ConsoleKeyInfo ('\0',
+					key,
+					(mod & ConsoleModifiers.Shift) != 0,
+					(mod & ConsoleModifiers.Alt) != 0,
+					(mod & ConsoleModifiers.Control) != 0);
+				break;
+			}
+		}
+
+		/// <summary>
+		/// Gets all the needed information about a escape sequence.
+		/// </summary>
+		/// <param name="kChar">The array with all chars.</param>
+		/// <returns>
+		/// The c1Control returned by <see cref="GetC1ControlChar(char)"/>, code, values and terminating.
+		/// </returns>
+		public static (string c1Control, string code, string [] values, string terminating) GetEscapeResult (char [] kChar)
+		{
+			if (kChar == null || kChar.Length == 0) {
+				return (null, null, null, null);
+			}
+			if (kChar [0] != '\x1b') {
+				throw new InvalidOperationException ("Invalid escape character!");
+			}
+			if (kChar.Length == 1) {
+				return ("ESC", null, null, null);
+			}
+			if (kChar.Length == 2) {
+				return ("ESC", null, null, kChar [1].ToString ());
+			}
+			string c1Control = GetC1ControlChar (kChar [1]);
+			string code = null;
+			int nSep = kChar.Count (x => x == ';') + 1;
+			string [] values = new string [nSep];
+			int valueIdx = 0;
+			string terminating = "";
+			for (int i = 2; i < kChar.Length; i++) {
+				var c = kChar [i];
+				if (char.IsDigit (c)) {
+					values [valueIdx] += c.ToString ();
+				} else if (c == ';') {
+					valueIdx++;
+				} else if (valueIdx == nSep - 1 || i == kChar.Length - 1) {
+					terminating += c.ToString ();
+				} else {
+					code += c.ToString ();
+				}
+			}
+
+			return (c1Control, code, values, terminating);
+		}
+
+		/// <summary>
+		/// Gets the c1Control used in the called escape sequence.
+		/// </summary>
+		/// <param name="c">The char used.</param>
+		/// <returns>The c1Control.</returns>
+		public static string GetC1ControlChar (char c)
+		{
+			// These control characters are used in the vtXXX emulation.
+			switch (c) {
+			case 'D':
+				return "IND"; // Index
+			case 'E':
+				return "NEL"; // Next Line
+			case 'H':
+				return "HTS"; // Tab Set
+			case 'M':
+				return "RI"; // Reverse Index
+			case 'N':
+				return "SS2"; // Single Shift Select of G2 Character Set: affects next character only
+			case 'O':
+				return "SS3"; // Single Shift Select of G3 Character Set: affects next character only
+			case 'P':
+				return "DCS"; // Device Control String
+			case 'V':
+				return "SPA"; // Start of Guarded Area
+			case 'W':
+				return "EPA"; // End of Guarded Area
+			case 'X':
+				return "SOS"; // Start of String
+			case 'Z':
+				return "DECID"; // Return Terminal ID Obsolete form of CSI c (DA)
+			case '[':
+				return "CSI"; // Control Sequence Introducer
+			case '\\':
+				return "ST"; // String Terminator
+			case ']':
+				return "OSC"; // Operating System Command
+			case '^':
+				return "PM"; // Privacy Message
+			case '_':
+				return "APC"; // Application Program Command
+			default:
+				return ""; // Not supported
+			}
+		}
+
+		/// <summary>
+		/// Gets the <see cref="ConsoleModifiers"/> from the value.
+		/// </summary>
+		/// <param name="value">The value.</param>
+		/// <returns>The <see cref="ConsoleModifiers"/> or zero.</returns>
+		public static ConsoleModifiers GetConsoleModifiers (string value)
+		{
+			switch (value) {
+			case "2":
+				return ConsoleModifiers.Shift;
+			case "3":
+				return ConsoleModifiers.Alt;
+			case "4":
+				return ConsoleModifiers.Shift | ConsoleModifiers.Alt;
+			case "5":
+				return ConsoleModifiers.Control;
+			case "6":
+				return ConsoleModifiers.Shift | ConsoleModifiers.Control;
+			case "7":
+				return ConsoleModifiers.Alt | ConsoleModifiers.Control;
+			case "8":
+				return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control;
+			default:
+				return 0;
+			}
+		}
+
+		/// <summary>
+		/// Gets the <see cref="ConsoleKey"/> depending on terminating and value.
+		/// </summary>
+		/// <param name="terminating">The terminating.</param>
+		/// <param name="value">The value.</param>
+		/// <param name="mod">The <see cref="ConsoleModifiers"/> which may changes.</param>
+		/// <returns>The <see cref="ConsoleKey"/> and probably the <see cref="ConsoleModifiers"/>.</returns>
+		public static ConsoleKey GetConsoleKey (char terminating, string value, ref ConsoleModifiers mod)
+		{
+			ConsoleKey key;
+			switch (terminating) {
+			case 'A':
+				key = ConsoleKey.UpArrow;
+				break;
+			case 'B':
+				key = ConsoleKey.DownArrow;
+				break;
+			case 'C':
+				key = ConsoleKey.RightArrow;
+				break;
+			case 'D':
+				key = ConsoleKey.LeftArrow;
+				break;
+			case 'F':
+				key = ConsoleKey.End;
+				break;
+			case 'H':
+				key = ConsoleKey.Home;
+				break;
+			case 'P':
+				key = ConsoleKey.F1;
+				break;
+			case 'Q':
+				key = ConsoleKey.F2;
+				break;
+			case 'R':
+				key = ConsoleKey.F3;
+				break;
+			case 'S':
+				key = ConsoleKey.F4;
+				break;
+			case 'Z':
+				key = ConsoleKey.Tab;
+				mod |= ConsoleModifiers.Shift;
+				break;
+			case '~':
+				switch (value) {
+				case "2":
+					key = ConsoleKey.Insert;
+					break;
+				case "3":
+					key = ConsoleKey.Delete;
+					break;
+				case "5":
+					key = ConsoleKey.PageUp;
+					break;
+				case "6":
+					key = ConsoleKey.PageDown;
+					break;
+				case "15":
+					key = ConsoleKey.F5;
+					break;
+				case "17":
+					key = ConsoleKey.F6;
+					break;
+				case "18":
+					key = ConsoleKey.F7;
+					break;
+				case "19":
+					key = ConsoleKey.F8;
+					break;
+				case "20":
+					key = ConsoleKey.F9;
+					break;
+				case "21":
+					key = ConsoleKey.F10;
+					break;
+				case "23":
+					key = ConsoleKey.F11;
+					break;
+				case "24":
+					key = ConsoleKey.F12;
+					break;
+				default:
+					key = 0;
+					break;
+				}
+				break;
+			default:
+				key = 0;
+				break;
+			}
+
+			return key;
+		}
+
+		/// <summary>
+		/// A helper to get only the <see cref="ConsoleKeyInfo.KeyChar"/> from the <see cref="ConsoleKeyInfo"/> array.
+		/// </summary>
+		/// <param name="cki"></param>
+		/// <returns>The char array of the escape sequence.</returns>
+		public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki)
+		{
+			char [] kChar = new char [] { };
+			var length = 0;
+			foreach (var kc in cki) {
+				length++;
+				Array.Resize (ref kChar, length);
+				kChar [length - 1] = kc.KeyChar;
+			}
+
+			return kChar;
+		}
+
+		private static MouseFlags? lastMouseButtonPressed;
+		//private static MouseFlags? lastMouseButtonReleased;
+		private static bool isButtonPressed;
+		//private static bool isButtonReleased;
+		private static bool isButtonClicked;
+		private static bool isButtonDoubleClicked;
+		private static bool isButtonTripleClicked;
+		private static Point point;
+
+		/// <summary>
+		/// Gets the <see cref="MouseFlags"/> mouse button flags and the position.
+		/// </summary>
+		/// <param name="cki">The <see cref="ConsoleKeyInfo"/> array.</param>
+		/// <param name="mouseFlags">The mouse button flags.</param>
+		/// <param name="pos">The mouse position.</param>
+		/// <param name="continuousButtonPressedHandler">The handler that will process the event.</param>
+		public static void GetMouse (ConsoleKeyInfo [] cki, out List<MouseFlags> mouseFlags, out Point pos, Action<MouseFlags, Point> continuousButtonPressedHandler)
+		{
+			MouseFlags buttonState = 0;
+			pos = new Point ();
+			int buttonCode = 0;
+			bool foundButtonCode = false;
+			int foundPoint = 0;
+			string value = "";
+			var kChar = GetKeyCharArray (cki);
+			//System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}");
+			for (int i = 0; i < kChar.Length; i++) {
+				var c = kChar [i];
+				if (c == '<') {
+					foundButtonCode = true;
+				} else if (foundButtonCode && c != ';') {
+					value += c.ToString ();
+				} else if (c == ';') {
+					if (foundButtonCode) {
+						foundButtonCode = false;
+						buttonCode = int.Parse (value);
+					}
+					if (foundPoint == 1) {
+						pos.X = int.Parse (value) - 1;
+					}
+					value = "";
+					foundPoint++;
+				} else if (foundPoint > 0 && c != 'm' && c != 'M') {
+					value += c.ToString ();
+				} else if (c == 'm' || c == 'M') {
+					//pos.Y = int.Parse (value) + Console.WindowTop - 1;
+					pos.Y = int.Parse (value) - 1;
+
+					switch (buttonCode) {
+					case 0:
+					case 8:
+					case 16:
+					case 24:
+					case 32:
+					case 36:
+					case 40:
+					case 48:
+					case 56:
+						buttonState = c == 'M' ? MouseFlags.Button1Pressed
+							: MouseFlags.Button1Released;
+						break;
+					case 1:
+					case 9:
+					case 17:
+					case 25:
+					case 33:
+					case 37:
+					case 41:
+					case 45:
+					case 49:
+					case 53:
+					case 57:
+					case 61:
+						buttonState = c == 'M' ? MouseFlags.Button2Pressed
+							: MouseFlags.Button2Released;
+						break;
+					case 2:
+					case 10:
+					case 14:
+					case 18:
+					case 22:
+					case 26:
+					case 30:
+					case 34:
+					case 42:
+					case 46:
+					case 50:
+					case 54:
+					case 58:
+					case 62:
+						buttonState = c == 'M' ? MouseFlags.Button3Pressed
+							: MouseFlags.Button3Released;
+						break;
+					case 35:
+					//// Needed for Windows OS
+					//if (isButtonPressed && c == 'm'
+					//	&& (lastMouseEvent.ButtonState == MouseFlags.Button1Pressed
+					//	|| lastMouseEvent.ButtonState == MouseFlags.Button2Pressed
+					//	|| lastMouseEvent.ButtonState == MouseFlags.Button3Pressed)) {
+
+					//	switch (lastMouseEvent.ButtonState) {
+					//	case MouseFlags.Button1Pressed:
+					//		buttonState = MouseFlags.Button1Released;
+					//		break;
+					//	case MouseFlags.Button2Pressed:
+					//		buttonState = MouseFlags.Button2Released;
+					//		break;
+					//	case MouseFlags.Button3Pressed:
+					//		buttonState = MouseFlags.Button3Released;
+					//		break;
+					//	}
+					//} else {
+					//	buttonState = MouseFlags.ReportMousePosition;
+					//}
+					//break;
+					case 39:
+					case 43:
+					case 47:
+					case 51:
+					case 55:
+					case 59:
+					case 63:
+						buttonState = MouseFlags.ReportMousePosition;
+						break;
+					case 64:
+						buttonState = MouseFlags.WheeledUp;
+						break;
+					case 65:
+						buttonState = MouseFlags.WheeledDown;
+						break;
+					case 68:
+					case 72:
+					case 80:
+						buttonState = MouseFlags.WheeledLeft;       // Shift/Ctrl+WheeledUp
+						break;
+					case 69:
+					case 73:
+					case 81:
+						buttonState = MouseFlags.WheeledRight;      // Shift/Ctrl+WheeledDown
+						break;
+					}
+					// Modifiers.
+					switch (buttonCode) {
+					case 8:
+					case 9:
+					case 10:
+					case 43:
+						buttonState |= MouseFlags.ButtonAlt;
+						break;
+					case 14:
+					case 47:
+						buttonState |= MouseFlags.ButtonAlt | MouseFlags.ButtonShift;
+						break;
+					case 16:
+					case 17:
+					case 18:
+					case 51:
+						buttonState |= MouseFlags.ButtonCtrl;
+						break;
+					case 22:
+					case 55:
+						buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift;
+						break;
+					case 24:
+					case 25:
+					case 26:
+					case 59:
+						buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt;
+						break;
+					case 30:
+					case 63:
+						buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt;
+						break;
+					case 32:
+					case 33:
+					case 34:
+						buttonState |= MouseFlags.ReportMousePosition;
+						break;
+					case 36:
+					case 37:
+						buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonShift;
+						break;
+					case 39:
+					case 68:
+					case 69:
+						buttonState |= MouseFlags.ButtonShift;
+						break;
+					case 40:
+					case 41:
+					case 42:
+						buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt;
+						break;
+					case 45:
+					case 46:
+						buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt | MouseFlags.ButtonShift;
+						break;
+					case 48:
+					case 49:
+					case 50:
+						buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl;
+						break;
+					case 53:
+					case 54:
+						buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift;
+						break;
+					case 56:
+					case 57:
+					case 58:
+						buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt;
+						break;
+					case 61:
+					case 62:
+						buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt;
+						break;
+					}
+				}
+			}
+
+			mouseFlags = new List<MouseFlags> () { MouseFlags.AllEvents };
+
+			if (lastMouseButtonPressed != null && !isButtonPressed && !buttonState.HasFlag (MouseFlags.ReportMousePosition)
+				&& !buttonState.HasFlag (MouseFlags.Button1Released)
+				&& !buttonState.HasFlag (MouseFlags.Button2Released)
+				&& !buttonState.HasFlag (MouseFlags.Button3Released)
+				&& !buttonState.HasFlag (MouseFlags.Button4Released)) {
+
+				lastMouseButtonPressed = null;
+				isButtonPressed = false;
+			}
+
+			if (!isButtonClicked && !isButtonDoubleClicked && ((buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
+				  buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed) && lastMouseButtonPressed == null) ||
+				  isButtonPressed && lastMouseButtonPressed != null && buttonState.HasFlag (MouseFlags.ReportMousePosition)) {
+
+				mouseFlags [0] = buttonState;
+				lastMouseButtonPressed = buttonState;
+				isButtonPressed = true;
+
+				if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0) {
+					point = new Point () {
+						X = pos.X,
+						Y = pos.Y
+					};
+
+					Application.MainLoop.AddIdle (() => {
+						Task.Run (async () => await ProcessContinuousButtonPressedAsync (buttonState, continuousButtonPressedHandler));
+						return false;
+					});
+				} else if (mouseFlags [0] == MouseFlags.ReportMousePosition) {
+					isButtonPressed = false;
+				}
+
+			} else if (isButtonDoubleClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
+				buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) {
+
+				mouseFlags [0] = GetButtonTripleClicked (buttonState);
+				isButtonDoubleClicked = false;
+				isButtonTripleClicked = true;
+
+			} else if (isButtonClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
+				buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) {
+
+				mouseFlags [0] = GetButtonDoubleClicked (buttonState);
+				isButtonClicked = false;
+				isButtonDoubleClicked = true;
+				Application.MainLoop.AddIdle (() => {
+					Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
+					return false;
+				});
+
+			}
+			//else if (isButtonReleased && !isButtonClicked && buttonState == MouseFlags.ReportMousePosition) {
+			//	mouseFlag [0] = GetButtonClicked ((MouseFlags)lastMouseButtonReleased);
+			//	lastMouseButtonReleased = null;
+			//	isButtonReleased = false;
+			//	isButtonClicked = true;
+			//	Application.MainLoop.AddIdle (() => {
+			//		Task.Run (async () => await ProcessButtonClickedAsync ());
+			//		return false;
+			//	});
+
+			//} 
+			else if (!isButtonClicked && !isButtonDoubleClicked && (buttonState == MouseFlags.Button1Released || buttonState == MouseFlags.Button2Released ||
+				  buttonState == MouseFlags.Button3Released || buttonState == MouseFlags.Button4Released)) {
+
+				mouseFlags [0] = buttonState;
+				isButtonPressed = false;
+
+				if (isButtonTripleClicked) {
+					isButtonTripleClicked = false;
+				} else if (pos.X == point.X && pos.Y == point.Y) {
+					mouseFlags.Add (GetButtonClicked (buttonState));
+					isButtonClicked = true;
+					Application.MainLoop.AddIdle (() => {
+						Task.Run (async () => await ProcessButtonClickedAsync ());
+						return false;
+					});
+				}
+
+				point = pos;
+
+				//if ((lastMouseButtonPressed & MouseFlags.ReportMousePosition) == 0) {
+				//	lastMouseButtonReleased = buttonState;
+				//	isButtonPressed = false;
+				//	isButtonReleased = true;
+				//} else {
+				//	lastMouseButtonPressed = null;
+				//	isButtonPressed = false;
+				//}
+
+			} else if (buttonState == MouseFlags.WheeledUp) {
+
+				mouseFlags [0] = MouseFlags.WheeledUp;
+
+			} else if (buttonState == MouseFlags.WheeledDown) {
+
+				mouseFlags [0] = MouseFlags.WheeledDown;
+
+			} else if (buttonState == MouseFlags.WheeledLeft) {
+
+				mouseFlags [0] = MouseFlags.WheeledLeft;
+
+			} else if (buttonState == MouseFlags.WheeledRight) {
+
+				mouseFlags [0] = MouseFlags.WheeledRight;
+
+			} else if (buttonState == MouseFlags.ReportMousePosition) {
+				mouseFlags [0] = MouseFlags.ReportMousePosition;
+
+			} else {
+				mouseFlags [0] = buttonState;
+				//foreach (var flag in buttonState.GetUniqueFlags()) {
+				//	mouseFlag [0] |= flag;
+				//}
+			}
+
+			mouseFlags [0] = SetControlKeyStates (buttonState, mouseFlags [0]);
+			//buttonState = mouseFlags;
+
+			//System.Diagnostics.Debug.WriteLine ($"buttonState: {buttonState} X: {pos.X} Y: {pos.Y}");
+			//foreach (var mf in mouseFlags) {
+			//	System.Diagnostics.Debug.WriteLine ($"mouseFlags: {mf} X: {pos.X} Y: {pos.Y}");
+			//}
+		}
+
+		private static async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag, Action<MouseFlags, Point> continuousButtonPressedHandler)
+		{
+			while (isButtonPressed) {
+				await Task.Delay (100);
+				//var me = new MouseEvent () {
+				//	X = point.X,
+				//	Y = point.Y,
+				//	Flags = mouseFlag
+				//};
+
+				var view = Application.WantContinuousButtonPressedView;
+				if (view == null)
+					break;
+				if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
+					Application.MainLoop.Invoke (() => continuousButtonPressedHandler (mouseFlag, point));
+				}
+			}
+		}
+
+		private static async Task ProcessButtonClickedAsync ()
+		{
+			await Task.Delay (300);
+			isButtonClicked = false;
+		}
+
+		private static async Task ProcessButtonDoubleClickedAsync ()
+		{
+			await Task.Delay (300);
+			isButtonDoubleClicked = false;
+		}
+
+		private static MouseFlags GetButtonClicked (MouseFlags mouseFlag)
+		{
+			MouseFlags mf = default;
+			switch (mouseFlag) {
+			case MouseFlags.Button1Released:
+				mf = MouseFlags.Button1Clicked;
+				break;
+
+			case MouseFlags.Button2Released:
+				mf = MouseFlags.Button2Clicked;
+				break;
+
+			case MouseFlags.Button3Released:
+				mf = MouseFlags.Button3Clicked;
+				break;
+			}
+			return mf;
+		}
+
+		private static MouseFlags GetButtonDoubleClicked (MouseFlags mouseFlag)
+		{
+			MouseFlags mf = default;
+			switch (mouseFlag) {
+			case MouseFlags.Button1Pressed:
+				mf = MouseFlags.Button1DoubleClicked;
+				break;
+
+			case MouseFlags.Button2Pressed:
+				mf = MouseFlags.Button2DoubleClicked;
+				break;
+
+			case MouseFlags.Button3Pressed:
+				mf = MouseFlags.Button3DoubleClicked;
+				break;
+			}
+			return mf;
+		}
+
+		private static MouseFlags GetButtonTripleClicked (MouseFlags mouseFlag)
+		{
+			MouseFlags mf = default;
+			switch (mouseFlag) {
+			case MouseFlags.Button1Pressed:
+				mf = MouseFlags.Button1TripleClicked;
+				break;
+
+			case MouseFlags.Button2Pressed:
+				mf = MouseFlags.Button2TripleClicked;
+				break;
+
+			case MouseFlags.Button3Pressed:
+				mf = MouseFlags.Button3TripleClicked;
+				break;
+			}
+			return mf;
+		}
+
+		private static MouseFlags SetControlKeyStates (MouseFlags buttonState, MouseFlags mouseFlag)
+		{
+			if ((buttonState & MouseFlags.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0)
+				mouseFlag |= MouseFlags.ButtonCtrl;
+
+			if ((buttonState & MouseFlags.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0)
+				mouseFlag |= MouseFlags.ButtonShift;
+
+			if ((buttonState & MouseFlags.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0)
+				mouseFlag |= MouseFlags.ButtonAlt;
+			return mouseFlag;
+		}
+
+		/// <summary>
+		/// Get the terminal that holds the console driver.
+		/// </summary>
+		/// <param name="process">The process.</param>
+		/// <returns>If supported the executable console process, null otherwise.</returns>
+		public static Process GetParentProcess (Process process)
+		{
+			if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
+				return null;
+			}
+
+			string query = "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = " + process.Id;
+			using (ManagementObjectSearcher mos = new ManagementObjectSearcher (query)) {
+				foreach (ManagementObject mo in mos.Get ()) {
+					if (mo ["ParentProcessId"] != null) {
+						try {
+							var id = Convert.ToInt32 (mo ["ParentProcessId"]);
+							return Process.GetProcessById (id);
+						} catch {
+						}
+					}
+				}
+			}
+			return null;
+		}
+	}
+}

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

@@ -749,7 +749,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		WheeledUp = unchecked((int)0x10000000),
 		/// <summary>
-		/// Vertical button wheeled up.
+		/// Vertical button wheeled down.
 		/// </summary>
 		WheeledDown = unchecked((int)0x20000000),
 		/// <summary>

+ 2 - 2
Terminal.Gui/Core/PosDim.cs

@@ -345,7 +345,7 @@ namespace Terminal.Gui {
 				case 3: tside = "bottom"; break;
 				default: tside = "unknown"; break;
 				}
-				return $"View({tside},{Target.ToString()})";
+				return $"View({tside},{Target.ToString ()})";
 			}
 
 			public override int GetHashCode () => Target.GetHashCode ();
@@ -691,7 +691,7 @@ namespace Terminal.Gui {
 				case 1: tside = "Width"; break;
 				default: tside = "unknown"; break;
 				}
-				return $"DimView({tside},{Target.ToString ()})";
+				return $"View({tside},{Target.ToString ()})";
 			}
 
 			public override int GetHashCode () => Target.GetHashCode ();

+ 20 - 9
Terminal.Gui/Core/TextFormatter.cs

@@ -1176,22 +1176,29 @@ namespace Terminal.Gui {
 			}
 
 			var isVertical = IsVerticalDirection (textDirection);
-			var savedClip = Application.Driver?.Clip;
 			var maxBounds = bounds;
 			if (Application.Driver != null) {
-				Application.Driver.Clip = maxBounds = containerBounds == default
+				maxBounds = containerBounds == default
 					? bounds
 					: new Rect (Math.Max (containerBounds.X, bounds.X),
 					Math.Max (containerBounds.Y, bounds.Y),
 					Math.Max (Math.Min (containerBounds.Width, containerBounds.Right - bounds.Left), 0),
 					Math.Max (Math.Min (containerBounds.Height, containerBounds.Bottom - bounds.Top), 0));
 			}
+			if (maxBounds.Width == 0 || maxBounds.Height == 0) {
+				return;
+			}
+			var savedClip = Application.Driver?.Clip;
+			if (Application.Driver != null) {
+				Application.Driver.Clip = maxBounds;
+			}
+			var lineOffset = !isVertical && bounds.Y < 0 ? Math.Abs (bounds.Y) : 0;
 
-			for (int line = 0; line < linesFormated.Count; line++) {
+			for (int line = lineOffset; line < linesFormated.Count; line++) {
 				if ((isVertical && line > bounds.Width) || (!isVertical && line > bounds.Height))
 					continue;
 				if ((isVertical && line >= maxBounds.Left + maxBounds.Width)
-					|| (!isVertical && line >= maxBounds.Top + maxBounds.Height))
+					|| (!isVertical && line >= maxBounds.Top + maxBounds.Height + lineOffset))
 
 					break;
 
@@ -1267,18 +1274,21 @@ namespace Terminal.Gui {
 					throw new ArgumentOutOfRangeException ();
 				}
 
+				var colOffset = bounds.X < 0 ? Math.Abs (bounds.X) : 0;
 				var start = isVertical ? bounds.Top : bounds.Left;
 				var size = isVertical ? bounds.Height : bounds.Width;
-				var current = start;
+				var current = start + colOffset;
 
-				for (var idx = (isVertical ? start - y : start - x); current < start + size; idx++) {
-					if (!fillRemaining && idx < 0) {
+				for (var idx = (isVertical ? start - y : start - x) + colOffset; current < start + size; idx++) {
+					if (idx < 0 || x + current + colOffset < 0) {
 						current++;
 						continue;
 					} else if (!fillRemaining && idx > runes.Length - 1) {
 						break;
 					}
-					if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X) || (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
+					if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X + colOffset)
+						|| (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
+
 						break;
 
 					var rune = (Rune)' ';
@@ -1316,8 +1326,9 @@ namespace Terminal.Gui {
 					}
 				}
 			}
-			if (Application.Driver != null)
+			if (Application.Driver != null) {
 				Application.Driver.Clip = (Rect)savedClip;
+			}
 		}
 	}
 }

+ 8 - 4
Terminal.Gui/Core/Toplevel.cs

@@ -167,6 +167,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		virtual public void OnLoaded ()
 		{
+			IsLoaded = true;
 			foreach (Toplevel tl in Subviews.Where (v => v is Toplevel)) {
 				tl.OnLoaded ();
 			}
@@ -367,6 +368,13 @@ namespace Terminal.Gui {
 			}
 		}
 
+		/// <summary>
+		/// <see langword="true"/> if was already loaded by the <see cref="Application.Begin(Toplevel)"/>
+		/// <see langword="false"/>, otherwise. This is used to avoid the <see cref="View.NeedDisplay"/>
+		/// having wrong values while this was not yet loaded.
+		/// </summary>
+		public bool IsLoaded { get; private set; }
+
 		///<inheritdoc/>
 		public override bool OnKeyDown (KeyEvent keyEvent)
 		{
@@ -741,12 +749,8 @@ namespace Terminal.Gui {
 					if (view.Frame.IntersectsWith (bounds) && !OutsideTopFrame (this)) {
 						view.SetNeedsLayout ();
 						view.SetNeedsDisplay (view.Bounds);
-						//view.Redraw (view.Bounds);
 					}
 				}
-
-				ClearLayoutNeeded ();
-				ClearNeedsDisplay ();
 			}
 
 			base.Redraw (Bounds);

+ 78 - 71
Terminal.Gui/Core/View.cs

@@ -447,16 +447,34 @@ namespace Terminal.Gui {
 		public virtual Rect Frame {
 			get => frame;
 			set {
-				var rect = GetMaxNeedDisplay (frame, value);
 				frame = new Rect (value.X, value.Y, Math.Max (value.Width, 0), Math.Max (value.Height, 0));
 				TextFormatter.Size = GetBoundsTextFormatterSize ();
 				SetNeedsLayout ();
-				SetNeedsDisplay (rect);
+				SetNeedsDisplay ();
 			}
 		}
 
+		/// <summary>
+		/// The Thickness that separates a View from other SubViews of the same SuperView. 
+		/// The Margin is not part of the View's content and is not clipped by the View's Clip Area. 
+		/// </summary>
 		public Frame Margin { get; set; }
+
+		/// <summary>
+		///  Thickness where a visual border (drawn using line-drawing glyphs) and the Title are drawn. 
+		///  The Border expands inward; in other words if `Border.Thickness.Top == 2` the border and 
+		///  title will take up the first row and the second row will be filled with spaces. 
+		///  The Border is not part of the View's content and is not clipped by the View's `ClipArea`.
+		/// </summary>
 		public Frame BorderFrame { get; set; }
+
+		/// <summary>
+		/// Means the Thickness inside of an element that offsets the `Content` from the Border. 
+		/// Padding is `{0, 0, 0, 0}` by default. Padding is not part of the View's content and is not clipped by the View's `ClipArea`.
+		/// </summary>
+		/// <remarks>
+		/// (NOTE: in v1 `Padding` is OUTSIDE of the `Border`). 
+		/// </remarks>
 		public Frame Padding { get; set; }
 
 		/// <summary>
@@ -501,7 +519,7 @@ namespace Terminal.Gui {
 		ustring title;
 
 		/// <summary>
-		/// The title to be displayed for this <see cref="View2"/>.
+		/// The title to be displayed for this <see cref="View"/>.
 		/// </summary>
 		/// <value>The title.</value>
 		public ustring Title {
@@ -860,7 +878,6 @@ namespace Terminal.Gui {
 		{
 			var actX = x is Pos.PosAbsolute ? x.Anchor (0) : frame.X;
 			var actY = y is Pos.PosAbsolute ? y.Anchor (0) : frame.Y;
-			Rect oldFrame = frame;
 
 			if (AutoSize) {
 				var s = GetAutoSize ();
@@ -875,21 +892,7 @@ namespace Terminal.Gui {
 			}
 			TextFormatter.Size = GetBoundsTextFormatterSize ();
 			SetNeedsLayout ();
-			SetNeedsDisplay (GetMaxNeedDisplay (oldFrame, frame));
-		}
-
-		Rect GetMaxNeedDisplay (Rect oldFrame, Rect newFrame)
-		{
-			var rect = new Rect () {
-				X = Math.Min (oldFrame.X, newFrame.X),
-				Y = Math.Min (oldFrame.Y, newFrame.Y),
-				Width = Math.Max (oldFrame.Width, newFrame.Width),
-				Height = Math.Max (oldFrame.Height, newFrame.Height)
-			};
-			rect.Width += Math.Max (oldFrame.X - newFrame.X, 0);
-			rect.Height += Math.Max (oldFrame.Y - newFrame.Y, 0);
-
-			return rect;
+			SetNeedsDisplay ();
 		}
 
 		void TextFormatter_HotKeyChanged (Key obj)
@@ -1161,15 +1164,8 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public void Clear ()
 		{
-			Rect containerBounds = GetContainerBounds ();
-			Rect viewBounds = Bounds;
-			if (!containerBounds.IsEmpty) {
-				viewBounds.Width = Math.Min (viewBounds.Width, containerBounds.Width);
-				viewBounds.Height = Math.Min (viewBounds.Height, containerBounds.Height);
-			}
-
-			var h = viewBounds.Height;
-			var w = viewBounds.Width;
+			var h = Frame.Height;
+			var w = Frame.Width;
 			for (var line = 0; line < h; line++) {
 				Move (0, line);
 				for (var col = 0; col < w; col++)
@@ -1432,10 +1428,10 @@ namespace Terminal.Gui {
 		public virtual void OnAdded (View view)
 		{
 			view.IsAdded = true;
-			view.x = view.x ?? view.frame.X;
-			view.y = view.y ?? view.frame.Y;
-			view.width = view.width ?? view.frame.Width;
-			view.height = view.height ?? view.frame.Height;
+			view.x ??= view.frame.X;
+			view.y ??= view.frame.Y;
+			view.width ??= view.frame.Width;
+			view.height ??= view.frame.Height;
 
 			view.Added?.Invoke (this);
 		}
@@ -1573,9 +1569,10 @@ namespace Terminal.Gui {
 			}
 
 			var boundsAdjustedForBorder = Bounds;
-			if (!IgnoreBorderPropertyOnRedraw && Border != null) {
-				throw new InvalidOperationException("Don't use border!");
-			} else if (ustring.IsNullOrEmpty (TextFormatter.Text) &&
+			//if (!IgnoreBorderPropertyOnRedraw && Border != null) {
+			//	throw new InvalidOperationException("Don't use border!");
+			//} else 
+			if (ustring.IsNullOrEmpty (TextFormatter.Text) &&
 				(GetType ().IsNestedPublic && !IsOverridden (this, "Redraw") || GetType ().Name == "View") &&
 				(!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded)) {
 
@@ -1585,15 +1582,17 @@ namespace Terminal.Gui {
 
 			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
 				Rect containerBounds = GetContainerBounds ();
-				Clear (ViewToScreen (GetNeedDisplay (containerBounds)));
-				SetChildNeedsDisplay ();
-				// Draw any Text
-				if (TextFormatter != null) {
-					TextFormatter.NeedsFormat = true;
+				if (!containerBounds.IsEmpty) {
+					Clear (GetNeedDisplay (containerBounds));
+					SetChildNeedsDisplay ();
+					// Draw any Text
+					if (TextFormatter != null) {
+						TextFormatter.NeedsFormat = true;
+					}
+					TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
+					    HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+					    containerBounds);
 				}
-				TextFormatter?.Draw (ViewToScreen (boundsAdjustedForBorder), HasFocus ? ColorScheme.Focus : GetNormalColor (),
-				    HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled,
-				    containerBounds);
 			}
 
 			// Invoke DrawContentEvent
@@ -1602,7 +1601,7 @@ namespace Terminal.Gui {
 			if (subviews != null) {
 				foreach (var view in subviews) {
 					if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay || view.LayoutNeeded) {
-						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (boundsAdjustedForBorder) || boundsAdjustedForBorder.X < 0 || bounds.Y < 0)) {
+						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
 							if (view.LayoutNeeded) {
 								view.LayoutSubviews ();
 							}
@@ -1631,7 +1630,7 @@ namespace Terminal.Gui {
 
 		Rect GetNeedDisplay (Rect containerBounds)
 		{
-			Rect rect = NeedDisplay;
+			Rect rect = ViewToScreen (NeedDisplay);
 			if (!containerBounds.IsEmpty) {
 				rect.Width = Math.Min (NeedDisplay.Width, containerBounds.Width);
 				rect.Height = Math.Min (NeedDisplay.Height, containerBounds.Height);
@@ -2272,8 +2271,7 @@ namespace Terminal.Gui {
 					newLocation = pos.Anchor (superviewDimension - newDimension);
 					break;
 
-				case Pos.PosCombine:
-					var combine = pos as Pos.PosCombine;
+				case Pos.PosCombine combine:
 					int left, right;
 					(left, newDimension) = GetNewLocationAndDimension (superviewLocation, superviewDimension, combine.left, dim, autosizeDimension);
 					(right, newDimension) = GetNewLocationAndDimension (superviewLocation, superviewDimension, combine.right, dim, autosizeDimension);
@@ -2300,7 +2298,7 @@ namespace Terminal.Gui {
 
 			// Recursively calculates the new dimension (width or height) of the given Dim given:
 			//   the current location (x or y)
-			//   the current dimennsion (width or height)
+			//   the current dimension (width or height)
 			int CalculateNewDimension (Dim d, int location, int dimension, int autosize)
 			{
 				int newDimension;
@@ -2318,12 +2316,12 @@ namespace Terminal.Gui {
 					}
 					newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
 					break;
-					
+
 				case Dim.DimFactor factor when !factor.IsFromRemaining ():
 					newDimension = d.Anchor (dimension);
 					newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
 					break;
-					
+
 				case Dim.DimFill:
 				default:
 					newDimension = Math.Max (d.Anchor (dimension - location), 0);
@@ -2335,11 +2333,11 @@ namespace Terminal.Gui {
 			}
 
 
-			// horiztonal
-			(newX, newW) = GetNewLocationAndDimension (superviewFrame.X, superviewFrame.Width, x, Width, autosize.Width);
+			// horizontal
+			(newX, newW) = GetNewLocationAndDimension (superviewFrame.X, superviewFrame.Width, x, width, autosize.Width);
 
 			// vertical
-			(newY, newH) = GetNewLocationAndDimension (superviewFrame.Y, superviewFrame.Height, y, Height, autosize.Height);
+			(newY, newH) = GetNewLocationAndDimension (superviewFrame.Y, superviewFrame.Height, y, height, autosize.Height);
 
 			var r = new Rect (newX, newY, newW, newH);
 			if (Frame != r) {
@@ -2488,7 +2486,7 @@ namespace Terminal.Gui {
 
 			if (edges.Any ()) {
 				(var from, var to) = edges.First ();
-				if (from != Application.Top) {
+				if (from != superView?.GetTopSuperView (to, from)) {
 					if (!ReferenceEquals (from, to)) {
 						throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {from} linked with {to}. Did you forget to add it to {superView}?");
 					} else {
@@ -2499,8 +2497,8 @@ namespace Terminal.Gui {
 			// return L (a topologically sorted order)
 			return result;
 		} // TopologicalSort
-		
-		
+
+
 		/// <summary>
 		/// Invoked when a view starts executing or when the dimensions of the view have changed, for example in
 		/// response to the container view or terminal resizing.
@@ -2525,20 +2523,16 @@ namespace Terminal.Gui {
 			CollectAll (this, ref nodes, ref edges);
 			var ordered = View.TopologicalSort (SuperView, nodes, edges);
 			foreach (var v in ordered) {
-				if (v.LayoutStyle == LayoutStyle.Computed) {
-					v.SetRelativeLayout (Frame);
-				}
-
-				v.LayoutSubviews ();
-				v.LayoutNeeded = false;
+				LayoutSubview (v, Frame);
 			}
 
-			// If our SuperView is Application.Top and the layoutstyle is Computed it's a special-cass.
-			// Use SetRelativeaLayout with the Frame of the Application.Top
-			if (SuperView != null && SuperView == Application.Top && LayoutNeeded
-			    && ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) {
-				Debug.Assert (Application.Top.Frame.Location == Point.Empty);
-				SetRelativeLayout (Application.Top.Frame);
+			// If the 'to' is rooted to 'from' and the layoutstyle is Computed it's a special-case.
+			// Use LayoutSubview with the Frame of the 'from' 
+			if (SuperView != null && GetTopSuperView () != null && LayoutNeeded
+			    && ordered.Count == 0 && edges.Count > 0 && LayoutStyle == LayoutStyle.Computed) {
+
+				(var from, var to) = edges.First ();
+				LayoutSubview (to, from.Frame);
 			}
 
 			LayoutNeeded = false;
@@ -2546,6 +2540,16 @@ namespace Terminal.Gui {
 			OnLayoutComplete (new LayoutEventArgs () { OldBounds = oldBounds });
 		}
 
+		private void LayoutSubview (View v, Rect hostFrame)
+		{
+			if (v.LayoutStyle == LayoutStyle.Computed) {
+				v.SetRelativeLayout (hostFrame);
+			}
+
+			v.LayoutSubviews ();
+			v.LayoutNeeded = false;
+		}
+
 		ustring text;
 
 		/// <summary>
@@ -3211,11 +3215,14 @@ namespace Terminal.Gui {
 		/// Get the top superview of a given <see cref="View"/>.
 		/// </summary>
 		/// <returns>The superview view.</returns>
-		public View GetTopSuperView ()
+		public View GetTopSuperView (View view = null, View superview = null)
 		{
-			View top = Application.Top;
-			for (var v = this?.SuperView; v != null; v = v.SuperView) {
+			View top = superview ?? Application.Top;
+			for (var v = view?.SuperView ?? (this?.SuperView); v != null; v = v.SuperView) {
 				top = v;
+				if (top == superview) {
+					break;
+				}
 			}
 
 			return top;

+ 87 - 15
Terminal.Gui/Core/Window.cs

@@ -7,7 +7,7 @@
 //  - FrameView Does not support padding (but should)
 //  - FrameView Does not support mouse dragging
 //  - FrameView Does not support IEnumerable
-// Any udpates done here should probably be done in FrameView as well; TODO: Merge these classes
+// Any updates done here should probably be done in FrameView as well; TODO: Merge these classes
 
 using System;
 using System.Collections;
@@ -108,7 +108,7 @@ namespace Terminal.Gui {
 				Border = new Border () {
 					BorderStyle = DefaultBorderStyle,
 					Padding = new Thickness (padding),
-					BorderBrush = ColorScheme.Normal.Background
+					//Title = title
 				};
 			} else {
 				Border = border;
@@ -136,10 +136,82 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 			base.Remove (view);
 			RemoveMenuStatusBar (view);
+
 		}
 
+		///// <inheritdoc/>
+		//public override void RemoveAll ()
+		//{
+		//	contentView.RemoveAll ();
+		//}
+
+		/////<inheritdoc/>
+		//public override void Redraw (Rect bounds)
+		//{
+		//	var padding = Border.GetSumThickness ();
+		//	var scrRect = ViewToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
+
+		//	if (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded) {
+		//		Driver.SetAttribute (GetNormalColor ());
+		//		Clear ();
+		//		contentView.SetNeedsDisplay ();
+		//	}
+		//	var savedClip = contentView.ClipToBounds ();
+
+		//	// Redraw our contentView
+		//	// DONE: smartly constrict contentView.Bounds to just be what intersects with the 'bounds' we were passed
+		//	contentView.Redraw (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded ? contentView.Bounds : bounds);
+		//	Driver.Clip = savedClip;
+
+		//	ClearLayoutNeeded ();
+		//	ClearNeedsDisplay ();
+
+		//	Driver.SetAttribute (GetNormalColor ());
+		//	//Driver.DrawWindowFrame (scrRect, padding.Left + borderLength, padding.Top + borderLength, padding.Right + borderLength, padding.Bottom + borderLength,
+		//	//	Border.BorderStyle != BorderStyle.None, fill: true, Border.BorderStyle);
+		//	Border.DrawContent (this, false);
+		//	if (HasFocus)
+		//		Driver.SetAttribute (ColorScheme.HotNormal);
+		//	if (Border.DrawMarginFrame)
+		//		Driver.DrawWindowTitle (scrRect, Title, padding.Left, padding.Top, padding.Right, padding.Bottom);
+		//	Driver.SetAttribute (GetNormalColor ());
+		//}
+
+		///// <inheritdoc/>
+		//public override void OnCanFocusChanged ()
+		//{
+		//	if (contentView != null) {
+		//		contentView.CanFocus = CanFocus;
+		//	}
+		//	base.OnCanFocusChanged ();
+		//}
+
+		///// <summary>
+		/////   The text displayed by the <see cref="Label"/>.
+		///// </summary>
+		//public override ustring Text {
+		//	get => contentView?.Text;
+		//	set {
+		//		base.Text = value;
+		//		if (contentView != null) {
+		//			contentView.Text = value;
+		//		}
+		//	}
+		//}
+
+		///// <summary>
+		///// Controls the text-alignment property of the label, changing it will redisplay the <see cref="Label"/>.
+		///// </summary>
+		///// <value>The text alignment.</value>
+		//public override TextAlignment TextAlignment {
+		//	get => contentView.TextAlignment;
+		//	set {
+		//		base.TextAlignment = contentView.TextAlignment = value;
+		//	}
+		//}
+
 		/// <summary>
-		/// Event arguments for <see cref="Title"/> chane events.
+		/// Event arguments for <see cref="View.Title"/> change events.
 		/// </summary>
 		public class TitleEventArgs : EventArgs {
 			/// <summary>
@@ -153,15 +225,15 @@ namespace Terminal.Gui {
 			public ustring OldTitle { get; set; }
 
 			/// <summary>
-			/// Flag which allows cancelling the Title change.
+			/// Flag which allows canceling the Title change.
 			/// </summary>
 			public bool Cancel { get; set; }
 
 			/// <summary>
 			/// Initializes a new instance of <see cref="TitleEventArgs"/>
 			/// </summary>
-			/// <param name="oldTitle">The <see cref="Window.Title"/> that is/has been replaced.</param>
-			/// <param name="newTitle">The new <see cref="Window.Title"/> to be replaced.</param>
+			/// <param name="oldTitle">The <see cref="View.Title"/> that is/has been replaced.</param>
+			/// <param name="newTitle">The new <see cref="View.Title"/> to be replaced.</param>
 			public TitleEventArgs (ustring oldTitle, ustring newTitle)
 			{
 				OldTitle = oldTitle;
@@ -169,11 +241,11 @@ namespace Terminal.Gui {
 			}
 		}
 		/// <summary>
-		/// Called before the <see cref="Window.Title"/> changes. Invokes the <see cref="TitleChanging"/> event, which can be cancelled.
+		/// Called before the <see cref="View.Title"/> changes. Invokes the <see cref="TitleChanging"/> event, which can be cancelled.
 		/// </summary>
-		/// <param name="oldTitle">The <see cref="Window.Title"/> that is/has been replaced.</param>
-		/// <param name="newTitle">The new <see cref="Window.Title"/> to be replaced.</param>
-		/// <returns>`true` if an event handler cancelled the Title change.</returns>
+		/// <param name="oldTitle">The <see cref="View.Title"/> that is/has been replaced.</param>
+		/// <param name="newTitle">The new <see cref="View.Title"/> to be replaced.</param>
+		/// <returns>`true` if an event handler canceled the Title change.</returns>
 		public virtual bool OnTitleChanging (ustring oldTitle, ustring newTitle)
 		{
 			var args = new TitleEventArgs (oldTitle, newTitle);
@@ -182,16 +254,16 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Event fired when the <see cref="Window.Title"/> is changing. Set <see cref="TitleEventArgs.Cancel"/> to 
+		/// Event fired when the <see cref="View.Title"/> is changing. Set <see cref="TitleEventArgs.Cancel"/> to 
 		/// `true` to cancel the Title change.
 		/// </summary>
 		public event Action<TitleEventArgs> TitleChanging;
 
 		/// <summary>
-		/// Called when the <see cref="Window.Title"/> has been changed. Invokes the <see cref="TitleChanged"/> event.
+		/// Called when the <see cref="View.Title"/> has been changed. Invokes the <see cref="TitleChanged"/> event.
 		/// </summary>
-		/// <param name="oldTitle">The <see cref="Window.Title"/> that is/has been replaced.</param>
-		/// <param name="newTitle">The new <see cref="Window.Title"/> to be replaced.</param>
+		/// <param name="oldTitle">The <see cref="View.Title"/> that is/has been replaced.</param>
+		/// <param name="newTitle">The new <see cref="View.Title"/> to be replaced.</param>
 		public virtual void OnTitleChanged (ustring oldTitle, ustring newTitle)
 		{
 			var args = new TitleEventArgs (oldTitle, newTitle);
@@ -199,7 +271,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Event fired after the <see cref="Window.Title"/> has been changed. 
+		/// Event fired after the <see cref="View.Title"/> has been changed. 
 		/// </summary>
 		public event Action<TitleEventArgs> TitleChanged;
 	}

+ 1 - 1
Terminal.Gui/Resources/config.json

@@ -23,7 +23,7 @@
       "Ctrl"
     ]
   },
-  "Application.HeightAsBuffer": false,
+  "Application.EnableConsoleScrolling": false,
   "Application.QuitKey": {
     "Key": "Q",
     "Modifiers": [

+ 9 - 10
Terminal.Gui/Terminal.Gui.csproj

@@ -10,10 +10,10 @@
     <!-- Version numbers are automatically updated by gitversion when a release is released -->
     <!-- In the source tree the version will always be 1.0 for all projects. -->
     <!-- Do not modify these. Do NOT commit after manually running `dotnet-gitversion /updateprojectfiles` -->
-    <AssemblyVersion>1.0</AssemblyVersion>
-    <FileVersion>1.0</FileVersion>
-    <Version>1.0</Version>
-    <InformationalVersion>1.0</InformationalVersion>
+    <AssemblyVersion>2.0</AssemblyVersion>
+    <FileVersion>2.0</FileVersion>
+    <Version>2.0</Version>
+    <InformationalVersion>2.0</InformationalVersion>
   </PropertyGroup>
   <ItemGroup>
     <None Remove="Resources\config.json" />
@@ -22,11 +22,15 @@
     <EmbeddedResource Include="Resources\config.json" />
   </ItemGroup>
   <ItemGroup>
+    <!-- Enable Nuget Source Link for github -->
+    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
     <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
     <PackageReference Include="NStack.Core" Version="1.0.7" />
+    <PackageReference Include="System.Text.Json" Version="7.0.1" />
+    <PackageReference Include="System.Management" Version="7.0.0" />
     <InternalsVisibleTo Include="UnitTests" />
-  </ItemGroup>
+   </ItemGroup>
   <!-- Uncomment the RestoreSources element to have dotnet restore pull NStack from a local dir for testing -->
   <PropertyGroup>
     <!-- See https://stackoverflow.com/a/44463578/297526 -->
@@ -56,11 +60,6 @@
       <LastGenOutput>Strings.Designer.cs</LastGenOutput>
     </EmbeddedResource>
   </ItemGroup>
-  <!-- Enable Nuget Source Link for github -->
-  <ItemGroup>
-    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
-    <PackageReference Include="System.Text.Json" Version="7.0.1" />
-  </ItemGroup>
   <PropertyGroup>
     <TargetFrameworks>net7.0</TargetFrameworks>
     <LangVersion>9.0</LangVersion>

+ 73 - 2
Terminal.Gui/Views/FrameView.cs

@@ -11,6 +11,42 @@ namespace Terminal.Gui {
 	/// </summary>
 	public class FrameView : View {
 
+		//internal class FrameViewConfig : Configuration.Config<FrameViewConfig> {
+
+		//	/// <summary>
+		//	/// 
+		//	/// </summary>
+		//	/// 
+		//	[JsonConverter (typeof (JsonStringEnumConverter))]
+		//	public BorderStyle? DefaultBorderStyle { get; set; }
+
+		//	public override void Apply ()
+		//	{
+		//		if (DefaultBorderStyle.HasValue) {
+		//			FrameView.DefaultBorderStyle = DefaultBorderStyle.Value;
+		//		}
+		//	}
+
+		//	public override void CopyUpdatedProperitesFrom (FrameViewConfig changedConfig)
+		//	{
+		//		if (changedConfig.DefaultBorderStyle.HasValue) {
+		//			DefaultBorderStyle = changedConfig.DefaultBorderStyle;
+		//		}
+		//	}
+
+		//	public override void GetHardCodedDefaults ()
+		//	{
+		//		DefaultBorderStyle = FrameView.DefaultBorderStyle;
+		//	}
+		//}
+
+		//[Configuration.ConfigProperty]
+		//internal static FrameViewConfig Config { get; set; } = new FrameViewConfig ();
+
+		
+
+
+
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Gui.FrameView"/> class using <see cref="LayoutStyle.Absolute"/> layout.
 		/// </summary>
@@ -49,17 +85,52 @@ namespace Terminal.Gui {
 
 		void Initialize (Rect frame, ustring title, View [] views = null, Border border = null)
 		{
-			if (title == null) title = ustring.Empty;
 			this.Title = title;
 			if (border == null) {
 				Border = new Border () {
-					BorderStyle = DefaultBorderStyle
+					BorderStyle = DefaultBorderStyle,
+					//Title = title
 				};
 			} else {
 				Border = border;
+				//if (ustring.IsNullOrEmpty (border.Title)) {
+				//	border.Title = title;
+				//}
 			}
 		}
 
+		
+
+		void DrawFrame ()
+		{
+			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), 0, fill: true);
+		}
+
+		/// <summary>
+		/// Add the specified <see cref="View"/> to this container.
+		/// </summary>
+		/// <param name="view"><see cref="View"/> to add to this container</param>
+		public override void Add (View view)
+		{
+			if (view.CanFocus)
+				CanFocus = true;
+		}
+
+
+		/// <summary>
+		///   Removes a <see cref="View"/> from this container.
+		/// </summary>
+		/// <remarks>
+		/// </remarks>
+		public override void Remove (View view)
+		{
+			if (view == null)
+				return;
+
+			SetNeedsDisplay ();
+		}
+
+
 		///<inheritdoc/>
 		public override bool OnEnter (View view)
 		{

+ 209 - 132
Terminal.Gui/Views/TableView.cs

@@ -99,7 +99,7 @@ namespace Terminal.Gui {
 		/// When <see cref="MultiSelect"/> is enabled this property contain all rectangles of selected cells.  Rectangles describe column/rows selected in <see cref="Table"/> (not screen coordinates)
 		/// </summary>
 		/// <returns></returns>
-		public Stack<TableSelection> MultiSelectedRegions { get; } = new Stack<TableSelection> ();
+		public Stack<TableSelection> MultiSelectedRegions { get; private set; } = new Stack<TableSelection> ();
 
 		/// <summary>
 		/// Horizontal scroll offset.  The index of the first column in <see cref="Table"/> to display when when rendering the view.
@@ -109,7 +109,7 @@ namespace Terminal.Gui {
 			get => columnOffset;
 
 			//try to prevent this being set to an out of bounds column
-			set => columnOffset = TableIsNullOrInvisible() ? 0 : Math.Max (0, Math.Min (Table.Columns.Count - 1, value));
+			set => columnOffset = TableIsNullOrInvisible () ? 0 : Math.Max (0, Math.Min (Table.Columns.Count - 1, value));
 		}
 
 		/// <summary>
@@ -186,7 +186,7 @@ namespace Terminal.Gui {
 			set {
 				if (cellActivationKey != value) {
 					ReplaceKeyBinding (cellActivationKey, value);
-					
+
 					// of API user is mixing and matching old and new methods of keybinding then they may have lost
 					// the old binding (e.g. with ClearKeybindings) so ReplaceKeyBinding alone will fail
 					AddKeyBinding (value, Command.Accept);
@@ -218,9 +218,9 @@ namespace Terminal.Gui {
 			AddCommand (Command.LineDown, () => { ChangeSelectionByOffset (0, 1, false); return true; });
 			AddCommand (Command.PageUp, () => { PageUp (false); return true; });
 			AddCommand (Command.PageDown, () => { PageDown (false); return true; });
-			AddCommand (Command.LeftHome, () => { ChangeSelectionToStartOfRow (false);  return true; });
+			AddCommand (Command.LeftHome, () => { ChangeSelectionToStartOfRow (false); return true; });
 			AddCommand (Command.RightEnd, () => { ChangeSelectionToEndOfRow (false); return true; });
-			AddCommand (Command.TopHome, () => { ChangeSelectionToStartOfTable(false); return true; });
+			AddCommand (Command.TopHome, () => { ChangeSelectionToStartOfTable (false); return true; });
 			AddCommand (Command.BottomEnd, () => { ChangeSelectionToEndOfTable (false); return true; });
 
 			AddCommand (Command.RightExtend, () => { ChangeSelectionByOffset (1, 0, true); return true; });
@@ -234,8 +234,10 @@ namespace Terminal.Gui {
 			AddCommand (Command.TopHomeExtend, () => { ChangeSelectionToStartOfTable (true); return true; });
 			AddCommand (Command.BottomEndExtend, () => { ChangeSelectionToEndOfTable (true); return true; });
 
-			AddCommand (Command.SelectAll, () => { SelectAll(); return true; });
-			AddCommand (Command.Accept, () => { OnCellActivated(new CellActivatedEventArgs (Table, SelectedColumn, SelectedRow)); return true; });
+			AddCommand (Command.SelectAll, () => { SelectAll (); return true; });
+			AddCommand (Command.Accept, () => { OnCellActivated (new CellActivatedEventArgs (Table, SelectedColumn, SelectedRow)); return true; });
+
+			AddCommand (Command.ToggleChecked, () => { ToggleCurrentCellSelection (); return true; });
 
 			// Default keybindings for this view
 			AddKeyBinding (Key.CursorLeft, Command.Left);
@@ -252,7 +254,7 @@ namespace Terminal.Gui {
 			AddKeyBinding (Key.CursorLeft | Key.ShiftMask, Command.LeftExtend);
 			AddKeyBinding (Key.CursorRight | Key.ShiftMask, Command.RightExtend);
 			AddKeyBinding (Key.CursorUp | Key.ShiftMask, Command.LineUpExtend);
-			AddKeyBinding (Key.CursorDown| Key.ShiftMask, Command.LineDownExtend);
+			AddKeyBinding (Key.CursorDown | Key.ShiftMask, Command.LineDownExtend);
 			AddKeyBinding (Key.PageUp | Key.ShiftMask, Command.PageUpExtend);
 			AddKeyBinding (Key.PageDown | Key.ShiftMask, Command.PageDownExtend);
 			AddKeyBinding (Key.Home | Key.ShiftMask, Command.LeftHomeExtend);
@@ -264,33 +266,34 @@ namespace Terminal.Gui {
 			AddKeyBinding (CellActivationKey, Command.Accept);
 		}
 
+
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
-			{
-				Move (0, 0);
-				var frame = Frame;
+		{
+			Move (0, 0);
+			var frame = Frame;
 
-				scrollRightPoint = null;
-				scrollLeftPoint = null;
+			scrollRightPoint = null;
+			scrollLeftPoint = null;
 
-				// What columns to render at what X offset in viewport
-				var columnsToRender = CalculateViewport (bounds).ToArray ();
+			// What columns to render at what X offset in viewport
+			var columnsToRender = CalculateViewport (bounds).ToArray ();
 
-				Driver.SetAttribute (GetNormalColor ());
+			Driver.SetAttribute (GetNormalColor ());
 
-				//invalidate current row (prevents scrolling around leaving old characters in the frame
-				Driver.AddStr (new string (' ', bounds.Width));
+			//invalidate current row (prevents scrolling around leaving old characters in the frame
+			Driver.AddStr (new string (' ', bounds.Width));
 
-				int line = 0;
+			int line = 0;
 
-				if (ShouldRenderHeaders ()) {
-					// Render something like:
-					/*
-						┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐
-						│ArithmeticComparator│chi       │Healthboard│Interpretation│Labnumber│
-						└────────────────────┴──────────┴───────────┴──────────────┴─────────┘
-					*/
-			if (Style.ShowHorizontalHeaderOverline) {
+			if (ShouldRenderHeaders ()) {
+				// Render something like:
+				/*
+					┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐
+					│ArithmeticComparator│chi       │Healthboard│Interpretation│Labnumber│
+					└────────────────────┴──────────┴───────────┴──────────────┴─────────┘
+				*/
+				if (Style.ShowHorizontalHeaderOverline) {
 					RenderHeaderOverline (line, bounds.Width, columnsToRender);
 					line++;
 				}
@@ -436,19 +439,19 @@ namespace Terminal.Gui {
 			bool moreColumnsToLeft = ColumnOffset > 0;
 
 			// if we moved left would we find a new column (or are they all invisible?)
-			if(!TryGetNearestVisibleColumn (ColumnOffset-1, false, false, out _)) {
+			if (!TryGetNearestVisibleColumn (ColumnOffset - 1, false, false, out _)) {
 				moreColumnsToLeft = false;
 			}
 
 			// are there visible columns to the right that have not yet been reached?
 			// lets find out, what is the column index of the last column we are rendering
 			int lastColumnIdxRendered = ColumnOffset + columnsToRender.Length - 1;
-			
+
 			// are there more valid indexes?
 			bool moreColumnsToRight = lastColumnIdxRendered < Table.Columns.Count;
 
 			// if we went right from the last column would we find a new visible column?
-			if(!TryGetNearestVisibleColumn (lastColumnIdxRendered + 1, true, false, out _)) {
+			if (!TryGetNearestVisibleColumn (lastColumnIdxRendered + 1, true, false, out _)) {
 				// no we would not
 				moreColumnsToRight = false;
 			}
@@ -466,7 +469,7 @@ namespace Terminal.Gui {
 				// whole way but update to instead draw a header indicator
 				// or scroll arrow etc
 				var rune = Driver.HLine;
-				
+
 				if (Style.ShowVerticalHeaderLines) {
 					if (c == 0) {
 						// for first character render line
@@ -475,12 +478,11 @@ namespace Terminal.Gui {
 						// unless we have horizontally scrolled along
 						// in which case render an arrow, to indicate user
 						// can scroll left
-						if(Style.ShowHorizontalScrollIndicators && moreColumnsToLeft)
-						{
+						if (Style.ShowHorizontalScrollIndicators && moreColumnsToLeft) {
 							rune = Driver.LeftArrow;
-							scrollLeftPoint = new Point(c,row);
+							scrollLeftPoint = new Point (c, row);
 						}
-							
+
 					}
 					// if the next column is the start of a header
 					else if (columnsToRender.Any (r => r.X == c + 1)) {
@@ -495,10 +497,9 @@ namespace Terminal.Gui {
 						// unless there is more of the table we could horizontally
 						// scroll along to see. In which case render an arrow,
 						// to indicate user can scroll right
-						if(Style.ShowHorizontalScrollIndicators && moreColumnsToRight)
-						{
+						if (Style.ShowHorizontalScrollIndicators && moreColumnsToRight) {
 							rune = Driver.RightArrow;
-							scrollRightPoint = new Point(c,row);
+							scrollRightPoint = new Point (c, row);
 						}
 
 					}
@@ -518,7 +519,7 @@ namespace Terminal.Gui {
 			var focused = HasFocus;
 
 			var rowScheme = (Style.RowColorGetter?.Invoke (
-				new RowColorGetterArgs(Table,rowToRender))) ?? ColorScheme;
+				new RowColorGetterArgs (Table, rowToRender))) ?? ColorScheme;
 
 			//render start of line
 			if (style.ShowVerticalCellLines)
@@ -529,11 +530,9 @@ namespace Terminal.Gui {
 
 			Attribute color;
 
-			if(FullRowSelect && IsSelected (0, rowToRender)) {
+			if (FullRowSelect && IsSelected (0, rowToRender)) {
 				color = focused ? rowScheme.HotFocus : rowScheme.HotNormal;
-			}
-			else 
-			{
+			} else {
 				color = Enabled ? rowScheme.Normal : rowScheme.Disabled;
 			}
 
@@ -562,17 +561,16 @@ namespace Terminal.Gui {
 				var colorSchemeGetter = colStyle?.ColorGetter;
 
 				ColorScheme scheme;
-				if(colorSchemeGetter != null) {
+				if (colorSchemeGetter != null) {
 					// user has a delegate for defining row color per cell, call it
-					scheme = colorSchemeGetter(
-						new CellColorGetterArgs (Table, rowToRender, current.Column.Ordinal, val, representation,rowScheme));
+					scheme = colorSchemeGetter (
+						new CellColorGetterArgs (Table, rowToRender, current.Column.Ordinal, val, representation, rowScheme));
 
 					// if users custom color getter returned null, use the row scheme
-					if(scheme == null) {
+					if (scheme == null) {
 						scheme = rowScheme;
 					}
-				}
-				else {
+				} else {
 					// There is no custom cell coloring delegate so use the scheme for the row
 					scheme = rowScheme;
 				}
@@ -588,16 +586,15 @@ namespace Terminal.Gui {
 
 				// While many cells can be selected (see MultiSelectedRegions) only one cell is the primary (drives navigation etc)
 				bool isPrimaryCell = current.Column.Ordinal == selectedColumn && rowToRender == selectedRow;
-				
-				RenderCell (cellColor,render,isPrimaryCell);
-								
+
+				RenderCell (cellColor, render, isPrimaryCell);
+
 				// Reset color scheme to normal for drawing separators if we drew text with custom scheme
 				if (scheme != rowScheme) {
 
-					if(isSelectedCell) {
+					if (isSelectedCell) {
 						color = focused ? rowScheme.HotFocus : rowScheme.HotNormal;
-					}
-					else {
+					} else {
 						color = Enabled ? rowScheme.Normal : rowScheme.Disabled;
 					}
 					Driver.SetAttribute (color);
@@ -629,7 +626,7 @@ namespace Terminal.Gui {
 		/// <param name="cellColor"></param>
 		/// <param name="render"></param>
 		/// <param name="isPrimaryCell"></param>
-		protected virtual void RenderCell (Attribute cellColor, string render,bool isPrimaryCell)
+		protected virtual void RenderCell (Attribute cellColor, string render, bool isPrimaryCell)
 		{
 			// If the cell is the selected col/row then draw the first rune in inverted colors
 			// this allows the user to track which cell is the active one during a multi cell
@@ -740,12 +737,15 @@ namespace Terminal.Gui {
 
 			col = GetNearestVisibleColumn (col, lookRight, true);
 
-			if (!MultiSelect || !extendExistingSelection)
-				MultiSelectedRegions.Clear ();
+			if (!MultiSelect || !extendExistingSelection) {
+				ClearMultiSelectedRegions (true);
+			}
+
 
 			if (extendExistingSelection) {
+
 				// If we are extending current selection but there isn't one
-				if (MultiSelectedRegions.Count == 0) {
+				if (MultiSelectedRegions.Count == 0 || MultiSelectedRegions.All(m=>m.IsToggled)) {
 					// Create a new region between the old active cell and the new cell
 					var rect = CreateTableSelection (SelectedColumn, SelectedRow, col, row);
 					MultiSelectedRegions.Push (rect);
@@ -761,6 +761,24 @@ namespace Terminal.Gui {
 			SelectedRow = row;
 		}
 
+		private void ClearMultiSelectedRegions (bool keepToggledSelections)
+		{
+			if (!keepToggledSelections) {
+				MultiSelectedRegions.Clear ();
+				return;
+			}
+
+			var oldRegions = MultiSelectedRegions.ToArray ().Reverse ();
+
+			MultiSelectedRegions.Clear ();
+
+			foreach (var region in oldRegions) {
+				if (region.IsToggled) {
+					MultiSelectedRegions.Push (region);
+				}
+			}
+		}
+
 		/// <summary>
 		/// Unions the current selected cell (and/or regions) with the provided cell and makes
 		/// it the active one.
@@ -769,10 +787,10 @@ namespace Terminal.Gui {
 		/// <param name="row"></param>
 		private void UnionSelection (int col, int row)
 		{
-			if (!MultiSelect || TableIsNullOrInvisible()) {
+			if (!MultiSelect || TableIsNullOrInvisible ()) {
 				return;
 			}
-			
+
 			EnsureValidSelection ();
 
 			var oldColumn = SelectedColumn;
@@ -812,7 +830,7 @@ namespace Terminal.Gui {
 		/// Moves the selection up by one page
 		/// </summary>
 		/// <param name="extend">true to extend the current selection (if any) instead of replacing</param>
-		public void PageUp(bool extend)
+		public void PageUp (bool extend)
 		{
 			ChangeSelectionByOffset (0, -(Bounds.Height - GetHeaderHeightIfAny ()), extend);
 			Update ();
@@ -822,7 +840,7 @@ namespace Terminal.Gui {
 		/// Moves the selection down by one page
 		/// </summary>
 		/// <param name="extend">true to extend the current selection (if any) instead of replacing</param>
-		public void PageDown(bool extend)
+		public void PageDown (bool extend)
 		{
 			ChangeSelectionByOffset (0, Bounds.Height - GetHeaderHeightIfAny (), extend);
 			Update ();
@@ -846,7 +864,7 @@ namespace Terminal.Gui {
 		/// to (<see cref="SelectedColumn"/>,nY) i.e. no horizontal scrolling.
 		/// </summary>
 		/// <param name="extend">true to extend the current selection (if any) instead of replacing</param>
-		public void ChangeSelectionToEndOfTable(bool extend)
+		public void ChangeSelectionToEndOfTable (bool extend)
 		{
 			var finalColumn = Table.Columns.Count - 1;
 
@@ -880,10 +898,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void SelectAll ()
 		{
-			if (TableIsNullOrInvisible() || !MultiSelect || Table.Rows.Count == 0)
+			if (TableIsNullOrInvisible () || !MultiSelect || Table.Rows.Count == 0)
 				return;
 
-			MultiSelectedRegions.Clear ();
+			ClearMultiSelectedRegions (true);
 
 			// Create a single region over entire table, set the origin of the selection to the active cell so that a followup spread selection e.g. shift-right behaves properly
 			MultiSelectedRegions.Push (new TableSelection (new Point (SelectedColumn, SelectedRow), new Rect (0, 0, Table.Columns.Count, table.Rows.Count)));
@@ -893,16 +911,18 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Returns all cells in any <see cref="MultiSelectedRegions"/> (if <see cref="MultiSelect"/> is enabled) and the selected cell
 		/// </summary>
-		/// <remarks>Return value is not affected by <see cref="FullRowSelect"/> (i.e. returned <see cref="Point"/>s are not expanded to 
-		/// include all points on row).</remarks>
 		/// <returns></returns>
 		public IEnumerable<Point> GetAllSelectedCells ()
 		{
 			if (TableIsNullOrInvisible () || Table.Rows.Count == 0)
-				yield break;
+			{
+				return Enumerable.Empty<Point>();				
+			}
 
 			EnsureValidSelection ();
 
+			var toReturn = new HashSet<Point>();
+
 			// If there are one or more rectangular selections
 			if (MultiSelect && MultiSelectedRegions.Any ()) {
 
@@ -916,25 +936,27 @@ namespace Terminal.Gui {
 				for (int y = yMin; y < yMax; y++) {
 					for (int x = xMin; x < xMax; x++) {
 						if (IsSelected (x, y)) {
-							yield return new Point (x, y);
+							toReturn.Add(new Point (x, y));
 						}
 					}
 				}
-			} else {
+			} 
 
-				// if there are no region selections then it is just the active cell
 
-				// if we are selecting the full row
-				if (FullRowSelect) {
-					// all cells in active row are selected
-					for (int x = 0; x < Table.Columns.Count; x++) {
-						yield return new Point (x, SelectedRow);
-					}
-				} else {
-					// Not full row select and no multi selections
-					yield return new Point (SelectedColumn, SelectedRow);
+			// if there are no region selections then it is just the active cell
+
+			// if we are selecting the full row
+			if (FullRowSelect) {
+				// all cells in active row are selected
+				for (int x = 0; x < Table.Columns.Count; x++) {
+					toReturn.Add(new Point (x, SelectedRow));
 				}
+			} else {
+				// Not full row select and no multi selections
+				toReturn.Add(new Point (SelectedColumn, SelectedRow));
 			}
+
+			return toReturn;		
 		}
 
 		/// <summary>
@@ -944,17 +966,60 @@ namespace Terminal.Gui {
 		/// <param name="pt1Y">Origin point for the selection in Y</param>
 		/// <param name="pt2X">End point for the selection in X</param>
 		/// <param name="pt2Y">End point for the selection in Y</param>
+		/// <param name="toggle">True if selection is result of <see cref="Command.ToggleChecked"/></param>
 		/// <returns></returns>
-		private TableSelection CreateTableSelection (int pt1X, int pt1Y, int pt2X, int pt2Y)
+		private TableSelection CreateTableSelection (int pt1X, int pt1Y, int pt2X, int pt2Y, bool toggle = false)
 		{
-			var top = Math.Max(Math.Min (pt1Y, pt2Y), 0);
-			var bot = Math.Max(Math.Max (pt1Y, pt2Y), 0);
+			var top = Math.Max (Math.Min (pt1Y, pt2Y), 0);
+			var bot = Math.Max (Math.Max (pt1Y, pt2Y), 0);
 
-			var left = Math.Max(Math.Min (pt1X, pt2X), 0);
-			var right = Math.Max(Math.Max (pt1X, pt2X), 0);
+			var left = Math.Max (Math.Min (pt1X, pt2X), 0);
+			var right = Math.Max (Math.Max (pt1X, pt2X), 0);
 
 			// Rect class is inclusive of Top Left but exclusive of Bottom Right so extend by 1
-			return new TableSelection (new Point (pt1X, pt1Y), new Rect (left, top, right - left + 1, bot - top + 1));
+			return new TableSelection (new Point (pt1X, pt1Y), new Rect (left, top, right - left + 1, bot - top + 1)) {
+				IsToggled = toggle
+			};
+		}
+
+		private void ToggleCurrentCellSelection ()
+		{
+			if (!MultiSelect) {
+				return;
+			}
+
+			var regions = GetMultiSelectedRegionsContaining(selectedColumn, selectedRow).ToArray();
+			var toggles = regions.Where(s=>s.IsToggled).ToArray ();
+
+			// Toggle it off
+			if (toggles.Any ()) {
+
+				var oldRegions = MultiSelectedRegions.ToArray ().Reverse ();
+				MultiSelectedRegions.Clear ();
+
+				foreach (var region in oldRegions) {
+					if (!toggles.Contains (region))
+						MultiSelectedRegions.Push (region);
+				}
+			} else {
+				
+				// user is toggling selection within a rectangular
+				// select.  So toggle the full region
+				if(regions.Any())
+				{
+					foreach(var r in regions)
+					{
+						r.IsToggled = true;
+					}
+				}
+				else{
+					// Toggle on a single cell selection
+					MultiSelectedRegions.Push (
+					CreateTableSelection (selectedColumn, SelectedRow, selectedColumn, selectedRow, true)
+					);
+				}
+
+			}
 		}
 
 		/// <summary>
@@ -978,22 +1043,36 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public bool IsSelected (int col, int row)
 		{
-			if(!IsColumnVisible(col)) {
+			if (!IsColumnVisible (col)) {
 				return false;
-			}	
-
-			// Cell is also selected if in any multi selection region
-			if (MultiSelect && MultiSelectedRegions.Any (r => r.Rect.Contains (col, row)))
-				return true;
+			}
 
-			// Cell is also selected if Y axis appears in any region (when FullRowSelect is enabled)
-			if (FullRowSelect && MultiSelect && MultiSelectedRegions.Any (r => r.Rect.Bottom > row && r.Rect.Top <= row))
+			if(GetMultiSelectedRegionsContaining(col,row).Any())
+			{
 				return true;
+			}
 
 			return row == SelectedRow &&
 					(col == SelectedColumn || FullRowSelect);
 		}
 
+		private IEnumerable<TableSelection> GetMultiSelectedRegionsContaining(int col, int row)
+		{
+			if(!MultiSelect)
+			{
+				return Enumerable.Empty<TableSelection>();
+			}
+		
+			if(FullRowSelect)
+			{
+				return MultiSelectedRegions.Where (r => r.Rect.Bottom > row && r.Rect.Top <= row);
+			}
+			else
+			{
+				return MultiSelectedRegions.Where (r => r.Rect.Contains (col, row));
+			}
+		}
+
 		/// <summary>
 		/// Returns true if the given <paramref name="columnIndex"/> indexes a visible
 		/// column otherwise false.  Returns false for indexes that are out of bounds.
@@ -1071,19 +1150,17 @@ namespace Terminal.Gui {
 
 			if (me.Flags.HasFlag (MouseFlags.Button1Clicked)) {
 
-				if (scrollLeftPoint != null 
+				if (scrollLeftPoint != null
 					&& scrollLeftPoint.Value.X == me.X
-					&& scrollLeftPoint.Value.Y == me.Y)
-				{
+					&& scrollLeftPoint.Value.Y == me.Y) {
 					ColumnOffset--;
 					EnsureValidScrollOffsets ();
 					SetNeedsDisplay ();
 				}
 
-				if (scrollRightPoint != null 
+				if (scrollRightPoint != null
 					&& scrollRightPoint.Value.X == me.X
-					&& scrollRightPoint.Value.Y == me.Y)
-				{
+					&& scrollRightPoint.Value.Y == me.Y) {
 					ColumnOffset++;
 					EnsureValidScrollOffsets ();
 					SetNeedsDisplay ();
@@ -1092,8 +1169,8 @@ namespace Terminal.Gui {
 				var hit = ScreenToCell (me.X, me.Y);
 				if (hit != null) {
 
-					if(MultiSelect && HasControlOrAlt(me)) {
-						UnionSelection(hit.Value.X, hit.Value.Y);
+					if (MultiSelect && HasControlOrAlt (me)) {
+						UnionSelection (hit.Value.X, hit.Value.Y);
 					} else {
 						SetSelection (hit.Value.X, hit.Value.Y, me.Flags.HasFlag (MouseFlags.ButtonShift));
 					}
@@ -1128,7 +1205,7 @@ namespace Terminal.Gui {
 		/// <returns>Cell clicked or null.</returns>
 		public Point? ScreenToCell (int clientX, int clientY)
 		{
-			return ScreenToCell(clientX, clientY, out _);
+			return ScreenToCell (clientX, clientY, out _);
 		}
 
 		/// <inheritdoc cref="ScreenToCell(int, int)"/>
@@ -1153,7 +1230,7 @@ namespace Terminal.Gui {
 				headerIfAny = col?.Column;
 				return null;
 			}
-				
+
 
 			var rowIdx = RowOffset - headerHeight + clientY;
 
@@ -1161,7 +1238,7 @@ namespace Terminal.Gui {
 			// invalid index back to user!
 			if (rowIdx >= Table.Rows.Count) {
 				return null;
-			}	
+			}
 
 			if (col != null && rowIdx >= 0) {
 
@@ -1242,10 +1319,10 @@ namespace Terminal.Gui {
 		/// <remarks>Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDisplay()"/></remarks>
 		public void EnsureValidSelection ()
 		{
-			if (TableIsNullOrInvisible()) {
+			if (TableIsNullOrInvisible ()) {
 
 				// Table doesn't exist, we should probably clear those selections
-				MultiSelectedRegions.Clear ();
+				ClearMultiSelectedRegions (false);
 				return;
 			}
 
@@ -1315,8 +1392,7 @@ namespace Terminal.Gui {
 		/// Use false if you are primarily interested in learning about directional column visibility.</param>
 		private int GetNearestVisibleColumn (int columnIndex, bool lookRight, bool allowBumpingInOppositeDirection)
 		{
-			if(TryGetNearestVisibleColumn(columnIndex,lookRight,allowBumpingInOppositeDirection, out var answer))
-			{
+			if (TryGetNearestVisibleColumn (columnIndex, lookRight, allowBumpingInOppositeDirection, out var answer)) {
 				return answer;
 			}
 
@@ -1335,7 +1411,7 @@ namespace Terminal.Gui {
 			// get the column visibility by index (if no style visible is true)
 			bool [] columnVisibility = Table.Columns.Cast<DataColumn> ()
 				.Select (c => this.Style.GetColumnStyleIfAny (c)?.Visible ?? true)
-				.ToArray();
+				.ToArray ();
 
 			// column is visible
 			if (columnVisibility [columnIndex]) {
@@ -1346,10 +1422,9 @@ namespace Terminal.Gui {
 			int increment = lookRight ? 1 : -1;
 
 			// move in that direction
-			for (int i = columnIndex; i >=0 && i < columnVisibility.Length; i += increment) {
+			for (int i = columnIndex; i >= 0 && i < columnVisibility.Length; i += increment) {
 				// if we find a visible column
-				if(columnVisibility [i]) 
-				{
+				if (columnVisibility [i]) {
 					idx = i;
 					return true;
 				}
@@ -1357,7 +1432,7 @@ namespace Terminal.Gui {
 
 			// Caller only wants to look in one direction and we did not find any
 			// visible columns in that direction
-			if(!allowBumpingInOppositeDirection) {
+			if (!allowBumpingInOppositeDirection) {
 				idx = columnIndex;
 				return false;
 			}
@@ -1400,10 +1475,10 @@ namespace Terminal.Gui {
 			//if we have scrolled too far to the right
 			if (SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) {
 
-				if(Style.SmoothHorizontalScrolling) {
+				if (Style.SmoothHorizontalScrolling) {
 
 					// Scroll right 1 column at a time until the users selected column is visible
-					while(SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) {
+					while (SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) {
 
 						ColumnOffset++;
 						columnsToRender = CalculateViewport (Bounds).ToArray ();
@@ -1414,11 +1489,10 @@ namespace Terminal.Gui {
 							break;
 
 					}
-				}
-				else {
+				} else {
 					ColumnOffset = SelectedColumn;
 				}
-				
+
 			}
 
 			//if we have scrolled too far down
@@ -1482,7 +1556,7 @@ namespace Terminal.Gui {
 				int colWidth;
 
 				// if column is not being rendered
-				if(colStyle?.Visible == false) {
+				if (colStyle?.Visible == false) {
 					// do not add it to the returned columns
 					continue;
 				}
@@ -1492,16 +1566,14 @@ namespace Terminal.Gui {
 
 				// there is not enough space for this columns 
 				// visible content
-				if (usedSpace + colWidth > availableHorizontalSpace)
-				{
+				if (usedSpace + colWidth > availableHorizontalSpace) {
 					bool showColumn = false;
 
 					// if this column accepts flexible width rendering and
 					// is therefore happy rendering into less space
-					if ( colStyle != null && colStyle.MinAcceptableWidth > 0 &&
+					if (colStyle != null && colStyle.MinAcceptableWidth > 0 &&
 						// is there enough space to meet the MinAcceptableWidth
-						(availableHorizontalSpace - usedSpace) >= colStyle.MinAcceptableWidth)
-					{
+						(availableHorizontalSpace - usedSpace) >= colStyle.MinAcceptableWidth) {
 						// show column and use use whatever space is 
 						// left for rendering it
 						showColumn = true;
@@ -1510,14 +1582,13 @@ namespace Terminal.Gui {
 
 					// If its the only column we are able to render then
 					// accept it anyway (that must be one massively wide column!)
-					if (first)
-					{
+					if (first) {
 						showColumn = true;
 					}
 
 					// no special exceptions and we are out of space
 					// so stop accepting new columns for the render area
-					if(!showColumn)
+					if (!showColumn)
 						break;
 				}
 
@@ -1771,7 +1842,7 @@ namespace Terminal.Gui {
 			/// Delegate for coloring specific rows in a different color.  For cell color <see cref="ColumnStyle.ColorGetter"/>
 			/// </summary>
 			/// <value></value>
-			public RowColorGetterDelegate RowColorGetter {get;set;}
+			public RowColorGetterDelegate RowColorGetter { get; set; }
 
 			/// <summary>
 			/// Determines rendering when the last column in the table is visible but it's
@@ -1781,7 +1852,7 @@ namespace Terminal.Gui {
 			/// and leave a blank column that cannot be selected in the remaining space.  
 			/// </summary>
 			/// <value></value>
-			public bool ExpandLastColumn {get;set;} = true;
+			public bool ExpandLastColumn { get; set; } = true;
 
 			/// <summary>
 			/// <para>
@@ -1798,7 +1869,7 @@ namespace Terminal.Gui {
 			/// </para>
 			/// </summary>
 			public bool SmoothHorizontalScrolling { get; set; } = true;
-			
+
 			/// <summary>
 			/// Returns the entry from <see cref="ColumnStyles"/> for the given <paramref name="col"/> or null if no custom styling is defined for it
 			/// </summary>
@@ -2003,6 +2074,12 @@ namespace Terminal.Gui {
 			/// <value></value>
 			public Rect Rect { get; set; }
 
+			/// <summary>
+			/// True if the selection was made through <see cref="Command.ToggleChecked"/>
+			/// and therefore should persist even through keyboard navigation.
+			/// </summary>
+			public bool IsToggled { get; set; }
+
 			/// <summary>
 			/// Creates a new selected area starting at the origin corner and covering the provided rectangular area
 			/// </summary>

+ 33 - 17
Terminal.Gui/Views/TileView.cs

@@ -13,6 +13,12 @@ namespace Terminal.Gui {
 	public class TileView : View {
 		TileView parentTileView;
 
+		/// <summary>
+		/// The keyboard key that the user can press to toggle resizing
+		/// of splitter lines.  Mouse drag splitting is always enabled.
+		/// </summary>
+		public Key ToggleResizable { get; set; } = Key.CtrlMask | Key.F10;
+
 		/// <summary>
 		/// A single <see cref="ContentView"/> presented in a <see cref="TileView"/>. To create
 		/// new instances use <see cref="TileView.RebuildForTileCount(int)"/> 
@@ -167,7 +173,6 @@ namespace Terminal.Gui {
 		/// <param name="tiles"></param>
 		public TileView (int tiles)
 		{
-			CanFocus = true;
 			RebuildForTileCount (tiles);
 			IgnoreBorderPropertyOnRedraw = true;
 			Border = new Border () {
@@ -403,15 +408,6 @@ namespace Terminal.Gui {
 			return true;
 		}
 
-		/// <inheritdoc/>
-		public override bool OnEnter (View view)
-		{
-			Driver.SetCursorVisibility (CursorVisibility.Invisible);
-			if (!Tiles.Where (t => t.ContentView.HasFocus).Any ()) {
-				Tiles.FirstOrDefault ()?.ContentView.SetFocus ();
-			}
-			return base.OnEnter (view);
-		}
 
 		/// <inheritdoc/>
 		public override void Redraw (Rect bounds)
@@ -550,6 +546,30 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		/// <inheritdoc/>
+		public override bool ProcessHotKey (KeyEvent keyEvent)
+		{
+			bool focusMoved = false;
+
+			if(keyEvent.Key == ToggleResizable) {
+				foreach(var l in splitterLines) {
+
+					var iniBefore = l.IsInitialized;
+					l.IsInitialized = false;
+					l.CanFocus = !l.CanFocus;
+					l.IsInitialized = iniBefore;
+
+					if (l.CanFocus && !focusMoved) {
+						l.SetFocus ();
+						focusMoved = true;
+					}
+				}
+				return true;
+			}
+
+			return base.ProcessHotKey (keyEvent);
+		}
+
 		private bool IsValidNewSplitterPos (int idx, Pos value, int fullSpace)
 		{
 			int newSize = value.Anchor (fullSpace);
@@ -750,7 +770,7 @@ namespace Terminal.Gui {
 					tile.ContentView.Width = GetTileWidthOrHeight (i, Bounds.Width, visibleTiles, visibleSplitterLines);
 				} else {
 					tile.ContentView.X = bounds.X;
-					tile.ContentView.Y = i == 0 ? 0 : Pos.Bottom (visibleSplitterLines [i - 1]);
+					tile.ContentView.Y = i == 0 ? bounds.Y : Pos.Bottom (visibleSplitterLines [i - 1]);
 					tile.ContentView.Width = bounds.Width;
 					tile.ContentView.Height = GetTileWidthOrHeight (i, Bounds.Height, visibleTiles, visibleSplitterLines);
 				}
@@ -864,7 +884,7 @@ namespace Terminal.Gui {
 
 			public TileViewLineView (TileView parent, int idx)
 			{
-				CanFocus = true;
+				CanFocus = false;
 				TabStop = true;
 
 				this.Parent = parent;
@@ -929,7 +949,7 @@ namespace Terminal.Gui {
 
 			public void DrawSplitterSymbol ()
 			{
-				if (CanFocus && HasFocus) {
+				if (dragPosition != null || CanFocus) {
 					var location = moveRuneRenderLocation ??
 						new Point (Bounds.Width / 2, Bounds.Height / 2);
 
@@ -939,10 +959,6 @@ namespace Terminal.Gui {
 
 			public override bool MouseEvent (MouseEvent mouseEvent)
 			{
-				if (!CanFocus) {
-					return true;
-				}
-
 				if (!dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed)) {
 
 					// Start a Drag

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

@@ -79,6 +79,7 @@ namespace Terminal.Gui {
 			Modal = true;
 			ButtonAlignment = DefaultButtonAlignment;
 			Border = DefaultBorder;
+			//Border.Title = title;
 
 			if (buttons != null) {
 				foreach (var b in buttons) {

+ 6 - 14
UICatalog/Properties/launchSettings.json

@@ -3,6 +3,12 @@
     "UICatalog": {
       "commandName": "Project"
     },
+    "WSL : UICatalog": {
+      "commandName": "Executable",
+      "executablePath": "wsl",
+      "commandLineArgs": "dotnet UICatalog.dll",
+      "distributionName": ""
+    },
     "UICatalog -usc": {
       "commandName": "Project",
       "commandLineArgs": "-usc"
@@ -29,10 +35,6 @@
       "commandName": "Project",
       "commandLineArgs": "WizardAsView"
     },
-    "VkeyPacketSimulator": {
-      "commandName": "Project",
-      "commandLineArgs": "VkeyPacketSimulator"
-    },
     "CollectionNavigatorTester": {
       "commandName": "Project",
       "commandLineArgs": "\"Search Collection Nav\""
@@ -48,16 +50,6 @@
     "Windows & FrameViews": {
       "commandName": "Project",
       "commandLineArgs": "\"Windows & FrameViews\""
-    },
-    "WSL : UICatalog": {
-      "commandName": "Executable",
-      "executablePath": "wsl",
-      "commandLineArgs": "dotnet UICatalog.dll",
-      "distributionName": ""
-    },
-    "Tile View Experiments": {
-      "commandName": "Project",
-      "commandLineArgs": "\"Tile View Experiments\""
     }
   }
 }

+ 313 - 0
UICatalog/Scenarios/ASCIICustomButton.cs

@@ -0,0 +1,313 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios {
+	[ScenarioMetadata (Name: "ASCIICustomButtonTest", Description: "ASCIICustomButton sample")]
+	[ScenarioCategory ("Controls")]
+	public class ASCIICustomButtonTest : Scenario {
+		private static bool smallerWindow;
+		private ScrollViewTestWindow scrollViewTestWindow;
+		private MenuItem miSmallerWindow;
+
+		public override void Init (ColorScheme colorScheme)
+		{
+			Application.Init ();
+			scrollViewTestWindow = new ScrollViewTestWindow ();
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem("Window Size", new MenuItem [] {
+					miSmallerWindow = new MenuItem ("Smaller Window", "", ChangeWindowSize) {
+						CheckType = MenuItemCheckStyle.Checked
+					},
+					null,
+					new MenuItem("Quit", "",() => Application.RequestStop(),null,null, Key.Q | Key.CtrlMask)
+				})
+			});
+			Application.Top.Add (menu, scrollViewTestWindow);
+			Application.Run ();
+		}
+
+		private void ChangeWindowSize ()
+		{
+			smallerWindow = (bool)(miSmallerWindow.Checked = !miSmallerWindow.Checked);
+			scrollViewTestWindow.Dispose ();
+			Application.Top.Remove (scrollViewTestWindow);
+			scrollViewTestWindow = new ScrollViewTestWindow ();
+			Application.Top.Add (scrollViewTestWindow);
+		}
+
+		public override void Run ()
+		{
+		}
+
+		public class ASCIICustomButton : Button {
+			public string Description => $"Description of: {id}";
+
+			public event Action<ASCIICustomButton> PointerEnter;
+
+			private Label fill;
+			private FrameView border;
+			private string id;
+
+			public ASCIICustomButton (string text, Pos x, Pos y, int width, int height) : base (text)
+			{
+				CustomInitialize ("", text, x, y, width, height);
+			}
+
+			public ASCIICustomButton (string id, string text, Pos x, Pos y, int width, int height) : base (text)
+			{
+				CustomInitialize (id, text, x, y, width, height);
+			}
+
+			private void CustomInitialize (string id, string text, Pos x, Pos y, int width, int height)
+			{
+				this.id = id;
+				X = x;
+				Y = y;
+
+				Frame = new Rect {
+					Width = width,
+					Height = height
+				};
+
+				border = new FrameView () {
+					Width = width,
+					Height = height
+				};
+
+				AutoSize = false;
+
+				var fillText = new System.Text.StringBuilder ();
+				for (int i = 0; i < Bounds.Height; i++) {
+					if (i > 0) {
+						fillText.AppendLine ("");
+					}
+					for (int j = 0; j < Bounds.Width; j++) {
+						fillText.Append ("█");
+					}
+				}
+
+				fill = new Label (fillText.ToString ()) {
+					Visible = false,
+					CanFocus = false
+				};
+
+				var title = new Label (text) {
+					X = Pos.Center (),
+					Y = Pos.Center (),
+				};
+
+				border.MouseClick += This_MouseClick;
+				border.Subviews [0].MouseClick += This_MouseClick;
+				fill.MouseClick += This_MouseClick;
+				title.MouseClick += This_MouseClick;
+
+				Add (border, fill, title);
+			}
+
+			private void This_MouseClick (MouseEventArgs obj)
+			{
+				OnMouseEvent (obj.MouseEvent);
+			}
+
+			public override bool OnMouseEvent (MouseEvent mouseEvent)
+			{
+				Debug.WriteLine ($"{mouseEvent.Flags}");
+				if (mouseEvent.Flags == MouseFlags.Button1Clicked) {
+					if (!HasFocus && SuperView != null) {
+						if (!SuperView.HasFocus) {
+							SuperView.SetFocus ();
+						}
+						SetFocus ();
+						SetNeedsDisplay ();
+					}
+
+					OnClicked ();
+					return true;
+				}
+				return base.OnMouseEvent (mouseEvent);
+			}
+
+			public override bool OnEnter (View view)
+			{
+				border.Visible = false;
+				fill.Visible = true;
+				PointerEnter.Invoke (this);
+				view = this;
+				return base.OnEnter (view);
+			}
+
+			public override bool OnLeave (View view)
+			{
+				border.Visible = true;
+				fill.Visible = false;
+				if (view == null)
+					view = this;
+				return base.OnLeave (view);
+			}
+		}
+
+		public class ScrollViewTestWindow : Window {
+			private List<Button> buttons;
+			private const int BUTTONS_ON_PAGE = 7;
+			private const int BUTTON_HEIGHT = 3;
+
+			private ScrollView scrollView;
+			private ASCIICustomButton selected;
+
+			public ScrollViewTestWindow ()
+			{
+				Title = "ScrollViewTestWindow";
+
+				Label titleLabel = null;
+				if (smallerWindow) {
+					Width = 80;
+					Height = 25;
+
+					scrollView = new ScrollView () {
+						X = 3,
+						Y = 1,
+						Width = 24,
+						Height = BUTTONS_ON_PAGE * BUTTON_HEIGHT,
+						ShowVerticalScrollIndicator = true,
+						ShowHorizontalScrollIndicator = false
+					};
+				} else {
+					Width = Dim.Fill ();
+					Height = Dim.Fill ();
+
+					titleLabel = new Label ("DOCUMENTS") {
+						X = 0,
+						Y = 0
+					};
+
+					scrollView = new ScrollView () {
+						X = 0,
+						Y = 1,
+						Width = 27,
+						Height = BUTTONS_ON_PAGE * BUTTON_HEIGHT,
+						ShowVerticalScrollIndicator = true,
+						ShowHorizontalScrollIndicator = false
+					};
+				}
+
+				scrollView.ClearKeybindings ();
+
+				buttons = new List<Button> ();
+				Button prevButton = null;
+				int count = 20;
+				for (int j = 0; j < count; j++) {
+					Pos yPos = prevButton == null ? 0 : Pos.Bottom (prevButton);
+					var button = new ASCIICustomButton (j.ToString (), $"section {j}", 0, yPos, 25, BUTTON_HEIGHT);
+					button.Id = $"button{j}";
+					button.Clicked += Button_Clicked;
+					button.PointerEnter += Button_PointerEnter;
+					button.MouseClick += Button_MouseClick;
+					button.KeyPress += Button_KeyPress;
+					scrollView.Add (button);
+					buttons.Add (button);
+					prevButton = button;
+				}
+
+				var closeButton = new ASCIICustomButton ("close", "Close", 0, Pos.Bottom (prevButton), 25, BUTTON_HEIGHT);
+				closeButton.Clicked += Button_Clicked;
+				closeButton.PointerEnter += Button_PointerEnter;
+				closeButton.MouseClick += Button_MouseClick;
+				closeButton.KeyPress += Button_KeyPress;
+				scrollView.Add (closeButton);
+				buttons.Add (closeButton);
+
+				var pages = buttons.Count / BUTTONS_ON_PAGE;
+				if (buttons.Count % BUTTONS_ON_PAGE > 0)
+					pages++;
+
+				scrollView.ContentSize = new Size (25, pages * BUTTONS_ON_PAGE * BUTTON_HEIGHT);
+				if (smallerWindow) {
+					Add (scrollView);
+				} else {
+					Add (titleLabel, scrollView);
+				}
+			}
+
+			private void Button_KeyPress (KeyEventEventArgs obj)
+			{
+				switch (obj.KeyEvent.Key) {
+				case Key.End:
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						 -(scrollView.ContentSize.Height - scrollView.Frame.Height
+						 + (scrollView.ShowHorizontalScrollIndicator ? 1 : 0)));
+					obj.Handled = true;
+					return;
+				case Key.Home:
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X, 0);
+					obj.Handled = true;
+					return;
+				case Key.PageDown:
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						 Math.Max (scrollView.ContentOffset.Y - scrollView.Frame.Height,
+						 -(scrollView.ContentSize.Height - scrollView.Frame.Height
+						 + (scrollView.ShowHorizontalScrollIndicator ? 1 : 0))));
+					obj.Handled = true;
+					return;
+				case Key.PageUp:
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						 Math.Min (scrollView.ContentOffset.Y + scrollView.Frame.Height, 0));
+					obj.Handled = true;
+					return;
+				}
+			}
+
+			private void Button_MouseClick (MouseEventArgs obj)
+			{
+				if (obj.MouseEvent.Flags == MouseFlags.WheeledDown) {
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						scrollView.ContentOffset.Y - BUTTON_HEIGHT);
+					obj.Handled = true;
+				} else if (obj.MouseEvent.Flags == MouseFlags.WheeledUp) {
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						Math.Min (scrollView.ContentOffset.Y + BUTTON_HEIGHT, 0));
+					obj.Handled = true;
+				}
+			}
+
+			private void Button_Clicked ()
+			{
+				MessageBox.Query ("Button clicked.", $"'{selected.Text}' clicked!", "Ok");
+				if (selected.Text == "Close") {
+					Application.RequestStop ();
+				}
+			}
+
+			private void Button_PointerEnter (ASCIICustomButton obj)
+			{
+				bool? moveDown;
+				if (obj.Frame.Y > selected?.Frame.Y) {
+					moveDown = true;
+				} else if (obj.Frame.Y < selected?.Frame.Y) {
+					moveDown = false;
+				} else {
+					moveDown = null;
+				}
+				var offSet = selected != null ? obj.Frame.Y - selected.Frame.Y + (-scrollView.ContentOffset.Y % BUTTON_HEIGHT) : 0;
+				selected = obj;
+				if (moveDown == true && selected.Frame.Y + scrollView.ContentOffset.Y + BUTTON_HEIGHT >= scrollView.Frame.Height && offSet != BUTTON_HEIGHT) {
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						Math.Min (scrollView.ContentOffset.Y - BUTTON_HEIGHT, -(selected.Frame.Y - scrollView.Frame.Height + BUTTON_HEIGHT)));
+				} else if (moveDown == true && selected.Frame.Y + scrollView.ContentOffset.Y >= scrollView.Frame.Height) {
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						scrollView.ContentOffset.Y - BUTTON_HEIGHT);
+				} else if (moveDown == true && selected.Frame.Y + scrollView.ContentOffset.Y < 0) {
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						-selected.Frame.Y);
+				} else if (moveDown == false && selected.Frame.Y < -scrollView.ContentOffset.Y) {
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						Math.Max (scrollView.ContentOffset.Y + BUTTON_HEIGHT, selected.Frame.Y));
+				} else if (moveDown == false && selected.Frame.Y + scrollView.ContentOffset.Y > scrollView.Frame.Height) {
+					scrollView.ContentOffset = new Point (scrollView.ContentOffset.X,
+						 -(selected.Frame.Y - scrollView.Frame.Height + BUTTON_HEIGHT));
+				}
+			}
+		}
+	}
+}

+ 6 - 5
UICatalog/Scenarios/BordersComparisons.cs

@@ -12,9 +12,10 @@ namespace UICatalog.Scenarios {
 			var borderStyle = BorderStyle.Double;
 			var drawMarginFrame = false;
 			var borderThickness = new Thickness (1, 2, 3, 4);
-			var borderBrush = Colors.Base.HotFocus.Foreground;
+			var borderBrush = Color.BrightMagenta;
+			;
 			var padding = new Thickness (1, 2, 3, 4);
-			var background = Colors.Base.HotNormal.Foreground;
+			var background = Color.Cyan;
 			var effect3D = true;
 
 			var win = new Window (new Rect (5, 5, 40, 20), "Test", 8,
@@ -37,7 +38,7 @@ namespace UICatalog.Scenarios {
 			button.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a Window?", "Yes", "No");
 			var label = new Label ("I'm a Window") {
 				X = Pos.Center (),
-				Y = Pos.Center () - 3,
+				Y = Pos.Center () - 1,
 			};
 			var tv = new TextView () {
 				Y = Pos.AnchorEnd (2),
@@ -76,7 +77,7 @@ namespace UICatalog.Scenarios {
 			button2.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a Toplevel?", "Yes", "No");
 			var label2 = new Label ("I'm a Toplevel") {
 				X = Pos.Center (),
-				Y = Pos.Center () - 3,
+				Y = Pos.Center () - 1,
 			};
 			var tv2 = new TextView () {
 				Y = Pos.AnchorEnd (2),
@@ -112,7 +113,7 @@ namespace UICatalog.Scenarios {
 			button3.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a FrameView?", "Yes", "No");
 			var label3 = new Label ("I'm a FrameView") {
 				X = Pos.Center (),
-				Y = Pos.Center () - 3,
+				Y = Pos.Center () - 1,
 			};
 			var tv3 = new TextView () {
 				Y = Pos.AnchorEnd (2),

+ 52 - 55
UICatalog/Scenarios/BordersOnToplevel.cs → UICatalog/Scenarios/BordersOnContainers.cs

@@ -4,11 +4,8 @@ using System.Linq;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios {
-	[ScenarioMetadata (Name: "Borders on Toplevel", Description: "Demonstrates Toplevel borders manipulation.")]
-	[ScenarioCategory ("Layout")]
-	[ScenarioCategory ("Borders")]
-	public class BordersOnToplevel : Scenario {
-		public override void Setup ()
+	public class BordersOnContainers : Window {
+		public BordersOnContainers (NStack.ustring title, string typeName, View smartView)
 		{
 			var borderStyle = BorderStyle.Double;
 			var drawMarginFrame = false;
@@ -18,23 +15,21 @@ namespace UICatalog.Scenarios {
 			var background = Colors.Base.HotNormal.Foreground;
 			var effect3D = true;
 
-			var smartView = new Window () {
-				X = Pos.Center (),
-				Y = 0, // Y is set below 
-				Width = 40,
-				Height = 20,
-				Border = new Border () {
-					BorderStyle = borderStyle,
-					DrawMarginFrame = drawMarginFrame,
-					BorderThickness = borderThickness,
-					BorderBrush = borderBrush,
-					Padding = padding,
-					Background = background,
-					Effect3D = effect3D,
-				},
-				Title = "Toplevel",
-				ColorScheme = Colors.TopLevel
-			};
+			smartView.X = Pos.Center ();
+			smartView.Y = 0;
+			smartView.Width = 40;
+			smartView.Height = 20;
+			smartView.Border = new Border () {
+				BorderStyle = borderStyle,
+				DrawMarginFrame = drawMarginFrame,
+				BorderThickness = borderThickness,
+				BorderBrush = borderBrush,
+				Padding = padding,
+				Background = background,
+				Effect3D = effect3D,
+				//Title = typeName
+			};
+			smartView.ColorScheme = Colors.TopLevel;
 
 			var tf1 = new TextField ("1234567890") { Width = 10 };
 
@@ -42,10 +37,10 @@ namespace UICatalog.Scenarios {
 				X = Pos.Center (),
 				Y = Pos.Center (),
 			};
-			button.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a Toplevel?", "Yes", "No");
-			var label = new Label ("I'm a Toplevel") {
+			button.Clicked += () => MessageBox.Query (20, 7, "Hi", $"I'm a {typeName}?", "Yes", "No");
+			var label = new Label ($"I'm a {typeName}") {
 				X = Pos.Center (),
-				Y = Pos.Center () - 3,
+				Y = Pos.Center () - 1,
 			};
 			var tf2 = new TextField ("1234567890") {
 				X = Pos.AnchorEnd (10),
@@ -60,7 +55,7 @@ namespace UICatalog.Scenarios {
 			};
 			smartView.Add (tf1, button, label, tf2, tv);
 
-			Win.Add (new Label ("Padding:") {
+			Add (new Label ("Padding:") {
 				X = Pos.Center () - 23,
 			});
 
@@ -82,7 +77,7 @@ namespace UICatalog.Scenarios {
 			};
 			paddingTopEdit.Text = $"{smartView.Border.Padding.Top}";
 
-			Win.Add (paddingTopEdit);
+			Add (paddingTopEdit);
 
 			var paddingLeftEdit = new TextField ("") {
 				X = Pos.Center () - 30,
@@ -101,7 +96,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 			paddingLeftEdit.Text = $"{smartView.Border.Padding.Left}";
-			Win.Add (paddingLeftEdit);
+			Add (paddingLeftEdit);
 
 			var paddingRightEdit = new TextField ("") {
 				X = Pos.Center () - 15,
@@ -120,7 +115,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 			paddingRightEdit.Text = $"{smartView.Border.Padding.Right}";
-			Win.Add (paddingRightEdit);
+			Add (paddingRightEdit);
 
 			var paddingBottomEdit = new TextField ("") {
 				X = Pos.Center () - 22,
@@ -139,10 +134,10 @@ namespace UICatalog.Scenarios {
 				}
 			};
 			paddingBottomEdit.Text = $"{smartView.Border.Padding.Bottom}";
-			Win.Add (paddingBottomEdit);
+			Add (paddingBottomEdit);
 
 			var replacePadding = new Button ("Replace all based on top") {
-				X = Pos.Left(paddingLeftEdit),
+				X = Pos.Left (paddingLeftEdit),
 				Y = 5
 			};
 			replacePadding.Clicked += () => {
@@ -152,9 +147,9 @@ namespace UICatalog.Scenarios {
 				}
 				paddingBottomEdit.Text = paddingLeftEdit.Text = paddingRightEdit.Text = paddingTopEdit.Text;
 			};
-			Win.Add (replacePadding);
+			Add (replacePadding);
 
-			Win.Add (new Label ("Border:") {
+			Add (new Label ("Border:") {
 				X = Pos.Center () + 11,
 			});
 
@@ -176,7 +171,7 @@ namespace UICatalog.Scenarios {
 			};
 			borderTopEdit.Text = $"{smartView.Border.BorderThickness.Top}";
 
-			Win.Add (borderTopEdit);
+			Add (borderTopEdit);
 
 			var borderLeftEdit = new TextField ("") {
 				X = Pos.Center () + 5,
@@ -195,7 +190,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 			borderLeftEdit.Text = $"{smartView.Border.BorderThickness.Left}";
-			Win.Add (borderLeftEdit);
+			Add (borderLeftEdit);
 
 			var borderRightEdit = new TextField ("") {
 				X = Pos.Center () + 19,
@@ -214,7 +209,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 			borderRightEdit.Text = $"{smartView.Border.BorderThickness.Right}";
-			Win.Add (borderRightEdit);
+			Add (borderRightEdit);
 
 			var borderBottomEdit = new TextField ("") {
 				X = Pos.Center () + 12,
@@ -233,10 +228,10 @@ namespace UICatalog.Scenarios {
 				}
 			};
 			borderBottomEdit.Text = $"{smartView.Border.BorderThickness.Bottom}";
-			Win.Add (borderBottomEdit);
+			Add (borderBottomEdit);
 
 			var replaceBorder = new Button ("Replace all based on top") {
-				X = Pos.Left(borderLeftEdit),
+				X = Pos.Left (borderLeftEdit),
 				Y = 5
 			};
 			replaceBorder.Clicked += () => {
@@ -246,11 +241,11 @@ namespace UICatalog.Scenarios {
 				}
 				borderBottomEdit.Text = borderLeftEdit.Text = borderRightEdit.Text = borderTopEdit.Text;
 			};
-			Win.Add (replaceBorder);
+			Add (replaceBorder);
 
-			smartView.Y = Pos.Bottom (replaceBorder) + 1;
+			smartView.Y = Pos.Center () + 4;
 
-			Win.Add (new Label ("BorderStyle:"));
+			Add (new Label ("BorderStyle:"));
 
 			var borderStyleEnum = Enum.GetValues (typeof (BorderStyle)).Cast<BorderStyle> ().ToList ();
 			var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
@@ -260,7 +255,7 @@ namespace UICatalog.Scenarios {
 				Y = 1,
 				SelectedItem = (int)smartView.Border.BorderStyle
 			};
-			Win.Add (rbBorderStyle);
+			Add (rbBorderStyle);
 
 			var cbDrawMarginFrame = new CheckBox ("Draw Margin Frame", smartView.Border.DrawMarginFrame) {
 				X = Pos.AnchorEnd (20),
@@ -275,7 +270,7 @@ namespace UICatalog.Scenarios {
 					}
 				} catch { }
 			};
-			Win.Add (cbDrawMarginFrame);
+			Add (cbDrawMarginFrame);
 
 			rbBorderStyle.SelectedItemChanged += (e) => {
 				smartView.Border.BorderStyle = (BorderStyle)e.SelectedItem;
@@ -290,13 +285,13 @@ namespace UICatalog.Scenarios {
 				Y = 1,
 				Width = 5
 			};
-			Win.Add (cbEffect3D);
+			Add (cbEffect3D);
 
-			Win.Add (new Label ("Effect3D Offset:") {
+			Add (new Label ("Effect3D Offset:") {
 				X = Pos.AnchorEnd (20),
 				Y = 2
 			});
-			Win.Add (new Label ("X:") {
+			Add (new Label ("X:") {
 				X = Pos.AnchorEnd (19),
 				Y = 3
 			});
@@ -317,9 +312,9 @@ namespace UICatalog.Scenarios {
 				}
 			};
 			effect3DOffsetX.Text = $"{smartView.Border.Effect3DOffset.X}";
-			Win.Add (effect3DOffsetX);
+			Add (effect3DOffsetX);
 
-			Win.Add (new Label ("Y:") {
+			Add (new Label ("Y:") {
 				X = Pos.AnchorEnd (10),
 				Y = 3
 			});
@@ -340,7 +335,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 			effect3DOffsetY.Text = $"{smartView.Border.Effect3DOffset.Y}";
-			Win.Add (effect3DOffsetY);
+			Add (effect3DOffsetY);
 
 			cbEffect3D.Toggled += (e) => {
 				try {
@@ -349,7 +344,7 @@ namespace UICatalog.Scenarios {
 				} catch { }
 			};
 
-			Win.Add (new Label ("Background:") {
+			Add (new Label ("Background:") {
 				Y = 5
 			});
 
@@ -364,9 +359,9 @@ namespace UICatalog.Scenarios {
 			rbBackground.SelectedItemChanged += (e) => {
 				smartView.Border.Background = (Color)e.SelectedItem;
 			};
-			Win.Add (rbBackground);
+			Add (rbBackground);
 
-			Win.Add (new Label ("BorderBrush:") {
+			Add (new Label ("BorderBrush:") {
 				X = Pos.AnchorEnd (20),
 				Y = 5
 			});
@@ -381,9 +376,11 @@ namespace UICatalog.Scenarios {
 			rbBorderBrush.SelectedItemChanged += (e) => {
 				smartView.Border.BorderBrush = (Color)e.SelectedItem;
 			};
-			Win.Add (rbBorderBrush);
+			Add (rbBorderBrush);
+
+			Add (smartView);
 
-			Win.Add (smartView);
+			Title = title;
 		}
 	}
-}
+}

+ 12 - 376
UICatalog/Scenarios/BordersOnFrameView.cs

@@ -1,389 +1,25 @@
-using System;
-using System.Globalization;
-using System.Linq;
-using Terminal.Gui;
+using Terminal.Gui;
 
 namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Borders on FrameView", Description: "Demonstrate FrameView borders manipulation.")]
 	[ScenarioCategory ("Layout")]
 	[ScenarioCategory ("Borders")]
 	public class BordersOnFrameView : Scenario {
-		public override void Setup ()
+		public override void Init (ColorScheme colorScheme)
 		{
-			var borderStyle = BorderStyle.Double;
-			var drawMarginFrame = false;
-			var borderThickness = new Thickness (1, 2, 3, 4);
-			var borderBrush = Colors.Base.HotFocus.Foreground;
-			var padding = new Thickness (1, 2, 3, 4);
-			var background = Colors.Base.HotNormal.Foreground;
-			var effect3D = true;
+			Application.Init ();
 
-			var smartView = new FrameView () {
-				X = Pos.Center (),
-				Y = 0, // Y is set below 
-				Width = 40,
-				Height = 20,
-				Border = new Border () {
-					BorderStyle = borderStyle,
-					DrawMarginFrame = drawMarginFrame,
-					BorderThickness = borderThickness,
-					BorderBrush = borderBrush,
-					Padding = padding,
-					Background = background,
-					Effect3D = effect3D,
-				},
-				Title = "Frame",
-				ColorScheme = Colors.TopLevel
-			};
+			var boc = new BordersOnContainers (
+				$"CTRL-Q to Close - Scenario: {GetName ()}",
+				"FrameView",
+				new FrameView ());
 
-			var tf1 = new TextField ("1234567890") { Width = 10 };
-
-			var button = new Button ("Press me!") {
-				X = Pos.Center (),
-				Y = Pos.Center (),
-			};
-			button.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a FrameView?", "Yes", "No");
-			var label = new Label ("I'm a FrameView") {
-				X = Pos.Center (),
-				Y = Pos.Center () - 3,
-			};
-			var tf2 = new TextField ("1234567890") {
-				X = Pos.AnchorEnd (10),
-				Y = Pos.AnchorEnd (1),
-				Width = 10
-			};
-			var tv = new TextView () {
-				Y = Pos.AnchorEnd (2),
-				Width = 10,
-				Height = Dim.Fill (),
-				Text = "1234567890"
-			};
-			smartView.Add (tf1, button, label, tf2, tv);
-
-			Win.Add (new Label ("Padding:") {
-				X = Pos.Center () - 23,
-			});
-
-			var paddingTopEdit = new TextField ("") {
-				X = Pos.Center () - 22,
-				Y = 1,
-				Width = 5
-			};
-			paddingTopEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.Padding = new Thickness (smartView.Border.Padding.Left,
-						int.Parse (e.NewText.ToString ()), smartView.Border.Padding.Right,
-						smartView.Border.Padding.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingTopEdit.Text = $"{smartView.Border.Padding.Top}";
-
-			Win.Add (paddingTopEdit);
-
-			var paddingLeftEdit = new TextField ("") {
-				X = Pos.Center () - 30,
-				Y = 2,
-				Width = 5
-			};
-			paddingLeftEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.Padding = new Thickness (int.Parse (e.NewText.ToString ()),
-						smartView.Border.Padding.Top, smartView.Border.Padding.Right,
-						smartView.Border.Padding.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingLeftEdit.Text = $"{smartView.Border.Padding.Left}";
-			Win.Add (paddingLeftEdit);
-
-			var paddingRightEdit = new TextField ("") {
-				X = Pos.Center () - 15,
-				Y = 2,
-				Width = 5
-			};
-			paddingRightEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.Padding = new Thickness (smartView.Border.Padding.Left,
-						smartView.Border.Padding.Top, int.Parse (e.NewText.ToString ()),
-						smartView.Border.Padding.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingRightEdit.Text = $"{smartView.Border.Padding.Right}";
-			Win.Add (paddingRightEdit);
-
-			var paddingBottomEdit = new TextField ("") {
-				X = Pos.Center () - 22,
-				Y = 3,
-				Width = 5
-			};
-			paddingBottomEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.Padding = new Thickness (smartView.Border.Padding.Left,
-						smartView.Border.Padding.Top, smartView.Border.Padding.Right,
-						int.Parse (e.NewText.ToString ()));
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingBottomEdit.Text = $"{smartView.Border.Padding.Bottom}";
-			Win.Add (paddingBottomEdit);
-
-			var replacePadding = new Button ("Replace all based on top") {
-				X = Pos.Left (paddingLeftEdit),
-				Y = 5
-			};
-			replacePadding.Clicked += () => {
-				smartView.Border.Padding = new Thickness (smartView.Border.Padding.Top);
-				if (paddingTopEdit.Text.IsEmpty) {
-					paddingTopEdit.Text = "0";
-				}
-				paddingBottomEdit.Text = paddingLeftEdit.Text = paddingRightEdit.Text = paddingTopEdit.Text;
-			};
-			Win.Add (replacePadding);
-
-			Win.Add (new Label ("Border:") {
-				X = Pos.Center () + 11,
-			});
-
-			var borderTopEdit = new TextField ("") {
-				X = Pos.Center () + 12,
-				Y = 1,
-				Width = 5
-			};
-			borderTopEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.BorderThickness = new Thickness (smartView.Border.BorderThickness.Left,
-						int.Parse (e.NewText.ToString ()), smartView.Border.BorderThickness.Right,
-						smartView.Border.BorderThickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderTopEdit.Text = $"{smartView.Border.BorderThickness.Top}";
-
-			Win.Add (borderTopEdit);
-
-			var borderLeftEdit = new TextField ("") {
-				X = Pos.Center () + 5,
-				Y = 2,
-				Width = 5
-			};
-			borderLeftEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.BorderThickness = new Thickness (int.Parse (e.NewText.ToString ()),
-						smartView.Border.BorderThickness.Top, smartView.Border.BorderThickness.Right,
-						smartView.Border.BorderThickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderLeftEdit.Text = $"{smartView.Border.BorderThickness.Left}";
-			Win.Add (borderLeftEdit);
-
-			var borderRightEdit = new TextField ("") {
-				X = Pos.Center () + 19,
-				Y = 2,
-				Width = 5
-			};
-			borderRightEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.BorderThickness = new Thickness (smartView.Border.BorderThickness.Left,
-						smartView.Border.BorderThickness.Top, int.Parse (e.NewText.ToString ()),
-						smartView.Border.BorderThickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderRightEdit.Text = $"{smartView.Border.BorderThickness.Right}";
-			Win.Add (borderRightEdit);
-
-			var borderBottomEdit = new TextField ("") {
-				X = Pos.Center () + 12,
-				Y = 3,
-				Width = 5
-			};
-			borderBottomEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.BorderThickness = new Thickness (smartView.Border.BorderThickness.Left,
-						smartView.Border.BorderThickness.Top, smartView.Border.BorderThickness.Right,
-						int.Parse (e.NewText.ToString ()));
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderBottomEdit.Text = $"{smartView.Border.BorderThickness.Bottom}";
-			Win.Add (borderBottomEdit);
-
-			var replaceBorder = new Button ("Replace all based on top") {
-				X = Pos.Left (borderLeftEdit),
-				Y = 5
-			};
-			replaceBorder.Clicked += () => {
-				smartView.Border.BorderThickness = new Thickness (smartView.Border.BorderThickness.Top);
-				if (borderTopEdit.Text.IsEmpty) {
-					borderTopEdit.Text = "0";
-				}
-				borderBottomEdit.Text = borderLeftEdit.Text = borderRightEdit.Text = borderTopEdit.Text;
-			};
-			Win.Add (replaceBorder);
-
-			smartView.Y = Pos.Bottom (replaceBorder) + 1;
-
-			Win.Add (new Label ("BorderStyle:"));
-
-			var borderStyleEnum = Enum.GetValues (typeof (BorderStyle)).Cast<BorderStyle> ().ToList ();
-			var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
-				e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
-
-				X = 2,
-				Y = 1,
-				SelectedItem = (int)smartView.Border.BorderStyle
-			};
-			Win.Add (rbBorderStyle);
-
-			var cbDrawMarginFrame = new CheckBox ("Draw Margin Frame", smartView.Border.DrawMarginFrame) {
-				X = Pos.AnchorEnd (20),
-				Y = 0,
-				Width = 5
-			};
-			cbDrawMarginFrame.Toggled += (e) => {
-				try {
-					smartView.Border.DrawMarginFrame = (bool)cbDrawMarginFrame.Checked;
-					if (cbDrawMarginFrame.Checked != smartView.Border.DrawMarginFrame) {
-						cbDrawMarginFrame.Checked = smartView.Border.DrawMarginFrame;
-					}
-				} catch { }
-			};
-			Win.Add (cbDrawMarginFrame);
-
-			rbBorderStyle.SelectedItemChanged += (e) => {
-				smartView.Border.BorderStyle = (BorderStyle)e.SelectedItem;
-				smartView.SetNeedsDisplay ();
-				if (cbDrawMarginFrame.Checked != smartView.Border.DrawMarginFrame) {
-					cbDrawMarginFrame.Checked = smartView.Border.DrawMarginFrame;
-				}
-			};
-
-			var cbEffect3D = new CheckBox ("Draw 3D effects", smartView.Border.Effect3D) {
-				X = Pos.AnchorEnd (20),
-				Y = 1,
-				Width = 5
-			};
-			Win.Add (cbEffect3D);
-
-			Win.Add (new Label ("Effect3D Offset:") {
-				X = Pos.AnchorEnd (20),
-				Y = 2
-			});
-			Win.Add (new Label ("X:") {
-				X = Pos.AnchorEnd (19),
-				Y = 3
-			});
-
-			var effect3DOffsetX = new TextField ("") {
-				X = Pos.AnchorEnd (16),
-				Y = 3,
-				Width = 5
-			};
-			effect3DOffsetX.TextChanging += (e) => {
-				try {
-					smartView.Border.Effect3DOffset = new Point (int.Parse (e.NewText.ToString ()),
-						smartView.Border.Effect3DOffset.Y);
-				} catch {
-					if (!e.NewText.IsEmpty && e.NewText != CultureInfo.CurrentCulture.NumberFormat.NegativeSign) {
-						e.Cancel = true;
-					}
-				}
-			};
-			effect3DOffsetX.Text = $"{smartView.Border.Effect3DOffset.X}";
-			Win.Add (effect3DOffsetX);
-
-			Win.Add (new Label ("Y:") {
-				X = Pos.AnchorEnd (10),
-				Y = 3
-			});
-
-			var effect3DOffsetY = new TextField ("") {
-				X = Pos.AnchorEnd (7),
-				Y = 3,
-				Width = 5
-			};
-			effect3DOffsetY.TextChanging += (e) => {
-				try {
-					smartView.Border.Effect3DOffset = new Point (smartView.Border.Effect3DOffset.X,
-						int.Parse (e.NewText.ToString ()));
-				} catch {
-					if (!e.NewText.IsEmpty && e.NewText != CultureInfo.CurrentCulture.NumberFormat.NegativeSign) {
-						e.Cancel = true;
-					}
-				}
-			};
-			effect3DOffsetY.Text = $"{smartView.Border.Effect3DOffset.Y}";
-			Win.Add (effect3DOffsetY);
-
-			cbEffect3D.Toggled += (e) => {
-				try {
-					smartView.Border.Effect3D = effect3DOffsetX.Enabled =
-						effect3DOffsetY.Enabled = (bool)cbEffect3D.Checked;
-				} catch { }
-			};
-
-			Win.Add (new Label ("Background:") {
-				Y = 5
-			});
-
-			var colorEnum = Enum.GetValues (typeof (Color)).Cast<Color> ().ToList ();
-			var rbBackground = new RadioGroup (colorEnum.Select (
-				e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
-
-				X = 2,
-				Y = 6,
-				SelectedItem = (int)smartView.Border.Background
-			};
-			rbBackground.SelectedItemChanged += (e) => {
-				smartView.Border.Background = (Color)e.SelectedItem;
-			};
-			Win.Add (rbBackground);
-
-			Win.Add (new Label ("BorderBrush:") {
-				X = Pos.AnchorEnd (20),
-				Y = 5
-			});
-
-			var rbBorderBrush = new RadioGroup (colorEnum.Select (
-				e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
-
-				X = Pos.AnchorEnd (18),
-				Y = 6,
-				SelectedItem = (int)smartView.Border.BorderBrush
-			};
-			rbBorderBrush.SelectedItemChanged += (e) => {
-				smartView.Border.BorderBrush = (Color)e.SelectedItem;
-			};
-			Win.Add (rbBorderBrush);
+			Application.Run (boc);
+			Application.Shutdown ();
+		}
 
-			Win.Add (smartView);
+		public override void Run ()
+		{
 		}
 	}
 }

+ 12 - 375
UICatalog/Scenarios/BordersOnWindow.cs

@@ -1,388 +1,25 @@
-using System;
-using System.Globalization;
-using System.Linq;
-using Terminal.Gui;
+using Terminal.Gui;
 
 namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Borders on Window", Description: "Demonstrates Window borders manipulation.")]
 	[ScenarioCategory ("Layout")]
 	[ScenarioCategory ("Borders")]
 	public class BordersOnWindow : Scenario {
-		public override void Setup ()
+		public override void Init (ColorScheme colorScheme)
 		{
-			var borderStyle = BorderStyle.Double;
-			var drawMarginFrame = false;
-			var borderThickness = new Thickness (1, 2, 3, 4);
-			var borderBrush = Colors.Base.HotFocus.Foreground;
-			var padding = new Thickness (1, 2, 3, 4);
-			var background = Colors.Base.HotNormal.Foreground;
-			var effect3D = true;
+			Application.Init ();
 
-			var smartView = new Window () {
-				X = Pos.Center (),
-				Width = 40,
-				Height = 20,
-				Border = new Border () {
-					BorderStyle = borderStyle,
-					DrawMarginFrame = drawMarginFrame,
-					BorderThickness = borderThickness,
-					BorderBrush = borderBrush,
-					Padding = padding,
-					Background = background,
-					Effect3D = effect3D,
-				},
-				Title = "Window",
-				ColorScheme = Colors.TopLevel
-			};
+			var boc = new BordersOnContainers (
+				$"CTRL-Q to Close - Scenario: {GetName ()}",
+				"Window",
+				new Window ());
 
-			var tf1 = new TextField ("1234567890") { Width = 10 };
-
-			var button = new Button ("Press me!") {
-				X = Pos.Center (),
-				Y = Pos.Center (),
-			};
-			button.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a Window?", "Yes", "No");
-			var label = new Label ("I'm a Window") {
-				X = Pos.Center (),
-				Y = Pos.Center () - 3,
-			};
-			var tf2 = new TextField ("1234567890") {
-				X = Pos.AnchorEnd (10),
-				Y = Pos.AnchorEnd (1),
-				Width = 10
-			};
-			var tv = new TextView () {
-				Y = Pos.AnchorEnd (2),
-				Width = 10,
-				Height = Dim.Fill (),
-				Text = "1234567890"
-			};
-			smartView.Add (tf1, button, label, tf2, tv);
-
-			Win.Add (new Label ("Padding:") {
-				X = Pos.Center () - 23,
-			});
-
-			var paddingTopEdit = new TextField ("") {
-				X = Pos.Center () - 22,
-				Y = 1,
-				Width = 5
-			};
-			paddingTopEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.Padding = new Thickness (smartView.Border.Padding.Left,
-						int.Parse (e.NewText.ToString ()), smartView.Border.Padding.Right,
-						smartView.Border.Padding.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingTopEdit.Text = $"{smartView.Border.Padding.Top}";
-
-			Win.Add (paddingTopEdit);
-
-			var paddingLeftEdit = new TextField ("") {
-				X = Pos.Center () - 30,
-				Y = 2,
-				Width = 5
-			};
-			paddingLeftEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.Padding = new Thickness (int.Parse (e.NewText.ToString ()),
-						smartView.Border.Padding.Top, smartView.Border.Padding.Right,
-						smartView.Border.Padding.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingLeftEdit.Text = $"{smartView.Border.Padding.Left}";
-			Win.Add (paddingLeftEdit);
-
-			var paddingRightEdit = new TextField ("") {
-				X = Pos.Center () - 15,
-				Y = 2,
-				Width = 5
-			};
-			paddingRightEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.Padding = new Thickness (smartView.Border.Padding.Left,
-						smartView.Border.Padding.Top, int.Parse (e.NewText.ToString ()),
-						smartView.Border.Padding.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingRightEdit.Text = $"{smartView.Border.Padding.Right}";
-			Win.Add (paddingRightEdit);
-
-			var paddingBottomEdit = new TextField ("") {
-				X = Pos.Center () - 22,
-				Y = 3,
-				Width = 5
-			};
-			paddingBottomEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.Padding = new Thickness (smartView.Border.Padding.Left,
-						smartView.Border.Padding.Top, smartView.Border.Padding.Right,
-						int.Parse (e.NewText.ToString ()));
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingBottomEdit.Text = $"{smartView.Border.Padding.Bottom}";
-			Win.Add (paddingBottomEdit);
-
-			var replacePadding = new Button ("Replace all based on top") {
-				X = Pos.Left (paddingLeftEdit),
-				Y = 5
-			};
-			replacePadding.Clicked += () => {
-				smartView.Border.Padding = new Thickness (smartView.Border.Padding.Top);
-				if (paddingTopEdit.Text.IsEmpty) {
-					paddingTopEdit.Text = "0";
-				}
-				paddingBottomEdit.Text = paddingLeftEdit.Text = paddingRightEdit.Text = paddingTopEdit.Text;
-			};
-			Win.Add (replacePadding);
-
-			Win.Add (new Label ("Border:") {
-				X = Pos.Center () + 11,
-			});
-
-			var borderTopEdit = new TextField ("") {
-				X = Pos.Center () + 12,
-				Y = 1,
-				Width = 5
-			};
-			borderTopEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.BorderThickness = new Thickness (smartView.Border.BorderThickness.Left,
-						int.Parse (e.NewText.ToString ()), smartView.Border.BorderThickness.Right,
-						smartView.Border.BorderThickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderTopEdit.Text = $"{smartView.Border.BorderThickness.Top}";
-
-			Win.Add (borderTopEdit);
-
-			var borderLeftEdit = new TextField ("") {
-				X = Pos.Center () + 5,
-				Y = 2,
-				Width = 5
-			};
-			borderLeftEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.BorderThickness = new Thickness (int.Parse (e.NewText.ToString ()),
-						smartView.Border.BorderThickness.Top, smartView.Border.BorderThickness.Right,
-						smartView.Border.BorderThickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderLeftEdit.Text = $"{smartView.Border.BorderThickness.Left}";
-			Win.Add (borderLeftEdit);
-
-			var borderRightEdit = new TextField ("") {
-				X = Pos.Center () + 19,
-				Y = 2,
-				Width = 5
-			};
-			borderRightEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.BorderThickness = new Thickness (smartView.Border.BorderThickness.Left,
-						smartView.Border.BorderThickness.Top, int.Parse (e.NewText.ToString ()),
-						smartView.Border.BorderThickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderRightEdit.Text = $"{smartView.Border.BorderThickness.Right}";
-			Win.Add (borderRightEdit);
-
-			var borderBottomEdit = new TextField ("") {
-				X = Pos.Center () + 12,
-				Y = 3,
-				Width = 5
-			};
-			borderBottomEdit.TextChanging += (e) => {
-				try {
-					smartView.Border.BorderThickness = new Thickness (smartView.Border.BorderThickness.Left,
-						smartView.Border.BorderThickness.Top, smartView.Border.BorderThickness.Right,
-						int.Parse (e.NewText.ToString ()));
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderBottomEdit.Text = $"{smartView.Border.BorderThickness.Bottom}";
-			Win.Add (borderBottomEdit);
-
-			var replaceBorder = new Button ("Replace all based on top") {
-				X = Pos.Left (borderLeftEdit),
-				Y = 5
-			};
-			replaceBorder.Clicked += () => {
-				smartView.Border.BorderThickness = new Thickness (smartView.Border.BorderThickness.Top);
-				if (borderTopEdit.Text.IsEmpty) {
-					borderTopEdit.Text = "0";
-				}
-				borderBottomEdit.Text = borderLeftEdit.Text = borderRightEdit.Text = borderTopEdit.Text;
-			};
-			Win.Add (replaceBorder);
-
-			Win.Add (new Label ("BorderStyle:"));
-
-			smartView.Y = Pos.Bottom (replaceBorder) + 1;
-
-			var borderStyleEnum = Enum.GetValues (typeof (BorderStyle)).Cast<BorderStyle> ().ToList ();
-			var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
-				e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
-
-				X = 2,
-				Y = 1,
-				SelectedItem = (int)smartView.Border.BorderStyle
-			};
-			Win.Add (rbBorderStyle);
-
-			var cbDrawMarginFrame = new CheckBox ("Draw Margin Frame", smartView.Border.DrawMarginFrame) {
-				X = Pos.AnchorEnd (20),
-				Y = 0,
-				Width = 5
-			};
-			cbDrawMarginFrame.Toggled += (e) => {
-				try {
-					smartView.Border.DrawMarginFrame = (bool)cbDrawMarginFrame.Checked;
-					if (cbDrawMarginFrame.Checked != smartView.Border.DrawMarginFrame) {
-						cbDrawMarginFrame.Checked = smartView.Border.DrawMarginFrame;
-					}
-				} catch { }
-			};
-			Win.Add (cbDrawMarginFrame);
-
-			rbBorderStyle.SelectedItemChanged += (e) => {
-				smartView.Border.BorderStyle = (BorderStyle)e.SelectedItem;
-				smartView.SetNeedsDisplay ();
-				if (cbDrawMarginFrame.Checked != smartView.Border.DrawMarginFrame) {
-					cbDrawMarginFrame.Checked = smartView.Border.DrawMarginFrame;
-				}
-			};
-
-			var cbEffect3D = new CheckBox ("Draw 3D effects", smartView.Border.Effect3D) {
-				X = Pos.AnchorEnd (20),
-				Y = 1,
-				Width = 5
-			};
-			Win.Add (cbEffect3D);
-
-			Win.Add (new Label ("Effect3D Offset:") {
-				X = Pos.AnchorEnd (20),
-				Y = 2
-			});
-			Win.Add (new Label ("X:") {
-				X = Pos.AnchorEnd (19),
-				Y = 3
-			});
-
-			var effect3DOffsetX = new TextField ("") {
-				X = Pos.AnchorEnd (16),
-				Y = 3,
-				Width = 5
-			};
-			effect3DOffsetX.TextChanging += (e) => {
-				try {
-					smartView.Border.Effect3DOffset = new Point (int.Parse (e.NewText.ToString ()),
-						smartView.Border.Effect3DOffset.Y);
-				} catch {
-					if (!e.NewText.IsEmpty && e.NewText != CultureInfo.CurrentCulture.NumberFormat.NegativeSign) {
-						e.Cancel = true;
-					}
-				}
-			};
-			effect3DOffsetX.Text = $"{smartView.Border.Effect3DOffset.X}";
-			Win.Add (effect3DOffsetX);
-
-			Win.Add (new Label ("Y:") {
-				X = Pos.AnchorEnd (10),
-				Y = 3
-			});
-
-			var effect3DOffsetY = new TextField ("") {
-				X = Pos.AnchorEnd (7),
-				Y = 3,
-				Width = 5
-			};
-			effect3DOffsetY.TextChanging += (e) => {
-				try {
-					smartView.Border.Effect3DOffset = new Point (smartView.Border.Effect3DOffset.X,
-						int.Parse (e.NewText.ToString ()));
-				} catch {
-					if (!e.NewText.IsEmpty && e.NewText != CultureInfo.CurrentCulture.NumberFormat.NegativeSign) {
-						e.Cancel = true;
-					}
-				}
-			};
-			effect3DOffsetY.Text = $"{smartView.Border.Effect3DOffset.Y}";
-			Win.Add (effect3DOffsetY);
-
-			cbEffect3D.Toggled += (e) => {
-				try {
-					smartView.Border.Effect3D = effect3DOffsetX.Enabled =
-						effect3DOffsetY.Enabled = (bool)cbEffect3D.Checked;
-				} catch { }
-			};
-
-			Win.Add (new Label ("Background:") {
-				Y = 5
-			});
-
-			var colorEnum = Enum.GetValues (typeof (Color)).Cast<Color> ().ToList ();
-			var rbBackground = new RadioGroup (colorEnum.Select (
-				e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
-
-				X = 2,
-				Y = 6,
-				SelectedItem = (int)smartView.Border.Background
-			};
-			rbBackground.SelectedItemChanged += (e) => {
-				smartView.Border.Background = (Color)e.SelectedItem;
-			};
-			Win.Add (rbBackground);
-
-			Win.Add (new Label ("BorderBrush:") {
-				X = Pos.AnchorEnd (20),
-				Y = 5
-			});
-
-			var rbBorderBrush = new RadioGroup (colorEnum.Select (
-				e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
-
-				X = Pos.AnchorEnd (18),
-				Y = 6,
-				SelectedItem = (int)smartView.Border.BorderBrush
-			};
-			rbBorderBrush.SelectedItemChanged += (e) => {
-				smartView.Border.BorderBrush = (Color)e.SelectedItem;
-			};
-			Win.Add (rbBorderBrush);
+			Application.Run (boc);
+			Application.Shutdown ();
+		}
 
-			Win.Add (smartView);
+		public override void Run ()
+		{
 		}
 	}
 }

+ 2 - 0
UICatalog/Scenarios/TableEditor.cs

@@ -148,6 +148,8 @@ namespace UICatalog.Scenarios {
 					}
 				}
 			};
+
+			tableView.AddKeyBinding (Key.Space, Command.ToggleChecked);
 		}
 
 		private void ShowAllColumns ()

+ 2 - 2
UICatalog/Scenarios/TileViewExperiment.cs

@@ -33,7 +33,7 @@ namespace UICatalog.Scenarios {
 				Y = 0,
 				Width = 70, //Dim.Fill (),
 				Height = 15, //Dim.Fill (),
-				//IgnoreBorderPropertyOnRedraw = true
+					     //IgnoreBorderPropertyOnRedraw = true
 
 			};
 			frame1.Border.BorderStyle = BorderStyle.Double;
@@ -67,7 +67,7 @@ namespace UICatalog.Scenarios {
 					BorderStyle = BorderStyle.Single,
 					//BorderThickness = new Thickness (1), 
 					//DrawMarginFrame = true,
-					//Padding = new Thickness (1),
+					//Padding = new Thickness(1),
 					BorderBrush = Color.BrightMagenta,
 				}
 			};

+ 3 - 0
UICatalog/Scenarios/TileViewNesting.cs

@@ -94,6 +94,9 @@ namespace UICatalog.Scenarios {
 			bool? border = cbBorder.Checked;
 			bool? startHorizontal = cbHorizontal.Checked;
 
+			foreach(var sub in workArea.Subviews) {
+				sub.Dispose ();
+			}
 			workArea.RemoveAll ();
 
 			if (numberOfViews <= 0) {

+ 27 - 20
UICatalog/UICatalog.cs

@@ -84,12 +84,13 @@ namespace UICatalog {
 				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ());
 				Application.UseSystemConsole = _useSystemConsole;
 				Application.Init ();
-				_selectedScenario.Init (Colors.ColorSchemes [_topLevelColorScheme]);
+				_selectedScenario.Init (Colors.ColorSchemes [_topLevelColorScheme == null ? "Base" : _topLevelColorScheme]);
 				_selectedScenario.Setup ();
 				_selectedScenario.Run ();
 				_selectedScenario.Dispose ();
 				_selectedScenario = null;
 				Application.Shutdown ();
+				VerifyObjectsWereDisposed ();
 				return;
 			}
 
@@ -103,6 +104,8 @@ namespace UICatalog {
 			_aboutMessage.AppendLine (@"    | |  __/ |  | | | | | | | | | | (_| | || |__| | |_| | | ");
 			_aboutMessage.AppendLine (@"    |_|\___|_|  |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_| ");
 			_aboutMessage.AppendLine (@"");
+			_aboutMessage.AppendLine (@"v2 - Work in Progress");
+			_aboutMessage.AppendLine (@"");
 			_aboutMessage.AppendLine (@"https://github.com/gui-cs/Terminal.Gui");
 
 			Scenario scenario;
@@ -192,6 +195,9 @@ namespace UICatalog {
 			// Run UI Catalog UI. When it exits, if _selectedScenario is != null then
 			// a Scenario was selected. Otherwise, the user wants to exit UI Catalog.
 			Application.Init ();
+
+			Application.EnableConsoleScrolling = _enableConsoleScrolling;
+
 			Application.Run<UICatalogTopLevel> ();
 			Application.Shutdown ();
 
@@ -213,6 +219,7 @@ namespace UICatalog {
 
 		static bool _useSystemConsole = false;
 		static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
+		static bool _enableConsoleScrolling = false;
 		static bool _isFirstRunning = true;
 		static string _topLevelColorScheme;
 
@@ -225,7 +232,7 @@ namespace UICatalog {
 		/// </summary>
 		public class UICatalogTopLevel : Toplevel {
 			public MenuItem miIsMouseDisabled;
-			public MenuItem miHeightAsBuffer;
+			public MenuItem miEnableConsoleScrolling;
 
 			public TileView ContentPane;
 			public ListView CategoryListView;
@@ -349,6 +356,8 @@ namespace UICatalog {
 			{
 				ConfigChanged ();
 
+				miIsMouseDisabled.Checked = Application.IsMouseDisabled;
+				miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling;
 				DriverName.Title = $"Driver: {Driver.GetType ().Name}";
 				OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}";
 
@@ -403,13 +412,12 @@ namespace UICatalog {
 
 			List<MenuItem []> CreateDiagnosticMenuItems ()
 			{
-				List<MenuItem []> menuItems = new List<MenuItem []> {
-					CreateDiagnosticFlagsMenuItems (),
-					new MenuItem [] { null },
-					CreateHeightAsBufferMenuItems (),
-					CreateDisabledEnabledMouseItems (),
-					CreateKeybindingsMenuItems ()
-				};
+				List<MenuItem []> menuItems = new List<MenuItem []> ();
+				menuItems.Add (CreateDiagnosticFlagsMenuItems ());
+				menuItems.Add (new MenuItem [] { null });
+				menuItems.Add (CreateEnableConsoleScrollingMenuItems ());
+				menuItems.Add (CreateDisabledEnabledMouseItems ());
+				menuItems.Add (CreateKeybindingsMenuItems ());
 				return menuItems;
 			}
 
@@ -447,19 +455,18 @@ namespace UICatalog {
 				return menuItems.ToArray ();
 			}
 
-			MenuItem [] CreateHeightAsBufferMenuItems ()
+			MenuItem [] CreateEnableConsoleScrollingMenuItems ()
 			{
 				List<MenuItem> menuItems = new List<MenuItem> ();
-				miHeightAsBuffer = new MenuItem {
-					Title = "_Height As Buffer"
-				};
-				miHeightAsBuffer.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miHeightAsBuffer.Title.ToString ().Substring (1, 1) [0];
-				miHeightAsBuffer.CheckType |= MenuItemCheckStyle.Checked;
-				miHeightAsBuffer.Action += () => {
-					miHeightAsBuffer.Checked = !miHeightAsBuffer.Checked;
-					Application.HeightAsBuffer = (bool)miHeightAsBuffer.Checked;
+				miEnableConsoleScrolling = new MenuItem ();
+				miEnableConsoleScrolling.Title = "_Enable Console Scrolling";
+				miEnableConsoleScrolling.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miEnableConsoleScrolling.Title.ToString ().Substring (1, 1) [0];
+				miEnableConsoleScrolling.CheckType |= MenuItemCheckStyle.Checked;
+				miEnableConsoleScrolling.Action += () => {
+					miEnableConsoleScrolling.Checked = !miEnableConsoleScrolling.Checked;
+					Application.EnableConsoleScrolling = (bool)miEnableConsoleScrolling.Checked;
 				};
-				menuItems.Add (miHeightAsBuffer);
+				menuItems.Add (miEnableConsoleScrolling);
 
 				return menuItems.ToArray ();
 			}
@@ -634,7 +641,7 @@ namespace UICatalog {
 				StatusBar.Items [0].Title = $"~{Application.QuitKey} to quit";
 
 				miIsMouseDisabled.Checked = Application.IsMouseDisabled;
-				miHeightAsBuffer.Checked = Application.HeightAsBuffer;
+				miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling;
 
 				var height = (UICatalogApp.ShowStatusBar ? 1 : 0);// + (MenuBar.Visible ? 1 : 0);
 				ContentPane.Height = Dim.Fill (height);

+ 5 - 5
UICatalog/UICatalog.csproj

@@ -7,10 +7,10 @@
     <!-- Version numbers are automatically updated by gitversion when a release is released -->
     <!-- In the source tree the version will always be 2.0 for all projects. -->
     <!-- Do not modify these. -->
-    <AssemblyVersion>1.0</AssemblyVersion>
-    <FileVersion>1.0</FileVersion>
-    <Version>1.0</Version>
-    <InformationalVersion>1.0</InformationalVersion>
+    <AssemblyVersion>2.0</AssemblyVersion>
+    <FileVersion>2.0</FileVersion>
+    <Version>2.0</Version>
+    <InformationalVersion>2.0</InformationalVersion>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
     <DefineConstants>TRACE</DefineConstants>
@@ -28,7 +28,7 @@
   <None Update="./Scenarios/Spinning_globe_dark_small.gif" CopyToOutputDirectory="PreserveNewest" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
+    <PackageReference Include="SixLabors.ImageSharp" Version="3.0.0" />
     <PackageReference Include="CsvHelper" Version="30.0.1" />
     <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
   </ItemGroup>

+ 6 - 7
UnitTests/Application/ApplicationTests.cs

@@ -24,8 +24,7 @@ namespace Terminal.Gui.ApplicationTests {
 			Assert.Null (Application.Driver);
 			Assert.Null (Application.Top);
 			Assert.Null (Application.Current);
-			// removed below as HeightAsBuffer now works without a driver loaded
-			//Assert.Throws<ArgumentNullException> (() => Application.HeightAsBuffer == true);
+			Assert.False (Application.EnableConsoleScrolling);
 			Assert.Null (Application.MainLoop);
 			Assert.Null (Application.Iteration);
 			Assert.Null (Application.RootMouseEvent);
@@ -37,7 +36,7 @@ namespace Terminal.Gui.ApplicationTests {
 			Assert.NotNull (Application.Driver);
 			Assert.NotNull (Application.Top);
 			Assert.NotNull (Application.Current);
-			Assert.False (Application.HeightAsBuffer);
+			Assert.False (Application.EnableConsoleScrolling);
 			Assert.NotNull (Application.MainLoop);
 			Assert.Null (Application.Iteration);
 			Assert.Null (Application.RootMouseEvent);
@@ -314,7 +313,7 @@ namespace Terminal.Gui.ApplicationTests {
 		public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws ()
 		{
 			Init ();
-			
+
 			Application.Driver = null;
 
 			Application.Iteration = () => {
@@ -334,8 +333,8 @@ namespace Terminal.Gui.ApplicationTests {
 		[Fact]
 		public void Run_T_NoInit_DoesNotThrow ()
 		{
-			Application.ForceFakeConsole = true; 
-			
+			Application.ForceFakeConsole = true;
+
 			Application.Iteration = () => {
 				Application.RequestStop ();
 			};
@@ -431,7 +430,7 @@ namespace Terminal.Gui.ApplicationTests {
 		}
 
 		// TODO: Add tests for Run that test errorHandler
-		
+
 		#endregion
 
 		#region ShutdownTests

+ 13 - 13
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -182,12 +182,13 @@ namespace Terminal.Gui.ConfigurationTests {
 		/// Save the `config.json` file; this can be used to update the file in `Terminal.Gui.Resources.config.json'.
 		/// </summary>
 		/// <remarks>
-		/// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Conifg Properties
-		/// are all satic and thus can be overwritten by other tests.</remarks>
+		/// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Config Properties
+		/// are all static and thus can be overwritten by other tests.</remarks>
 		[Fact]
 		public void SaveDefaults ()
 		{
 			ConfigurationManager.Initialize ();
+			ConfigurationManager.Reset ();
 
 			// Get the hard coded settings
 			ConfigurationManager.GetHardCodedDefaults ();
@@ -226,7 +227,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
 			ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true;
 			ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true;
-			ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true;
+			ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true;
 			ConfigurationManager.Settings.Apply ();
 
 			// assert apply worked
@@ -235,7 +236,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			Assert.Equal (Key.B, Application.AlternateBackwardKey);
 			Assert.True (Application.UseSystemConsole);
 			Assert.True (Application.IsMouseDisabled);
-			Assert.True (Application.HeightAsBuffer);
+			Assert.True (Application.EnableConsoleScrolling);
 
 			//act
 			ConfigurationManager.Reset ();
@@ -248,7 +249,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey);
 			Assert.False (Application.UseSystemConsole);
 			Assert.False (Application.IsMouseDisabled);
-			Assert.False (Application.HeightAsBuffer);
+			Assert.False (Application.EnableConsoleScrolling);
 
 			// arrange
 			ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q;
@@ -256,7 +257,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
 			ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true;
 			ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true;
-			ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true;
+			ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true;
 			ConfigurationManager.Settings.Apply ();
 
 
@@ -274,7 +275,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey);
 			Assert.False (Application.UseSystemConsole);
 			Assert.False (Application.IsMouseDisabled);
-			Assert.False (Application.HeightAsBuffer);
+			Assert.False (Application.EnableConsoleScrolling);
 
 		}
 
@@ -316,12 +317,11 @@ namespace Terminal.Gui.ConfigurationTests {
 		[Fact, AutoInitShutdown]
 		public void TestConfigurationManagerToJson ()
 		{
+			ConfigurationManager.Reset ();
 			ConfigurationManager.GetHardCodedDefaults ();
 			var stream = ConfigurationManager.ToStream ();
-
 			
 			ConfigurationManager.Settings.Update (stream, "TestConfigurationManagerToJson");
-
 		}
 
 		[Fact, AutoInitShutdown (configLocation: ConfigLocations.None)]
@@ -771,7 +771,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
 			ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true;
 			ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true;
-			ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true;
+			ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true;
 
 			ConfigurationManager.Updated += ConfigurationManager_Updated;
 			bool fired = false;
@@ -784,7 +784,7 @@ namespace Terminal.Gui.ConfigurationTests {
 				Assert.Equal (Key.PageUp | Key.CtrlMask, ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue);
 				Assert.False ((bool)ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue);
 				Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue);
-				Assert.False ((bool)ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue);
+				Assert.False ((bool)ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue);
 			}
 
 			ConfigurationManager.Load (true);
@@ -810,7 +810,7 @@ namespace Terminal.Gui.ConfigurationTests {
 				Assert.Equal (Key.B, Application.AlternateBackwardKey);
 				Assert.True (Application.UseSystemConsole);
 				Assert.True (Application.IsMouseDisabled);
-				Assert.True (Application.HeightAsBuffer);
+				Assert.True (Application.EnableConsoleScrolling);
 			}
 
 			// act
@@ -819,7 +819,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
 			ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true;
 			ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true;
-			ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true;
+			ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true;
 
 			ConfigurationManager.Apply ();
 

+ 6 - 6
UnitTests/Configuration/SettingsScopeTests.cs

@@ -26,7 +26,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			Assert.True (ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue is Key);
 			Assert.True (ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue is bool);
 			Assert.True (ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue is bool);
-			Assert.True (ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue is bool);
+			Assert.True (ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue is bool);
 
 			Assert.True (ConfigurationManager.Settings ["Theme"].PropertyValue is string);
 			Assert.Equal ("Default", ConfigurationManager.Settings ["Theme"].PropertyValue as string);
@@ -45,7 +45,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			Assert.Equal (Key.PageUp | Key.CtrlMask, (Key)ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue);
 			Assert.False ((bool)ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue);
 			Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue);
-			Assert.False ((bool)ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue);
+			Assert.False ((bool)ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue);
 
 			// act
 			ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q;
@@ -53,7 +53,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
 			ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true;
 			ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true;
-			ConfigurationManager.Settings ["Application.HeightAsBuffer"].PropertyValue = true;
+			ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true;
 
 			ConfigurationManager.Settings.Apply ();
 
@@ -63,7 +63,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			Assert.Equal (Key.B, Application.AlternateBackwardKey);
 			Assert.True (Application.UseSystemConsole);
 			Assert.True (Application.IsMouseDisabled);
-			Assert.True (Application.HeightAsBuffer);
+			Assert.True (Application.EnableConsoleScrolling);
 		}
 
 		[Fact, AutoInitShutdown]
@@ -78,7 +78,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			updatedSettings["Application.AlternateBackwardKey"].PropertyValue = Key.B;
 			updatedSettings["Application.UseSystemConsole"].PropertyValue = true;
 			updatedSettings["Application.IsMouseDisabled"].PropertyValue = true;
-			updatedSettings["Application.HeightAsBuffer"].PropertyValue = true;
+			updatedSettings["Application.EnableConsoleScrolling"].PropertyValue = true;
 
 			ConfigurationManager.Settings.Update (updatedSettings);
 			Assert.Equal (Key.End, ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue);
@@ -86,7 +86,7 @@ namespace Terminal.Gui.ConfigurationTests {
 			Assert.Equal (Key.B, updatedSettings ["Application.AlternateBackwardKey"].PropertyValue);
 			Assert.True ((bool)updatedSettings ["Application.UseSystemConsole"].PropertyValue);
 			Assert.True ((bool)updatedSettings ["Application.IsMouseDisabled"].PropertyValue);
-			Assert.True ((bool)updatedSettings ["Application.HeightAsBuffer"].PropertyValue);
+			Assert.True ((bool)updatedSettings ["Application.EnableConsoleScrolling"].PropertyValue);
 		}
 	}
 }

+ 141 - 67
UnitTests/Core/BorderTests.cs

@@ -1,15 +1,19 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Reflection.Emit;
 using Xunit;
+using Xunit.Abstractions;
 using Rune = System.Rune;
 
 namespace Terminal.Gui.CoreTests {
 	public class BorderTests {
-		[Fact]
-		[AutoInitShutdown]
+		readonly ITestOutputHelper output;
+
+		public BorderTests (ITestOutputHelper output)
+		{
+			this.output = output;
+		}
+
+		[Fact, AutoInitShutdown]
 		public void Constructor_Defaults ()
 		{
 			var b = new Border ();
@@ -40,15 +44,10 @@ namespace Terminal.Gui.CoreTests {
 			Assert.False (b.DrawMarginFrame);
 		}
 
-		//[Fact]
-		//[AutoInitShutdown]
-		//public void ActualWidth_ActualHeight ()
-		//{
-		//	var v = new View (new Rect (5, 10, 60, 20), "", new Border ());
-
-		//	Assert.Equal (60, v.Border.ActualWidth);
-		//	Assert.Equal (20, v.Border.ActualHeight);
-		//}
+		//[Fact, AutoInitShutdown]
+		// public void ActualWidth_ActualHeight ()
+		// {
+		// 	var v = new View (new Rect (5, 10, 60, 20), "", new Border ());
 
 		//[Fact]
 		//public void ToplevelContainer_LayoutStyle_Computed_Constuctor_ ()
@@ -83,23 +82,12 @@ namespace Terminal.Gui.CoreTests {
 		//	var top = Application.Top;
 		//	var driver = (FakeDriver)Application.Driver;
 
-		//	var label = new Label () {
-		//		X = Pos.Center (),
-		//		Y = Pos.Center (),
-		//		Border = new Border () {
-		//			BorderStyle = BorderStyle.Single,
-		//			Padding = new Thickness (2),
-		//			BorderThickness = new Thickness (2),
-		//			BorderBrush = Color.Red,
-		//			Background = Color.BrightGreen,
-		//			Effect3D = true,
-		//			Effect3DOffset = new Point (2, -3)
-		//		},
-		//		ColorScheme = Colors.TopLevel,
-		//		Text = "This is a test"
-		//	};
-		//	label.Border.Child = label;
-		//	top.Add (label);
+		// [Fact]
+		// [AutoInitShutdown]
+		// public void DrawContent_With_Child_Border ()
+		// {
+		// 	var top = Application.Top;
+		// 	var driver = (FakeDriver)Application.Driver;
 
 		//	top.LayoutSubviews ();
 		//	label.Redraw (label.Bounds);
@@ -243,11 +231,34 @@ namespace Terminal.Gui.CoreTests {
 		//	}
 		//	Assert.Equal ("This is a test", text.Trim ());
 
-		//	// Check the upper Effect3D
-		//	for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y;
-		//		r < frame.Y - drawMarginFrame - sumThickness.Top; r++) {
-		//		for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X;
-		//			c < frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X; c++) {
+			// 		var color = (Attribute)driver.Contents [r, c, 1];
+			// 		var rune = (Rune)driver.Contents [r, c, 0];
+			// 		if (r == frame.Y - drawMarginFrame || r == frame.Bottom + drawMarginFrame - 1
+			// 			|| c == frame.X - drawMarginFrame || c == frame.Right + drawMarginFrame - 1) {
+			// 			Assert.Equal (Color.BrightGreen, color.Background);
+			// 		} else {
+			// 			Assert.Equal (Color.Black, color.Background);
+			// 		}
+			// 		if (c == frame.X - drawMarginFrame && r == frame.Y - drawMarginFrame) {
+			// 			Assert.Equal (uLCorner, rune);
+			// 		} else if (c == frame.Right && r == frame.Y - drawMarginFrame) {
+			// 			Assert.Equal (uRCorner, rune);
+			// 		} else if (c == frame.X - drawMarginFrame && r == frame.Bottom) {
+			// 			Assert.Equal (lLCorner, rune);
+			// 		} else if (c == frame.Right && r == frame.Bottom) {
+			// 			Assert.Equal (lRCorner, rune);
+			// 		} else if (c >= frame.X && (r == frame.Y - drawMarginFrame
+			// 			|| r == frame.Bottom)) {
+			// 			Assert.Equal (hLine, rune);
+			// 		} else if ((c == frame.X - drawMarginFrame || c == frame.Right)
+			// 			&& r >= frame.Y && r <= frame.Bottom - drawMarginFrame) {
+			// 			Assert.Equal (vLine, rune);
+			// 		} else {
+			// 			text += rune.ToString ();
+			// 		}
+			// 	}
+			// }
+			// Assert.Equal ("This is a test", text.Trim ());
 
 		//			var color = (Attribute)driver.Contents [r, c, 1];
 		//			Assert.Equal (Color.DarkGray, color.Background);
@@ -305,27 +316,12 @@ namespace Terminal.Gui.CoreTests {
 		//	var top = Application.Top;
 		//	var driver = (FakeDriver)Application.Driver;
 
-		//	var frameView = new FrameView () {
-		//		X = Pos.Center (),
-		//		Y = Pos.Center (),
-		//		Width = 24,
-		//		Height = 13,
-		//		Border = new Border () {
-		//			BorderStyle = BorderStyle.Single,
-		//			Padding = new Thickness (2),
-		//			BorderThickness = new Thickness (2),
-		//			BorderBrush = Color.Red,
-		//			Background = Color.BrightGreen,
-		//			Effect3D = true,
-		//			Effect3DOffset = new Point (2, -3)
-		//		}
-		//	};
-		//	frameView.Add (new Label () {
-		//		ColorScheme = Colors.TopLevel,
-		//		Text = "This is a test"
-		//	});
-		//	//frameView.Border.Child = frameView;
-		//	top.Add (frameView);
+		// [Fact]
+		// [AutoInitShutdown]
+		// public void DrawContent_With_Parent_Border ()
+		// {
+		// 	var top = Application.Top;
+		// 	var driver = (FakeDriver)Application.Driver;
 
 		//	top.LayoutSubviews ();
 		//	frameView.Redraw (frameView.Bounds);
@@ -481,13 +477,40 @@ namespace Terminal.Gui.CoreTests {
 		//	//}
 		//	//Assert.Equal ("This is a test", text.Trim ());
 
-		//	// TODO: Re-enable 3deffect
-			
-		//	//// Check the upper Effect3D
-		//	//for (int r = frame.Y + effect3DOffset.Y;
-		//	//	r < frame.Y; r++) {
-		//	//	for (int c = frame.X + effect3DOffset.X;
-		//	//		c < frame.Right + effect3DOffset.X; c++) {
+			// 		var color = (Attribute)driver.Contents [r, c, 1];
+			// 		var rune = (Rune)driver.Contents [r, c, 0];
+			// 		Assert.Equal (Color.Black, color.Background);
+			// 		if (c == frame.X + sumThickness.Left && r == frame.Y + sumThickness.Top) {
+			// 			Assert.Equal (uLCorner, rune);
+			// 		} else if (c == frame.Right - drawMarginFrame - sumThickness.Right
+			// 			&& r == frame.Y + sumThickness.Top) {
+			// 			Assert.Equal (uRCorner, rune);
+			// 		} else if (c == frame.X + sumThickness.Left
+			// 			&& r == frame.Bottom - drawMarginFrame - sumThickness.Bottom) {
+			// 			Assert.Equal (lLCorner, rune);
+			// 		} else if (c == frame.Right - drawMarginFrame - sumThickness.Right
+			// 			&& r == frame.Bottom - drawMarginFrame - sumThickness.Bottom) {
+			// 			Assert.Equal (lRCorner, rune);
+			// 		} else if (c > frame.X + sumThickness.Left
+			// 			&& (r == frame.Y + sumThickness.Top
+			// 			|| r == frame.Bottom - drawMarginFrame - sumThickness.Bottom)) {
+			// 			Assert.Equal (hLine, rune);
+			// 		} else if ((c == frame.X + sumThickness.Left
+			// 			|| c == frame.Right - drawMarginFrame - sumThickness.Right)
+			// 			&& r >= frame.Y + drawMarginFrame + sumThickness.Top) {
+			// 			Assert.Equal (vLine, rune);
+			// 		} else {
+			// 			text += rune.ToString ();
+			// 		}
+			// 	}
+			// }
+			// Assert.Equal ("This is a test", text.Trim ());
+
+			// // Check the upper Effect3D
+			// for (int r = frame.Y + effect3DOffset.Y;
+			// 	r < frame.Y; r++) {
+			// 	for (int c = frame.X + effect3DOffset.X;
+			// 		c < frame.Right + effect3DOffset.X; c++) {
 
 		//	//		var color = (Attribute)driver.Contents [r, c, 1];
 		//	//		Assert.Equal (Color.DarkGray, color.Background);
@@ -540,8 +563,7 @@ namespace Terminal.Gui.CoreTests {
 		//	//}
 		//}
 
-		[Fact]
-		[AutoInitShutdown]
+		[Fact, AutoInitShutdown]
 		public void BorderOnControlWithNoChildren ()
 		{
 			var label = new TextField ("Loading...") {
@@ -557,5 +579,57 @@ namespace Terminal.Gui.CoreTests {
 
 			Assert.Null (Record.Exception (() => label.Redraw (label.Bounds)));
 		}
+
+		[Fact, AutoInitShutdown]
+		public void BorderStyle_And_DrawMarginFrame_Gets_Sets ()
+		{
+			var lblTop = new Label ("At 0,0");
+			var lblFrame = new Label ("Centered") { X = Pos.Center (), Y = Pos.Center () };
+			var frame = new FrameView () { Y = 1, Width = 20, Height = 3 };
+			var lblFill = new Label () { Width = Dim.Fill(),Height = Dim.Fill(), Visible = false };
+			var fillText = new System.Text.StringBuilder ();
+			for (int i = 0; i < frame.Bounds.Height; i++) {
+				if (i > 0) {
+					fillText.AppendLine ("");
+				}
+				for (int j = 0; j < frame.Bounds.Width; j++) {
+					fillText.Append ("█");
+				}
+			}
+			lblFill.Text = fillText.ToString ();
+			frame.Add (lblFill, lblFrame);
+			var lblBottom = new Label ("At 0,4") { Y = 4 };
+			Application.Top.Add (lblTop, frame, lblBottom);
+			Application.Begin (Application.Top);
+
+			Assert.Equal (BorderStyle.Single, frame.Border.BorderStyle);
+			Assert.True (frame.Border.DrawMarginFrame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+At 0,0              
+┌──────────────────┐
+│     Centered     │
+└──────────────────┘
+At 0,4              ", output);
+
+			frame.Border.BorderStyle = BorderStyle.None;
+			Application.Refresh ();
+			Assert.True (frame.Border.DrawMarginFrame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+At 0,0        
+              
+      Centered
+              
+At 0,4        ", output);
+
+			frame.Border.DrawMarginFrame = false;
+			lblFill.Visible = true;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+At 0,0              
+████████████████████
+██████Centered██████
+████████████████████
+At 0,4              ", output);
+		}
 	}
 }

+ 78 - 0
UnitTests/Core/EscSeqReqTests.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Terminal.Gui.CoreTests {
+	public class EscSeqReqTests {
+		[Fact]
+		public void Constructor_Defaults ()
+		{
+			var escSeqReq = new EscSeqReqProc ();
+			Assert.NotNull (escSeqReq.EscSeqReqStats);
+			Assert.Empty (escSeqReq.EscSeqReqStats);
+		}
+
+		[Fact]
+		public void Add_Tests ()
+		{
+			var escSeqReq = new EscSeqReqProc ();
+			escSeqReq.Add ("t");
+			Assert.Single (escSeqReq.EscSeqReqStats);
+			Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating);
+			Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumRequests);
+			Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding);
+
+			escSeqReq.Add ("t", 2);
+			Assert.Single (escSeqReq.EscSeqReqStats);
+			Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating);
+			Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumRequests);
+			Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding);
+
+			escSeqReq = new EscSeqReqProc ();
+			escSeqReq.Add ("t", 2);
+			Assert.Single (escSeqReq.EscSeqReqStats);
+			Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating);
+			Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests);
+			Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOutstanding);
+
+			escSeqReq.Add ("t", 3);
+			Assert.Single (escSeqReq.EscSeqReqStats);
+			Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating);
+			Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests);
+			Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOutstanding);
+		}
+
+		[Fact]
+		public void Remove_Tests ()
+		{
+			var escSeqReq = new EscSeqReqProc ();
+			escSeqReq.Add ("t");
+			escSeqReq.Remove ("t");
+			Assert.Empty (escSeqReq.EscSeqReqStats);
+
+			escSeqReq.Add ("t", 2);
+			escSeqReq.Remove ("t");
+			Assert.Single (escSeqReq.EscSeqReqStats);
+			Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating);
+			Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests);
+			Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding);
+
+			escSeqReq.Remove ("t");
+			Assert.Empty (escSeqReq.EscSeqReqStats);
+		}
+
+		[Fact]
+		public void Requested_Tests ()
+		{
+			var escSeqReq = new EscSeqReqProc ();
+			Assert.False (escSeqReq.Requested ("t"));
+
+			escSeqReq.Add ("t");
+			Assert.False (escSeqReq.Requested ("r"));
+			Assert.True (escSeqReq.Requested ("t"));
+		}
+	}
+}

+ 870 - 0
UnitTests/Core/EscSeqUtilsTests.cs

@@ -0,0 +1,870 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace Terminal.Gui.CoreTests {
+	public class EscSeqUtilsTests {
+		[Fact]
+		public void Defaults_Values ()
+		{
+			Assert.Equal ('\x1b', EscSeqUtils.KeyEsc);
+			Assert.Equal ("\x1b[", EscSeqUtils.KeyCSI);
+			Assert.Equal ("\x1b[?1003h", EscSeqUtils.CSI_EnableAnyEventMouse);
+			Assert.Equal ("\x1b[?1006h", EscSeqUtils.CSI_EnableSgrExtModeMouse);
+			Assert.Equal ("\x1b[?1015h", EscSeqUtils.CSI_EnableUrxvtExtModeMouse);
+			Assert.Equal ("\x1b[?1003l", EscSeqUtils.CSI_DisableAnyEventMouse);
+			Assert.Equal ("\x1b[?1006l", EscSeqUtils.CSI_DisableSgrExtModeMouse);
+			Assert.Equal ("\x1b[?1015l", EscSeqUtils.CSI_DisableUrxvtExtModeMouse);
+			Assert.Equal ("\x1b[?1003h\x1b[?1015h\u001b[?1006h", EscSeqUtils.EnableMouseEvents);
+			Assert.Equal ("\x1b[?1003l\x1b[?1015l\u001b[?1006l", EscSeqUtils.DisableMouseEvents);
+		}
+
+		[Fact]
+		public void GetConsoleInputKey_ConsoleKeyInfo ()
+		{
+			var cki = new ConsoleKeyInfo ('r', 0, false, false, false);
+			var expectedCki = new ConsoleKeyInfo ('r', 0, false, false, false);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('r', 0, true, false, false);
+			expectedCki = new ConsoleKeyInfo ('r', 0, true, false, false);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('r', 0, false, true, false);
+			expectedCki = new ConsoleKeyInfo ('r', 0, false, true, false);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('r', 0, false, false, true);
+			expectedCki = new ConsoleKeyInfo ('r', 0, false, false, true);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('r', 0, true, true, false);
+			expectedCki = new ConsoleKeyInfo ('r', 0, true, true, false);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('r', 0, false, true, true);
+			expectedCki = new ConsoleKeyInfo ('r', 0, false, true, true);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('r', 0, true, true, true);
+			expectedCki = new ConsoleKeyInfo ('r', 0, true, true, true);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('\u0012', 0, false, false, false);
+			expectedCki = new ConsoleKeyInfo ('R', ConsoleKey.R, false, false, true);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('\0', (ConsoleKey)64, false, false, true);
+			expectedCki = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, false, false, true);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('\r', 0, false, false, false);
+			expectedCki = new ConsoleKeyInfo ('\r', ConsoleKey.Enter, false, false, false);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('\u007f', 0, false, false, false);
+			expectedCki = new ConsoleKeyInfo ('\u007f', ConsoleKey.Backspace, false, false, false);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+
+			cki = new ConsoleKeyInfo ('R', 0, false, false, false);
+			expectedCki = new ConsoleKeyInfo ('R', 0, false, false, false);
+			Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki));
+		}
+
+		[Fact]
+		public void ResizeArray_ConsoleKeyInfo ()
+		{
+			ConsoleKeyInfo [] expectedCkInfos = null;
+			var cki = new ConsoleKeyInfo ('\u001b', ConsoleKey.Escape, false, false, false);
+			expectedCkInfos = EscSeqUtils.ResizeArray (cki, expectedCkInfos);
+			Assert.Single (expectedCkInfos);
+			Assert.Equal (cki, expectedCkInfos [0]);
+		}
+
+		private EscSeqReqProc escSeqReqProc;
+		private ConsoleKeyInfo newConsoleKeyInfo;
+		private ConsoleKey key;
+		private ConsoleKeyInfo [] cki;
+		private ConsoleModifiers mod;
+		private string c1Control, code, terminating;
+		private string [] values;
+		private bool isKeyMouse;
+		private bool isReq;
+		private List<MouseFlags> mouseFlags;
+		Point pos;
+		private MouseFlags arg1;
+		private Point arg2;
+		private bool actionStarted;
+
+		[Fact, AutoInitShutdown]
+		public void DecodeEscSeq_Tests ()
+		{
+			// ESC
+			cki = new ConsoleKeyInfo [] { new ConsoleKeyInfo ('\u001b', 0, false, false, false) };
+			var expectedCki = new ConsoleKeyInfo ('\u001b', ConsoleKey.Escape, false, false, false);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.Escape, key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("ESC", c1Control);
+			Assert.Null (code);
+			Assert.Null (values);
+			Assert.Null (terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('\u0012', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\u0012', ConsoleKey.R, false, true, true);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.R, key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("ESC", c1Control);
+			Assert.Null (code);
+			Assert.Null (values);
+			Assert.Equal ("\u0012", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('r', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('R', ConsoleKey.R, false, true, false);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.R, key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("ESC", c1Control);
+			Assert.Null (code);
+			Assert.Null (values);
+			Assert.Equal ("r", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			// SS3
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('O', 0, false, false, false),
+				new ConsoleKeyInfo ('R', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, false, false);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.F3, key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("SS3", c1Control);
+			Assert.Null (code);
+			Assert.Single (values);
+			Assert.Null (values [0]);
+			Assert.Equal ("R", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			// CSI
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('1', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo ('R', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, true, false, false);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.F3, key);
+			Assert.Equal (ConsoleModifiers.Shift, mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (2, values.Length);
+			Assert.Equal ("1", values [0]);
+			Assert.Equal ("2", values [^1]);
+			Assert.Equal ("R", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('1', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('R', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, true, false);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.F3, key);
+			Assert.Equal (ConsoleModifiers.Alt, mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (2, values.Length);
+			Assert.Equal ("1", values [0]);
+			Assert.Equal ("3", values [^1]);
+			Assert.Equal ("R", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('1', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('4', 0, false, false, false),
+				new ConsoleKeyInfo ('R', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, true, true, false);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.F3, key);
+			Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Alt, mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (2, values.Length);
+			Assert.Equal ("1", values [0]);
+			Assert.Equal ("4", values [^1]);
+			Assert.Equal ("R", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('1', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('5', 0, false, false, false),
+				new ConsoleKeyInfo ('R', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, false, true);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.F3, key);
+			Assert.Equal (ConsoleModifiers.Control, mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (2, values.Length);
+			Assert.Equal ("1", values [0]);
+			Assert.Equal ("5", values [^1]);
+			Assert.Equal ("R", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('1', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('6', 0, false, false, false),
+				new ConsoleKeyInfo ('R', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, true, false, true);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.F3, key);
+			Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Control, mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (2, values.Length);
+			Assert.Equal ("1", values [0]);
+			Assert.Equal ("6", values [^1]);
+			Assert.Equal ("R", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('1', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('7', 0, false, false, false),
+				new ConsoleKeyInfo ('R', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, true, true);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.F3, key);
+			Assert.Equal (ConsoleModifiers.Alt | ConsoleModifiers.Control, mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (2, values.Length);
+			Assert.Equal ("1", values [0]);
+			Assert.Equal ("7", values [^1]);
+			Assert.Equal ("R", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('1', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('8', 0, false, false, false),
+				new ConsoleKeyInfo ('R', 0, false, false, false)
+			};
+			expectedCki = new ConsoleKeyInfo ('\0', ConsoleKey.F3, true, true, true);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (ConsoleKey.F3, key);
+			Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control, mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (2, values.Length);
+			Assert.Equal ("1", values [0]);
+			Assert.Equal ("8", values [^1]);
+			Assert.Equal ("R", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('M', 0, false, false, false)
+			};
+			expectedCki = default;
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (0, (int)key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Equal ("<", code);
+			Assert.Equal (3, values.Length);
+			Assert.Equal ("0", values [0]);
+			Assert.Equal ("2", values [1]);
+			Assert.Equal ("3", values [^1]);
+			Assert.Equal ("M", terminating);
+			Assert.True (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1Pressed }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('m', 0, false, false, false)
+			};
+			expectedCki = default;
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (0, (int)key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Equal ("<", code);
+			Assert.Equal (3, values.Length);
+			Assert.Equal ("0", values [0]);
+			Assert.Equal ("2", values [1]);
+			Assert.Equal ("3", values [^1]);
+			Assert.Equal ("m", terminating);
+			Assert.True (isKeyMouse);
+			Assert.Equal (2, mouseFlags.Count);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1Released, MouseFlags.Button1Clicked }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('M', 0, false, false, false)
+			};
+			expectedCki = default;
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (0, (int)key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Equal ("<", code);
+			Assert.Equal (3, values.Length);
+			Assert.Equal ("0", values [0]);
+			Assert.Equal ("2", values [1]);
+			Assert.Equal ("3", values [^1]);
+			Assert.Equal ("M", terminating);
+			Assert.True (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1DoubleClicked }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+			Assert.False (isReq);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('M', 0, false, false, false)
+			};
+			expectedCki = default;
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (0, (int)key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Equal ("<", code);
+			Assert.Equal (3, values.Length);
+			Assert.Equal ("0", values [0]);
+			Assert.Equal ("2", values [1]);
+			Assert.Equal ("3", values [^1]);
+			Assert.Equal ("M", terminating);
+			Assert.True (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1TripleClicked }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+			Assert.False (isReq);
+
+			var view = new View () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				WantContinuousButtonPressed = true
+			};
+			Application.Top.Add (view);
+			Application.Begin (Application.Top);
+
+			ReflectionTools.InvokePrivate (
+				typeof (Application),
+				"ProcessMouseEvent",
+				new MouseEvent () {
+					X = 0,
+					Y = 0,
+					Flags = 0
+				});
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('M', 0, false, false, false)
+			};
+			expectedCki = default;
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (0, (int)key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Equal ("<", code);
+			Assert.Equal (3, values.Length);
+			Assert.Equal ("0", values [0]);
+			Assert.Equal ("2", values [1]);
+			Assert.Equal ("3", values [^1]);
+			Assert.Equal ("M", terminating);
+			Assert.True (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1Pressed }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+			Assert.False (isReq);
+
+			Application.Iteration += () => {
+				if (actionStarted) {
+					// set Application.WantContinuousButtonPressedView to null
+					view.WantContinuousButtonPressed = false;
+					ReflectionTools.InvokePrivate (
+						typeof (Application),
+						"ProcessMouseEvent",
+						new MouseEvent () {
+							X = 0,
+							Y = 0,
+							Flags = 0
+						});
+
+					Application.RequestStop ();
+				}
+			};
+
+			Application.Run ();
+
+			Assert.Null (Application.WantContinuousButtonPressedView);
+
+			Assert.Equal (MouseFlags.Button1Pressed, arg1);
+			Assert.Equal (new Point (1, 2), arg2);
+
+			ClearAll ();
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('m', 0, false, false, false)
+			};
+			expectedCki = default;
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Null (escSeqReqProc);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (0, (int)key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Equal ("<", code);
+			Assert.Equal (3, values.Length);
+			Assert.Equal ("0", values [0]);
+			Assert.Equal ("2", values [1]);
+			Assert.Equal ("3", values [^1]);
+			Assert.Equal ("m", terminating);
+			Assert.True (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1Released }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+			Assert.False (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+
+			ClearAll ();
+
+			Assert.Null (escSeqReqProc);
+			escSeqReqProc = new EscSeqReqProc ();
+			escSeqReqProc.Add ("t");
+
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('8', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('1', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo ('t', 0, false, false, false)
+			};
+			expectedCki = default;
+			Assert.Single (escSeqReqProc.EscSeqReqStats);
+			Assert.Equal ("t", escSeqReqProc.EscSeqReqStats [^1].Terminating);
+			EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
+			Assert.Empty (escSeqReqProc.EscSeqReqStats);
+			Assert.Equal (expectedCki, newConsoleKeyInfo);
+			Assert.Equal (0, (int)key);
+			Assert.Equal (0, (int)mod);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (3, values.Length);
+			Assert.Equal ("8", values [0]);
+			Assert.Equal ("10", values [1]);
+			Assert.Equal ("20", values [^1]);
+			Assert.Equal ("t", terminating);
+			Assert.False (isKeyMouse);
+			Assert.Equal (new List<MouseFlags> () { 0 }, mouseFlags);
+			Assert.Equal (Point.Empty, pos);
+			Assert.True (isReq);
+			Assert.Equal (0, (int)arg1);
+			Assert.Equal (Point.Empty, arg2);
+		}
+
+		private void ClearAll ()
+		{
+			escSeqReqProc = default;
+			newConsoleKeyInfo = default;
+			key = default;
+			cki = default;
+			mod = default;
+			c1Control = default;
+			code = default;
+			terminating = default;
+			values = default;
+			isKeyMouse = default;
+			isReq = default;
+			mouseFlags = default;
+			pos = default;
+			arg1 = default;
+			arg2 = default;
+		}
+
+		private void ProcessContinuousButtonPressed (MouseFlags arg1, Point arg2)
+		{
+			this.arg1 = arg1;
+			this.arg2 = arg2;
+			actionStarted = true;
+		}
+
+		[Fact]
+		public void GetEscapeResult_Tests ()
+		{
+			char [] kChars = new char [] { '\u001b', '[', '5', ';', '1', '0', 'r' };
+			(c1Control, code, values, terminating) = EscSeqUtils.GetEscapeResult (kChars);
+			Assert.Equal ("CSI", c1Control);
+			Assert.Null (code);
+			Assert.Equal (2, values.Length);
+			Assert.Equal ("5", values [0]);
+			Assert.Equal ("10", values [^1]);
+			Assert.Equal ("r", terminating);
+		}
+
+		[Fact]
+		public void GetC1ControlChar_Tests ()
+		{
+			Assert.Equal ("IND", EscSeqUtils.GetC1ControlChar ('D'));
+			Assert.Equal ("NEL", EscSeqUtils.GetC1ControlChar ('E'));
+			Assert.Equal ("HTS", EscSeqUtils.GetC1ControlChar ('H'));
+			Assert.Equal ("RI", EscSeqUtils.GetC1ControlChar ('M'));
+			Assert.Equal ("SS2", EscSeqUtils.GetC1ControlChar ('N'));
+			Assert.Equal ("SS3", EscSeqUtils.GetC1ControlChar ('O'));
+			Assert.Equal ("DCS", EscSeqUtils.GetC1ControlChar ('P'));
+			Assert.Equal ("SPA", EscSeqUtils.GetC1ControlChar ('V'));
+			Assert.Equal ("EPA", EscSeqUtils.GetC1ControlChar ('W'));
+			Assert.Equal ("SOS", EscSeqUtils.GetC1ControlChar ('X'));
+			Assert.Equal ("DECID", EscSeqUtils.GetC1ControlChar ('Z'));
+			Assert.Equal ("CSI", EscSeqUtils.GetC1ControlChar ('['));
+			Assert.Equal ("ST", EscSeqUtils.GetC1ControlChar ('\\'));
+			Assert.Equal ("OSC", EscSeqUtils.GetC1ControlChar (']'));
+			Assert.Equal ("PM", EscSeqUtils.GetC1ControlChar ('^'));
+			Assert.Equal ("APC", EscSeqUtils.GetC1ControlChar ('_'));
+			Assert.Equal ("", EscSeqUtils.GetC1ControlChar ('\0'));
+		}
+
+		[Fact]
+		public void GetConsoleModifiers_Tests ()
+		{
+			Assert.Equal (ConsoleModifiers.Shift, EscSeqUtils.GetConsoleModifiers ("2"));
+			Assert.Equal (ConsoleModifiers.Alt, EscSeqUtils.GetConsoleModifiers ("3"));
+			Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Alt, EscSeqUtils.GetConsoleModifiers ("4"));
+			Assert.Equal (ConsoleModifiers.Control, EscSeqUtils.GetConsoleModifiers ("5"));
+			Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Control, EscSeqUtils.GetConsoleModifiers ("6"));
+			Assert.Equal (ConsoleModifiers.Alt | ConsoleModifiers.Control, EscSeqUtils.GetConsoleModifiers ("7"));
+			Assert.Equal (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control, EscSeqUtils.GetConsoleModifiers ("8"));
+			Assert.Equal (0, (int)EscSeqUtils.GetConsoleModifiers (""));
+		}
+
+		[Fact]
+		public void GetConsoleKey_Tests ()
+		{
+			ConsoleModifiers mod = 0;
+			Assert.Equal (ConsoleKey.UpArrow, EscSeqUtils.GetConsoleKey ('A', "", ref mod));
+			Assert.Equal (ConsoleKey.DownArrow, EscSeqUtils.GetConsoleKey ('B', "", ref mod));
+			Assert.Equal (key = ConsoleKey.RightArrow, EscSeqUtils.GetConsoleKey ('C', "", ref mod));
+			Assert.Equal (ConsoleKey.LeftArrow, EscSeqUtils.GetConsoleKey ('D', "", ref mod));
+			Assert.Equal (ConsoleKey.End, EscSeqUtils.GetConsoleKey ('F', "", ref mod));
+			Assert.Equal (ConsoleKey.Home, EscSeqUtils.GetConsoleKey ('H', "", ref mod));
+			Assert.Equal (ConsoleKey.F1, EscSeqUtils.GetConsoleKey ('P', "", ref mod));
+			Assert.Equal (ConsoleKey.F2, EscSeqUtils.GetConsoleKey ('Q', "", ref mod));
+			Assert.Equal (ConsoleKey.F3, EscSeqUtils.GetConsoleKey ('R', "", ref mod));
+			Assert.Equal (ConsoleKey.F4, EscSeqUtils.GetConsoleKey ('S', "", ref mod));
+			Assert.Equal (ConsoleKey.Tab, EscSeqUtils.GetConsoleKey ('Z', "", ref mod));
+			Assert.Equal (ConsoleModifiers.Shift, mod);
+			Assert.Equal (0, (int)EscSeqUtils.GetConsoleKey ('\0', "", ref mod));
+			Assert.Equal (ConsoleKey.Insert, EscSeqUtils.GetConsoleKey ('~', "2", ref mod));
+			Assert.Equal (ConsoleKey.Delete, EscSeqUtils.GetConsoleKey ('~', "3", ref mod));
+			Assert.Equal (ConsoleKey.PageUp, EscSeqUtils.GetConsoleKey ('~', "5", ref mod));
+			Assert.Equal (ConsoleKey.PageDown, EscSeqUtils.GetConsoleKey ('~', "6", ref mod));
+			Assert.Equal (ConsoleKey.F5, EscSeqUtils.GetConsoleKey ('~', "15", ref mod));
+			Assert.Equal (ConsoleKey.F6, EscSeqUtils.GetConsoleKey ('~', "17", ref mod));
+			Assert.Equal (ConsoleKey.F7, EscSeqUtils.GetConsoleKey ('~', "18", ref mod));
+			Assert.Equal (ConsoleKey.F8, EscSeqUtils.GetConsoleKey ('~', "19", ref mod));
+			Assert.Equal (ConsoleKey.F9, EscSeqUtils.GetConsoleKey ('~', "20", ref mod));
+			Assert.Equal (ConsoleKey.F10, EscSeqUtils.GetConsoleKey ('~', "21", ref mod));
+			Assert.Equal (ConsoleKey.F11, EscSeqUtils.GetConsoleKey ('~', "23", ref mod));
+			Assert.Equal (ConsoleKey.F12, EscSeqUtils.GetConsoleKey ('~', "24", ref mod));
+			Assert.Equal (0, (int)EscSeqUtils.GetConsoleKey ('~', "", ref mod));
+		}
+
+		[Fact]
+		public void GetKeyCharArray_Tests ()
+		{
+			var cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo('[', 0, false, false, false),
+				new ConsoleKeyInfo('5', 0, false, false, false),
+				new ConsoleKeyInfo(';', 0, false, false, false),
+				new ConsoleKeyInfo('1', 0, false, false, false),
+				new ConsoleKeyInfo('0', 0, false, false, false),
+				new ConsoleKeyInfo('r', 0, false, false, false),
+			};
+
+			Assert.Equal (new char [] { '\u001b', '[', '5', ';', '1', '0', 'r' }, EscSeqUtils.GetKeyCharArray (cki));
+		}
+
+		[Fact, AutoInitShutdown]
+		public void GetMouse_Tests ()
+		{
+			var cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('M', 0, false, false, false)
+			};
+			EscSeqUtils.GetMouse (cki, out List<MouseFlags> mouseFlags, out Point pos, ProcessContinuousButtonPressed);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1Pressed }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('m', 0, false, false, false)
+			};
+			EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed);
+			Assert.Equal (2, mouseFlags.Count);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1Released, MouseFlags.Button1Clicked }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('M', 0, false, false, false)
+			};
+			EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1DoubleClicked }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('M', 0, false, false, false)
+			};
+			EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1TripleClicked }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+
+			cki = new ConsoleKeyInfo [] {
+				new ConsoleKeyInfo ('\u001b', 0, false, false, false),
+				new ConsoleKeyInfo ('[', 0, false, false, false),
+				new ConsoleKeyInfo ('<', 0, false, false, false),
+				new ConsoleKeyInfo ('0', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('2', 0, false, false, false),
+				new ConsoleKeyInfo (';', 0, false, false, false),
+				new ConsoleKeyInfo ('3', 0, false, false, false),
+				new ConsoleKeyInfo ('m', 0, false, false, false)
+			};
+			EscSeqUtils.GetMouse (cki, out mouseFlags, out pos, ProcessContinuousButtonPressed);
+			Assert.Equal (new List<MouseFlags> () { MouseFlags.Button1Released }, mouseFlags);
+			Assert.Equal (new Point (1, 2), pos);
+		}
+
+		[Fact]
+		public void GetParentProcess_Tests ()
+		{
+			if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
+				Assert.NotNull (EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ()));
+			} else {
+				Assert.Null (EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ()));
+			}
+		}
+	}
+}

+ 16 - 4
UnitTests/Core/LayoutTests.cs

@@ -328,12 +328,15 @@ namespace Terminal.Gui.CoreTests {
 			Application.Begin (Application.Top);
 
 			Assert.True (label.AutoSize);
+			// Here the AutoSize ensuring the right size with width 28 (Dim.Fill)
+			// and height 0 because wasn't set and the text is empty
 			Assert.Equal ("{X=0,Y=0,Width=28,Height=0}", label.Bounds.ToString ());
 
 			label.Text = "First line\nSecond line";
 			Application.Refresh ();
 
-			// Here the AutoSize ensuring the right size
+			// Here the AutoSize ensuring the right size with width 28 (Dim.Fill)
+			// and height 2 because wasn't set and the text has 2 lines
 			Assert.True (label.AutoSize);
 			Assert.Equal ("{X=0,Y=0,Width=28,Height=2}", label.Bounds.ToString ());
 
@@ -347,12 +350,16 @@ namespace Terminal.Gui.CoreTests {
 			label.Text = "First changed line\nSecond changed line\nNew line";
 			Application.Refresh ();
 
+			// Here the AutoSize is false and the width 28 (Dim.Fill) and
+			// height 1 because wasn't set and SetMinWidthHeight ensuring the minimum height
 			Assert.False (label.AutoSize);
 			Assert.Equal ("{X=0,Y=0,Width=28,Height=1}", label.Bounds.ToString ());
 
 			label.AutoSize = true;
 			Application.Refresh ();
 
+			// Here the AutoSize ensuring the right size with width 28 (Dim.Fill)
+			// and height 3 because wasn't set and the text has 3 lines
 			Assert.True (label.AutoSize);
 			Assert.Equal ("{X=0,Y=0,Width=28,Height=3}", label.Bounds.ToString ());
 		}
@@ -461,9 +468,14 @@ Y
 			Assert.Equal ("123 ", GetContents ());
 
 			lbl.Text = "12";
-
-			lbl.SuperView.Redraw (lbl.SuperView.NeedDisplay);
-
+			// Here the AutoSize ensuring the right size with width 3 (Dim.Absolute)
+			// that was set on the OnAdded method with the text length of 3
+			// and height 1 because wasn't set and the text has 1 line
+			Assert.Equal (new Rect (0, 0, 3, 1), lbl.Frame);
+			Assert.Equal (new Rect (0, 0, 3, 1), lbl.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 0, 0), lbl.SuperView.NeedDisplay);
+			Assert.True (lbl.SuperView.LayoutNeeded);
+			lbl.SuperView.Redraw (lbl.SuperView.Bounds);
 			Assert.Equal ("12  ", GetContents ());
 
 			string GetContents ()

+ 141 - 45
UnitTests/Core/ViewTests.cs

@@ -2353,21 +2353,7 @@ This is a tes
 				return true;
 			}
 
-			public void CorrectRedraw (Rect bounds)
-			{
-				// Clear the old and new frame area
-				Clear (NeedDisplay);
-				DrawText ();
-			}
-
-			public void IncorrectRedraw (Rect bounds)
-			{
-				// Clear only the new frame area
-				Clear ();
-				DrawText ();
-			}
-
-			private void DrawText ()
+			public override void Redraw (Rect bounds)
 			{
 				var idx = 0;
 				for (int r = 0; r < Frame.Height; r++) {
@@ -2501,7 +2487,7 @@ This is a tes
 		[InlineData (false)]
 		public void Clear_Does_Not_Spillover_Its_Parent (bool label)
 		{
-			var root = new View () { Width = 20, Height = 10 };
+			var root = new View () { Width = 20, Height = 10, ColorScheme = Colors.Base };
 
 			var v = label == true ?
 				new Label (new string ('c', 100)) {
@@ -2533,15 +2519,17 @@ cccccccccccccccccccc", output);
 
 			var attributes = new Attribute [] {
 				Colors.TopLevel.Normal,
-				Colors.TopLevel.Focus,
-
+				Colors.Base.Normal,
+				Colors.Base.Focus
 			};
 			if (label) {
 				TestHelpers.AssertDriverColorsAre (@"
-000000000000000000000", attributes);
+111111111111111111110
+111111111111111111110", attributes);
 			} else {
 				TestHelpers.AssertDriverColorsAre (@"
-111111111111111111110", attributes);
+222222222222222222220
+222222222222222222220", attributes);
 			}
 
 			if (label) {
@@ -2552,7 +2540,8 @@ cccccccccccccccccccc", output);
 				Assert.True (v.HasFocus);
 				Application.Refresh ();
 				TestHelpers.AssertDriverColorsAre (@"
-111111111111111111110", attributes);
+222222222222222222220
+222222222222222222220", attributes);
 			}
 		}
 
@@ -2571,7 +2560,7 @@ cccccccccccccccccccc", output);
 			top.Add (label, view);
 			Application.Begin (top);
 
-			view.CorrectRedraw (view.Bounds);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2579,9 +2568,12 @@ At 0,0
    and also with two lines.  ", output);
 
 			view.Frame = new Rect (1, 1, 10, 1);
+			Assert.Equal (new Rect (1, 1, 10, 1), view.Frame);
+			Assert.Equal (LayoutStyle.Computed, view.LayoutStyle);
+			view.LayoutStyle = LayoutStyle.Absolute;
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (1, 1, 31, 3), view.NeedDisplay);
-			view.CorrectRedraw (view.Bounds);
+			Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0     
  A text wit", output);
@@ -2602,7 +2594,7 @@ At 0,0
 			top.Add (label, view);
 			Application.Begin (top);
 
-			view.CorrectRedraw (view.Bounds);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2615,8 +2607,8 @@ At 0,0
 			view.Height = 1;
 			Assert.Equal (new Rect (1, 1, 10, 1), view.Frame);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (1, 1, 31, 3), view.NeedDisplay);
-			view.CorrectRedraw (view.Bounds);
+			Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0     
  A text wit", output);
@@ -2637,7 +2629,7 @@ At 0,0
 			top.Add (label, view);
 			Application.Begin (top);
 
-			view.IncorrectRedraw (view.Bounds);
+			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2645,9 +2637,12 @@ At 0,0
    and also with two lines.  ", output);
 
 			view.Frame = new Rect (1, 1, 10, 1);
+			Assert.Equal (new Rect (1, 1, 10, 1), view.Frame);
+			Assert.Equal (LayoutStyle.Computed, view.LayoutStyle);
+			view.LayoutStyle = LayoutStyle.Absolute;
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (1, 1, 31, 3), view.NeedDisplay);
-			view.IncorrectRedraw (view.Bounds);
+			Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay);
+			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
  A text wit                  
@@ -2670,7 +2665,7 @@ At 0,0
 			top.Add (label, view);
 			Application.Begin (top);
 
-			view.IncorrectRedraw (view.Bounds);
+			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2683,8 +2678,8 @@ At 0,0
 			view.Height = 1;
 			Assert.Equal (new Rect (1, 1, 10, 1), view.Frame);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (1, 1, 31, 3), view.NeedDisplay);
-			view.IncorrectRedraw (view.Bounds);
+			Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay);
+			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
  A text wit                  
@@ -2707,7 +2702,6 @@ At 0,0
 			top.Add (label, view);
 			Application.Begin (top);
 
-			view.CorrectRedraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2715,9 +2709,12 @@ At 0,0
    and also with two lines.  ", output);
 
 			view.Frame = new Rect (3, 3, 10, 1);
+			Assert.Equal (new Rect (3, 3, 10, 1), view.Frame);
+			Assert.Equal (LayoutStyle.Computed, view.LayoutStyle);
+			view.LayoutStyle = LayoutStyle.Absolute;
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (2, 2, 30, 2), view.NeedDisplay);
-			view.CorrectRedraw (view.Bounds);
+			Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0       
              
@@ -2740,7 +2737,7 @@ At 0,0
 			top.Add (label, view);
 			Application.Begin (top);
 
-			view.CorrectRedraw (view.Bounds);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2753,8 +2750,8 @@ At 0,0
 			view.Height = 1;
 			Assert.Equal (new Rect (3, 3, 10, 1), view.Frame);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (2, 2, 30, 2), view.NeedDisplay);
-			view.CorrectRedraw (view.Bounds);
+			Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0       
              
@@ -2777,7 +2774,7 @@ At 0,0
 			top.Add (label, view);
 			Application.Begin (top);
 
-			view.IncorrectRedraw (view.Bounds);
+			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2786,8 +2783,8 @@ At 0,0
 
 			view.Frame = new Rect (3, 3, 10, 1);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (2, 2, 30, 2), view.NeedDisplay);
-			view.IncorrectRedraw (view.Bounds);
+			Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay);
+			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2810,7 +2807,7 @@ At 0,0
 			top.Add (label, view);
 			Application.Begin (top);
 
-			view.IncorrectRedraw (view.Bounds);
+			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2823,8 +2820,8 @@ At 0,0
 			view.Height = 1;
 			Assert.Equal (new Rect (3, 3, 10, 1), view.Frame);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (2, 2, 30, 2), view.NeedDisplay);
-			view.IncorrectRedraw (view.Bounds);
+			Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay);
+			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
                              
@@ -2857,5 +2854,104 @@ At 0,0
 222";
 			TestHelpers.AssertDriverContentsAre (looksLike, output);
 		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void Frame_Set_After_Initialze_Update_NeededDisplay ()
+		{
+			var frame = new FrameView ();
+
+			var label = new Label ("This should be the first line.") {
+				TextAlignment = Terminal.Gui.TextAlignment.Centered,
+				ColorScheme = Colors.Menu,
+				Width = Dim.Fill (),
+				X = Pos.Center (),
+				Y = Pos.Center () - 2  // center minus 2 minus two lines top and bottom borders equal to zero (4-2-2=0)
+			};
+
+			var button = new Button ("Press me!") {
+				X = Pos.Center (),
+				Y = Pos.Center ()
+			};
+
+			frame.Add (label, button);
+
+			frame.X = Pos.Center ();
+			frame.Y = Pos.Center ();
+			frame.Width = 40;
+			frame.Height = 8;
+
+			var top = Application.Top;
+
+			top.Add (frame);
+
+			Assert.Equal (new Rect (0, 0, 80, 25), top.Frame);
+			Assert.Equal (new Rect (0, 0, 40, 8), frame.Frame);
+			Assert.Equal (new Rect (1, 1, 0, 0), frame.Subviews [0].Frame);
+			Assert.Equal ("ContentView()({X=1,Y=1,Width=0,Height=0})", frame.Subviews [0].ToString ());
+			Assert.Equal (new Rect (0, 0, 40, 8), new Rect (
+				frame.Frame.Left, frame.Frame.Top,
+				frame.Frame.Right, frame.Frame.Bottom));
+			Assert.Equal (new Rect (0, 0, 30, 1), label.Frame);
+			Assert.Equal (new Rect (0, 0, 13, 1), button.Frame);
+
+			Assert.Equal (new Rect (0, 0, 80, 25), top.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 40, 8), frame.NeedDisplay);
+			Assert.Equal (Rect.Empty, frame.Subviews [0].NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 40, 8), new Rect (
+				frame.NeedDisplay.Left, frame.NeedDisplay.Top,
+				frame.NeedDisplay.Right, frame.NeedDisplay.Bottom));
+			Assert.Equal (new Rect (0, 0, 30, 1), label.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 13, 1), button.NeedDisplay);
+
+			top.LayoutComplete += e => {
+				Assert.Equal (new Rect (0, 0, 80, 25), top.NeedDisplay);
+			};
+
+			frame.LayoutComplete += e => {
+				Assert.Equal (new Rect (0, 0, 40, 8), frame.NeedDisplay);
+			};
+
+			frame.Subviews [0].LayoutComplete += e => {
+				if (top.IsLoaded) {
+					Assert.Equal (new Rect (0, 0, 38, 6), frame.Subviews [0].NeedDisplay);
+				} else {
+					Assert.Equal (new Rect (0, 0, 38, 6), frame.Subviews [0].NeedDisplay);
+				}
+			};
+
+			label.LayoutComplete += e => {
+				Assert.Equal (new Rect (0, 0, 38, 1), label.NeedDisplay);
+			};
+
+			button.LayoutComplete += e => {
+				Assert.Equal (new Rect (0, 0, 13, 1), button.NeedDisplay);
+			};
+
+			Application.Begin (top);
+
+			Assert.True (label.AutoSize);
+			Assert.Equal (new Rect (0, 0, 80, 25), top.Frame);
+			Assert.Equal (new Rect (20, 8, 40, 8), frame.Frame);
+			Assert.Equal (new Rect (1, 1, 38, 6), frame.Subviews [0].Frame);
+			Assert.Equal ("ContentView()({X=1,Y=1,Width=38,Height=6})", frame.Subviews [0].ToString ());
+			Assert.Equal (new Rect (20, 8, 60, 16), new Rect (
+				frame.Frame.Left, frame.Frame.Top,
+				frame.Frame.Right, frame.Frame.Bottom));
+			Assert.Equal (new Rect (0, 0, 38, 1), label.Frame);
+			Assert.Equal (new Rect (12, 2, 13, 1), button.Frame);
+			var expected = @"
+                    ┌──────────────────────────────────────┐
+                    │    This should be the first line.    │
+                    │                                      │
+                    │            [ Press me! ]             │
+                    │                                      │
+                    │                                      │
+                    │                                      │
+                    └──────────────────────────────────────┘
+";
+
+			TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+		}
 	}
 }

+ 11 - 8
UnitTests/Drivers/ClipboardTests.cs

@@ -102,8 +102,8 @@ namespace Terminal.Gui.DriverTests {
 		[Fact, AutoInitShutdown (useFakeClipboard: false)]
 		public void IsSupported_Get ()
 		{
-			if (Clipboard.IsSupported) 				Assert.True (Clipboard.IsSupported);
-else 				Assert.False (Clipboard.IsSupported);
+			if (Clipboard.IsSupported) Assert.True (Clipboard.IsSupported);
+			else Assert.False (Clipboard.IsSupported);
 		}
 
 		[Fact, AutoInitShutdown (useFakeClipboard: false)]
@@ -129,18 +129,19 @@ else 				Assert.False (Clipboard.IsSupported);
 		public void TrySetClipboardData_Sets_The_OS_Clipboard ()
 		{
 			var clipText = "The TrySetClipboardData_Sets_The_OS_Clipboard unit test pasted this to the OS clipboard.";
-			if (Clipboard.IsSupported) 				Assert.True (Clipboard.TrySetClipboardData (clipText));
-else 				Assert.False (Clipboard.TrySetClipboardData (clipText));
+			if (Clipboard.IsSupported) Assert.True (Clipboard.TrySetClipboardData (clipText));
+			else Assert.False (Clipboard.TrySetClipboardData (clipText));
 
 			Application.Iteration += () => Application.RequestStop ();
 
 			Application.Run ();
 
-			if (Clipboard.IsSupported) 				Assert.Equal (clipText, Clipboard.Contents);
-else 				Assert.NotEqual (clipText, Clipboard.Contents);
+			if (Clipboard.IsSupported) Assert.Equal (clipText, Clipboard.Contents);
+			else Assert.NotEqual (clipText, Clipboard.Contents);
 		}
 
-
+		// Disabling this test for now because it is not reliable 
+#if false
 		[Fact, AutoInitShutdown (useFakeClipboard: false)]
 		public void Contents_Copies_From_OS_Clipboard ()
 		{
@@ -262,12 +263,13 @@ else 				Assert.NotEqual (clipText, Clipboard.Contents);
 
 				Application.RequestStop ();
 			};
-
+			
 			Application.Run ();
 
 			if (!failed) 				Assert.Equal (clipText, clipReadText.TrimEnd ());
 
 		}
+#endif
 
 		bool Is_WSL_Platform ()
 		{
@@ -284,5 +286,6 @@ else 				Assert.NotEqual (clipText, Clipboard.Contents);
 				return false;
 			}
 		}
+
 	}
 }

+ 15 - 15
UnitTests/Drivers/ConsoleDriverTests.cs

@@ -171,7 +171,7 @@ namespace Terminal.Gui.DriverTests {
 
 			// MockDriver will still be 120x40
 			wasTerminalResized = false;
-			Application.HeightAsBuffer = true;
+			Application.EnableConsoleScrolling = true;
 			driver.SetWindowSize (40, 20);
 			Assert.Equal (120, Application.Driver.Cols);
 			Assert.Equal (40, Application.Driver.Rows);
@@ -186,12 +186,12 @@ namespace Terminal.Gui.DriverTests {
 
 		[Theory]
 		[InlineData (typeof (FakeDriver))]
-		public void HeightAsBuffer_Is_False_Left_And_Top_Is_Always_Zero (Type driverType)
+		public void EnableConsoleScrolling_Is_False_Left_And_Top_Is_Always_Zero (Type driverType)
 		{
 			var driver = (FakeDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
 
-			Assert.False (Application.HeightAsBuffer);
+			Assert.False (Application.EnableConsoleScrolling);
 			Assert.Equal (0, Console.WindowLeft);
 			Assert.Equal (0, Console.WindowTop);
 
@@ -204,13 +204,13 @@ namespace Terminal.Gui.DriverTests {
 
 		[Theory]
 		[InlineData (typeof (FakeDriver))]
-		public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType)
+		public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType)
 		{
 			var driver = (FakeDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
 
-			Application.HeightAsBuffer = true;
-			Assert.True (Application.HeightAsBuffer);
+			Application.EnableConsoleScrolling = true;
+			Assert.True (Application.EnableConsoleScrolling);
 
 			driver.SetWindowPosition (81, 25);
 			Assert.Equal (0, Console.WindowLeft);
@@ -221,13 +221,13 @@ namespace Terminal.Gui.DriverTests {
 
 		[Theory]
 		[InlineData (typeof (FakeDriver))]
-		public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType)
+		public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType)
 		{
 			var driver = (FakeDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
 
-			Application.HeightAsBuffer = true;
-			Assert.True (Application.HeightAsBuffer);
+			Application.EnableConsoleScrolling = true;
+			Assert.True (Application.EnableConsoleScrolling);
 
 			driver.SetWindowPosition (81, 25);
 			Assert.Equal (0, Console.WindowLeft);
@@ -261,13 +261,13 @@ namespace Terminal.Gui.DriverTests {
 
 		[Theory]
 		[InlineData (typeof (FakeDriver))]
-		public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType)
+		public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType)
 		{
 			var driver = (FakeDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
 
-			Application.HeightAsBuffer = true;
-			Assert.True (Application.HeightAsBuffer);
+			Application.EnableConsoleScrolling = true;
+			Assert.True (Application.EnableConsoleScrolling);
 
 			driver.SetWindowPosition (80, 26);
 			Assert.Equal (0, Console.WindowLeft);
@@ -278,13 +278,13 @@ namespace Terminal.Gui.DriverTests {
 
 		[Theory]
 		[InlineData (typeof (FakeDriver))]
-		public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType)
+		public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType)
 		{
 			var driver = (FakeDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
 
-			Application.HeightAsBuffer = true;
-			Assert.True (Application.HeightAsBuffer);
+			Application.EnableConsoleScrolling = true;
+			Assert.True (Application.EnableConsoleScrolling);
 
 			driver.SetWindowPosition (80, 26);
 			Assert.Equal (0, Console.WindowLeft);

+ 184 - 6
UnitTests/Text/TextFormatterTests.cs

@@ -2162,7 +2162,7 @@ namespace Terminal.Gui.TextTests {
 			var height = 8;
 			var wrappedLines = TextFormatter.WordWrap (text, width, true);
 			var breakLines = "";
-			foreach (var line in wrappedLines) 				breakLines += $"{line}{Environment.NewLine}";
+			foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}";
 			var label = new Label (breakLines) { Width = Dim.Fill (), Height = Dim.Fill () };
 			var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () };
 
@@ -2200,7 +2200,7 @@ namespace Terminal.Gui.TextTests {
 			var height = 3;
 			var wrappedLines = TextFormatter.WordWrap (text, height, true);
 			var breakLines = "";
-			for (int i = 0; i < wrappedLines.Count; i++) 				breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}";
+			for (int i = 0; i < wrappedLines.Count; i++) breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}";
 			var label = new Label (breakLines) {
 				TextDirection = TextDirection.TopBottom_LeftRight,
 				Width = Dim.Fill (),
@@ -2237,7 +2237,7 @@ namespace Terminal.Gui.TextTests {
 			var height = 8;
 			var wrappedLines = TextFormatter.WordWrap (text, width, true);
 			var breakLines = "";
-			foreach (var line in wrappedLines) 				breakLines += $"{line}{Environment.NewLine}";
+			foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}";
 			var label = new Label (breakLines) { Width = Dim.Fill (), Height = Dim.Fill () };
 			var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () };
 
@@ -2276,7 +2276,7 @@ namespace Terminal.Gui.TextTests {
 			var height = 4;
 			var wrappedLines = TextFormatter.WordWrap (text, width, true);
 			var breakLines = "";
-			for (int i = 0; i < wrappedLines.Count; i++) 				breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}";
+			for (int i = 0; i < wrappedLines.Count; i++) breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}";
 			var label = new Label (breakLines) {
 				TextDirection = TextDirection.TopBottom_LeftRight,
 				Width = Dim.Fill (),
@@ -2888,7 +2888,7 @@ namespace Terminal.Gui.TextTests {
 			Assert.Equal ("nd", list1 [10].ToString ());
 			Assert.Equal ("Line", list1 [11].ToString ());
 			Assert.Equal ("- 2.", list1 [^1].ToString ());
-			foreach (var txt in list1) 				wrappedText1 += txt;
+			foreach (var txt in list1) wrappedText1 += txt;
 			Assert.Equal (" Asentencehaswords.  This isthesecondLine- 2.", wrappedText1);
 
 			// With preserveTrailingSpaces = true.
@@ -2910,7 +2910,7 @@ namespace Terminal.Gui.TextTests {
 			Assert.Equal ("Line", list2 [13].ToString ());
 			Assert.Equal (" - ", list2 [14].ToString ());
 			Assert.Equal ("2. ", list2 [^1].ToString ());
-			foreach (var txt in list2) 				wrappedText2 += txt;
+			foreach (var txt in list2) wrappedText2 += txt;
 			Assert.Equal (" A sentence has words.  This is the second Line - 2. ", wrappedText2);
 		}
 
@@ -4288,5 +4288,183 @@ t     ", output);
 0
 0", new Attribute [] { Colors.Base.Normal });
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_Negative_Bounds_Horizontal_Without_New_Lines ()
+		{
+			var subView = new View () { Id = "subView", Y = 1, Width = 7, Text = "subView" };
+			var view = new View () { Id = "view", Width = 20, Height = 2, Text = "01234567890123456789" };
+			view.Add (subView);
+			var content = new View () { Id = "content", Width = 20, Height = 20 };
+			content.Add (view);
+			var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 };
+			container.Add (content);
+			var top = Application.Top;
+			top.Add (container);
+			Application.Driver.Clip = container.Frame;
+			Application.Begin (top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 01234
+ subVi", output);
+
+			content.X = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 12345
+ ubVie", output);
+
+			content.Y = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ ubVie", output);
+
+			content.Y = -2;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+
+			content.X = -20;
+			content.Y = 0;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_Negative_Bounds_Horizontal_With_New_Lines ()
+		{
+			var subView = new View () { Id = "subView", X = 1, Width = 1, Height = 7, Text = "s\nu\nb\nV\ni\ne\nw" };
+			var view = new View () { Id = "view", Width = 2, Height = 20, Text = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" };
+			view.Add (subView);
+			var content = new View () { Id = "content", Width = 20, Height = 20 };
+			content.Add (view);
+			var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 };
+			container.Add (content);
+			var top = Application.Top;
+			top.Add (container);
+			Application.Driver.Clip = container.Frame;
+			Application.Begin (top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 0s
+ 1u
+ 2b
+ 3V
+ 4i", output);
+
+			content.X = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ s
+ u
+ b
+ V
+ i", output);
+
+			content.X = -2;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"", output);
+
+			content.X = 0;
+			content.Y = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 1u
+ 2b
+ 3V
+ 4i
+ 5e", output);
+
+			content.Y = -6;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 6w
+ 7 
+ 8 
+ 9 
+ 0 ", output);
+
+			content.Y = -19;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 9", output);
+
+			content.Y = -20;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+
+			content.X = -2;
+			content.Y = 0;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_Negative_Bounds_Vertical ()
+		{
+			var subView = new View () { Id = "subView", X = 1, Width = 1, Height = 7, Text = "subView", TextDirection = TextDirection.TopBottom_LeftRight };
+			var view = new View () { Id = "view", Width = 2, Height = 20, Text = "01234567890123456789", TextDirection = TextDirection.TopBottom_LeftRight };
+			view.Add (subView);
+			var content = new View () { Id = "content", Width = 20, Height = 20 };
+			content.Add (view);
+			var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 };
+			container.Add (content);
+			var top = Application.Top;
+			top.Add (container);
+			Application.Driver.Clip = container.Frame;
+			Application.Begin (top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 0s
+ 1u
+ 2b
+ 3V
+ 4i", output);
+
+			content.X = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ s
+ u
+ b
+ V
+ i", output);
+
+			content.X = -2;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"", output);
+
+			content.X = 0;
+			content.Y = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 1u
+ 2b
+ 3V
+ 4i
+ 5e", output);
+
+			content.Y = -6;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 6w
+ 7 
+ 8 
+ 9 
+ 0 ", output);
+
+			content.Y = -19;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 9", output);
+
+			content.Y = -20;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+
+			content.X = -2;
+			content.Y = 0;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+		}
 	}
 }

+ 6 - 6
UnitTests/TopLevels/MessageBoxTests.cs

@@ -29,7 +29,7 @@ namespace Terminal.Gui.TopLevelTests {
 					Application.RequestStop ();
 
 				} else if (iterations == 1) {
-					Application.Top.Redraw (Application.Top.Bounds);
+					Application.Refresh ();
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
                 ┌ Title ───────────────────────────────────────┐
                 │                   Message                    │
@@ -71,7 +71,7 @@ namespace Terminal.Gui.TopLevelTests {
 
 					Application.RequestStop ();
 				} else if (iterations == 1) {
-					Application.Top.Redraw (Application.Top.Bounds);
+					Application.Refresh ();
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
          ┌ About UI Catalog ──────────────────────────────────────────┐
          │             A comprehensive sample library for             │
@@ -110,7 +110,7 @@ namespace Terminal.Gui.TopLevelTests {
 
 					Application.RequestStop ();
 				} else if (iterations == 1) {
-					Application.Top.Redraw (Application.Top.Bounds);
+					Application.Refresh ();
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
                                     ┌─────┐
                                     │Messa│
@@ -140,7 +140,7 @@ namespace Terminal.Gui.TopLevelTests {
 
 					Application.RequestStop ();
 				} else if (iterations == 1) {
-					Application.Top.Redraw (Application.Top.Bounds);
+					Application.Refresh ();
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
                                   ┌ Title ──┐
                                   │ Message │
@@ -170,7 +170,7 @@ namespace Terminal.Gui.TopLevelTests {
 
 					Application.RequestStop ();
 				} else if (iterations == 1) {
-					Application.Top.Redraw (Application.Top.Bounds);
+					Application.Refresh ();
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌ mywindow ────────────────────────────────────────────────────────────────────┐
 │ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff│
@@ -224,7 +224,7 @@ namespace Terminal.Gui.TopLevelTests {
 
 					Application.RequestStop ();
 				} else if (iterations == 1) {
-					Application.Top.Redraw (Application.Top.Bounds);
+					Application.Refresh ();
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌ mywindow ────────────────────────────────────────────────────────────────────┐
 │ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff │

+ 50 - 0
UnitTests/TopLevels/ToplevelTests.cs

@@ -149,7 +149,9 @@ namespace Terminal.Gui.TopLevelTests {
 		[AutoInitShutdown]
 		public void Internal_Tests ()
 		{
+			Toplevel.dragPosition = null; // dragPosition is `static` and must be reset for each instance or unit tests will fail?
 			var top = new Toplevel ();
+
 			var eventInvoked = "";
 
 			top.ChildUnloaded += (e) => eventInvoked = "ChildUnloaded";
@@ -1031,5 +1033,53 @@ namespace Terminal.Gui.TopLevelTests {
 			Application.Driver.GetCursorVisibility (out cursor);
 			Assert.Equal (CursorVisibility.Invisible, cursor);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void IsLoaded_Application_Begin ()
+		{
+			var top = Application.Top;
+			Assert.False (top.IsLoaded);
+
+			Application.Begin (top);
+			Assert.True (top.IsLoaded);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void IsLoaded_With_Sub_Toplevel_Application_Begin_NeedDisplay ()
+		{
+			var top = Application.Top;
+			var subTop = new Toplevel ();
+			var view = new View (new Rect (0, 0, 20, 10));
+			subTop.Add (view);
+			top.Add (subTop);
+
+			Assert.False (top.IsLoaded);
+			Assert.False (subTop.IsLoaded);
+			Assert.Equal (new Rect (0, 0, 20, 10), view.Frame);
+			Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay);
+
+			view.LayoutStarted += view_LayoutStarted;
+
+			void view_LayoutStarted (View.LayoutEventArgs e)
+			{
+				Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay);
+				view.LayoutStarted -= view_LayoutStarted;
+			}
+
+			Application.Begin (top);
+
+			Assert.True (top.IsLoaded);
+			Assert.True (subTop.IsLoaded);
+			Assert.Equal (new Rect (0, 0, 20, 10), view.Frame);
+
+			view.Frame = new Rect (1, 3, 10, 5);
+			Assert.Equal (new Rect (1, 3, 10, 5), view.Frame);
+			Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay);
+
+			view.Redraw (view.Bounds);
+			view.Frame = new Rect (1, 3, 10, 5);
+			Assert.Equal (new Rect (1, 3, 10, 5), view.Frame);
+			Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay);
+		}
 	}
 }

+ 15 - 14
UnitTests/Types/DimTests.cs

@@ -83,11 +83,11 @@ namespace Terminal.Gui.TypeTests {
 			var testVal = Rect.Empty;
 			testVal = Rect.Empty;
 			dim = Dim.Width (new View (testVal));
-			Assert.Equal ($"DimView(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
+			Assert.Equal ($"View(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
 
 			testVal = new Rect (1, 2, 3, 4);
 			dim = Dim.Width (new View (testVal));
-			Assert.Equal ($"DimView(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
+			Assert.Equal ($"View(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
 		}
 
 		[Fact]
@@ -141,11 +141,11 @@ namespace Terminal.Gui.TypeTests {
 			var testVal = Rect.Empty;
 			testVal = Rect.Empty;
 			dim = Dim.Height (new View (testVal));
-			Assert.Equal ($"DimView(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
+			Assert.Equal ($"View(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
 
 			testVal = new Rect (1, 2, 3, 4);
 			dim = Dim.Height (new View (testVal));
-			Assert.Equal ($"DimView(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
+			Assert.Equal ($"View(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
 		}
 
 		// TODO: Other Dim.Height tests (e.g. Equal?)
@@ -435,12 +435,12 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (49, f2.Frame.Width); // 50-1=49
 				Assert.Equal (5, f2.Frame.Height);
 	
-				Assert.Equal ("Combine(DimView(Width,FrameView()({X=0,Y=0,Width=49,Height=5}))-Absolute(2))", v1.Width.ToString ());
+				Assert.Equal ("Combine(View(Width,FrameView()({X=0,Y=0,Width=49,Height=5}))-Absolute(2))", v1.Width.ToString ());
 				Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
 				Assert.Equal (47, v1.Frame.Width); // 49-2=47
 				Assert.Equal (89, v1.Frame.Height); // 98-5-2-2=89
 
-				Assert.Equal ("Combine(DimView(Width,FrameView()({X=49,Y=0,Width=49,Height=5}))-Absolute(2))", v2.Width.ToString ());
+				Assert.Equal ("Combine(View(Width,FrameView()({X=49,Y=0,Width=49,Height=5}))-Absolute(2))", v2.Width.ToString ());
 				Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
 				Assert.Equal (47, v2.Frame.Width); // 49-2=47
 				Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89
@@ -455,8 +455,8 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (50, v4.Frame.Width);
 				Assert.Equal (50, v4.Frame.Height);
 
-				Assert.Equal ("Combine(DimView(Width,Button()({X=2,Y=7,Width=47,Height=89}))-DimView(Width,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Width.ToString ());
-				Assert.Equal ("Combine(DimView(Height,Button()({X=2,Y=7,Width=47,Height=89}))-DimView(Height,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Height.ToString ());
+				Assert.Equal ("Combine(View(Width,Button()({X=2,Y=7,Width=47,Height=89}))-View(Width,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Width.ToString ());
+				Assert.Equal ("Combine(View(Height,Button()({X=2,Y=7,Width=47,Height=89}))-View(Height,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Height.ToString ());
 				Assert.Equal (38, v5.Frame.Width); // 47-9=38
 				Assert.Equal (80, v5.Frame.Height); // 89-9=80
 
@@ -466,6 +466,7 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (18, v6.Frame.Height); // 89*20%=18
 
 				w.Width = 200;
+				Assert.True (t.LayoutNeeded);
 				w.Height = 200;
 				t.LayoutSubviews ();
 
@@ -487,13 +488,13 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (5, f2.Frame.Height);
 
 				v1.Text = "Button1";
-				Assert.Equal ("Combine(DimView(Width,FrameView()({X=0,Y=0,Width=99,Height=5}))-Absolute(2))", v1.Width.ToString ());
+				Assert.Equal ("Combine(View(Width,FrameView()({X=0,Y=0,Width=99,Height=5}))-Absolute(2))", v1.Width.ToString ());
 				Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
 				Assert.Equal (97, v1.Frame.Width); // 99-2=97
 				Assert.Equal (189, v1.Frame.Height); // 198-2-7=189
 
 				v2.Text = "Button2";
-				Assert.Equal ("Combine(DimView(Width,FrameView()({X=99,Y=0,Width=99,Height=5}))-Absolute(2))", v2.Width.ToString ());
+				Assert.Equal ("Combine(View(Width,FrameView()({X=99,Y=0,Width=99,Height=5}))-Absolute(2))", v2.Width.ToString ());
 				Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
 				Assert.Equal (97, v2.Frame.Width); // 99-2=97
 				Assert.Equal (189, v2.Frame.Height); // 198-2-7=189
@@ -517,16 +518,16 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (1, v4.Frame.Height); // 1 because is Dim.DimAbsolute
 
 				v5.Text = "Button5";
-				Assert.Equal ("Combine(DimView(Width,Button()({X=2,Y=7,Width=97,Height=189}))-DimView(Width,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Width.ToString ());
-				Assert.Equal ("Combine(DimView(Height,Button()({X=2,Y=7,Width=97,Height=189}))-DimView(Height,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Height.ToString ());
-				Assert.Equal (78, v5.Frame.Width); // 97-19=78
+				Assert.Equal ("Combine(View(Width,Button()({X=2,Y=7,Width=97,Height=189}))-View(Width,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Width.ToString ());
+				Assert.Equal ("Combine(View(Height,Button()({X=2,Y=7,Width=97,Height=189}))-View(Height,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Height.ToString ());
+				Assert.Equal (78, v5.Frame.Width); // 97-9=78
 				Assert.Equal (170, v5.Frame.Height); // 189-19=170
 
 				v6.Text = "Button6";
 				Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ());
 				Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ());
 				Assert.Equal (19, v6.Frame.Width); // 99*20%=19
-				Assert.Equal (38, v6.Frame.Height); // 198-7*20=38
+				Assert.Equal (38, v6.Frame.Height); // 198-7*20=18
 			};
 
 			Application.Iteration += () => Application.RequestStop ();

+ 2 - 2
UnitTests/UnitTests.csproj

@@ -21,8 +21,8 @@
     <DefineConstants>TRACE;DEBUG_IDISPOSABLE</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
-    <PackageReference Include="ReportGenerator" Version="5.1.12" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+    <PackageReference Include="ReportGenerator" Version="5.1.18" />
     <PackageReference Include="System.Collections" Version="4.3.0" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">

+ 10 - 10
UnitTests/Views/ScrollBarViewTests.cs

@@ -364,12 +364,12 @@ namespace Terminal.Gui.ViewTests {
 			Assert.True (_scrollBar.Visible);
 			Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ());
 			Assert.Equal (1, _scrollBar.Bounds.Width);
-			Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
+			Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
 				_scrollBar.Height.ToString ());
 			Assert.Equal (24, _scrollBar.Bounds.Height);
 			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
 			Assert.True (_scrollBar.OtherScrollBarView.Visible);
-			Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
+			Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
 				_scrollBar.OtherScrollBarView.Width.ToString ());
 			Assert.Equal (79, _scrollBar.OtherScrollBarView.Bounds.Width);
 			Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
@@ -381,12 +381,12 @@ namespace Terminal.Gui.ViewTests {
 			Assert.False (_scrollBar.Visible);
 			Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ());
 			Assert.Equal (1, _scrollBar.Bounds.Width);
-			Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
+			Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
 				_scrollBar.Height.ToString ());
 			Assert.Equal (24, _scrollBar.Bounds.Height);
 			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
 			Assert.True (_scrollBar.OtherScrollBarView.Visible);
-			Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))",
+			Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))",
 				_scrollBar.OtherScrollBarView.Width.ToString ());
 			Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width);
 			Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
@@ -398,12 +398,12 @@ namespace Terminal.Gui.ViewTests {
 			Assert.False (_scrollBar.Visible);
 			Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ());
 			Assert.Equal (1, _scrollBar.Bounds.Width);
-			Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
+			Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
 				_scrollBar.Height.ToString ());
 			Assert.Equal (24, _scrollBar.Bounds.Height);
 			Assert.False (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
 			Assert.False (_scrollBar.OtherScrollBarView.Visible);
-			Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))",
+			Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))",
 				_scrollBar.OtherScrollBarView.Width.ToString ());
 			Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width);
 			Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
@@ -415,12 +415,12 @@ namespace Terminal.Gui.ViewTests {
 			Assert.True (_scrollBar.Visible);
 			Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ());
 			Assert.Equal (1, _scrollBar.Bounds.Width);
-			Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))",
+			Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))",
 				_scrollBar.Height.ToString ());
 			Assert.Equal (25, _scrollBar.Bounds.Height);
 			Assert.False (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
 			Assert.False (_scrollBar.OtherScrollBarView.Visible);
-			Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))",
+			Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(0))",
 				_scrollBar.OtherScrollBarView.Width.ToString ());
 			Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width);
 			Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
@@ -432,12 +432,12 @@ namespace Terminal.Gui.ViewTests {
 			Assert.True (_scrollBar.Visible);
 			Assert.Equal ("Absolute(1)", _scrollBar.Width.ToString ());
 			Assert.Equal (1, _scrollBar.Bounds.Width);
-			Assert.Equal ("Combine(DimView(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
+			Assert.Equal ("Combine(View(Height,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
 				_scrollBar.Height.ToString ());
 			Assert.Equal (24, _scrollBar.Bounds.Height);
 			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
 			Assert.True (_scrollBar.OtherScrollBarView.Visible);
-			Assert.Equal ("Combine(DimView(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
+			Assert.Equal ("Combine(View(Width,HostView()({X=0,Y=0,Width=80,Height=25}))-Absolute(1))",
 				_scrollBar.OtherScrollBarView.Width.ToString ());
 			Assert.Equal (79, _scrollBar.OtherScrollBarView.Bounds.Width);
 			Assert.Equal ("Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());

+ 223 - 5
UnitTests/Views/ScrollViewTests.cs

@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using NStack;
 using Xunit;
 using Xunit.Abstractions;
 
@@ -280,5 +276,227 @@ namespace Terminal.Gui.ViewTests {
 ◄░░░├─┤░► 
 ", output);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Frame_And_Labels_Does_Not_Overspill_ScrollView ()
+		{
+			var sv = new ScrollView {
+				X = 3,
+				Y = 3,
+				Width = 10,
+				Height = 10,
+				ContentSize = new Size (50, 50)
+			};
+			for (int i = 0; i < 8; i++) {
+				sv.Add (new CustomButton ("█", $"Button {i}", 20, 3) { Y = i * 3 });
+			}
+			Application.Top.Add (sv);
+			Application.Begin (Application.Top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+   █████████▲
+   ██████But┬
+   █████████┴
+   ┌────────░
+   │     But░
+   └────────░
+   ┌────────░
+   │     But░
+   └────────▼
+   ◄├┤░░░░░► ", output);
+
+			sv.ContentOffset = new Point (5, 5);
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+   ─────────▲
+   ─────────┬
+    Button 2│
+   ─────────┴
+   ─────────░
+    Button 3░
+   ─────────░
+   ─────────░
+    Button 4▼
+   ◄├─┤░░░░► ", output);
+		}
+
+		private class CustomButton : FrameView {
+			private Label labelFill;
+			private Label labelText;
+
+			public CustomButton (string fill, ustring text, int width, int height)
+			{
+				Width = width;
+				Height = height;
+				labelFill = new Label () { AutoSize = false, Width = Dim.Fill (), Height = Dim.Fill (), Visible = false };
+				var fillText = new System.Text.StringBuilder ();
+				for (int i = 0; i < Bounds.Height; i++) {
+					if (i > 0) {
+						fillText.AppendLine ("");
+					}
+					for (int j = 0; j < Bounds.Width; j++) {
+						fillText.Append (fill);
+					}
+				}
+				labelFill.Text = fillText.ToString ();
+				labelText = new Label (text) { X = Pos.Center (), Y = Pos.Center () };
+				Add (labelFill, labelText);
+				CanFocus = true;
+			}
+
+			public override bool OnEnter (View view)
+			{
+				Border.BorderStyle = BorderStyle.None;
+				Border.DrawMarginFrame = false;
+				labelFill.Visible = true;
+				view = this;
+				return base.OnEnter (view);
+			}
+
+			public override bool OnLeave (View view)
+			{
+				Border.BorderStyle = BorderStyle.Single;
+				Border.DrawMarginFrame = true;
+				labelFill.Visible = false;
+				if (view == null)
+					view = this;
+				return base.OnLeave (view);
+			}
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Clear_Window_Inside_ScrollView ()
+		{
+			var topLabel = new Label ("At 15,0") { X = 15 };
+			var sv = new ScrollView {
+				X = 3,
+				Y = 3,
+				Width = 10,
+				Height = 10,
+				ContentSize = new Size (23, 23),
+				KeepContentAlwaysInViewport = false
+			};
+			var bottomLabel = new Label ("At 15,15") { X = 15, Y = 15 };
+			Application.Top.Add (topLabel, sv, bottomLabel);
+			Application.Begin (Application.Top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+               At 15,0 
+                       
+                       
+            ▲          
+            ┬          
+            ┴          
+            ░          
+            ░          
+            ░          
+            ░          
+            ░          
+            ▼          
+   ◄├┤░░░░░►           
+                       
+                       
+               At 15,15", output);
+
+			var attributes = new Attribute [] {
+				Colors.TopLevel.Normal,
+				Colors.TopLevel.Focus,
+				Colors.Base.Normal
+			};
+
+			TestHelpers.AssertDriverColorsAre (@"
+00000000000000000000000
+00000000000000000000000
+00000000000000000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00011111111110000000000
+00000000000000000000000
+00000000000000000000000
+00000000000000000000000", attributes);
+
+			sv.Add (new Window ("1") { X = 3, Y = 3, Width = 20, Height = 20 });
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+               At 15,0 
+                       
+                       
+            ▲          
+            ┬          
+            ┴          
+      ┌ 1 ──░          
+      │     ░          
+      │     ░          
+      │     ░          
+      │     ░          
+      │     ▼          
+   ◄├┤░░░░░►           
+                       
+                       
+               At 15,15", output);
+
+			TestHelpers.AssertDriverColorsAre (@"
+00000000000000000000000
+00000000000000000000000
+00000000000000000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000022222210000000000
+00000022222210000000000
+00000022222210000000000
+00000022222210000000000
+00000022222210000000000
+00000022222210000000000
+00011111111110000000000
+00000000000000000000000
+00000000000000000000000
+00000000000000000000000", attributes);
+
+			sv.ContentOffset = new Point (20, 20);
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+               At 15,0 
+                       
+                       
+     │      ▲          
+     │      ░          
+   ──┘      ░          
+            ░          
+            ░          
+            ┬          
+            │          
+            ┴          
+            ▼          
+   ◄░░░░├─┤►           
+                       
+                       
+               At 15,15", output);
+
+			TestHelpers.AssertDriverColorsAre (@"
+00000000000000000000000
+00000000000000000000000
+00000000000000000000000
+00022200000010000000000
+00022200000010000000000
+00022200000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00000000000010000000000
+00011111111110000000000
+00000000000000000000000
+00000000000000000000000
+00000000000000000000000", attributes);
+		}
 	}
 }

+ 181 - 0
UnitTests/Views/TableViewTests.cs

@@ -321,6 +321,8 @@ namespace Terminal.Gui.ViewTests {
 				Bounds = new Rect (0, 0, 10, 5)
 			};
 
+			tableView.ChangeSelectionToEndOfTable(false);
+
 			// select the last row
 			tableView.MultiSelectedRegions.Clear ();
 			tableView.MultiSelectedRegions.Push (new TableView.TableSelection (new Point (0, 3), new Rect (0, 3, 4, 1)));
@@ -1506,6 +1508,185 @@ namespace Terminal.Gui.ViewTests {
 
 			Assert.DoesNotContain (new Point (1, 0), tableView.GetAllSelectedCells ());
 		}
+
+		[Fact, AutoInitShutdown]
+		public void TestToggleCells_MultiSelectOn ()
+		{
+			// 2 row table
+			var tableView = GetABCDEFTableView (out var dt);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+
+			tableView.MultiSelect = true;
+			tableView.AddKeyBinding(Key.Space,Command.ToggleChecked);
+
+			var selectedCell = tableView.GetAllSelectedCells().Single();
+			Assert.Equal(0,selectedCell.X);
+			Assert.Equal(0,selectedCell.Y);
+
+			// Go Right
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight });
+
+			selectedCell = tableView.GetAllSelectedCells().Single();
+			Assert.Equal(1,selectedCell.X);
+			Assert.Equal(0,selectedCell.Y);
+
+			// Toggle Select
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+			var m = tableView.MultiSelectedRegions.Single();
+			Assert.True(m.IsToggled);
+			Assert.Equal(1,m.Origin.X);
+			Assert.Equal(0,m.Origin.Y);
+			selectedCell = tableView.GetAllSelectedCells().Single();
+			Assert.Equal(1,selectedCell.X);
+			Assert.Equal(0,selectedCell.Y);
+
+			// Go Left
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft });
+
+			// Both Toggled and Moved to should be selected
+			Assert.Equal(2,tableView.GetAllSelectedCells().Count());
+			var s1 = tableView.GetAllSelectedCells().ElementAt(0);
+			var s2 = tableView.GetAllSelectedCells().ElementAt(1);
+			Assert.Equal(1,s1.X);
+			Assert.Equal(0,s1.Y);
+			Assert.Equal(0,s2.X);
+			Assert.Equal(0,s2.Y);
+
+			// Go Down
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown });
+
+			// Both Toggled and Moved to should be selected but not 0,0
+			// which we moved down from
+			Assert.Equal(2,tableView.GetAllSelectedCells().Count());
+			s1 = tableView.GetAllSelectedCells().ElementAt(0);
+			s2 = tableView.GetAllSelectedCells().ElementAt(1);
+			Assert.Equal(1,s1.X);
+			Assert.Equal(0,s1.Y);
+			Assert.Equal(0,s2.X);
+			Assert.Equal(1,s2.Y);
+
+
+			// Go back to the toggled cell
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight});
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp});
+
+			// Toggle off 
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+
+			// Go Left
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft});
+
+			selectedCell = tableView.GetAllSelectedCells().Single();
+			Assert.Equal(0,selectedCell.X);
+			Assert.Equal(0,selectedCell.Y);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void TestToggleCells_MultiSelectOn_FullRowSelect ()
+		{
+			// 2 row table
+			var tableView = GetABCDEFTableView (out var dt);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			tableView.FullRowSelect = true;
+			tableView.MultiSelect = true;
+			tableView.AddKeyBinding(Key.Space,Command.ToggleChecked);
+
+			// Toggle Select Cell 0,0
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+
+			// Go Down
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown });
+
+			var m = tableView.MultiSelectedRegions.Single();
+			Assert.True(m.IsToggled);
+			Assert.Equal(0,m.Origin.X);
+			Assert.Equal(0,m.Origin.Y);
+
+			//First row toggled and Second row active = 12 selected cells
+			Assert.Equal(12,tableView.GetAllSelectedCells().Count());
+
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight });
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp });
+			
+			Assert.Single(tableView.MultiSelectedRegions.Where(r=>r.IsToggled));
+
+			// Can untoggle at 1,0 even though 0,0 was initial toggle because FullRowSelect is on
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+
+			Assert.Empty(tableView.MultiSelectedRegions.Where(r=>r.IsToggled));
+
+		}
+
+
+		[Fact, AutoInitShutdown]
+		public void TestToggleCells_MultiSelectOn_SquareSelectToggled ()
+		{
+			// 3 row table
+			var tableView = GetABCDEFTableView (out var dt);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			tableView.MultiSelect = true;
+			tableView.AddKeyBinding(Key.Space,Command.ToggleChecked);
+
+			// Make a square selection
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown});
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight});
+
+			Assert.Equal(4,tableView.GetAllSelectedCells().Count());
+
+			// Toggle the square selected region on
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+
+			// Go Right
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight });
+
+			//Toggled on square + the active cell (x=2,y=1)
+			Assert.Equal(5,tableView.GetAllSelectedCells().Count());
+			Assert.Equal(2,tableView.SelectedColumn);
+			Assert.Equal(1,tableView.SelectedRow);
+
+			// Untoggle the rectangular region by hitting toggle in
+			// any cell in that rect
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp });
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft });
+
+			Assert.Equal(4,tableView.GetAllSelectedCells().Count());
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space });
+			Assert.Equal(1,tableView.GetAllSelectedCells().Count());
+		}
+
+
+
+		[Fact, AutoInitShutdown]
+		public void TestToggleCells_MultiSelectOn_Two_SquareSelects_BothToggled ()
+		{
+			// 6 row table
+			var tableView = GetABCDEFTableView (out var dt);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			tableView.MultiSelect = true;
+			tableView.AddKeyBinding(Key.Space,Command.ToggleChecked);
+
+			// Make first square selection (0,0 to 1,1)
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown});
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight});
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+			Assert.Equal(4,tableView.GetAllSelectedCells().Count());
+
+			// Make second square selection leaving 1 unselected line between them
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft });
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown });
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown });
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown});
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight});
+			
+			// 2 square selections
+			Assert.Equal(8,tableView.GetAllSelectedCells().Count());
+		}
+
 		
 		[Theory, AutoInitShutdown]
 		[InlineData(new object[] { true,true })]

+ 61 - 40
UnitTests/Views/TileViewTests.cs

@@ -1,6 +1,6 @@
 using System;
+using System.ComponentModel;
 using System.Linq;
-using Terminal.Gui;
 using Terminal.Gui.Graphs;
 using Xunit;
 using Xunit.Abstractions;
@@ -60,7 +60,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_Focused ()
 		{
 			var tileView = Get11By3TileView (out var line);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 
 			tileView.Redraw (tileView.Bounds);
 
@@ -100,7 +100,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_Focused_WithBorder ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 
 			tileView.Redraw (tileView.Bounds);
 
@@ -141,9 +141,10 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_Focused_50PercentSplit ()
 		{
 			var tileView = Get11By3TileView (out var line);
-			SetInputFocusLine (tileView);
 			tileView.SetSplitterPos (0, Pos.Percent (50));
 			Assert.IsType<Pos.PosFactor> (tileView.SplitterDistances.ElementAt (0));
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
+
 			tileView.Redraw (tileView.Bounds);
 
 			string looksLike =
@@ -209,7 +210,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_View1MinSize_Absolute ()
 		{
 			var tileView = Get11By3TileView (out var line);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 			tileView.Tiles.ElementAt (0).MinSize = 6;
 
 			// distance is too small (below 6)
@@ -254,7 +255,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_View1MinSize_Absolute_WithBorder ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 			tileView.Tiles.ElementAt (0).MinSize = 5;
 
 			// distance is too small (below 5)
@@ -298,7 +299,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_View2MinSize_Absolute ()
 		{
 			var tileView = Get11By3TileView (out var line);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 			tileView.Tiles.ElementAt (1).MinSize = 6;
 
 			// distance leaves too little space for view2 (less than 6 would remain)
@@ -342,7 +343,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_View2MinSize_Absolute_WithBorder ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 			tileView.Tiles.ElementAt (1).MinSize = 5;
 
 			// distance leaves too little space for view2 (less than 5 would remain)
@@ -386,8 +387,6 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_InsertPanelAtStart ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
-
 			tileView.InsertTile (0);
 
 			tileView.Redraw (tileView.Bounds);
@@ -405,8 +404,6 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_InsertPanelMiddle ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
-
 			tileView.InsertTile (1);
 
 			tileView.Redraw (tileView.Bounds);
@@ -424,8 +421,6 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_InsertPanelAtEnd ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
-
 			tileView.InsertTile (2);
 
 			tileView.Redraw (tileView.Bounds);
@@ -445,7 +440,9 @@ namespace Terminal.Gui.ViewTests {
 			var tileView = Get11By3TileView (out var line);
 
 			tileView.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal;
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
+
+			Assert.True (line.HasFocus);
 
 			tileView.Redraw (tileView.Bounds);
 
@@ -485,9 +482,9 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Horizontal_View1MinSize_Absolute ()
 		{
 			var tileView = Get11By3TileView (out var line);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 
 			tileView.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal;
-			SetInputFocusLine (tileView);
 			tileView.Tiles.ElementAt (0).MinSize = 1;
 
 			// 0 should not be allowed because it brings us below minimum size of View1
@@ -2045,6 +2042,45 @@ namespace Terminal.Gui.ViewTests {
 
 		}
 
+		[Fact, AutoInitShutdown]
+		public void Test_SplitTop_WholeBottom()
+		{
+			var tileView = new TileView (2) {
+				Width = 20,
+				Height = 10,
+				Orientation = Orientation.Horizontal,
+			};
+			tileView.Border.BorderStyle = BorderStyle.Single;
+
+			Assert.True (tileView.TrySplitTile (0,2,out TileView top));
+
+			top.Tiles.ElementAt (0).ContentView.Add (new Label ("bleh"));
+			top.Tiles.ElementAt (1).ContentView.Add (new Label ("blah"));
+
+			tileView.Tiles.ElementAt (1).ContentView.Add (new Label ("Hello"));
+			tileView.ColorScheme = new ColorScheme ();
+			top.ColorScheme = new ColorScheme ();
+			tileView.LayoutSubviews ();
+
+			tileView.Redraw (tileView.Bounds);
+
+			string looksLike =
+@"
+┌─────────┬────────┐
+│bleh     │blah    │
+│         │        │
+│         │        │
+│         │        │
+├─────────┴────────┤
+│Hello             │
+│                  │
+│                  │
+└──────────────────┘";
+
+			TestHelpers.AssertDriverContentsAre (looksLike, output);
+
+		}
+
 		[Fact, AutoInitShutdown]
 		public void TestNestedContainer3RightAnd1Down_TitleDoesNotOverspill()
 		{
@@ -2094,12 +2130,10 @@ namespace Terminal.Gui.ViewTests {
 
 			TestHelpers.AssertDriverContentsAre (looksLike, output);
 		}
-
-
-		[Fact,AutoInitShutdown]
+		[Fact, AutoInitShutdown]
 		public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRebuildForTileCount ()
 		{
-			var tv = GetTileView (20,10);
+			var tv = GetTileView (20, 10);
 
 			var myReusableView = new DisposeCounter ();
 
@@ -2113,10 +2147,10 @@ namespace Terminal.Gui.ViewTests {
 			// but I still want my view in the first tile
 			tv.Tiles.ElementAt (0).ContentView.Add (myReusableView);
 			Assert.Multiple (
-				()=>Assert.Equal (0, myReusableView.DisposalCount)
-				,()=> {
+				() => Assert.Equal (0, myReusableView.DisposalCount)
+				, () => {
 					tv.Dispose ();
-					Assert.Equal (1, myReusableView.DisposalCount); 
+					Assert.Equal (1, myReusableView.DisposalCount);
 				});
 		}
 		[Fact, AutoInitShutdown]
@@ -2140,15 +2174,13 @@ namespace Terminal.Gui.ViewTests {
 				() => Assert.Equal (0, myReusableView.DisposalCount)
 				, () => {
 					tv.Dispose ();
-
-					// TODO seems to be double disposed ?!
-					Assert.True (myReusableView.DisposalCount >= 1);
+					Assert.True (myReusableView.DisposalCount>=1);
 				});
 		}
 		[Theory, AutoInitShutdown]
-		[InlineData(0)]
+		[InlineData (0)]
 		[InlineData (1)]
-		public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile(int idx)
+		public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile (int idx)
 		{
 			var tv = GetTileView (20, 10);
 
@@ -2166,14 +2198,10 @@ namespace Terminal.Gui.ViewTests {
 				() => Assert.Equal (0, myReusableView.DisposalCount)
 				, () => {
 					tv.Dispose ();
-
-					// TODO seems to be double disposed ?!
 					Assert.True (myReusableView.DisposalCount >= 1);
 				});
 		}
-
-		private class DisposeCounter : View
-		{
+		private class DisposeCounter : View {
 			public int DisposalCount;
 			protected override void Dispose (bool disposing)
 			{
@@ -2255,13 +2283,6 @@ namespace Terminal.Gui.ViewTests {
 			return tileView.Subviews.OfType<LineView> ().Single ();
 		}
 
-		private void SetInputFocusLine (TileView tileView)
-		{
-			var line = GetLine (tileView);
-			line.SetFocus ();
-			Assert.True (line.HasFocus);
-		}
-
 
 		private TileView Get5x1TilesView (bool border = true)
 		{

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio