Browse Source

Fixes #2493. Move all layout code out of View (and Toplevel) into a layout helper class (#2544)

* Comment/warning clean up

* Moved Text and Drawing out

* Moved Layout out

* Removed extra lines

* Removed Mouse out

* Reorgainzed View

* API docs

* removed border.cs

* TopLevel.Resized -> TerminalResized

* Mdi -> Overlapped

* Removed confusing and un-needed WillPresent

* privates -> _

* Tweaked RunLoop API
Tig 2 years ago
parent
commit
b4552ee14b
100 changed files with 4342 additions and 4230 deletions
  1. 1 1
      .editorconfig
  2. 561 677
      Terminal.Gui/Application.cs
  3. 0 1
      Terminal.Gui/Configuration/DictionaryJsonConverter.cs
  4. 0 1
      Terminal.Gui/Configuration/Scope.cs
  5. 0 1
      Terminal.Gui/Configuration/SettingsScope.cs
  6. 2 2
      Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
  7. 1 1
      Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs
  8. 0 1
      Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
  9. 0 1
      Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs
  10. 1 1
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs
  11. 1 4
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  12. 1 2
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  13. 14 16
      Terminal.Gui/Drawing/LineCanvas.cs
  14. 0 1
      Terminal.Gui/Drawing/Ruler.cs
  15. 0 3
      Terminal.Gui/FileServices/AllowedType.cs
  16. 0 1
      Terminal.Gui/FileServices/FileDialogHistory.cs
  17. 0 1
      Terminal.Gui/FileServices/FileDialogState.cs
  18. 0 5
      Terminal.Gui/FileServices/FileDialogStyle.cs
  19. 0 1
      Terminal.Gui/FileServices/FileDialogTreeBuilder.cs
  20. 0 2
      Terminal.Gui/FileServices/FileSystemInfoStats.cs
  21. 0 2
      Terminal.Gui/FileServices/IFileOperations.cs
  22. 2 3
      Terminal.Gui/Input/Command.cs
  23. 35 33
      Terminal.Gui/MainLoop.cs
  24. 1 2
      Terminal.Gui/README.md
  25. 0 3
      Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
  26. 0 4
      Terminal.Gui/Text/Autocomplete/AutocompleteBase.cs
  27. 0 3
      Terminal.Gui/Text/Autocomplete/IAutocomplete.cs
  28. 0 1
      Terminal.Gui/Text/Autocomplete/ISuggestionGenerator.cs
  29. 0 4
      Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs
  30. 0 1
      Terminal.Gui/Text/Autocomplete/SingleWordSuggestionGenerator.cs
  31. 0 1
      Terminal.Gui/Text/Autocomplete/Suggestion.cs
  32. 0 2
      Terminal.Gui/Text/TextFormatter.cs
  33. 0 1
      Terminal.Gui/TimeoutEventArgs.cs
  34. 0 2
      Terminal.Gui/Types/PointF.cs
  35. 0 1
      Terminal.Gui/Types/Rect.cs
  36. 0 1
      Terminal.Gui/Types/SizeF.cs
  37. 0 10
      Terminal.Gui/View/Border.cs
  38. 0 3
      Terminal.Gui/View/Frame.cs
  39. 1 1
      Terminal.Gui/View/Layout/ResizedEventArgs.cs
  40. 267 3108
      Terminal.Gui/View/View.cs
  41. 492 0
      Terminal.Gui/View/ViewDrawing.cs
  42. 464 0
      Terminal.Gui/View/ViewKeyboard.cs
  43. 1157 0
      Terminal.Gui/View/ViewLayout.cs
  44. 114 0
      Terminal.Gui/View/ViewMouse.cs
  45. 723 0
      Terminal.Gui/View/ViewSubViews.cs
  46. 196 0
      Terminal.Gui/View/ViewText.cs
  47. 0 4
      Terminal.Gui/Views/AutocompleteFilepathContext.cs
  48. 2 2
      Terminal.Gui/Views/ContextMenu.cs
  49. 0 2
      Terminal.Gui/Views/Dialog.cs
  50. 0 16
      Terminal.Gui/Views/FileDialog.cs
  51. 0 1
      Terminal.Gui/Views/FrameView.cs
  52. 0 3
      Terminal.Gui/Views/GraphView/Annotations.cs
  53. 0 5
      Terminal.Gui/Views/GraphView/Axis.cs
  54. 0 5
      Terminal.Gui/Views/GraphView/GraphView.cs
  55. 0 3
      Terminal.Gui/Views/GraphView/Series.cs
  56. 2 0
      Terminal.Gui/Views/Line.cs
  57. 1 1
      Terminal.Gui/Views/Menu.cs
  58. 0 1
      Terminal.Gui/Views/MessageBox.cs
  59. 0 1
      Terminal.Gui/Views/SaveDialog.cs
  60. 0 1
      Terminal.Gui/Views/ScrollView.cs
  61. 0 23
      Terminal.Gui/Views/TabView.cs
  62. 0 2
      Terminal.Gui/Views/TableView/CellActivatedEventArgs.cs
  63. 0 5
      Terminal.Gui/Views/TableView/SelectedCellChangedEventArgs.cs
  64. 0 13
      Terminal.Gui/Views/TableView/TableView.cs
  65. 0 1
      Terminal.Gui/Views/TextField.cs
  66. 0 1
      Terminal.Gui/Views/TextValidateField.cs
  67. 3 6
      Terminal.Gui/Views/TextView.cs
  68. 1 4
      Terminal.Gui/Views/TileView.cs
  69. 45 122
      Terminal.Gui/Views/Toplevel.cs
  70. 5 5
      Terminal.Gui/Views/ToplevelEventArgs.cs
  71. 226 0
      Terminal.Gui/Views/ToplevelOverlapped.cs
  72. 0 2
      Terminal.Gui/Views/TreeView/Branch.cs
  73. 0 1
      Terminal.Gui/Views/TreeView/ObjectActivatedEventArgs.cs
  74. 0 3
      Terminal.Gui/Views/TreeView/TreeView.cs
  75. 0 1
      Terminal.Gui/Views/Window.cs
  76. 1 2
      Terminal.Gui/Views/Wizard/Wizard.cs
  77. 1 0
      Terminal.sln
  78. 0 2
      UICatalog/KeyBindingsDialog.cs
  79. 0 1
      UICatalog/Scenarios/AllViewsTester.cs
  80. 0 3
      UICatalog/Scenarios/Animation.cs
  81. 21 21
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  82. 0 2
      UICatalog/Scenarios/ClassExplorer.cs
  83. 0 1
      UICatalog/Scenarios/ComputedLayout.cs
  84. 0 5
      UICatalog/Scenarios/CsvEditor.cs
  85. 0 2
      UICatalog/Scenarios/Dialogs.cs
  86. 0 7
      UICatalog/Scenarios/DynamicMenuBar.cs
  87. 0 6
      UICatalog/Scenarios/DynamicStatusBar.cs
  88. 0 1
      UICatalog/Scenarios/FileDialogExamples.cs
  89. 0 11
      UICatalog/Scenarios/GraphViewExample.cs
  90. 0 1
      UICatalog/Scenarios/LineCanvasExperiment.cs
  91. 0 2
      UICatalog/Scenarios/LineDrawing.cs
  92. 0 4
      UICatalog/Scenarios/LineViewExample.cs
  93. 0 1
      UICatalog/Scenarios/ListsAndCombos.cs
  94. 0 2
      UICatalog/Scenarios/Snake.cs
  95. 0 3
      UICatalog/Scenarios/SyntaxHighlighting.cs
  96. 0 2
      UICatalog/Scenarios/TabViewExample.cs
  97. 0 2
      UICatalog/Scenarios/TableEditor.cs
  98. 0 2
      UICatalog/Scenarios/Text.cs
  99. 0 3
      UICatalog/Scenarios/TextAlignmentsAndDirection.cs
  100. 0 1
      UICatalog/Scenarios/TextViewAutocompletePopup.cs

+ 1 - 1
.editorconfig

@@ -20,4 +20,4 @@ csharp_style_var_when_type_is_apparent = true
 csharp_prefer_braces = false
 csharp_space_before_open_square_brackets = true
 csharp_space_between_method_call_name_and_opening_parenthesis = true
-csharp_space_between_method_declaration_name_and_open_parenthesis = true
+csharp_space_between_method_declaration_name_and_open_parenthesis = true

File diff suppressed because it is too large
+ 561 - 677
Terminal.Gui/Application.cs


+ 0 - 1
Terminal.Gui/Configuration/DictionaryJsonConverter.cs

@@ -24,7 +24,6 @@ namespace Terminal.Gui {
 			return dictionary;
 		}
 
-
 		public override void Write (Utf8JsonWriter writer, Dictionary<string, T> value, JsonSerializerOptions options)
 		{
 			writer.WriteStartArray ();

+ 0 - 1
Terminal.Gui/Configuration/Scope.cs

@@ -7,7 +7,6 @@ using System.Text.Json;
 using System.Text.Json.Serialization;
 using static Terminal.Gui.ConfigurationManager;
 
-
 #nullable enable
 
 namespace Terminal.Gui {

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

@@ -6,7 +6,6 @@ using System.Reflection;
 using System.Text.Json;
 using System.Text.Json.Serialization;
 
-
 #nullable enable
 
 namespace Terminal.Gui {

+ 2 - 2
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -534,7 +534,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// The application toplevel color scheme, for the default toplevel views.
+		/// The application Toplevel color scheme, for the default Toplevel views.
 		/// </summary>
 		/// <remarks>
 		/// <para>
@@ -544,7 +544,7 @@ namespace Terminal.Gui {
 		public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); }
 
 		/// <summary>
-		/// The base color scheme, for the default toplevel views.
+		/// The base color scheme, for the default Toplevel views.
 		/// </summary>
 		/// <remarks>
 		/// <para>

+ 1 - 1
Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs

@@ -191,7 +191,7 @@ namespace Terminal.Gui {
 			return ic > 0;
 		}
 
-		void IMainLoopDriver.MainIteration ()
+		void IMainLoopDriver.Iteration ()
 		{
 			if (winChanged) {
 				winChanged = false;

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

@@ -73,7 +73,6 @@ namespace Unix.Terminal {
 		static UnmanagedLibrary curses_library;
 		static NativeMethods methods;
 
-
 		[DllImport ("libc")]
 		public extern static int setlocale (int cate, [MarshalAs (UnmanagedType.LPStr)] string locale);
 

+ 0 - 1
Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs

@@ -59,7 +59,6 @@ namespace Unix.Terminal {
 				}
 			}
 
-
 			public int wtimeout (int delay)
 			{
 				return Curses.wtimeout (Handle, delay);

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

@@ -87,7 +87,7 @@ namespace Terminal.Gui {
 			return ic > 0;
 		}
 
-		void IMainLoopDriver.MainIteration ()
+		void IMainLoopDriver.Iteration ()
 		{
 			if (keyResult.HasValue) {
 				KeyPressed?.Invoke (keyResult.Value);

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

@@ -1115,7 +1115,6 @@ namespace Terminal.Gui {
 		{
 		}
 
-
 		public override void SetAttribute (Attribute c)
 		{
 			base.SetAttribute (c);
@@ -1172,7 +1171,6 @@ namespace Terminal.Gui {
 			case ConsoleKey.Insert:
 				return MapKeyModifiers (keyInfo, Key.InsertChar);
 
-
 			case ConsoleKey.Oem1:
 			case ConsoleKey.Oem2:
 			case ConsoleKey.Oem3:
@@ -1441,7 +1439,6 @@ namespace Terminal.Gui {
 			return visibility == CursorVisibility.Default;
 		}
 
-
 		/// <inheritdoc/>
 		public override bool SetCursorVisibility (CursorVisibility visibility)
 		{
@@ -1634,7 +1631,7 @@ namespace Terminal.Gui {
 			return ic > 0;
 		}
 
-		void IMainLoopDriver.MainIteration ()
+		void IMainLoopDriver.Iteration ()
 		{
 			while (inputResult.Count > 0) {
 				ProcessInput?.Invoke (inputResult.Dequeue ().Value);

+ 1 - 2
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -577,7 +577,6 @@ namespace Terminal.Gui {
 		[DllImport ("kernel32.dll")]
 		static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
 
-
 		[DllImport ("kernel32.dll")]
 		static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
 
@@ -1980,7 +1979,7 @@ namespace Terminal.Gui {
 			return ic > 0;
 		}
 
-		void IMainLoopDriver.MainIteration ()
+		void IMainLoopDriver.Iteration ()
 		{
 			while (resultQueue.Count > 0) {
 				var inputRecords = resultQueue.Dequeue ();

+ 14 - 16
Terminal.Gui/Drawing/LineCanvas.cs

@@ -52,7 +52,6 @@ namespace Terminal.Gui {
 			{IntersectionRuneType.RightTee,new RightTeeIntersectionRuneResolver()},
 			{IntersectionRuneType.BottomTee,new BottomTeeIntersectionRuneResolver()},
 
-
 			{IntersectionRuneType.Crosshair,new CrosshairIntersectionRuneResolver()},
 			// TODO: Add other resolvers
 		};
@@ -132,6 +131,8 @@ namespace Terminal.Gui {
 			}
 		}
 
+		// TODO: Unless there's an obvious use case for this API we should delete it in favor of the
+		// simpler version that doensn't take an area.
 		/// <summary>
 		/// Evaluates the lines that have been added to the canvas and returns a map containing
 		/// the glyphs and their locations. The glyphs are the characters that should be rendered
@@ -168,8 +169,7 @@ namespace Terminal.Gui {
 		/// the glyphs and their locations. The glyphs are the characters that should be rendered
 		/// so that all lines connect up with the appropriate intersection symbols. 
 		/// </summary>
-		/// <param name="inArea">A rectangle to constrain the search by.</param>
-		/// <returns>A map of the points within the canvas that intersect with <paramref name="inArea"/>.</returns>
+		/// <returns>A map of all the points within the canvas.</returns>
 		public Dictionary<Point, Cell> GetCellMap ()
 		{
 			var map = new Dictionary<Point, Cell> ();
@@ -243,7 +243,6 @@ namespace Terminal.Gui {
 			return sb.ToString ();
 		}
 
-
 		private abstract class IntersectionRuneResolver {
 			readonly Rune round;
 			readonly Rune doubleH;
@@ -267,7 +266,6 @@ namespace Terminal.Gui {
 				bool doubleHorizontal = intersects.Any (l => l.Line.Orientation == Orientation.Horizontal && l.Line.Style == LineStyle.Double);
 				bool doubleVertical = intersects.Any (l => l.Line.Orientation == Orientation.Vertical && l.Line.Style == LineStyle.Double);
 
-
 				if (doubleHorizontal) {
 					return doubleVertical ? doubleBoth : doubleH;
 				}
@@ -392,19 +390,24 @@ namespace Terminal.Gui {
 
 		}
 
+		/// <summary>
+		/// Represents a single row/column within the <see cref="LineCanvas"/>. Includes the glyph and the foreground/background colors.
+		/// </summary>
 		public class Cell
 		{
-			public Cell ()
-			{
-
-			}
-
+			/// <summary>
+			/// The glyph to draw.
+			/// </summary>
 			public Rune? Rune { get; set; }
+
+			/// <summary>
+			/// The foreground color to draw the glyph with.
+			/// </summary>
 			public Attribute? Attribute { get; set; }
 
 		}
 
-		private Cell? GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects)
+		private Cell GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects)
 		{
 			if (!intersects.Any ()) {
 				return null;
@@ -416,7 +419,6 @@ namespace Terminal.Gui {
 			return cell;
 		}
 
-
 		private IntersectionRuneType GetRuneTypeForIntersects (IntersectionDefinition [] intersects)
 		{
 			var set = new HashSet<IntersectionType> (intersects.Select (i => i.Type));
@@ -445,7 +447,6 @@ namespace Terminal.Gui {
 				return IntersectionRuneType.Crosshair;
 			}
 
-
 			if (Has (set,
 				IntersectionType.StartLeft,
 				IntersectionType.StartRight,
@@ -455,7 +456,6 @@ namespace Terminal.Gui {
 			}
 			#endregion
 
-
 			#region Corner Conditions
 			if (Exactly (set,
 				IntersectionType.StartRight,
@@ -507,7 +507,6 @@ namespace Terminal.Gui {
 				return IntersectionRuneType.BottomTee;
 			}
 
-
 			if (Has (set,
 				IntersectionType.PassOverVertical,
 				IntersectionType.StartRight)) {
@@ -520,7 +519,6 @@ namespace Terminal.Gui {
 				return IntersectionRuneType.LeftTee;
 			}
 
-
 			if (Has (set,
 				IntersectionType.PassOverVertical,
 				IntersectionType.StartLeft)) {

+ 0 - 1
Terminal.Gui/Drawing/Ruler.cs

@@ -33,7 +33,6 @@ namespace Terminal.Gui {
 		string _hTemplate { get; } = "|123456789";
 		string _vTemplate { get; } = "-123456789";
 
-
 		/// <summary>
 		/// Draws the <see cref="Ruler"/>. 
 		/// </summary>

+ 0 - 3
Terminal.Gui/FileServices/AllowedType.cs

@@ -22,7 +22,6 @@ namespace Terminal.Gui {
 		bool IsAllowed (string path);
 	}
 
-
 	/// <summary>
 	/// <see cref="IAllowedType"/> that allows selection of any types (*.*).
 	/// </summary>
@@ -74,7 +73,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public string [] Extensions { get; set; }
 
-
 		/// <summary>
 		/// Returns <see cref="Description"/> plus all <see cref="Extensions"/> separated by semicolons.
 		/// </summary>
@@ -100,7 +98,6 @@ namespace Terminal.Gui {
 				return false;
 			}
 
-
 			return this.Extensions.Any (e => e.Equals (extension));
 		}
 	}

+ 0 - 1
Terminal.Gui/FileServices/FileDialogHistory.cs

@@ -78,7 +78,6 @@ namespace Terminal.Gui {
 			return this.dlg.State?.Directory.Parent != null;
 		}
 
-
 		internal void Push (FileDialogState state, bool clearForward)
 		{
 			if (state == null) {

+ 0 - 1
Terminal.Gui/FileServices/FileDialogState.cs

@@ -55,7 +55,6 @@ namespace Terminal.Gui {
 					children = children.Where (MatchesApiFilter).ToList ();
 				}
 
-
 				// allow navigating up as '..'
 				if (dir.Parent != null) {
 					children.Add (new FileSystemInfoStats (dir.Parent) { IsParent = true });

+ 0 - 5
Terminal.Gui/FileServices/FileDialogStyle.cs

@@ -23,7 +23,6 @@ namespace Terminal.Gui {
 		[SerializableConfigurationProperty(Scope = typeof (SettingsScope))]
 		public static bool DefaultUseColors { get; set; }
 
-
 		/// <summary>
 		/// Gets or sets the default value to use for <see cref="UseUnicodeCharacters"/>.
 		/// This can be populated from .tui config files via <see cref="ConfigurationManager"/>
@@ -158,14 +157,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool UseUnicodeCharacters { get; set; } = DefaultUseUnicodeCharacters;
 
-
 		/// <summary>
 		/// User defined delegate for picking which character(s)/unicode
 		/// symbol(s) to use as an 'icon' for files/folders. 
 		/// </summary>
 		public Func<IFileSystemInfo, string> IconGetter { get; set; }
 
-
 		/// <summary>
 		/// Gets or sets the format to use for date/times in the Modified column.
 		/// Defaults to <see cref="DateTimeFormatInfo.SortableDateTimePattern"/> 
@@ -173,7 +170,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public string DateFormat { get; set; }
 
-
 		/// <summary>
 		/// Creates a new instance of the <see cref="FileDialogStyle"/> class.
 		/// </summary>
@@ -233,7 +229,6 @@ namespace Terminal.Gui {
 				// Cannot get the system disks thats fine
 			}
 
-
 			try {
 				foreach (var special in Enum.GetValues (typeof (Environment.SpecialFolder)).Cast<SpecialFolder> ()) {
 					try {

+ 0 - 1
Terminal.Gui/FileServices/FileDialogTreeBuilder.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 
-
 namespace Terminal.Gui {
 
 	class FileDialogTreeBuilder : ITreeBuilder<object> {

+ 0 - 2
Terminal.Gui/FileServices/FileSystemInfoStats.cs

@@ -12,7 +12,6 @@ namespace Terminal.Gui {
 	/// </summary>
 	internal class FileSystemInfoStats {
 
-
 		/* ---- Colors used by the ls command line tool ----
 		 *
 		* Blue: Directory
@@ -127,7 +126,6 @@ namespace Terminal.Gui {
 			int mag = (int)Math.Log (value, ByteConversion);
 			double adjustedSize = value / Math.Pow (1000, mag);
 
-
 			return string.Format ("{0:n2} {1}", adjustedSize, SizeSuffixes [mag]);
 		}
 	}

+ 0 - 2
Terminal.Gui/FileServices/IFileOperations.cs

@@ -19,7 +19,6 @@ namespace Terminal.Gui {
 		/// error handling (e.g. showing a <see cref="MessageBox"/></remarks>
 		bool Delete (IEnumerable<IFileSystemInfo> toDelete);
 
-
 		/// <summary>
 		/// Specifies how to handle file/directory rename attempts
 		/// in <see cref="FileDialog"/>.
@@ -31,7 +30,6 @@ namespace Terminal.Gui {
 		/// error handling (e.g. showing a <see cref="MessageBox"/></remarks>
 		IFileSystemInfo Rename (IFileSystem fileSystem, IFileSystemInfo toRename);
 
-
 		/// <summary>
 		/// Specifies how to handle 'new directory' operation
 		/// in <see cref="FileDialog"/>.

+ 2 - 3
Terminal.Gui/Input/Command.cs

@@ -127,7 +127,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		ToggleOverwrite,
 
-
 		/// <summary>
 		/// Enables overwrite mode such that newly typed text overwrites the text that is
 		/// already there (typically associated with the Insert key).
@@ -356,12 +355,12 @@ namespace Terminal.Gui {
 		PreviousView,
 
 		/// <summary>
-		/// Moves focus to the next view or toplevel (case of MDI).
+		/// Moves focus to the next view or Toplevel (case of Overlapped).
 		/// </summary>
 		NextViewOrTop,
 
 		/// <summary>
-		/// Moves focus to the next previous or toplevel (case of MDI).
+		/// Moves focus to the next previous or Toplevel (case of Overlapped).
 		/// </summary>
 		PreviousViewOrTop,
 

+ 35 - 33
Terminal.Gui/MainLoop.cs

@@ -10,17 +10,17 @@ using System.Collections.ObjectModel;
 
 namespace Terminal.Gui {
 	/// <summary>
-	/// Public interface to create your own platform specific main loop driver.
+	/// Public interface to create a platform specific <see cref="MainLoop"/> driver.
 	/// </summary>
 	public interface IMainLoopDriver {
 		/// <summary>
-		/// Initializes the main loop driver, gets the calling main loop for the initialization.
+		/// Initializes the <see cref="MainLoop"/>, gets the calling main loop for the initialization.
 		/// </summary>
 		/// <param name="mainLoop">Main loop.</param>
 		void Setup (MainLoop mainLoop);
 
 		/// <summary>
-		/// Wakes up the mainloop that might be waiting on input, must be thread safe.
+		/// Wakes up the <see cref="MainLoop"/> that might be waiting on input, must be thread safe.
 		/// </summary>
 		void Wakeup ();
 
@@ -34,7 +34,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The iteration function.
 		/// </summary>
-		void MainIteration ();
+		void Iteration ();
 	}
 
 	/// <summary>
@@ -61,12 +61,12 @@ namespace Terminal.Gui {
 		}
 
 		internal SortedList<long, Timeout> timeouts = new SortedList<long, Timeout> ();
-		object timeoutsLockToken = new object ();
+		object _timeoutsLockToken = new object ();
 
 		/// <summary>
 		/// The idle handlers and lock that must be held while manipulating them
 		/// </summary>
-		object idleHandlersLock = new object ();
+		object _idleHandlersLock = new object ();
 		internal List<Func<bool>> idleHandlers = new List<Func<bool>> ();
 
 		/// <summary>
@@ -81,14 +81,14 @@ namespace Terminal.Gui {
 		/// </summary>
 		public ReadOnlyCollection<Func<bool>> IdleHandlers {
 			get {
-				lock (idleHandlersLock) {
+				lock (_idleHandlersLock) {
 					return new List<Func<bool>> (idleHandlers).AsReadOnly ();
 				}
 			}
 		}
 
 		/// <summary>
-		/// The current IMainLoopDriver in use.
+		/// The current <see cref="IMainLoopDriver"/> in use.
 		/// </summary>
 		/// <value>The driver.</value>
 		public IMainLoopDriver Driver { get; }
@@ -123,7 +123,8 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Adds specified idle handler function to mainloop processing. The handler function will be called once per iteration of the main loop after other events have been handled.
+		///   Adds specified idle handler function to <see cref="MainLoop"/> processing. 
+		///   The handler function will be called once per iteration of the main loop after other events have been handled.
 		/// </summary>
 		/// <remarks>
 		/// <para>
@@ -136,7 +137,7 @@ namespace Terminal.Gui {
 		/// <param name="idleHandler">Token that can be used to remove the idle handler with <see cref="RemoveIdle(Func{bool})"/> .</param>
 		public Func<bool> AddIdle (Func<bool> idleHandler)
 		{
-			lock (idleHandlersLock) {
+			lock (_idleHandlersLock) {
 				idleHandlers.Add (idleHandler);
 			}
 
@@ -152,13 +153,13 @@ namespace Terminal.Gui {
 		///  This method also returns <c>false</c> if the idle handler is not found.
 		public bool RemoveIdle (Func<bool> token)
 		{
-			lock (idleHandlersLock)
+			lock (_idleHandlersLock)
 				return idleHandlers.Remove (token);
 		}
 
 		void AddTimeout (TimeSpan time, Timeout timeout)
 		{
-			lock (timeoutsLockToken) {
+			lock (_timeoutsLockToken) {
 				var k = (DateTime.UtcNow + time).Ticks;
 				timeouts.Add (NudgeToUniqueKey (k), timeout);
 				TimeoutAdded?.Invoke (this, new TimeoutEventArgs(timeout, k));
@@ -166,7 +167,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Adds a timeout to the mainloop.
+		///   Adds a timeout to the <see cref="MainLoop"/>.
 		/// </summary>
 		/// <remarks>
 		///   When time specified passes, the callback will be invoked.
@@ -198,7 +199,7 @@ namespace Terminal.Gui {
 		/// This method also returns <c>false</c> if the timeout is not found.
 		public bool RemoveTimeout (object token)
 		{
-			lock (timeoutsLockToken) {
+			lock (_timeoutsLockToken) {
 				var idx = timeouts.IndexOfValue (token as Timeout);
 				if (idx == -1)
 					return false;
@@ -216,7 +217,7 @@ namespace Terminal.Gui {
 			// after we have taken the copy but before
 			// we have allocated a new list (which would
 			// result in lost timeouts or errors during enumeration)
-			lock (timeoutsLockToken) {
+			lock (_timeoutsLockToken) {
 				copy = timeouts;
 				timeouts = new SortedList<long, Timeout> ();
 			}
@@ -228,7 +229,7 @@ namespace Terminal.Gui {
 					if (timeout.Callback (this))
 						AddTimeout (timeout.Span, timeout);
 				} else {
-					lock (timeoutsLockToken) {
+					lock (_timeoutsLockToken) {
 						timeouts.Add (NudgeToUniqueKey (k), timeout);
 					}
 				}
@@ -243,7 +244,7 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		private long NudgeToUniqueKey (long k)
 		{
-			lock (timeoutsLockToken) {
+			lock (_timeoutsLockToken) {
 				while (timeouts.ContainsKey (k)) {
 					k++;
 				}
@@ -255,26 +256,26 @@ namespace Terminal.Gui {
 		void RunIdle ()
 		{
 			List<Func<bool>> iterate;
-			lock (idleHandlersLock) {
+			lock (_idleHandlersLock) {
 				iterate = idleHandlers;
 				idleHandlers = new List<Func<bool>> ();
 			}
 
 			foreach (var idle in iterate) {
 				if (idle ())
-					lock (idleHandlersLock)
+					lock (_idleHandlersLock)
 						idleHandlers.Add (idle);
 			}
 		}
 
-		bool running;
+		bool _running;
 
 		/// <summary>
 		///   Stops the mainloop.
 		/// </summary>
 		public void Stop ()
 		{
-			running = false;
+			_running = false;
 			Driver.Wakeup ();
 		}
 
@@ -295,20 +296,21 @@ namespace Terminal.Gui {
 		///   Runs one iteration of timers and file watches
 		/// </summary>
 		/// <remarks>
-		///   You use this to process all pending events (timers, idle handlers and file watches).
+		///   Use this to process all pending events (timers, idle handlers and file watches).
 		///
-		///   You can use it like this:
-		///     while (main.EvensPending ()) MainIteration ();
+		///   <code>
+		///     while (main.EvenstPending ()) RunIteration ();
+		///   </code>
 		/// </remarks>
-		public void MainIteration ()
+		public void RunIteration ()
 		{
 			if (timeouts.Count > 0)
 				RunTimers ();
 
-			Driver.MainIteration ();
+			Driver.Iteration ();
 
 			bool runIdle = false;
-			lock (idleHandlersLock) {
+			lock (_idleHandlersLock) {
 				runIdle = idleHandlers.Count > 0;
 			}
 			if (runIdle) {
@@ -317,17 +319,17 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Runs the mainloop.
+		///   Runs the <see cref="MainLoop"/>.
 		/// </summary>
 		public void Run ()
 		{
-			bool prev = running;
-			running = true;
-			while (running) {
+			bool prev = _running;
+			_running = true;
+			while (_running) {
 				EventsPending (true);
-				MainIteration ();
+				RunIteration ();
 			}
-			running = prev;
+			_running = prev;
 		}
 	}
 }

+ 1 - 2
Terminal.Gui/README.md

@@ -34,13 +34,12 @@ All files required to build the **Terminal.Gui** library (and NuGet package).
 
 - `Views\` - Sub-classes of `View` 
 	- `Toplevel` - Derived from `View`, the base class for modal visual elements such as top-level windows and dialogs. Supports the concept of `MenuBar` and `StatusBar`.
-	- `Window` - Derived from `TopLevel`; implements toplevel views with a visible frame and Title.
+	- `Window` - Derived from `TopLevel`; implements Toplevel views with a visible frame and Title.
 	- `Dialog` -
 	- etc...
 
 - `Types/` - A folder (not namespace) containing implementations of `Point`, `Rect`, and `Size` which are ancient versions of the modern `System.Drawing.Point`, `System.Drawing.Size`, and `System.Drawning.Rectangle`.
 
-
 ## Version numbers
 
 Version info for Terminal.Gui is managed by [gitversion](https://gitversion.net).

+ 0 - 3
Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs

@@ -30,7 +30,6 @@ namespace Terminal.Gui {
 			this.textField = textField;
 			SelectionKey = Key.Tab;
 
-
 			ColorScheme = new ColorScheme{
 				Normal = new Attribute(Color.DarkGray,0),
 				Focus = new Attribute(Color.DarkGray,0),
@@ -147,7 +146,6 @@ namespace Terminal.Gui {
 			return false;
 		}
 
-
 		internal void SetTextTo (FileSystemInfo fileSystemInfo)
 		{
 			var newText = fileSystemInfo.FullName;
@@ -158,7 +156,6 @@ namespace Terminal.Gui {
 			textField.MoveEnd ();
 		}
 
-
 		/// <summary>
 		/// Returns true if there is a suggestion that can be made and the control
 		/// is in a state where user would expect to see auto-complete (i.e. focused and

+ 0 - 4
Terminal.Gui/Text/Autocomplete/AutocompleteBase.cs

@@ -26,7 +26,6 @@ namespace Terminal.Gui {
 
 		/// <inheritdoc/>
 
-
 		/// <inheritdoc/>
 		public virtual bool Visible { get; set; }
 
@@ -34,11 +33,9 @@ namespace Terminal.Gui {
 		public virtual ReadOnlyCollection<Suggestion> Suggestions { get; set; } = new ReadOnlyCollection<Suggestion> (new Suggestion [0]);
 
 
-
 		/// <inheritdoc/>
 		public virtual int SelectedIdx { get; set; }
 
-
 		/// <inheritdoc/>
 		public abstract ColorScheme ColorScheme { get; set; }
 
@@ -65,7 +62,6 @@ namespace Terminal.Gui {
 			Suggestions = Enumerable.Empty<Suggestion> ().ToList ().AsReadOnly ();
 		}
 
-
 		/// <inheritdoc/>
 		public virtual void GenerateSuggestions (AutocompleteContext context)
 		{

+ 0 - 3
Terminal.Gui/Text/Autocomplete/IAutocomplete.cs

@@ -75,7 +75,6 @@ namespace Terminal.Gui {
 		/// <param name="renderAt"></param>
 		void RenderOverlay (Point renderAt);
 
-
 		/// <summary>
 		/// Handle key events before <see cref="HostControl"/> e.g. to make key events like
 		/// up/down apply to the autocomplete control instead of changing the cursor position in
@@ -100,14 +99,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		void ClearSuggestions ();
 
-
 		/// <summary>
 		/// Gets or Sets the class responsible for generating <see cref="Suggestions"/>
 		/// based on a given <see cref="AutocompleteContext"/> of the <see cref="HostControl"/>.
 		/// </summary>
 		ISuggestionGenerator SuggestionGenerator { get; set; }
 
-
 		/// <summary>
 		/// Populates <see cref="Suggestions"/> with all <see cref="Suggestion"/> 
 		/// proposed by <see cref="SuggestionGenerator"/> at the given <paramref name="context"/>

+ 0 - 1
Terminal.Gui/Text/Autocomplete/ISuggestionGenerator.cs

@@ -12,7 +12,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		IEnumerable<Suggestion> GenerateSuggestions (AutocompleteContext context);
 
-
 		/// <summary>
 		/// Returns <see langword="true"/> if <paramref name="rune"/> is a character that
 		/// would continue autocomplete suggesting. Returns <see langword="false"/> if it

+ 0 - 4
Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs

@@ -120,7 +120,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-
 		/// <summary>
 		/// When more suggestions are available than can be rendered the user
 		/// can scroll down the dropdown list. This indicates how far down they
@@ -256,7 +255,6 @@ namespace Terminal.Gui {
 		{
 			base.EnsureSelectedIdxIsValid ();
 
-
 			// if user moved selection up off top of current scroll window
 			if (SelectedIdx < ScrollOffset) {
 				ScrollOffset = SelectedIdx;
@@ -410,7 +408,6 @@ namespace Terminal.Gui {
 		}
 
 
-
 		/// <summary>
 		/// Completes the autocomplete selection process. Called when user hits the <see cref="IAutocomplete.SelectionKey"/>.
 		/// </summary>
@@ -447,7 +444,6 @@ namespace Terminal.Gui {
 			return true;
 		}
 
-
 		/// <summary>
 		/// Deletes the text backwards before insert the selected text in the <see cref="HostControl"/>.
 		/// </summary>

+ 0 - 1
Terminal.Gui/Text/Autocomplete/SingleWordSuggestionGenerator.cs

@@ -53,7 +53,6 @@ namespace Terminal.Gui {
 			return Char.IsLetterOrDigit ((char)rune);
 		}
 
-
 		/// <summary>
 		/// <para>
 		/// Given a <paramref name="line"/> of characters, returns the word which ends at <paramref name="idx"/> 

+ 0 - 1
Terminal.Gui/Text/Autocomplete/Suggestion.cs

@@ -21,7 +21,6 @@
 		/// </summary>
 		public string Replacement { get; }
 
-
 		/// <summary>
 		/// Creates a new instance of the <see cref="Suggestion"/> class.
 		/// </summary>

+ 0 - 2
Terminal.Gui/Text/TextFormatter.cs

@@ -877,7 +877,6 @@ namespace Terminal.Gui {
 				i++;
 			}
 
-
 			// Legacy support - use first upper case char if the specifier was not found
 			if (hot_pos == -1 && firstUpperCase) {
 				i = 0;
@@ -1106,7 +1105,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool WordWrap { get; set; } = false;
 
-
 		/// <summary>
 		/// Gets or sets the size of the area the text will be constrained to when formatted.
 		/// </summary>

+ 0 - 1
Terminal.Gui/TimeoutEventArgs.cs

@@ -17,7 +17,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public long Ticks { get; }
 
-
 		/// <summary>
 		/// Creates a new instance of the <see cref="TimeoutEventArgs"/> class.
 		/// </summary>

+ 0 - 2
Terminal.Gui/Types/PointF.cs

@@ -104,7 +104,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public static PointF Subtract (PointF pt, SizeF sz) => new PointF (pt.X - sz.Width, pt.Y - sz.Height);
 
-
 		/// <summary>
 		/// Compares two <see cref='Terminal.Gui.PointF'/> objects. The result specifies whether the values of the
 		/// <see cref='Terminal.Gui.PointF.X'/> and <see cref='Terminal.Gui.PointF.Y'/> properties of the two
@@ -112,7 +111,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public override bool Equals (object obj) => obj is PointF && Equals ((PointF)obj);
 
-
 		/// <summary>
 		/// Compares two <see cref='Terminal.Gui.PointF'/> objects. The result specifies whether the values of the
 		/// <see cref='Terminal.Gui.PointF.X'/> and <see cref='Terminal.Gui.PointF.Y'/> properties of the two

+ 0 - 1
Terminal.Gui/Types/Rect.cs

@@ -261,7 +261,6 @@ namespace Terminal.Gui
 		}
 
 
-
 		/// <summary>
 		///	Bottom Property
 		/// </summary>

+ 0 - 1
Terminal.Gui/Types/SizeF.cs

@@ -136,7 +136,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public override bool Equals (object obj) => obj is SizeF && Equals ((SizeF)obj);
 
-
 		/// <summary>
 		/// Tests whether two <see cref='Terminal.Gui.SizeF'/> objects are identical.
 		/// </summary>

+ 0 - 10
Terminal.Gui/View/Border.cs

@@ -1,10 +0,0 @@
-using NStack;
-using System;
-using System.Text.Json.Serialization;
-using System.Data;
-using System.Text;
-using System.Collections.Generic;
-
-namespace Terminal.Gui {
-	
-}

+ 0 - 3
Terminal.Gui/View/Frame.cs

@@ -12,7 +12,6 @@ namespace Terminal.Gui {
 	// TODO: v2 - If a Frame has focus, navigation keys (e.g Command.NextView) should cycle through SubViews of the Frame
 	// QUESTION: How does a user navigate out of a Frame to another Frame, or back into the Parent's SubViews?
 
-
 	/// <summary>
 	/// Frames are a special form of <see cref="View"/> that act as adornments; they appear outside of the <see cref="View.Bounds"/>
 	/// enabling borders, menus, etc... 
@@ -184,7 +183,6 @@ namespace Terminal.Gui {
 				sideLineLength++;
 			}
 
-
 			if (Id == "Border" && Thickness.Top > 0 && maxTitleWidth > 0 && !ustring.IsNullOrEmpty (Parent?.Title)) {
 				var prevAttr = Driver.GetAttribute ();
 				Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
@@ -278,7 +276,6 @@ namespace Terminal.Gui {
 				}
 			}
 
-
 			Driver.Clip = prevClip;
 		}
 

+ 1 - 1
Terminal.Gui/View/Layout/ResizedEventArgs.cs

@@ -17,7 +17,7 @@ using System;
 namespace Terminal.Gui {
 
 	/// <summary>
-	/// Event arguments for the <see cref="Application.Resized"/> event.
+	/// Event arguments for the <see cref="Application.TerminalResized"/> event.
 	/// </summary>
 	public class ResizedEventArgs : EventArgs {
 		/// <summary>

File diff suppressed because it is too large
+ 267 - 3108
Terminal.Gui/View/View.cs


+ 492 - 0
Terminal.Gui/View/ViewDrawing.cs

@@ -0,0 +1,492 @@
+using System;
+using System.Linq;
+using NStack;
+
+namespace Terminal.Gui {
+	public partial class View {
+
+		ColorScheme _colorScheme;
+
+		/// <summary>
+		/// The color scheme for this view, if it is not defined, it returns the <see cref="SuperView"/>'s
+		/// color scheme.
+		/// </summary>
+		public virtual ColorScheme ColorScheme {
+			get {
+				if (_colorScheme == null) {
+					return SuperView?.ColorScheme;
+				}
+				return _colorScheme;
+			}
+			set {
+				if (_colorScheme != value) {
+					_colorScheme = value;
+					SetNeedsDisplay ();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.
+		/// </summary>
+		/// <returns><see cref="Terminal.Gui.ColorScheme.Normal"/> if <see cref="Enabled"/> is <see langword="true"/>
+		/// or <see cref="Terminal.Gui.ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/>.
+		/// If it's overridden can return other values.</returns>
+		public virtual Attribute GetNormalColor ()
+		{
+			return Enabled ? ColorScheme.Normal : ColorScheme.Disabled;
+		}
+
+		/// <summary>
+		/// Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.
+		/// </summary>
+		/// <returns><see cref="Terminal.Gui.ColorScheme.Focus"/> if <see cref="Enabled"/> is <see langword="true"/>
+		/// or <see cref="Terminal.Gui.ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/>.
+		/// If it's overridden can return other values.</returns>
+		public virtual Attribute GetFocusColor ()
+		{
+			return Enabled ? ColorScheme.Focus : ColorScheme.Disabled;
+		}
+
+		/// <summary>
+		/// Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.
+		/// </summary>
+		/// <returns><see cref="Terminal.Gui.ColorScheme.HotNormal"/> if <see cref="Enabled"/> is <see langword="true"/>
+		/// or <see cref="Terminal.Gui.ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/>.
+		/// If it's overridden can return other values.</returns>
+		public virtual Attribute GetHotNormalColor ()
+		{
+			return Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled;
+		}
+
+		/// <summary>
+		/// Displays the specified character in the specified column and row of the View.
+		/// </summary>
+		/// <param name="col">Column (view-relative).</param>
+		/// <param name="row">Row (view-relative).</param>
+		/// <param name="ch">Ch.</param>
+		public void AddRune (int col, int row, Rune ch)
+		{
+			if (row < 0 || col < 0)
+				return;
+			if (row > _frame.Height - 1 || col > _frame.Width - 1)
+				return;
+			Move (col, row);
+			Driver.AddRune (ch);
+		}
+
+		/// <summary>
+		/// Removes the <see cref="_needsDisplay"/> and the <see cref="_childNeedsDisplay"/> setting on this view.
+		/// </summary>
+		protected void ClearNeedsDisplay ()
+		{
+			_needsDisplay = Rect.Empty;
+			_childNeedsDisplay = false;
+		}
+
+		// The view-relative region that needs to be redrawn
+		internal Rect _needsDisplay { get; private set; } = Rect.Empty;
+
+		/// <summary>
+		/// Sets a flag indicating this view needs to be redisplayed because its state has changed.
+		/// </summary>
+		public void SetNeedsDisplay ()
+		{
+			if (!IsInitialized) return;
+			SetNeedsDisplay (Bounds);
+		}
+
+		/// <summary>
+		/// Flags the view-relative region on this View as needing to be redrawn.
+		/// </summary>
+		/// <param name="region">The view-relative region that needs to be redrawn.</param>
+		public void SetNeedsDisplay (Rect region)
+		{
+			if (_needsDisplay.IsEmpty)
+				_needsDisplay = region;
+			else {
+				var x = Math.Min (_needsDisplay.X, region.X);
+				var y = Math.Min (_needsDisplay.Y, region.Y);
+				var w = Math.Max (_needsDisplay.Width, region.Width);
+				var h = Math.Max (_needsDisplay.Height, region.Height);
+				_needsDisplay = new Rect (x, y, w, h);
+			}
+			_superView?.SetSubViewNeedsDisplay ();
+
+			if (_subviews == null)
+				return;
+
+			foreach (var view in _subviews)
+				if (view.Frame.IntersectsWith (region)) {
+					var childRegion = Rect.Intersect (view.Frame, region);
+					childRegion.X -= view.Frame.X;
+					childRegion.Y -= view.Frame.Y;
+					view.SetNeedsDisplay (childRegion);
+				}
+		}
+
+		internal bool _childNeedsDisplay { get; private set; }
+
+		/// <summary>
+		/// Indicates that any Subviews (in the <see cref="Subviews"/> list) need to be repainted.
+		/// </summary>
+		public void SetSubViewNeedsDisplay ()
+		{
+			if (_childNeedsDisplay) {
+				return;
+			}
+			_childNeedsDisplay = true;
+			if (_superView != null && !_superView._childNeedsDisplay)
+				_superView.SetSubViewNeedsDisplay ();
+		}
+
+		/// <summary>
+		///   Clears the view region with the current color.
+		/// </summary>
+		/// <remarks>
+		///   <para>
+		///     This clears the entire region used by this view.
+		///   </para>
+		/// </remarks>
+		public void Clear ()
+		{
+			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++)
+					Driver.AddRune (' ');
+			}
+		}
+
+		// BUGBUG: Stupid that this takes screen-relative. We should have a tenet that says 
+		// "View APIs only deal with View-relative coords". 
+		/// <summary>
+		///   Clears the specified region with the current color. 
+		/// </summary>
+		/// <remarks>
+		/// </remarks>
+		/// <param name="regionScreen">The screen-relative region to clear.</param>
+		public void Clear (Rect regionScreen)
+		{
+			var h = regionScreen.Height;
+			var w = regionScreen.Width;
+			for (var line = regionScreen.Y; line < regionScreen.Y + h; line++) {
+				Driver.Move (regionScreen.X, line);
+				for (var col = 0; col < w; col++)
+					Driver.AddRune (' ');
+			}
+		}
+
+		// Clips a rectangle in screen coordinates to the dimensions currently available on the screen
+		internal Rect ScreenClip (Rect regionScreen)
+		{
+			var x = regionScreen.X < 0 ? 0 : regionScreen.X;
+			var y = regionScreen.Y < 0 ? 0 : regionScreen.Y;
+			var w = regionScreen.X + regionScreen.Width >= Driver.Cols ? Driver.Cols - regionScreen.X : regionScreen.Width;
+			var h = regionScreen.Y + regionScreen.Height >= Driver.Rows ? Driver.Rows - regionScreen.Y : regionScreen.Height;
+
+			return new Rect (x, y, w, h);
+		}
+
+		/// <summary>
+		/// Sets the <see cref="ConsoleDriver"/>'s clip region to <see cref="Bounds"/>.
+		/// </summary>
+		/// <returns>The current screen-relative clip region, which can be then re-applied by setting <see cref="ConsoleDriver.Clip"/>.</returns>
+		/// <remarks>
+		/// <para>
+		/// <see cref="Bounds"/> is View-relative.
+		/// </para>
+		/// <para>
+		/// If <see cref="ConsoleDriver.Clip"/> and <see cref="Bounds"/> do not intersect, the clip region will be set to <see cref="Rect.Empty"/>.
+		/// </para>
+		/// </remarks>
+		public Rect ClipToBounds ()
+		{
+			var clip = Bounds;
+
+			return SetClip (clip);
+		}
+
+		// BUGBUG: v2 - SetClip should return VIEW-relative so that it can be used to reset it; using Driver.Clip directly should not be necessary. 
+		/// <summary>
+		/// Sets the clip region to the specified view-relative region.
+		/// </summary>
+		/// <returns>The current screen-relative clip region, which can be then re-applied by setting <see cref="ConsoleDriver.Clip"/>.</returns>
+		/// <param name="region">View-relative clip region.</param>
+		/// <remarks>
+		/// If <see cref="ConsoleDriver.Clip"/> and <paramref name="region"/> do not intersect, the clip region will be set to <see cref="Rect.Empty"/>.
+		/// </remarks>
+		public Rect SetClip (Rect region)
+		{
+			var previous = Driver.Clip;
+			Driver.Clip = Rect.Intersect (previous, ViewToScreen (region));
+			return previous;
+		}
+
+		/// <summary>
+		/// Utility function to draw strings that contain a hotkey.
+		/// </summary>
+		/// <param name="text">String to display, the hotkey specifier before a letter flags the next letter as the hotkey.</param>
+		/// <param name="hotColor">Hot color.</param>
+		/// <param name="normalColor">Normal color.</param>
+		/// <remarks>
+		/// <para>The hotkey is any character following the hotkey specifier, which is the underscore ('_') character by default.</para>
+		/// <para>The hotkey specifier can be changed via <see cref="HotKeySpecifier"/></para>
+		/// </remarks>
+		public void DrawHotString (ustring text, Attribute hotColor, Attribute normalColor)
+		{
+			var hotkeySpec = HotKeySpecifier == (Rune)0xffff ? (Rune)'_' : HotKeySpecifier;
+			Application.Driver.SetAttribute (normalColor);
+			foreach (var rune in text) {
+				if (rune == hotkeySpec) {
+					Application.Driver.SetAttribute (hotColor);
+					continue;
+				}
+				Application.Driver.AddRune (rune);
+				Application.Driver.SetAttribute (normalColor);
+			}
+		}
+
+		/// <summary>
+		/// Utility function to draw strings that contains a hotkey using a <see cref="ColorScheme"/> and the "focused" state.
+		/// </summary>
+		/// <param name="text">String to display, the underscore before a letter flags the next letter as the hotkey.</param>
+		/// <param name="focused">If set to <see langword="true"/> this uses the focused colors from the color scheme, otherwise the regular ones.</param>
+		/// <param name="scheme">The color scheme to use.</param>
+		public void DrawHotString (ustring text, bool focused, ColorScheme scheme)
+		{
+			if (focused)
+				DrawHotString (text, scheme.HotFocus, scheme.Focus);
+			else
+				DrawHotString (text, Enabled ? scheme.HotNormal : scheme.Disabled, Enabled ? scheme.Normal : scheme.Disabled);
+		}
+
+		/// <summary>
+		/// This moves the cursor to the specified column and row in the view.
+		/// </summary>
+		/// <returns>The move.</returns>
+		/// <param name="col">The column to move to, in view-relative coordinates.</param>
+		/// <param name="row">the row to move to, in view-relative coordinates.</param>
+		/// <param name="clipped">Whether to clip the result of the ViewToScreen method,
+		///  If  <see langword="true"/>, the <paramref name="col"/> and <paramref name="row"/> values are clamped to the screen (terminal) dimensions (0..TerminalDim-1).</param>
+		public void Move (int col, int row, bool clipped = true)
+		{
+			if (Driver.Rows == 0) {
+				return;
+			}
+
+			ViewToScreen (col, row, out var rCol, out var rRow, clipped);
+			Driver.Move (rCol, rRow);
+		}
+		/// <summary>
+		/// The canvas that any line drawing that is to be shared by subviews of this view should add lines to.
+		/// </summary>
+		/// <remarks><see cref="Border"/> adds border lines to this LineCanvas.</remarks>
+		public virtual LineCanvas LineCanvas { get; set; } = new LineCanvas ();
+
+		/// <summary>
+		/// Gets or sets whether this View will use it's SuperView's <see cref="LineCanvas"/> for
+		/// rendering any border lines. If <see langword="true"/> the rendering of any borders drawn
+		/// by this Frame will be done by it's parent's SuperView. If <see langword="false"/> (the default)
+		/// this View's <see cref="OnDrawFrames()"/> method will be called to render the borders.
+		/// </summary>
+		public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
+
+		// TODO: Make this cancelable
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <returns></returns>
+		public virtual bool OnDrawFrames ()
+		{
+			if (!IsInitialized) {
+				return false;
+			}
+
+			var prevClip = Driver.Clip;
+			Driver.Clip = ViewToScreen (Frame);
+
+			// TODO: Figure out what we should do if we have no superview
+			//if (SuperView != null) {
+			// TODO: Clipping is disabled for now to ensure we see errors
+			Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows);// screenBounds;// SuperView.ClipToBounds ();
+										//}
+
+			// Each of these renders lines to either this View's LineCanvas 
+			// Those lines will be finally rendered in OnRenderLineCanvas
+			Margin?.Redraw (Margin.Frame);
+			Border?.Redraw (Border.Frame);
+			Padding?.Redraw (Padding.Frame);
+
+			Driver.Clip = prevClip;
+
+			return true;
+		}
+
+		/// <summary>
+		/// Redraws this view and its subviews; only redraws the views that have been flagged for a re-display.
+		/// </summary>
+		/// <param name="bounds">The bounds (view-relative region) to redraw.</param>
+		/// <remarks>
+		/// <para>
+		///    Always use <see cref="Bounds"/> (view-relative) when calling <see cref="Redraw(Rect)"/>, NOT <see cref="Frame"/> (superview-relative).
+		/// </para>
+		/// <para>
+		///    Views should set the color that they want to use on entry, as otherwise this will inherit
+		///    the last color that was set globally on the driver.
+		/// </para>
+		/// <para>
+		///    Overrides of <see cref="Redraw"/> must ensure they do not set <c>Driver.Clip</c> to a clip region
+		///    larger than the <ref name="bounds"/> parameter, as this will cause the driver to clip the entire region.
+		/// </para>
+		/// </remarks>
+		public virtual void Redraw (Rect bounds)
+		{
+			if (!CanBeVisible (this)) {
+				return;
+			}
+
+			OnDrawFrames ();
+
+			var prevClip = ClipToBounds ();
+
+			if (ColorScheme != null) {
+				//Driver.SetAttribute (HasFocus ? GetFocusColor () : GetNormalColor ());
+				Driver.SetAttribute (GetNormalColor ());
+			}
+
+			if (SuperView != null) {
+				Clear (ViewToScreen (bounds));
+			}
+
+			// Invoke DrawContentEvent
+			OnDrawContent (bounds);
+
+			// Draw subviews
+			// TODO: Implement OnDrawSubviews (cancelable);
+			if (_subviews != null) {
+				foreach (var view in _subviews) {
+					if (view.Visible) { //!view._needsDisplay.IsEmpty || view._childNeedsDisplay || view.LayoutNeeded) {
+						if (true) { //view.Frame.IntersectsWith (bounds)) { // && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
+							if (view.LayoutNeeded) {
+								view.LayoutSubviews ();
+							}
+
+							// Draw the subview
+							// Use the view's bounds (view-relative; Location will always be (0,0)
+							//if (view.Visible && view.Frame.Width > 0 && view.Frame.Height > 0) {
+							view.Redraw (view.Bounds);
+							//}
+						}
+						view.ClearNeedsDisplay ();
+					}
+				}
+			}
+
+			// Invoke DrawContentCompleteEvent
+			OnDrawContentComplete (bounds);
+			Driver.Clip = prevClip;
+
+			OnRenderLineCanvas ();
+
+			// BUGBUG: v2 - We should be able to use View.SetClip here and not have to resort to knowing Driver details.
+			ClearLayoutNeeded ();
+			ClearNeedsDisplay ();
+		}
+
+		internal void OnRenderLineCanvas ()
+		{
+			//Driver.SetAttribute (new Attribute(Color.White, Color.Black));
+
+			// If we have a SuperView, it'll render our frames.
+			if (!SuperViewRendersLineCanvas && LineCanvas.Bounds != Rect.Empty) {
+				foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map
+					Driver.SetAttribute (p.Value.Attribute?.Value ?? ColorScheme.Normal);
+					Driver.Move (p.Key.X, p.Key.Y);
+					Driver.AddRune (p.Value.Rune.Value);
+				}
+				LineCanvas.Clear ();
+			}
+
+			if (Subviews.Select (s => s.SuperViewRendersLineCanvas).Count () > 0) {
+				foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas)) {
+					// Combine the LineCavas'
+					LineCanvas.Merge (subview.LineCanvas);
+					subview.LineCanvas.Clear ();
+				}
+
+				foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map
+					Driver.SetAttribute (p.Value.Attribute?.Value ?? ColorScheme.Normal);
+					Driver.Move (p.Key.X, p.Key.Y);
+					Driver.AddRune (p.Value.Rune.Value);
+				}
+				LineCanvas.Clear ();
+			}
+		}
+
+		/// <summary>
+		/// Event invoked when the content area of the View is to be drawn.
+		/// </summary>
+		/// <remarks>
+		/// <para>
+		/// Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.
+		/// </para>
+		/// <para>
+		/// Rect provides the view-relative rectangle describing the currently visible viewport into the <see cref="View"/>.
+		/// </para>
+		/// </remarks>
+		public event EventHandler<DrawEventArgs> DrawContent;
+
+		/// <summary>
+		/// Enables overrides to draw infinitely scrolled content and/or a background behind added controls. 
+		/// </summary>
+		/// <param name="contentArea">The view-relative rectangle describing the currently visible viewport into the <see cref="View"/></param>
+		/// <remarks>
+		/// This method will be called before any subviews added with <see cref="Add(View)"/> have been drawn. 
+		/// </remarks>
+		public virtual void OnDrawContent (Rect contentArea)
+		{
+			// TODO: Make DrawContent a cancelable event
+			// if (!DrawContent?.Invoke(this, new DrawEventArgs (viewport)) {
+			DrawContent?.Invoke (this, new DrawEventArgs (contentArea));
+
+			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
+				if (TextFormatter != null) {
+					TextFormatter.NeedsFormat = true;
+				}
+				// This should NOT clear 
+				TextFormatter?.Draw (ViewToScreen (contentArea), HasFocus ? GetFocusColor () : GetNormalColor (),
+				    HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+				    Rect.Empty, false);
+				SetSubViewNeedsDisplay ();
+			}
+		}
+
+		/// <summary>
+		/// Event invoked when the content area of the View is completed drawing.
+		/// </summary>
+		/// <remarks>
+		/// <para>
+		/// Will be invoked after any subviews removed with <see cref="Remove(View)"/> have been completed drawing.
+		/// </para>
+		/// <para>
+		/// Rect provides the view-relative rectangle describing the currently visible viewport into the <see cref="View"/>.
+		/// </para>
+		/// </remarks>
+		public event EventHandler<DrawEventArgs> DrawContentComplete;
+
+		/// <summary>
+		/// Enables overrides after completed drawing infinitely scrolled content and/or a background behind removed controls.
+		/// </summary>
+		/// <param name="viewport">The view-relative rectangle describing the currently visible viewport into the <see cref="View"/></param>
+		/// <remarks>
+		/// This method will be called after any subviews removed with <see cref="Remove(View)"/> have been completed drawing.
+		/// </remarks>
+		public virtual void OnDrawContentComplete (Rect viewport)
+		{
+			DrawContentComplete?.Invoke (this, new DrawEventArgs (viewport));
+		}
+
+	}
+}

+ 464 - 0
Terminal.Gui/View/ViewKeyboard.cs

@@ -0,0 +1,464 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using NStack;
+
+namespace Terminal.Gui {
+	public partial class View  {
+		ShortcutHelper _shortcutHelper;
+
+		/// <summary>
+		/// Event invoked when the <see cref="HotKey"/> is changed.
+		/// </summary>
+		public event EventHandler<KeyChangedEventArgs> HotKeyChanged;
+
+		Key _hotKey = Key.Null;
+
+		/// <summary>
+		/// Gets or sets the HotKey defined for this view. A user pressing HotKey on the keyboard while this view has focus will cause the Clicked event to fire.
+		/// </summary>
+		public virtual Key HotKey {
+			get => _hotKey;
+			set {
+				if (_hotKey != value) {
+					var v = value == Key.Unknown ? Key.Null : value;
+					if (_hotKey != Key.Null && ContainsKeyBinding (Key.Space | _hotKey)) {
+						if (v == Key.Null) {
+							ClearKeybinding (Key.Space | _hotKey);
+						} else {
+							ReplaceKeyBinding (Key.Space | _hotKey, Key.Space | v);
+						}
+					} else if (v != Key.Null) {
+						AddKeyBinding (Key.Space | v, Command.Accept);
+					}
+					_hotKey = TextFormatter.HotKey = v;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the specifier character for the hotkey (e.g. '_'). Set to '\xffff' to disable hotkey support for this View instance. The default is '\xffff'. 
+		/// </summary>
+		public virtual Rune HotKeySpecifier {
+			get {
+				if (TextFormatter != null) {
+					return TextFormatter.HotKeySpecifier;
+				} else {
+					return new Rune ('\xFFFF');
+				}
+			}
+			set {
+				TextFormatter.HotKeySpecifier = value;
+				SetHotKey ();
+			}
+		}
+
+		/// <summary>
+		/// This is the global setting that can be used as a global shortcut to invoke an action if provided.
+		/// </summary>
+		public Key Shortcut {
+			get => _shortcutHelper.Shortcut;
+			set {
+				if (_shortcutHelper.Shortcut != value && (ShortcutHelper.PostShortcutValidation (value) || value == Key.Null)) {
+					_shortcutHelper.Shortcut = value;
+				}
+			}
+		}
+
+		/// <summary>
+		/// The keystroke combination used in the <see cref="Shortcut"/> as string.
+		/// </summary>
+		public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (_shortcutHelper.Shortcut);
+
+		/// <summary>
+		/// The action to run if the <see cref="Shortcut"/> is defined.
+		/// </summary>
+		public virtual Action ShortcutAction { get; set; }
+
+		// This is null, and allocated on demand.
+		List<View> _tabIndexes;
+
+		/// <summary>
+		/// Configurable keybindings supported by the control
+		/// </summary>
+		private Dictionary<Key, Command []> KeyBindings { get; set; } = new Dictionary<Key, Command []> ();
+		private Dictionary<Command, Func<bool?>> CommandImplementations { get; set; } = new Dictionary<Command, Func<bool?>> ();
+
+		/// <summary>
+		/// This returns a tab index list of the subviews contained by this view.
+		/// </summary>
+		/// <value>The tabIndexes.</value>
+		public IList<View> TabIndexes => _tabIndexes?.AsReadOnly () ?? _empty;
+
+		int _tabIndex = -1;
+
+		/// <summary>
+		/// Indicates the index of the current <see cref="View"/> from the <see cref="TabIndexes"/> list.
+		/// </summary>
+		public int TabIndex {
+			get { return _tabIndex; }
+			set {
+				if (!CanFocus) {
+					_tabIndex = -1;
+					return;
+				} else if (SuperView?._tabIndexes == null || SuperView?._tabIndexes.Count == 1) {
+					_tabIndex = 0;
+					return;
+				} else if (_tabIndex == value) {
+					return;
+				}
+				_tabIndex = value > SuperView._tabIndexes.Count - 1 ? SuperView._tabIndexes.Count - 1 : value < 0 ? 0 : value;
+				_tabIndex = GetTabIndex (_tabIndex);
+				if (SuperView._tabIndexes.IndexOf (this) != _tabIndex) {
+					SuperView._tabIndexes.Remove (this);
+					SuperView._tabIndexes.Insert (_tabIndex, this);
+					SetTabIndex ();
+				}
+			}
+		}
+
+		int GetTabIndex (int idx)
+		{
+			var i = 0;
+			foreach (var v in SuperView._tabIndexes) {
+				if (v._tabIndex == -1 || v == this) {
+					continue;
+				}
+				i++;
+			}
+			return Math.Min (i, idx);
+		}
+
+		void SetTabIndex ()
+		{
+			var i = 0;
+			foreach (var v in SuperView._tabIndexes) {
+				if (v._tabIndex == -1) {
+					continue;
+				}
+				v._tabIndex = i;
+				i++;
+			}
+		}
+
+		bool _tabStop = true;
+
+		/// <summary>
+		/// This only be <see langword="true"/> if the <see cref="CanFocus"/> is also <see langword="true"/> 
+		/// and the focus can be avoided by setting this to <see langword="false"/>
+		/// </summary>
+		public bool TabStop {
+			get => _tabStop;
+			set {
+				if (_tabStop == value) {
+					return;
+				}
+				_tabStop = CanFocus && value;
+			}
+		}
+
+		int _oldTabIndex;
+		
+		/// <summary>
+		/// Invoked when a character key is pressed and occurs after the key up event.
+		/// </summary>
+		public event EventHandler<KeyEventEventArgs> KeyPress;
+
+		/// <inheritdoc/>
+		public override bool ProcessKey (KeyEvent keyEvent)
+		{
+			if (!Enabled) {
+				return false;
+			}
+
+			var args = new KeyEventEventArgs (keyEvent);
+			KeyPress?.Invoke (this, args);
+			if (args.Handled)
+				return true;
+			if (Focused?.Enabled == true) {
+				Focused?.KeyPress?.Invoke (this, args);
+				if (args.Handled)
+					return true;
+			}
+
+			return Focused?.Enabled == true && Focused?.ProcessKey (keyEvent) == true;
+		}
+
+		/// <summary>
+		/// Invokes any binding that is registered on this <see cref="View"/>
+		/// and matches the <paramref name="keyEvent"/>
+		/// </summary>
+		/// <param name="keyEvent">The key event passed.</param>
+		protected bool? InvokeKeybindings (KeyEvent keyEvent)
+		{
+			bool? toReturn = null;
+
+			if (KeyBindings.ContainsKey (keyEvent.Key)) {
+
+				foreach (var command in KeyBindings [keyEvent.Key]) {
+
+					if (!CommandImplementations.ContainsKey (command)) {
+						throw new NotSupportedException ($"A KeyBinding was set up for the command {command} ({keyEvent.Key}) but that command is not supported by this View ({GetType ().Name})");
+					}
+
+					// each command has its own return value
+					var thisReturn = CommandImplementations [command] ();
+
+					// if we haven't got anything yet, the current command result should be used
+					if (toReturn == null) {
+						toReturn = thisReturn;
+					}
+
+					// if ever see a true then that's what we will return
+					if (thisReturn ?? false) {
+						toReturn = true;
+					}
+				}
+			}
+
+			return toReturn;
+		}
+
+		/// <summary>
+		/// <para>Adds a new key combination that will trigger the given <paramref name="command"/>
+		/// (if supported by the View - see <see cref="GetSupportedCommands"/>)
+		/// </para>
+		/// <para>If the key is already bound to a different <see cref="Command"/> it will be
+		/// rebound to this one</para>
+		/// <remarks>Commands are only ever applied to the current <see cref="View"/>(i.e. this feature
+		/// cannot be used to switch focus to another view and perform multiple commands there) </remarks>
+		/// </summary>
+		/// <param name="key"></param>
+		/// <param name="command">The command(s) to run on the <see cref="View"/> when <paramref name="key"/> is pressed.
+		/// When specifying multiple commands, all commands will be applied in sequence. The bound <paramref name="key"/> strike
+		/// will be consumed if any took effect.</param>
+		public void AddKeyBinding (Key key, params Command [] command)
+		{
+			if (command.Length == 0) {
+				throw new ArgumentException ("At least one command must be specified", nameof (command));
+			}
+
+			if (KeyBindings.ContainsKey (key)) {
+				KeyBindings [key] = command;
+			} else {
+				KeyBindings.Add (key, command);
+			}
+		}
+
+		/// <summary>
+		/// Replaces a key combination already bound to <see cref="Command"/>.
+		/// </summary>
+		/// <param name="fromKey">The key to be replaced.</param>
+		/// <param name="toKey">The new key to be used.</param>
+		protected void ReplaceKeyBinding (Key fromKey, Key toKey)
+		{
+			if (KeyBindings.ContainsKey (fromKey)) {
+				var value = KeyBindings [fromKey];
+				KeyBindings.Remove (fromKey);
+				KeyBindings [toKey] = value;
+			}
+		}
+
+		/// <summary>
+		/// Checks if the key binding already exists.
+		/// </summary>
+		/// <param name="key">The key to check.</param>
+		/// <returns><see langword="true"/> If the key already exist, <see langword="false"/> otherwise.</returns>
+		public bool ContainsKeyBinding (Key key)
+		{
+			return KeyBindings.ContainsKey (key);
+		}
+
+		/// <summary>
+		/// Removes all bound keys from the View and resets the default bindings.
+		/// </summary>
+		public void ClearKeybindings ()
+		{
+			KeyBindings.Clear ();
+		}
+
+		/// <summary>
+		/// Clears the existing keybinding (if any) for the given <paramref name="key"/>.
+		/// </summary>
+		/// <param name="key"></param>
+		public void ClearKeybinding (Key key)
+		{
+			KeyBindings.Remove (key);
+		}
+
+		/// <summary>
+		/// Removes all key bindings that trigger the given command. Views can have multiple different
+		/// keys bound to the same command and this method will clear all of them.
+		/// </summary>
+		/// <param name="command"></param>
+		public void ClearKeybinding (params Command [] command)
+		{
+			foreach (var kvp in KeyBindings.Where (kvp => kvp.Value.SequenceEqual (command)).ToArray ()) {
+				KeyBindings.Remove (kvp.Key);
+			}
+		}
+
+		/// <summary>
+		/// <para>States that the given <see cref="View"/> supports a given <paramref name="command"/>
+		/// and what <paramref name="f"/> to perform to make that command happen
+		/// </para>
+		/// <para>If the <paramref name="command"/> already has an implementation the <paramref name="f"/>
+		/// will replace the old one</para>
+		/// </summary>
+		/// <param name="command">The command.</param>
+		/// <param name="f">The function.</param>
+		protected void AddCommand (Command command, Func<bool?> f)
+		{
+			// if there is already an implementation of this command
+			if (CommandImplementations.ContainsKey (command)) {
+				// replace that implementation
+				CommandImplementations [command] = f;
+			} else {
+				// else record how to perform the action (this should be the normal case)
+				CommandImplementations.Add (command, f);
+			}
+		}
+
+		/// <summary>
+		/// Returns all commands that are supported by this <see cref="View"/>.
+		/// </summary>
+		/// <returns></returns>
+		public IEnumerable<Command> GetSupportedCommands ()
+		{
+			return CommandImplementations.Keys;
+		}
+
+		/// <summary>
+		/// Gets the key used by a command.
+		/// </summary>
+		/// <param name="command">The command to search.</param>
+		/// <returns>The <see cref="Key"/> used by a <see cref="Command"/></returns>
+		public Key GetKeyFromCommand (params Command [] command)
+		{
+			return KeyBindings.First (kb => kb.Value.SequenceEqual (command)).Key;
+		}
+
+		/// <inheritdoc/>
+		public override bool ProcessHotKey (KeyEvent keyEvent)
+		{
+			if (!Enabled) {
+				return false;
+			}
+
+			var args = new KeyEventEventArgs (keyEvent);
+			if (MostFocused?.Enabled == true) {
+				MostFocused?.KeyPress?.Invoke (this, args);
+				if (args.Handled)
+					return true;
+			}
+			if (MostFocused?.Enabled == true && MostFocused?.ProcessKey (keyEvent) == true)
+				return true;
+			if (_subviews == null || _subviews.Count == 0)
+				return false;
+
+			foreach (var view in _subviews)
+				if (view.Enabled && view.ProcessHotKey (keyEvent))
+					return true;
+			return false;
+		}
+
+		/// <inheritdoc/>
+		public override bool ProcessColdKey (KeyEvent keyEvent)
+		{
+			if (!Enabled) {
+				return false;
+			}
+
+			var args = new KeyEventEventArgs (keyEvent);
+			KeyPress?.Invoke (this, args);
+			if (args.Handled)
+				return true;
+			if (MostFocused?.Enabled == true) {
+				MostFocused?.KeyPress?.Invoke (this, args);
+				if (args.Handled)
+					return true;
+			}
+			if (MostFocused?.Enabled == true && MostFocused?.ProcessKey (keyEvent) == true)
+				return true;
+			if (_subviews == null || _subviews.Count == 0)
+				return false;
+
+			foreach (var view in _subviews)
+				if (view.Enabled && view.ProcessColdKey (keyEvent))
+					return true;
+			return false;
+		}
+
+		/// <summary>
+		/// Invoked when a key is pressed.
+		/// </summary>
+		public event EventHandler<KeyEventEventArgs> KeyDown;
+
+		/// <inheritdoc/>
+		public override bool OnKeyDown (KeyEvent keyEvent)
+		{
+			if (!Enabled) {
+				return false;
+			}
+
+			var args = new KeyEventEventArgs (keyEvent);
+			KeyDown?.Invoke (this, args);
+			if (args.Handled) {
+				return true;
+			}
+			if (Focused?.Enabled == true) {
+				Focused.KeyDown?.Invoke (this, args);
+				if (args.Handled) {
+					return true;
+				}
+				if (Focused?.OnKeyDown (keyEvent) == true) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+
+		/// <summary>
+		/// Invoked when a key is released.
+		/// </summary>
+		public event EventHandler<KeyEventEventArgs> KeyUp;
+
+		/// <inheritdoc/>
+		public override bool OnKeyUp (KeyEvent keyEvent)
+		{
+			if (!Enabled) {
+				return false;
+			}
+
+			var args = new KeyEventEventArgs (keyEvent);
+			KeyUp?.Invoke (this, args);
+			if (args.Handled) {
+				return true;
+			}
+			if (Focused?.Enabled == true) {
+				Focused.KeyUp?.Invoke (this, args);
+				if (args.Handled) {
+					return true;
+				}
+				if (Focused?.OnKeyUp (keyEvent) == true) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+		
+		void SetHotKey ()
+		{
+			if (TextFormatter == null) {
+				return; // throw new InvalidOperationException ("Can't set HotKey unless a TextFormatter has been created");
+			}
+			TextFormatter.FindHotKey (_text, HotKeySpecifier, true, out _, out var hk);
+			if (_hotKey != hk) {
+				HotKey = hk;
+			}
+		}
+	}
+}

+ 1157 - 0
Terminal.Gui/View/ViewLayout.cs

@@ -0,0 +1,1157 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using NStack;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Determines the LayoutStyle for a <see cref="View"/>, if Absolute, during <see cref="View.LayoutSubviews"/>, the
+	/// value from the <see cref="View.Frame"/> will be used, if the value is Computed, then <see cref="View.Frame"/>
+	/// will be updated from the X, Y <see cref="Pos"/> objects and the Width and Height <see cref="Dim"/> objects.
+	/// </summary>
+	public enum LayoutStyle {
+		/// <summary>
+		/// The position and size of the view are based <see cref="View.Frame"/>. 
+		/// </summary>
+		Absolute,
+
+		/// <summary>
+		/// The position and size of the view will be computed based on 
+		/// <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and <see cref="View.Height"/>. <see cref="View.Frame"/> will
+		/// provide the absolute computed values.
+		/// </summary>
+		Computed
+	}
+
+	public partial class View {
+
+		// The frame for the object. Superview relative.
+		Rect _frame;
+
+		/// <summary>
+		/// Gets or sets the frame for the view. The frame is relative to the view's container (<see cref="SuperView"/>).
+		/// </summary>
+		/// <value>The frame.</value>
+		/// <remarks>
+		/// <para>
+		///    Change the Frame when using the <see cref="Terminal.Gui.LayoutStyle.Absolute"/> layout style to move or resize views. 
+		/// </para>
+		/// <para>
+		///    Altering the Frame of a view will trigger the redrawing of the
+		///    view as well as the redrawing of the affected regions of the <see cref="SuperView"/>.
+		/// </para>
+		/// </remarks>
+		public virtual Rect Frame {
+			get => _frame;
+			set {
+				_frame = new Rect (value.X, value.Y, Math.Max (value.Width, 0), Math.Max (value.Height, 0));
+				if (IsInitialized || LayoutStyle == LayoutStyle.Absolute) {
+					LayoutFrames ();
+					TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
+					SetNeedsLayout ();
+					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; private 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>
+		/// <remarks>
+		/// <see cref="BorderStyle"/> provides a simple helper for turning a simple border frame on or off.
+		/// </remarks>
+		public Frame Border { get; private set; }
+
+		/// <summary>
+		/// Gets or sets whether the view has a one row/col thick border.
+		/// </summary>
+		/// <remarks>
+		/// <para>
+		/// This is a helper for manipulating the view's <see cref="Border"/>. Setting this property to any value other than
+		/// <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s <see cref="Frame.Thickness"/> 
+		/// to `1` and <see cref="BorderStyle"/> to the value. 
+		/// </para>
+		/// <para>
+		/// Setting this property to <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s <see cref="Frame.Thickness"/> 
+		/// to `0` and <see cref="BorderStyle"/> to <see cref="LineStyle.None"/>. 
+		/// </para>
+		/// <para>
+		/// For more advanced customization of the view's border, manipulate see <see cref="Border"/> directly.
+		/// </para>
+		/// </remarks>
+		public LineStyle BorderStyle {
+			get {
+				return Border?.BorderStyle ?? LineStyle.None;
+			}
+			set {
+				if (Border == null) {
+					throw new InvalidOperationException ("Border is null; this is likely a bug.");
+				}
+				if (value != LineStyle.None) {
+					Border.Thickness = new Thickness (1);
+				} else {
+					Border.Thickness = new Thickness (0);
+				}
+				Border.BorderStyle = value;
+				LayoutFrames ();
+				SetNeedsLayout ();
+			}
+		}
+
+		/// <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; private set; }
+
+		/// <summary>
+		/// Helper to get the total thickness of the <see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>. 
+		/// </summary>
+		/// <returns>A thickness that describes the sum of the Frames' thicknesses.</returns>
+		public Thickness GetFramesThickness ()
+		{
+			var left = Margin.Thickness.Left + Border.Thickness.Left + Padding.Thickness.Left;
+			var top = Margin.Thickness.Top + Border.Thickness.Top + Padding.Thickness.Top;
+			var right = Margin.Thickness.Right + Border.Thickness.Right + Padding.Thickness.Right;
+			var bottom = Margin.Thickness.Bottom + Border.Thickness.Bottom + Padding.Thickness.Bottom;
+			return new Thickness (left, top, right, bottom);
+		}
+
+		/// <summary>
+		/// Helper to get the X and Y offset of the Bounds from the Frame. This is the sum of the Left and Top properties of
+		/// <see cref="Margin"/>, <see cref="Border"/> and <see cref="Padding"/>.
+		/// </summary>
+		public Point GetBoundsOffset () => new Point (Padding?.Thickness.GetInside (Padding.Frame).X ?? 0, Padding?.Thickness.GetInside (Padding.Frame).Y ?? 0);
+
+		/// <summary>
+		/// Creates the view's <see cref="Frame"/> objects. This internal method is overridden by Frame to do nothing
+		/// to prevent recursion during View construction.
+		/// </summary>
+		internal virtual void CreateFrames ()
+		{
+			void ThicknessChangedHandler (object sender, EventArgs e)
+			{
+				LayoutFrames ();
+				SetNeedsLayout ();
+				SetNeedsDisplay ();
+			}
+
+			if (Margin != null) {
+				Margin.ThicknessChanged -= ThicknessChangedHandler;
+				Margin.Dispose ();
+			}
+			Margin = new Frame () { Id = "Margin", Thickness = new Thickness (0) };
+			Margin.ThicknessChanged += ThicknessChangedHandler;
+			Margin.Parent = this;
+
+			if (Border != null) {
+				Border.ThicknessChanged -= ThicknessChangedHandler;
+				Border.Dispose ();
+			}
+			Border = new Frame () { Id = "Border", Thickness = new Thickness (0) };
+			Border.ThicknessChanged += ThicknessChangedHandler;
+			Border.Parent = this;
+
+			// TODO: Create View.AddAdornment
+
+			if (Padding != null) {
+				Padding.ThicknessChanged -= ThicknessChangedHandler;
+				Padding.Dispose ();
+			}
+			Padding = new Frame () { Id = "Padding", Thickness = new Thickness (0) };
+			Padding.ThicknessChanged += ThicknessChangedHandler;
+			Padding.Parent = this;
+		}
+
+		LayoutStyle _layoutStyle;
+
+		/// <summary>
+		/// Controls how the View's <see cref="Frame"/> is computed during the LayoutSubviews method, if the style is set to
+		/// <see cref="Terminal.Gui.LayoutStyle.Absolute"/>, 
+		/// LayoutSubviews does not change the <see cref="Frame"/>. If the style is <see cref="Terminal.Gui.LayoutStyle.Computed"/>
+		/// the <see cref="Frame"/> is updated using
+		/// the <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> properties.
+		/// </summary>
+		/// <value>The layout style.</value>
+		public LayoutStyle LayoutStyle {
+			get => _layoutStyle;
+			set {
+				_layoutStyle = value;
+				SetNeedsLayout ();
+			}
+		}
+
+		/// <summary>
+		/// The View-relative rectangle where View content is displayed. SubViews are positioned relative to 
+		/// Bounds.<see cref="Rect.Location">Location</see> (which is always (0, 0)) and <see cref="Redraw(Rect)"/> clips drawing to 
+		/// Bounds.<see cref="Rect.Size">Size</see>.
+		/// </summary>
+		/// <value>The bounds.</value>
+		/// <remarks>
+		/// <para>
+		/// The <see cref="Rect.Location"/> of Bounds is always (0, 0). To obtain the offset of the Bounds from the Frame use 
+		/// <see cref="GetBoundsOffset"/>.
+		/// </para>
+		/// </remarks>
+		public virtual Rect Bounds {
+			get {
+#if DEBUG
+				if (LayoutStyle == LayoutStyle.Computed && !IsInitialized) {
+					Debug.WriteLine ($"WARNING: Bounds is being accessed before the View has been initialized. This is likely a bug. View: {this}");
+				}
+#endif // DEBUG
+				var frameRelativeBounds = Padding?.Thickness.GetInside (Padding.Frame) ?? new Rect (default, Frame.Size);
+				return new Rect (default, frameRelativeBounds.Size);
+			}
+			set {
+				// BUGBUG: Margin etc.. can be null (if typeof(Frame))
+				Frame = new Rect (Frame.Location,
+					new Size (
+						value.Size.Width + Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal,
+						value.Size.Height + Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical
+						)
+					);
+			}
+		}
+
+		// Diagnostics to highlight when X or Y is read before the view has been initialized
+		private Pos VerifyIsIntialized (Pos pos)
+		{
+#if DEBUG
+			if (LayoutStyle == LayoutStyle.Computed && (!IsInitialized)) {
+				Debug.WriteLine ($"WARNING: \"{this}\" has not been initialized; position is indeterminate {pos}. This is likely a bug.");
+			}
+#endif // DEBUG
+			return pos;
+		}
+
+		// Diagnostics to highlight when Width or Height is read before the view has been initialized
+		private Dim VerifyIsIntialized (Dim dim)
+		{
+#if DEBUG
+			if (LayoutStyle == LayoutStyle.Computed && (!IsInitialized)) {
+				Debug.WriteLine ($"WARNING: \"{this}\" has not been initialized; dimension is indeterminate: {dim}. This is likely a bug.");
+			}
+#endif // DEBUG		
+			return dim;
+		}
+
+		Pos _x, _y;
+
+		/// <summary>
+		/// Gets or sets the X position for the view (the column). Only used if the <see cref="LayoutStyle"/> is <see cref="Terminal.Gui.LayoutStyle.Computed"/>.
+		/// </summary>
+		/// <value>The X Position.</value>
+		/// <remarks>
+		/// If <see cref="LayoutStyle"/> is <see cref="Terminal.Gui.LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
+		/// </remarks>
+		public Pos X {
+			get => VerifyIsIntialized (_x);
+			set {
+				if (ForceValidatePosDim && !ValidatePosDim (_x, value)) {
+					throw new ArgumentException ();
+				}
+
+				_x = value;
+
+				OnResizeNeeded ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the Y position for the view (the row). Only used if the <see cref="LayoutStyle"/> is <see cref="Terminal.Gui.LayoutStyle.Computed"/>.
+		/// </summary>
+		/// <value>The y position (line).</value>
+		/// <remarks>
+		/// If <see cref="LayoutStyle"/> is <see cref="Terminal.Gui.LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
+		/// </remarks>
+		public Pos Y {
+			get => VerifyIsIntialized (_y);
+			set {
+				if (ForceValidatePosDim && !ValidatePosDim (_y, value)) {
+					throw new ArgumentException ();
+				}
+
+				_y = value;
+
+				OnResizeNeeded ();
+			}
+		}
+		Dim _width, _height;
+
+		/// <summary>
+		/// Gets or sets the width of the view. Only used the <see cref="LayoutStyle"/> is <see cref="Terminal.Gui.LayoutStyle.Computed"/>.
+		/// </summary>
+		/// <value>The width.</value>
+		/// <remarks>
+		/// If <see cref="LayoutStyle"/> is <see cref="Terminal.Gui.LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
+		/// </remarks>
+		public Dim Width {
+			get => VerifyIsIntialized (_width);
+			set {
+				if (ForceValidatePosDim && !ValidatePosDim (_width, value)) {
+					throw new ArgumentException ("ForceValidatePosDim is enabled", nameof (Width));
+				}
+
+				_width = value;
+
+				if (ForceValidatePosDim) {
+					var isValidNewAutSize = AutoSize && IsValidAutoSizeWidth (_width);
+
+					if (IsAdded && AutoSize && !isValidNewAutSize) {
+						throw new InvalidOperationException ("Must set AutoSize to false before set the Width.");
+					}
+				}
+				OnResizeNeeded ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the height of the view. Only used the <see cref="LayoutStyle"/> is <see cref="Terminal.Gui.LayoutStyle.Computed"/>.
+		/// </summary>
+		/// <value>The height.</value>
+		/// If <see cref="LayoutStyle"/> is <see cref="Terminal.Gui.LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
+		public Dim Height {
+			get => VerifyIsIntialized (_height);
+			set {
+				if (ForceValidatePosDim && !ValidatePosDim (_height, value)) {
+					throw new ArgumentException ("ForceValidatePosDim is enabled", nameof (Height));
+				}
+
+				_height = value;
+
+				if (ForceValidatePosDim) {
+					var isValidNewAutSize = AutoSize && IsValidAutoSizeHeight (_height);
+
+					if (IsAdded && AutoSize && !isValidNewAutSize) {
+						throw new InvalidOperationException ("Must set AutoSize to false before set the Height.");
+					}
+				}
+				OnResizeNeeded ();
+			}
+		}
+
+		/// <summary>
+		/// Forces validation with <see cref="Terminal.Gui.LayoutStyle.Computed"/> layout
+		///  to avoid breaking the <see cref="Pos"/> and <see cref="Dim"/> settings.
+		/// </summary>
+		public bool ForceValidatePosDim { get; set; }
+
+		bool ValidatePosDim (object oldValue, object newValue)
+		{
+			if (!IsInitialized || _layoutStyle == LayoutStyle.Absolute || oldValue == null || oldValue.GetType () == newValue.GetType () || this is Toplevel) {
+				return true;
+			}
+			if (_layoutStyle == LayoutStyle.Computed) {
+				if (oldValue.GetType () != newValue.GetType () && !(newValue is Pos.PosAbsolute || newValue is Dim.DimAbsolute)) {
+					return true;
+				}
+			}
+			return false;
+		}
+
+		// BUGBUG: This API is broken - It should be renamed to `GetMinimumBoundsForFrame and 
+		// should not assume Frame.Height == Bounds.Height
+		/// <summary>
+		/// Gets the minimum dimensions required to fit the View's <see cref="Text"/>, factoring in <see cref="TextDirection"/>.
+		/// </summary>
+		/// <param name="size">The minimum dimensions required.</param>
+		/// <returns><see langword="true"/> if the dimensions fit within the View's <see cref="Bounds"/>, <see langword="false"/> otherwise.</returns>
+		/// <remarks>
+		/// Always returns <see langword="false"/> if <see cref="AutoSize"/> is <see langword="true"/> or
+		/// if <see cref="Height"/> (Horizontal) or <see cref="Width"/> (Vertical) are not not set or zero.
+		/// Does not take into account word wrapping.
+		/// </remarks>
+		public bool GetMinimumBounds (out Size size)
+		{
+			size = Bounds.Size;
+
+			if (!AutoSize && !ustring.IsNullOrEmpty (TextFormatter.Text)) {
+				switch (TextFormatter.IsVerticalDirection (TextDirection)) {
+				case true:
+					var colWidth = TextFormatter.GetSumMaxCharWidth (new List<ustring> { TextFormatter.Text }, 0, 1);
+					// TODO: v2 - This uses frame.Width; it should only use Bounds
+					if (_frame.Width < colWidth &&
+						(Width == null ||
+							(Bounds.Width >= 0 &&
+								Width is Dim.DimAbsolute &&
+								Width.Anchor (0) >= 0 &&
+								Width.Anchor (0) < colWidth))) {
+						size = new Size (colWidth, Bounds.Height);
+						return true;
+					}
+					break;
+				default:
+					if (_frame.Height < 1 &&
+						(Height == null ||
+							(Height is Dim.DimAbsolute &&
+								Height.Anchor (0) == 0))) {
+						size = new Size (Bounds.Width, 1);
+						return true;
+					}
+					break;
+				}
+			}
+			return false;
+		}
+
+		// BUGBUG - v2 - Should be renamed "SetBoundsToFitFrame"
+		/// <summary>
+		/// Sets the size of the View to the minimum width or height required to fit <see cref="Text"/> (see <see cref="GetMinimumBounds(out Size)"/>.
+		/// </summary>
+		/// <returns><see langword="true"/> if the size was changed, <see langword="false"/> if <see cref="Text"/>
+		/// will not fit.</returns>
+		public bool SetMinWidthHeight ()
+		{
+			if (GetMinimumBounds (out Size size)) {
+				_frame = new Rect (_frame.Location, size);
+				return true;
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Called whenever the view needs to be resized. Sets <see cref="Frame"/> and
+		/// triggers a <see cref="LayoutSubviews()"/> call.		/// 
+		/// </summary>
+		/// <remarks>
+		/// Can be overridden if the view resize behavior is different than the default.
+		/// </remarks>
+		protected virtual void OnResizeNeeded ()
+		{
+			var actX = _x is Pos.PosAbsolute ? _x.Anchor (0) : _frame.X;
+			var actY = _y is Pos.PosAbsolute ? _y.Anchor (0) : _frame.Y;
+
+			if (AutoSize) {
+				//if (TextAlignment == TextAlignment.Justified) {
+				//	throw new InvalidOperationException ("TextAlignment.Justified cannot be used with AutoSize");
+				//}
+				var s = GetAutoSize ();
+				var w = _width is Dim.DimAbsolute && _width.Anchor (0) > s.Width ? _width.Anchor (0) : s.Width;
+				var h = _height is Dim.DimAbsolute && _height.Anchor (0) > s.Height ? _height.Anchor (0) : s.Height;
+				_frame = new Rect (new Point (actX, actY), new Size (w, h)); // Set frame, not Frame!
+			} else {
+				var w = _width is Dim.DimAbsolute ? _width.Anchor (0) : _frame.Width;
+				var h = _height is Dim.DimAbsolute ? _height.Anchor (0) : _frame.Height;
+				// BUGBUG: v2 - ? - If layoutstyle is absolute, this overwrites the current frame h/w with 0. Hmmm...
+				// This is needed for DimAbsolute values by setting the frame before LayoutSubViews.
+				_frame = new Rect (new Point (actX, actY), new Size (w, h)); // Set frame, not Frame!
+			}
+			//// BUGBUG: I think these calls are redundant or should be moved into just the AutoSize case
+			if (IsInitialized || LayoutStyle == LayoutStyle.Absolute) {
+				SetMinWidthHeight ();
+				LayoutFrames ();
+				TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
+				SetNeedsLayout ();
+				SetNeedsDisplay ();
+			}
+		}
+
+		internal bool LayoutNeeded { get; private set; } = true;
+
+		internal void SetNeedsLayout ()
+		{
+			if (LayoutNeeded)
+				return;
+			LayoutNeeded = true;
+			if (SuperView == null)
+				return;
+			SuperView.SetNeedsLayout ();
+			foreach (var view in Subviews) {
+				view.SetNeedsLayout ();
+			}
+			TextFormatter.NeedsFormat = true;
+		}
+
+		/// <summary>
+		/// Removes the <see cref="SetNeedsLayout"/> setting on this view.
+		/// </summary>
+		protected void ClearLayoutNeeded ()
+		{
+			LayoutNeeded = false;
+		}
+
+		/// <summary>
+		/// Converts a point from screen-relative coordinates to view-relative coordinates.
+		/// </summary>
+		/// <returns>The mapped point.</returns>
+		/// <param name="x">X screen-coordinate point.</param>
+		/// <param name="y">Y screen-coordinate point.</param>
+		public Point ScreenToView (int x, int y)
+		{
+			if (SuperView == null) {
+				return new Point (x - Frame.X, y - _frame.Y);
+			} else {
+				var parent = SuperView.ScreenToView (x, y);
+				return new Point (parent.X - _frame.X, parent.Y - _frame.Y);
+			}
+		}
+
+		/// <summary>
+		/// Converts a point from screen-relative coordinates to bounds-relative coordinates.
+		/// </summary>
+		/// <returns>The mapped point.</returns>
+		/// <param name="x">X screen-coordinate point.</param>
+		/// <param name="y">Y screen-coordinate point.</param>
+		public Point ScreenToBounds (int x, int y)
+		{
+			if (SuperView == null) {
+				var boundsOffset = GetBoundsOffset ();
+				return new Point (x - Frame.X + boundsOffset.X, y - Frame.Y + boundsOffset.Y);
+			} else {
+				var parent = SuperView.ScreenToView (x, y);
+				return new Point (parent.X - _frame.X, parent.Y - _frame.Y);
+			}
+		}
+
+		/// <summary>
+		/// Converts a view-relative location to a screen-relative location (col,row). The output is optionally clamped to the screen dimensions.
+		/// </summary>
+		/// <param name="col">View-relative column.</param>
+		/// <param name="row">View-relative row.</param>
+		/// <param name="rcol">Absolute column; screen-relative.</param>
+		/// <param name="rrow">Absolute row; screen-relative.</param>
+		/// <param name="clamped">If <see langword="true"/>, <paramref name="rcol"/> and <paramref name="rrow"/> will be clamped to the 
+		/// screen dimensions (they never be negative and will always be less than to <see cref="ConsoleDriver.Cols"/> and
+		/// <see cref="ConsoleDriver.Rows"/>, respectively.</param>
+		public virtual void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clamped = true)
+		{
+			var boundsOffset = GetBoundsOffset ();
+			rcol = col + Frame.X + boundsOffset.X;
+			rrow = row + Frame.Y + boundsOffset.Y;
+
+			var super = SuperView;
+			while (super != null) {
+				boundsOffset = super.GetBoundsOffset ();
+				rcol += super.Frame.X + boundsOffset.X;
+				rrow += super.Frame.Y + boundsOffset.Y;
+				super = super.SuperView;
+			}
+
+			// The following ensures that the cursor is always in the screen boundaries.
+			if (clamped) {
+				rrow = Math.Min (rrow, Driver.Rows - 1);
+				rcol = Math.Min (rcol, Driver.Cols - 1);
+			}
+		}
+
+		/// <summary>
+		/// Converts a region in view-relative coordinates to screen-relative coordinates.
+		/// </summary>
+		internal Rect ViewToScreen (Rect region)
+		{
+			ViewToScreen (region.X, region.Y, out var x, out var y, clamped: false);
+			return new Rect (x, y, region.Width, region.Height);
+		}
+
+
+		/// <summary>
+		/// Sets the View's <see cref="Frame"/> to the frame-relative coordinates if its container. The
+		/// container size and location are specified by <paramref name="superviewFrame"/> and are relative to the
+		/// View's superview.
+		/// </summary>
+		/// <param name="superviewFrame">The supserview-relative rectangle describing View's container (nominally the 
+		/// same as <c>this.SuperView.Frame</c>).</param>
+		internal void SetRelativeLayout (Rect superviewFrame)
+		{
+			int newX, newW, newY, newH;
+			var autosize = Size.Empty;
+
+			if (AutoSize) {
+				// Note this is global to this function and used as such within the local functions defined
+				// below. In v2 AutoSize will be re-factored to not need to be dealt with in this function.
+				autosize = GetAutoSize ();
+			}
+
+			// Returns the new dimension (width or height) and location (x or y) for the View given
+			//   the superview's Frame.X or Frame.Y
+			//   the superview's width or height
+			//   the current Pos (View.X or View.Y)
+			//   the current Dim (View.Width or View.Height)
+			(int newLocation, int newDimension) GetNewLocationAndDimension (int superviewLocation, int superviewDimension, Pos pos, Dim dim, int autosizeDimension)
+			{
+				int newDimension, newLocation;
+
+				switch (pos) {
+				case Pos.PosCenter:
+					if (dim == null) {
+						newDimension = AutoSize ? autosizeDimension : superviewDimension;
+					} else {
+						newDimension = dim.Anchor (superviewDimension);
+						newDimension = AutoSize && autosizeDimension > newDimension ? autosizeDimension : newDimension;
+					}
+					newLocation = pos.Anchor (superviewDimension - newDimension);
+					break;
+
+				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);
+					if (combine.add) {
+						newLocation = left + right;
+					} else {
+						newLocation = left - right;
+					}
+					newDimension = Math.Max (CalculateNewDimension (dim, newLocation, superviewDimension, autosizeDimension), 0);
+					break;
+
+				case Pos.PosAbsolute:
+				case Pos.PosAnchorEnd:
+				case Pos.PosFactor:
+				case Pos.PosFunc:
+				case Pos.PosView:
+				default:
+					newLocation = pos?.Anchor (superviewDimension) ?? 0;
+					newDimension = Math.Max (CalculateNewDimension (dim, newLocation, superviewDimension, autosizeDimension), 0);
+					break;
+				}
+				return (newLocation, newDimension);
+			}
+
+			// Recursively calculates the new dimension (width or height) of the given Dim given:
+			//   the current location (x or y)
+			//   the current dimension (width or height)
+			int CalculateNewDimension (Dim d, int location, int dimension, int autosize)
+			{
+				int newDimension;
+				switch (d) {
+				case null:
+					newDimension = AutoSize ? autosize : dimension;
+					break;
+				case Dim.DimCombine combine:
+					int leftNewDim = CalculateNewDimension (combine.left, location, dimension, autosize);
+					int rightNewDim = CalculateNewDimension (combine.right, location, dimension, autosize);
+					if (combine.add) {
+						newDimension = leftNewDim + rightNewDim;
+					} else {
+						newDimension = leftNewDim - rightNewDim;
+					}
+					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);
+					newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
+					break;
+				}
+
+				return newDimension;
+			}
+
+			// horizontal
+			(newX, newW) = GetNewLocationAndDimension (superviewFrame.X, superviewFrame.Width, _x, _width, autosize.Width);
+
+			// vertical
+			(newY, newH) = GetNewLocationAndDimension (superviewFrame.Y, superviewFrame.Height, _y, _height, autosize.Height);
+
+			var r = new Rect (newX, newY, newW, newH);
+			if (Frame != r) {
+				Frame = r;
+				// BUGBUG: Why is this AFTER setting Frame? Seems duplicative.
+				if (!SetMinWidthHeight ()) {
+					TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Fired after the View's <see cref="LayoutSubviews"/> method has completed. 
+		/// </summary>
+		/// <remarks>
+		/// Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has otherwise changed.
+		/// </remarks>
+		public event EventHandler<LayoutEventArgs> LayoutStarted;
+
+		/// <summary>
+		/// Raises the <see cref="LayoutStarted"/> event. Called from  <see cref="LayoutSubviews"/> before any subviews have been laid out.
+		/// </summary>
+		internal virtual void OnLayoutStarted (LayoutEventArgs args)
+		{
+			LayoutStarted?.Invoke (this, args);
+		}
+
+		/// <summary>
+		/// Fired after the View's <see cref="LayoutSubviews"/> method has completed. 
+		/// </summary>
+		/// <remarks>
+		/// Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has otherwise changed.
+		/// </remarks>
+		public event EventHandler<LayoutEventArgs> LayoutComplete;
+
+		/// <summary>
+		/// Event called only once when the <see cref="View"/> is being initialized for the first time.
+		/// Allows configurations and assignments to be performed before the <see cref="View"/> being shown.
+		/// This derived from <see cref="ISupportInitializeNotification"/> to allow notify all the views that are being initialized.
+		/// </summary>
+		public event EventHandler Initialized;
+
+		/// <summary>
+		/// Raises the <see cref="LayoutComplete"/> event. Called from  <see cref="LayoutSubviews"/> before all sub-views have been laid out.
+		/// </summary>
+		internal virtual void OnLayoutComplete (LayoutEventArgs args)
+		{
+			LayoutComplete?.Invoke (this, args);
+		}
+
+		internal void CollectPos (Pos pos, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
+		{
+			switch (pos) {
+			case Pos.PosView pv:
+				// See #2461
+				//if (!from.InternalSubviews.Contains (pv.Target)) {
+				//	throw new InvalidOperationException ($"View {pv.Target} is not a subview of {from}");
+				//}
+				if (pv.Target != this) {
+					nEdges.Add ((pv.Target, from));
+				}
+				return;
+			case Pos.PosCombine pc:
+				CollectPos (pc.left, from, ref nNodes, ref nEdges);
+				CollectPos (pc.right, from, ref nNodes, ref nEdges);
+				break;
+			}
+		}
+
+		internal void CollectDim (Dim dim, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
+		{
+			switch (dim) {
+			case Dim.DimView dv:
+				// See #2461
+				//if (!from.InternalSubviews.Contains (dv.Target)) {
+				//	throw new InvalidOperationException ($"View {dv.Target} is not a subview of {from}");
+				//}
+				if (dv.Target != this) {
+					nEdges.Add ((dv.Target, from));
+				}
+				return;
+			case Dim.DimCombine dc:
+				CollectDim (dc.left, from, ref nNodes, ref nEdges);
+				CollectDim (dc.right, from, ref nNodes, ref nEdges);
+				break;
+			}
+		}
+
+		internal void CollectAll (View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
+		{
+			foreach (var v in from.InternalSubviews) {
+				nNodes.Add (v);
+				if (v._layoutStyle != LayoutStyle.Computed) {
+					continue;
+				}
+				CollectPos (v.X, v, ref nNodes, ref nEdges);
+				CollectPos (v.Y, v, ref nNodes, ref nEdges);
+				CollectDim (v.Width, v, ref nNodes, ref nEdges);
+				CollectDim (v.Height, v, ref nNodes, ref nEdges);
+			}
+		}
+
+		// https://en.wikipedia.org/wiki/Topological_sorting
+		internal static List<View> TopologicalSort (View superView, IEnumerable<View> nodes, ICollection<(View From, View To)> edges)
+		{
+			var result = new List<View> ();
+
+			// Set of all nodes with no incoming edges
+			var noEdgeNodes = new HashSet<View> (nodes.Where (n => edges.All (e => !e.To.Equals (n))));
+
+			while (noEdgeNodes.Any ()) {
+				//  remove a node n from S
+				var n = noEdgeNodes.First ();
+				noEdgeNodes.Remove (n);
+
+				// add n to tail of L
+				if (n != superView)
+					result.Add (n);
+
+				// for each node m with an edge e from n to m do
+				foreach (var e in edges.Where (e => e.From.Equals (n)).ToArray ()) {
+					var m = e.To;
+
+					// remove edge e from the graph
+					edges.Remove (e);
+
+					// if m has no other incoming edges then
+					if (edges.All (me => !me.To.Equals (m)) && m != superView) {
+						// insert m into S
+						noEdgeNodes.Add (m);
+					}
+				}
+			}
+
+			if (edges.Any ()) {
+				foreach ((var from, var to) in edges) {
+					if (from == to) {
+						// if not yet added to the result, add it and remove from edge
+						if (result.Find (v => v == from) == null) {
+							result.Add (from);
+						}
+						edges.Remove ((from, to));
+					} else if (from.SuperView == to.SuperView) {
+						// if 'from' is not yet added to the result, add it
+						if (result.Find (v => v == from) == null) {
+							result.Add (from);
+						}
+						// if 'to' is not yet added to the result, add it
+						if (result.Find (v => v == to) == null) {
+							result.Add (to);
+						}
+						// remove from edge
+						edges.Remove ((from, to));
+					} else if (from != superView?.GetTopSuperView (to, from) && !ReferenceEquals (from, to)) {
+						if (ReferenceEquals (from.SuperView, to)) {
+							throw new InvalidOperationException ($"ComputedLayout for \"{superView}\": \"{to}\" references a SubView (\"{from}\").");
+						} else {
+							throw new InvalidOperationException ($"ComputedLayout for \"{superView}\": \"{from}\" linked with \"{to}\" was not found. Did you forget to add it to {superView}?");
+						}
+					}
+				}
+			}
+			// return L (a topologically sorted order)
+			return result;
+		} // TopologicalSort
+
+		/// <summary>
+		/// Overriden by <see cref="Frame"/> to do nothing, as the <see cref="Frame"/> does not have frames.
+		/// </summary>
+		internal virtual void LayoutFrames ()
+		{
+			if (Margin == null) return; // CreateFrames() has not been called yet
+
+			if (Margin.Frame.Size != Frame.Size) {
+				Margin._frame = new Rect (Point.Empty, Frame.Size);
+				Margin.X = 0;
+				Margin.Y = 0;
+				Margin.Width = Frame.Size.Width;
+				Margin.Height = Frame.Size.Height;
+				Margin.SetNeedsLayout ();
+				Margin.LayoutSubviews ();
+				Margin.SetNeedsDisplay ();
+			}
+
+			var border = Margin.Thickness.GetInside (Margin.Frame);
+			if (border != Border.Frame) {
+				Border._frame = new Rect (new Point (border.Location.X, border.Location.Y), border.Size);
+				Border.X = border.Location.X;
+				Border.Y = border.Location.Y;
+				Border.Width = border.Size.Width;
+				Border.Height = border.Size.Height;
+				Border.SetNeedsLayout ();
+				Border.LayoutSubviews ();
+				Border.SetNeedsDisplay ();
+			}
+
+			var padding = Border.Thickness.GetInside (Border.Frame);
+			if (padding != Padding.Frame) {
+				Padding._frame = new Rect (new Point (padding.Location.X, padding.Location.Y), padding.Size);
+				Padding.X = padding.Location.X;
+				Padding.Y = padding.Location.Y;
+				Padding.Width = padding.Size.Width;
+				Padding.Height = padding.Size.Height;
+				Padding.SetNeedsLayout ();
+				Padding.LayoutSubviews ();
+				Padding.SetNeedsDisplay ();
+			}
+		}
+
+		/// <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.
+		/// </summary>
+		/// <remarks>
+		/// Calls <see cref="OnLayoutComplete"/> (which raises the <see cref="LayoutComplete"/> event) before it returns.
+		/// </remarks>
+		public virtual void LayoutSubviews ()
+		{
+			if (!LayoutNeeded) {
+				return;
+			}
+
+			LayoutFrames ();
+
+			var oldBounds = Bounds;
+			OnLayoutStarted (new LayoutEventArgs () { OldBounds = oldBounds });
+
+			TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
+
+			// Sort out the dependencies of the X, Y, Width, Height properties
+			var nodes = new HashSet<View> ();
+			var edges = new HashSet<(View, View)> ();
+			CollectAll (this, ref nodes, ref edges);
+			var ordered = View.TopologicalSort (SuperView, nodes, edges);
+			foreach (var v in ordered) {
+				LayoutSubview (v, new Rect (GetBoundsOffset (), Bounds.Size));
+			}
+
+			// 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 && edges.Count > 0) {
+				foreach ((var from, var to) in edges) {
+					LayoutSubview (to, from.Frame);
+				}
+			}
+
+			LayoutNeeded = false;
+
+			OnLayoutComplete (new LayoutEventArgs () { OldBounds = oldBounds });
+		}
+
+		private void LayoutSubview (View v, Rect contentArea)
+		{
+			if (v.LayoutStyle == LayoutStyle.Computed) {
+				v.SetRelativeLayout (contentArea);
+			}
+
+			v.LayoutSubviews ();
+			v.LayoutNeeded = false;
+		}
+
+		bool _autoSize;
+
+		/// <summary>
+		/// Gets or sets a flag that determines whether the View will be automatically resized to fit the <see cref="Text"/> 
+		/// within <see cref="Bounds"/>
+		/// <para>
+		/// The default is <see langword="false"/>. Set to <see langword="true"/> to turn on AutoSize. If <see langword="true"/> then
+		/// <see cref="Width"/> and <see cref="Height"/> will be used if <see cref="Text"/> can fit; 
+		/// if <see cref="Text"/> won't fit the view will be resized as needed.
+		/// </para>
+		/// <para>
+		/// In addition, if <see cref="ForceValidatePosDim"/> is <see langword="true"/> the new values of <see cref="Width"/> and
+		/// <see cref="Height"/> must be of the same types of the existing one to avoid breaking the <see cref="Dim"/> settings.
+		/// </para>
+		/// </summary>
+		public virtual bool AutoSize {
+			get => _autoSize;
+			set {
+				var v = ResizeView (value);
+				TextFormatter.AutoSize = v;
+				if (_autoSize != v) {
+					_autoSize = v;
+					TextFormatter.NeedsFormat = true;
+					UpdateTextFormatterText ();
+					OnResizeNeeded ();
+				}
+			}
+		}
+
+		bool ResizeView (bool autoSize)
+		{
+			if (!autoSize) {
+				return false;
+			}
+
+			var aSize = true;
+			var nBoundsSize = GetAutoSize ();
+			if (nBoundsSize != Bounds.Size) {
+				if (ForceValidatePosDim) {
+					aSize = SetWidthHeight (nBoundsSize);
+				} else {
+					Height = nBoundsSize.Height;
+					Width = nBoundsSize.Width; // = new Rect (Bounds.X, Bounds.Y, nBoundsSize.Width, nBoundsSize.Height);
+				}
+			}
+			// BUGBUG: This call may be redundant
+			TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
+			return aSize;
+		}
+
+		/// <summary>
+		/// Resizes the View to fit the specified <see cref="Bounds"/> size.
+		/// </summary>
+		/// <param name="nBounds"></param>
+		/// <returns></returns>
+		bool SetWidthHeight (Size nBounds)
+		{
+			var aSize = false;
+			var canSizeW = TrySetWidth (nBounds.Width - GetHotKeySpecifierLength (), out var rW);
+			var canSizeH = TrySetHeight (nBounds.Height - GetHotKeySpecifierLength (false), out var rH);
+			if (canSizeW) {
+				aSize = true;
+				_width = rW;
+			}
+			if (canSizeH) {
+				aSize = true;
+				_height = rH;
+			}
+			if (aSize) {
+				Bounds = new Rect (Bounds.X, Bounds.Y, canSizeW ? rW : Bounds.Width, canSizeH ? rH : Bounds.Height);
+			}
+
+			return aSize;
+		}
+
+		/// <summary>
+		/// Gets the Frame dimensions required to fit <see cref="Text"/> using the text <see cref="Direction"/> specified by the
+		/// <see cref="TextFormatter"/> property and accounting for any <see cref="HotKeySpecifier"/> characters.
+		/// </summary>
+		/// <returns>The <see cref="Size"/> required to fit the text.</returns>
+		public Size GetAutoSize ()
+		{
+			var rect = TextFormatter.CalcRect (Bounds.X, Bounds.Y, TextFormatter.Text, TextFormatter.Direction);
+			var newWidth = rect.Size.Width - GetHotKeySpecifierLength () + Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal;
+			var newHeight = rect.Size.Height - GetHotKeySpecifierLength (false) + Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical;
+			return new Size (newWidth, newHeight);
+		}
+
+		bool IsValidAutoSize (out Size autoSize)
+		{
+			var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
+			autoSize = new Size (rect.Size.Width - GetHotKeySpecifierLength (),
+			    rect.Size.Height - GetHotKeySpecifierLength (false));
+			return !(ForceValidatePosDim && (!(Width is Dim.DimAbsolute) || !(Height is Dim.DimAbsolute))
+			    || _frame.Size.Width != rect.Size.Width - GetHotKeySpecifierLength ()
+			    || _frame.Size.Height != rect.Size.Height - GetHotKeySpecifierLength (false));
+		}
+
+		bool IsValidAutoSizeWidth (Dim width)
+		{
+			var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
+			var dimValue = width.Anchor (0);
+			return !(ForceValidatePosDim && (!(width is Dim.DimAbsolute)) || dimValue != rect.Size.Width
+			    - GetHotKeySpecifierLength ());
+		}
+
+		bool IsValidAutoSizeHeight (Dim height)
+		{
+			var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
+			var dimValue = height.Anchor (0);
+			return !(ForceValidatePosDim && (!(height is Dim.DimAbsolute)) || dimValue != rect.Size.Height
+			    - GetHotKeySpecifierLength (false));
+		}
+
+		/// <summary>
+		/// Determines if the View's <see cref="Width"/> can be set to a new value.
+		/// </summary>
+		/// <param name="desiredWidth"></param>
+		/// <param name="resultWidth">Contains the width that would result if <see cref="Width"/> were set to <paramref name="desiredWidth"/>"/> </param>
+		/// <returns><see langword="true"/> if the View's <see cref="Width"/> can be changed to the specified value. False otherwise.</returns>
+		internal bool TrySetWidth (int desiredWidth, out int resultWidth)
+		{
+			var w = desiredWidth;
+			bool canSetWidth;
+			switch (Width) {
+			case Dim.DimCombine _:
+			case Dim.DimView _:
+			case Dim.DimFill _:
+				// It's a Dim.DimCombine and so can't be assigned. Let it have it's Width anchored.
+				w = Width.Anchor (w);
+				canSetWidth = !ForceValidatePosDim;
+				break;
+			case Dim.DimFactor factor:
+				// Tries to get the SuperView Width otherwise the view Width.
+				var sw = SuperView != null ? SuperView.Frame.Width : w;
+				if (factor.IsFromRemaining ()) {
+					sw -= Frame.X;
+				}
+				w = Width.Anchor (sw);
+				canSetWidth = !ForceValidatePosDim;
+				break;
+			default:
+				canSetWidth = true;
+				break;
+			}
+			resultWidth = w;
+
+			return canSetWidth;
+		}
+
+		/// <summary>
+		/// Determines if the View's <see cref="Height"/> can be set to a new value.
+		/// </summary>
+		/// <param name="desiredHeight"></param>
+		/// <param name="resultHeight">Contains the width that would result if <see cref="Height"/> were set to <paramref name="desiredHeight"/>"/> </param>
+		/// <returns><see langword="true"/> if the View's <see cref="Height"/> can be changed to the specified value. False otherwise.</returns>
+		internal bool TrySetHeight (int desiredHeight, out int resultHeight)
+		{
+			var h = desiredHeight;
+			bool canSetHeight;
+			switch (Height) {
+			case Dim.DimCombine _:
+			case Dim.DimView _:
+			case Dim.DimFill _:
+				// It's a Dim.DimCombine and so can't be assigned. Let it have it's height anchored.
+				h = Height.Anchor (h);
+				canSetHeight = !ForceValidatePosDim;
+				break;
+			case Dim.DimFactor factor:
+				// Tries to get the SuperView height otherwise the view height.
+				var sh = SuperView != null ? SuperView.Frame.Height : h;
+				if (factor.IsFromRemaining ()) {
+					sh -= Frame.Y;
+				}
+				h = Height.Anchor (sh);
+				canSetHeight = !ForceValidatePosDim;
+				break;
+			default:
+				canSetHeight = true;
+				break;
+			}
+			resultHeight = h;
+
+			return canSetHeight;
+		}
+
+		/// <summary>
+		/// Finds which view that belong to the <paramref name="start"/> superview at the provided location.
+		/// </summary>
+		/// <param name="start">The superview where to look for.</param>
+		/// <param name="x">The column location in the superview.</param>
+		/// <param name="y">The row location in the superview.</param>
+		/// <param name="resx">The found view screen relative column location.</param>
+		/// <param name="resy">The found view screen relative row location.</param>
+		/// <returns>
+		///  The view that was found at the <praramref name="x"/> and <praramref name="y"/> coordinates.
+		///  <see langword="null"/> if no view was found.
+		/// </returns>
+		public static View FindDeepestView (View start, int x, int y, out int resx, out int resy)
+		{
+			var startFrame = start.Frame;
+
+			if (!startFrame.Contains (x, y)) {
+				resx = 0;
+				resy = 0;
+				return null;
+			}
+			if (start.InternalSubviews != null) {
+				int count = start.InternalSubviews.Count;
+				if (count > 0) {
+					var boundsOffset = start.GetBoundsOffset ();
+					var rx = x - (startFrame.X + boundsOffset.X);
+					var ry = y - (startFrame.Y + boundsOffset.Y);
+					for (int i = count - 1; i >= 0; i--) {
+						View v = start.InternalSubviews [i];
+						if (v.Visible && v.Frame.Contains (rx, ry)) {
+							var deep = FindDeepestView (v, rx, ry, out resx, out resy);
+							if (deep == null)
+								return v;
+							return deep;
+						}
+					}
+				}
+			}
+			resx = x - startFrame.X;
+			resy = y - startFrame.Y;
+			return start;
+		}
+	}
+}

+ 114 - 0
Terminal.Gui/View/ViewMouse.cs

@@ -0,0 +1,114 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using NStack;
+
+namespace Terminal.Gui {
+	public partial class View  {
+		/// <summary>
+		/// Event fired when the view receives the mouse event for the first time.
+		/// </summary>
+		public event EventHandler<MouseEventEventArgs> MouseEnter;
+
+		/// <summary>
+		/// Event fired when the view receives a mouse event for the last time.
+		/// </summary>
+		public event EventHandler<MouseEventEventArgs> MouseLeave;
+
+		/// <summary>
+		/// Event fired when a mouse event is generated.
+		/// </summary>
+		public event EventHandler<MouseEventEventArgs> MouseClick;
+
+		/// <inheritdoc/>
+		public override bool OnMouseEnter (MouseEvent mouseEvent)
+		{
+			if (!Enabled) {
+				return true;
+			}
+
+			if (!CanBeVisible (this)) {
+				return false;
+			}
+
+			var args = new MouseEventEventArgs (mouseEvent);
+			MouseEnter?.Invoke (this, args);
+
+			return args.Handled || base.OnMouseEnter (mouseEvent);
+		}
+
+		/// <inheritdoc/>
+		public override bool OnMouseLeave (MouseEvent mouseEvent)
+		{
+			if (!Enabled) {
+				return true;
+			}
+
+			if (!CanBeVisible (this)) {
+				return false;
+			}
+
+			var args = new MouseEventEventArgs (mouseEvent);
+			MouseLeave?.Invoke (this, args);
+
+			return args.Handled || base.OnMouseLeave (mouseEvent);
+		}
+
+		/// <summary>
+		/// Method invoked when a mouse event is generated
+		/// </summary>
+		/// <param name="mouseEvent"></param>
+		/// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
+		public virtual bool OnMouseEvent (MouseEvent mouseEvent)
+		{
+			if (!Enabled) {
+				return true;
+			}
+
+			if (!CanBeVisible (this)) {
+				return false;
+			}
+
+			var args = new MouseEventEventArgs (mouseEvent);
+			if (OnMouseClick (args))
+				return true;
+			if (MouseEvent (mouseEvent))
+				return true;
+
+			if (mouseEvent.Flags == MouseFlags.Button1Clicked) {
+				if (CanFocus && !HasFocus && SuperView != null) {
+					SuperView.SetFocus (this);
+					SetNeedsDisplay ();
+				}
+
+				return true;
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Invokes the MouseClick event.
+		/// </summary>
+		protected bool OnMouseClick (MouseEventEventArgs args)
+		{
+			if (!Enabled) {
+				return true;
+			}
+
+			MouseClick?.Invoke (this, args);
+			return args.Handled;
+		}
+
+		/// <summary>
+		/// Gets or sets a value indicating whether this <see cref="View"/> wants mouse position reports.
+		/// </summary>
+		/// <value><see langword="true"/> if want mouse position reports; otherwise, <see langword="false"/>.</value>
+		public virtual bool WantMousePositionReports { get; set; }
+
+		/// <summary>
+		/// Gets or sets a value indicating whether this <see cref="View"/> want continuous button pressed event.
+		/// </summary>
+		public virtual bool WantContinuousButtonPressed { get; set; }
+	}
+}

+ 723 - 0
Terminal.Gui/View/ViewSubViews.cs

@@ -0,0 +1,723 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using NStack;
+
+namespace Terminal.Gui {
+	public partial class View  {
+		static readonly IList<View> _empty = new List<View> (0).AsReadOnly ();
+
+		View _superView = null;
+
+		/// <summary>
+		/// Returns the container for this view, or null if this view has not been added to a container.
+		/// </summary>
+		/// <value>The super view.</value>
+		public virtual View SuperView {
+			get {
+				return _superView;
+			}
+			set {
+				throw new NotImplementedException ();
+			}
+		}
+
+		List<View> _subviews; // This is null, and allocated on demand.
+		/// <summary>
+		/// This returns a list of the subviews contained by this view.
+		/// </summary>
+		/// <value>The subviews.</value>
+		public IList<View> Subviews => _subviews?.AsReadOnly () ?? _empty;
+
+		// Internally, we use InternalSubviews rather than subviews, as we do not expect us
+		// to make the same mistakes our users make when they poke at the Subviews.
+		internal IList<View> InternalSubviews => _subviews ?? _empty;
+
+		/// <summary>
+		/// Returns a value indicating if this View is currently on Top (Active)
+		/// </summary>
+		public bool IsCurrentTop => Application.Current == this;
+
+		/// <summary>
+		/// Event fired when this view is added to another.
+		/// </summary>
+		public event EventHandler<SuperViewChangedEventArgs> Added;
+
+		internal bool _addingView;
+
+		/// <summary>
+		///   Adds a subview (child) to this view.
+		/// </summary>
+		/// <remarks>
+		/// The Views that have been added to this view can be retrieved via the <see cref="Subviews"/> property. 
+		/// See also <seealso cref="Remove(View)"/> <seealso cref="RemoveAll"/> 
+		/// </remarks>
+		public virtual void Add (View view)
+		{
+			if (view == null) {
+				return;
+			}
+			if (_subviews == null) {
+				_subviews = new List<View> ();
+			}
+			if (_tabIndexes == null) {
+				_tabIndexes = new List<View> ();
+			}
+			_subviews.Add (view);
+			_tabIndexes.Add (view);
+			view._superView = this;
+			if (view.CanFocus) {
+				_addingView = true;
+				if (SuperView?.CanFocus == false) {
+					SuperView._addingView = true;
+					SuperView.CanFocus = true;
+					SuperView._addingView = false;
+				}
+				CanFocus = true;
+				view._tabIndex = _tabIndexes.IndexOf (view);
+				_addingView = false;
+			}
+			if (view.Enabled && !Enabled) {
+				view._oldEnabled = true;
+				view.Enabled = false;
+			}
+			SetNeedsLayout ();
+			SetNeedsDisplay ();
+
+			OnAdded (new SuperViewChangedEventArgs (this, view));
+			if (IsInitialized && !view.IsInitialized) {
+				view.BeginInit ();
+				view.EndInit ();
+			}
+		}
+
+		/// <summary>
+		/// Adds the specified views (children) to the view.
+		/// </summary>
+		/// <param name="views">Array of one or more views (can be optional parameter).</param>
+		/// <remarks>
+		/// The Views that have been added to this view can be retrieved via the <see cref="Subviews"/> property. 
+		/// See also <seealso cref="Remove(View)"/> <seealso cref="RemoveAll"/> 
+		/// </remarks>
+		public void Add (params View [] views)
+		{
+			if (views == null) {
+				return;
+			}
+			foreach (var view in views) {
+				Add (view);
+			}
+		}
+
+		/// <summary>
+		/// Method invoked when a subview is being added to this view.
+		/// </summary>
+		/// <param name="e">Event where <see cref="ViewEventArgs.View"/> is the subview being added.</param>
+		public virtual void OnAdded (SuperViewChangedEventArgs e)
+		{
+			var view = e.Child;
+			view.IsAdded = true;
+			view.OnResizeNeeded ();
+			view._x ??= view._frame.X;
+			view._y ??= view._frame.Y;
+			view._width ??= view._frame.Width;
+			view._height ??= view._frame.Height;
+
+			view.Added?.Invoke (this, e);
+		}
+
+		/// <summary>
+		/// Gets information if the view was already added to the <see cref="SuperView"/>.
+		/// </summary>
+		public bool IsAdded { get; private set; }
+
+		/// <summary>
+		/// Event fired when this view is removed from another.
+		/// </summary>
+		public event EventHandler<SuperViewChangedEventArgs> Removed;
+
+		/// <summary>
+		///   Removes all subviews (children) added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
+		/// </summary>
+		public virtual void RemoveAll ()
+		{
+			if (_subviews == null) {
+				return;
+			}
+
+			while (_subviews.Count > 0) {
+				Remove (_subviews [0]);
+			}
+		}
+
+		/// <summary>
+		///   Removes a subview added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
+		/// </summary>
+		/// <remarks>
+		/// </remarks>
+		public virtual void Remove (View view)
+		{
+			if (view == null || _subviews == null) return;
+
+			var touched = view.Frame;
+			_subviews.Remove (view);
+			_tabIndexes.Remove (view);
+			view._superView = null;
+			view._tabIndex = -1;
+			SetNeedsLayout ();
+			SetNeedsDisplay ();
+
+			foreach (var v in _subviews) {
+				if (v.Frame.IntersectsWith (touched))
+					view.SetNeedsDisplay ();
+			}
+			OnRemoved (new SuperViewChangedEventArgs (this, view));
+			if (_focused == view) {
+				_focused = null;
+			}
+		}
+
+		/// <summary>
+		/// Method invoked when a subview is being removed from this view.
+		/// </summary>
+		/// <param name="e">Event args describing the subview being removed.</param>
+		public virtual void OnRemoved (SuperViewChangedEventArgs e)
+		{
+			var view = e.Child;
+			view.IsAdded = false;
+			view.Removed?.Invoke (this, e);
+		}
+
+
+		void PerformActionForSubview (View subview, Action<View> action)
+		{
+			if (_subviews.Contains (subview)) {
+				action (subview);
+			}
+
+			SetNeedsDisplay ();
+			subview.SetNeedsDisplay ();
+		}
+
+		/// <summary>
+		/// Brings the specified subview to the front so it is drawn on top of any other views.
+		/// </summary>
+		/// <param name="subview">The subview to send to the front</param>
+		/// <remarks>
+		///   <seealso cref="SendSubviewToBack"/>.
+		/// </remarks>
+		public void BringSubviewToFront (View subview)
+		{
+			PerformActionForSubview (subview, x => {
+				_subviews.Remove (x);
+				_subviews.Add (x);
+			});
+		}
+
+		/// <summary>
+		/// Sends the specified subview to the front so it is the first view drawn
+		/// </summary>
+		/// <param name="subview">The subview to send to the front</param>
+		/// <remarks>
+		///   <seealso cref="BringSubviewToFront(View)"/>.
+		/// </remarks>
+		public void SendSubviewToBack (View subview)
+		{
+			PerformActionForSubview (subview, x => {
+				_subviews.Remove (x);
+				_subviews.Insert (0, subview);
+			});
+		}
+
+		/// <summary>
+		/// Moves the subview backwards in the hierarchy, only one step
+		/// </summary>
+		/// <param name="subview">The subview to send backwards</param>
+		/// <remarks>
+		/// If you want to send the view all the way to the back use SendSubviewToBack.
+		/// </remarks>
+		public void SendSubviewBackwards (View subview)
+		{
+			PerformActionForSubview (subview, x => {
+				var idx = _subviews.IndexOf (x);
+				if (idx > 0) {
+					_subviews.Remove (x);
+					_subviews.Insert (idx - 1, x);
+				}
+			});
+		}
+
+		/// <summary>
+		/// Moves the subview backwards in the hierarchy, only one step
+		/// </summary>
+		/// <param name="subview">The subview to send backwards</param>
+		/// <remarks>
+		/// If you want to send the view all the way to the back use SendSubviewToBack.
+		/// </remarks>
+		public void BringSubviewForward (View subview)
+		{
+			PerformActionForSubview (subview, x => {
+				var idx = _subviews.IndexOf (x);
+				if (idx + 1 < _subviews.Count) {
+					_subviews.Remove (x);
+					_subviews.Insert (idx + 1, x);
+				}
+			});
+		}
+
+		/// <summary>
+		/// Get the top superview of a given <see cref="View"/>.
+		/// </summary>
+		/// <returns>The superview view.</returns>
+		public View GetTopSuperView (View view = null, View superview = null)
+		{
+			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;
+		}
+
+
+
+		#region Focus
+		View _focused = null;
+
+		internal enum Direction {
+			Forward,
+			Backward
+		}
+
+		/// <summary>
+		/// Event fired when the view gets focus.
+		/// </summary>
+		public event EventHandler<FocusEventArgs> Enter;
+
+		/// <summary>
+		/// Event fired when the view looses focus.
+		/// </summary>
+		public event EventHandler<FocusEventArgs> Leave;
+
+		Direction _focusDirection;
+		internal Direction FocusDirection {
+			get => SuperView?.FocusDirection ?? _focusDirection;
+			set {
+				if (SuperView != null)
+					SuperView.FocusDirection = value;
+				else
+					_focusDirection = value;
+			}
+		}
+
+
+		// BUGBUG: v2 - Seems weird that this is in View and not Responder.
+		bool _hasFocus;
+
+		/// <inheritdoc/>
+		public override bool HasFocus => _hasFocus;
+
+		void SetHasFocus (bool value, View view, bool force = false)
+		{
+			if (_hasFocus != value || force) {
+				_hasFocus = value;
+				if (value) {
+					OnEnter (view);
+				} else {
+					OnLeave (view);
+				}
+				SetNeedsDisplay ();
+			}
+
+			// Remove focus down the chain of subviews if focus is removed
+			if (!value && _focused != null) {
+				var f = _focused;
+				f.OnLeave (view);
+				f.SetHasFocus (false, view);
+				_focused = null;
+			}
+		}
+
+		/// <summary>
+		/// Event fired when the <see cref="CanFocus"/> value is being changed.
+		/// </summary>
+		public event EventHandler CanFocusChanged;
+
+		/// <inheritdoc/>
+		public override void OnCanFocusChanged () => CanFocusChanged?.Invoke (this, EventArgs.Empty);
+
+		bool _oldCanFocus;
+		/// <inheritdoc/>
+		public override bool CanFocus {
+			get => base.CanFocus;
+			set {
+				if (!_addingView && IsInitialized && SuperView?.CanFocus == false && value) {
+					throw new InvalidOperationException ("Cannot set CanFocus to true if the SuperView CanFocus is false!");
+				}
+				if (base.CanFocus != value) {
+					base.CanFocus = value;
+
+					switch (value) {
+					case false when _tabIndex > -1:
+						TabIndex = -1;
+						break;
+					case true when SuperView?.CanFocus == false && _addingView:
+						SuperView.CanFocus = true;
+						break;
+					}
+
+					if (value && _tabIndex == -1) {
+						TabIndex = SuperView != null ? SuperView._tabIndexes.IndexOf (this) : -1;
+					}
+					TabStop = value;
+
+					if (!value && SuperView?.Focused == this) {
+						SuperView._focused = null;
+					}
+					if (!value && HasFocus) {
+						SetHasFocus (false, this);
+						SuperView?.EnsureFocus ();
+						if (SuperView != null && SuperView.Focused == null) {
+							SuperView.FocusNext ();
+							if (SuperView.Focused == null) {
+								Application.Current.FocusNext ();
+							}
+							Application.BringOverlappedTopToFront ();
+						}
+					}
+					if (_subviews != null && IsInitialized) {
+						foreach (var view in _subviews) {
+							if (view.CanFocus != value) {
+								if (!value) {
+									view._oldCanFocus = view.CanFocus;
+									view._oldTabIndex = view._tabIndex;
+									view.CanFocus = false;
+									view._tabIndex = -1;
+								} else {
+									if (_addingView) {
+										view._addingView = true;
+									}
+									view.CanFocus = view._oldCanFocus;
+									view._tabIndex = view._oldTabIndex;
+									view._addingView = false;
+								}
+							}
+						}
+					}
+					OnCanFocusChanged ();
+					SetNeedsDisplay ();
+				}
+			}
+		}
+
+
+		/// <inheritdoc/>
+		public override bool OnEnter (View view)
+		{
+			var args = new FocusEventArgs (view);
+			Enter?.Invoke (this, args);
+			if (args.Handled)
+				return true;
+			if (base.OnEnter (view))
+				return true;
+
+			return false;
+		}
+
+		/// <inheritdoc/>
+		public override bool OnLeave (View view)
+		{
+			var args = new FocusEventArgs (view);
+			Leave?.Invoke (this, args);
+			if (args.Handled)
+				return true;
+			if (base.OnLeave (view))
+				return true;
+
+			return false;
+		}
+
+		/// <summary>
+		/// Returns the currently focused view inside this view, or null if nothing is focused.
+		/// </summary>
+		/// <value>The focused.</value>
+		public View Focused => _focused;
+
+		/// <summary>
+		/// Returns the most focused view in the chain of subviews (the leaf view that has the focus).
+		/// </summary>
+		/// <value>The most focused View.</value>
+		public View MostFocused {
+			get {
+				if (Focused == null)
+					return null;
+				var most = Focused.MostFocused;
+				if (most != null)
+					return most;
+				return Focused;
+			}
+		}
+
+		/// <summary>
+		/// Causes the specified subview to have focus.
+		/// </summary>
+		/// <param name="view">View.</param>
+		void SetFocus (View view)
+		{
+			if (view == null) {
+				return;
+			}
+			//Console.WriteLine ($"Request to focus {view}");
+			if (!view.CanFocus || !view.Visible || !view.Enabled) {
+				return;
+			}
+			if (_focused?._hasFocus == true && _focused == view) {
+				return;
+			}
+			if ((_focused?._hasFocus == true && _focused?.SuperView == view) || view == this) {
+
+				if (!view._hasFocus) {
+					view._hasFocus = true;
+				}
+				return;
+			}
+			// Make sure that this view is a subview
+			View c;
+			for (c = view._superView; c != null; c = c._superView)
+				if (c == this)
+					break;
+			if (c == null)
+				throw new ArgumentException ("the specified view is not part of the hierarchy of this view");
+
+			if (_focused != null)
+				_focused.SetHasFocus (false, view);
+
+			var f = _focused;
+			_focused = view;
+			_focused.SetHasFocus (true, f);
+			_focused.EnsureFocus ();
+
+			// Send focus upwards
+			if (SuperView != null) {
+				SuperView.SetFocus (this);
+			} else {
+				SetFocus (this);
+			}
+		}
+
+		/// <summary>
+		/// Causes the specified view and the entire parent hierarchy to have the focused order updated.
+		/// </summary>
+		public void SetFocus ()
+		{
+			if (!CanBeVisible (this) || !Enabled) {
+				if (HasFocus) {
+					SetHasFocus (false, this);
+				}
+				return;
+			}
+
+			if (SuperView != null) {
+				SuperView.SetFocus (this);
+			} else {
+				SetFocus (this);
+			}
+		}
+
+		/// <summary>
+		/// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, does nothing.
+		/// </summary>
+		public void EnsureFocus ()
+		{
+			if (_focused == null && _subviews?.Count > 0) {
+				if (FocusDirection == Direction.Forward) {
+					FocusFirst ();
+				} else {
+					FocusLast ();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Focuses the first focusable subview if one exists.
+		/// </summary>
+		public void FocusFirst ()
+		{
+			if (!CanBeVisible (this)) {
+				return;
+			}
+
+			if (_tabIndexes == null) {
+				SuperView?.SetFocus (this);
+				return;
+			}
+
+			foreach (var view in _tabIndexes) {
+				if (view.CanFocus && view._tabStop && view.Visible && view.Enabled) {
+					SetFocus (view);
+					return;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Focuses the last focusable subview if one exists.
+		/// </summary>
+		public void FocusLast ()
+		{
+			if (!CanBeVisible (this)) {
+				return;
+			}
+
+			if (_tabIndexes == null) {
+				SuperView?.SetFocus (this);
+				return;
+			}
+
+			for (var i = _tabIndexes.Count; i > 0;) {
+				i--;
+
+				var v = _tabIndexes [i];
+				if (v.CanFocus && v._tabStop && v.Visible && v.Enabled) {
+					SetFocus (v);
+					return;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Focuses the previous view.
+		/// </summary>
+		/// <returns><see langword="true"/> if previous was focused, <see langword="false"/> otherwise.</returns>
+		public bool FocusPrev ()
+		{
+			if (!CanBeVisible (this)) {
+				return false;
+			}
+
+			FocusDirection = Direction.Backward;
+			if (_tabIndexes == null || _tabIndexes.Count == 0)
+				return false;
+
+			if (_focused == null) {
+				FocusLast ();
+				return _focused != null;
+			}
+
+			var focusedIdx = -1;
+			for (var i = _tabIndexes.Count; i > 0;) {
+				i--;
+				var w = _tabIndexes [i];
+
+				if (w.HasFocus) {
+					if (w.FocusPrev ())
+						return true;
+					focusedIdx = i;
+					continue;
+				}
+				if (w.CanFocus && focusedIdx != -1 && w._tabStop && w.Visible && w.Enabled) {
+					_focused.SetHasFocus (false, w);
+
+					if (w.CanFocus && w._tabStop && w.Visible && w.Enabled)
+						w.FocusLast ();
+
+					SetFocus (w);
+					return true;
+				}
+			}
+			if (_focused != null) {
+				_focused.SetHasFocus (false, this);
+				_focused = null;
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Focuses the next view.
+		/// </summary>
+		/// <returns><see langword="true"/> if next was focused, <see langword="false"/> otherwise.</returns>
+		public bool FocusNext ()
+		{
+			if (!CanBeVisible (this)) {
+				return false;
+			}
+
+			FocusDirection = Direction.Forward;
+			if (_tabIndexes == null || _tabIndexes.Count == 0)
+				return false;
+
+			if (_focused == null) {
+				FocusFirst ();
+				return _focused != null;
+			}
+			var focusedIdx = -1;
+			for (var i = 0; i < _tabIndexes.Count; i++) {
+				var w = _tabIndexes [i];
+
+				if (w.HasFocus) {
+					if (w.FocusNext ())
+						return true;
+					focusedIdx = i;
+					continue;
+				}
+				if (w.CanFocus && focusedIdx != -1 && w._tabStop && w.Visible && w.Enabled) {
+					_focused.SetHasFocus (false, w);
+
+					if (w.CanFocus && w._tabStop && w.Visible && w.Enabled)
+						w.FocusFirst ();
+
+					SetFocus (w);
+					return true;
+				}
+			}
+			if (_focused != null) {
+				_focused.SetHasFocus (false, this);
+				_focused = null;
+			}
+			return false;
+		}
+
+		View GetMostFocused (View view)
+		{
+			if (view == null) {
+				return null;
+			}
+
+			return view._focused != null ? GetMostFocused (view._focused) : view;
+		}
+
+		/// <summary>
+		///   Positions the cursor in the right position based on the currently focused view in the chain.
+		/// </summary>
+		///    Views that are focusable should override <see cref="PositionCursor"/> to ensure
+		///    the cursor is placed in a location that makes sense. Unix terminals do not have
+		///    a way of hiding the cursor, so it can be distracting to have the cursor left at
+		///    the last focused view. Views should make sure that they place the cursor
+		///    in a visually sensible place.
+		public virtual void PositionCursor ()
+		{
+			if (!CanBeVisible (this) || !Enabled) {
+				return;
+			}
+
+			// BUGBUG: v2 - This needs to support children of Frames too
+
+			if (_focused == null && SuperView != null) {
+				SuperView.EnsureFocus ();
+			} else if (_focused?.Visible == true && _focused?.Enabled == true && _focused?.Frame.Width > 0 && _focused.Frame.Height > 0) {
+				_focused.PositionCursor ();
+			} else if (_focused?.Visible == true && _focused?.Enabled == false) {
+				_focused = null;
+			} else if (CanFocus && HasFocus && Visible && Frame.Width > 0 && Frame.Height > 0) {
+				Move (TextFormatter.HotKeyPos == -1 ? 0 : TextFormatter.CursorPosition, 0);
+			} else {
+				Move (_frame.X, _frame.Y);
+			}
+		}
+		#endregion Focus
+	}
+}

+ 196 - 0
Terminal.Gui/View/ViewText.cs

@@ -0,0 +1,196 @@
+using NStack;
+using System;
+
+namespace Terminal.Gui {
+
+	public partial class View {
+		ustring _text;
+
+		/// <summary>
+		///   The text displayed by the <see cref="View"/>.
+		/// </summary>
+		/// <remarks>
+		/// <para>
+		///  The text will be drawn before any subviews are drawn.
+		/// </para>
+		/// <para>
+		///  The text will be drawn starting at the view origin (0, 0) and will be formatted according
+		///  to <see cref="TextAlignment"/> and <see cref="TextDirection"/>. 
+		/// </para>
+		/// <para>
+		///  The text will word-wrap to additional lines if it does not fit horizontally. If <see cref="Bounds"/>'s height
+		///  is 1, the text will be clipped.	
+		///  </para>
+		/// <para>
+		///  Set the <see cref="HotKeySpecifier"/> to enable hotkey support. To disable hotkey support set <see cref="HotKeySpecifier"/> to
+		///  <c>(Rune)0xffff</c>.
+		/// </para>
+		/// </remarks>
+		public virtual ustring Text {
+			get => _text;
+			set {
+				_text = value;
+				SetHotKey ();
+				UpdateTextFormatterText ();
+				//TextFormatter.Format ();
+				OnResizeNeeded ();
+
+#if DEBUG
+				if (_text != null && string.IsNullOrEmpty (Id)) {
+					Id = _text.ToString ();
+				}
+#endif
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the <see cref="Gui.TextFormatter"/> used to format <see cref="Text"/>.
+		/// </summary>
+		public TextFormatter TextFormatter { get; set; }
+
+		void TextFormatter_HotKeyChanged (object sender, KeyChangedEventArgs e)
+		{
+			HotKeyChanged?.Invoke (this, e);
+		}
+
+		/// <summary>
+		/// Can be overridden if the <see cref="Terminal.Gui.TextFormatter.Text"/> has
+		///  different format than the default.
+		/// </summary>
+		protected virtual void UpdateTextFormatterText ()
+		{
+			if (TextFormatter != null) {
+				TextFormatter.Text = _text;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
+		/// or not when <see cref="TextFormatter.WordWrap"/> is enabled. 
+		/// If <see langword="true"/> trailing spaces at the end of wrapped lines will be removed when 
+		/// <see cref="Text"/> is formatted for display. The default is <see langword="false"/>.
+		/// </summary>
+		public virtual bool PreserveTrailingSpaces {
+			get => TextFormatter.PreserveTrailingSpaces;
+			set {
+				if (TextFormatter.PreserveTrailingSpaces != value) {
+					TextFormatter.PreserveTrailingSpaces = value;
+					TextFormatter.NeedsFormat = true;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets how the View's <see cref="Text"/> is aligned horizontally when drawn. Changing this property will redisplay the <see cref="View"/>.
+		/// </summary>
+		/// <value>The text alignment.</value>
+		public virtual TextAlignment TextAlignment {
+			get => TextFormatter.Alignment;
+			set {
+				TextFormatter.Alignment = value;
+				UpdateTextFormatterText ();
+				OnResizeNeeded ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets how the View's <see cref="Text"/> is aligned vertically when drawn. Changing this property will redisplay the <see cref="View"/>.
+		/// </summary>
+		/// <value>The text alignment.</value>
+		public virtual VerticalTextAlignment VerticalTextAlignment {
+			get => TextFormatter.VerticalAlignment;
+			set {
+				TextFormatter.VerticalAlignment = value;
+				SetNeedsDisplay ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the direction of the View's <see cref="Text"/>. Changing this property will redisplay the <see cref="View"/>.
+		/// </summary>
+		/// <value>The text alignment.</value>
+		public virtual TextDirection TextDirection {
+			get => TextFormatter.Direction;
+			set {
+				UpdateTextDirection (value);
+				TextFormatter.Direction = value;
+			}
+		}
+
+		private void UpdateTextDirection (TextDirection newDirection)
+		{
+			var directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction)
+			    != TextFormatter.IsHorizontalDirection (newDirection);
+			TextFormatter.Direction = newDirection;
+
+			var isValidOldAutoSize = AutoSize && IsValidAutoSize (out var _);
+
+			UpdateTextFormatterText ();
+
+			if ((!ForceValidatePosDim && directionChanged && AutoSize)
+			    || (ForceValidatePosDim && directionChanged && AutoSize && isValidOldAutoSize)) {
+				OnResizeNeeded ();
+			} else if (directionChanged && IsAdded) {
+				SetWidthHeight (Bounds.Size);
+				SetMinWidthHeight ();
+			} else {
+				SetMinWidthHeight ();
+			}
+			TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
+			SetNeedsDisplay ();
+		}
+
+		/// <summary>
+		/// Gets the width or height of the <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/> characters 
+		/// in the <see cref="Text"/> property.
+		/// </summary>
+		/// <remarks>
+		/// Only the first hotkey specifier found in <see cref="Text"/> is supported.
+		/// </remarks>
+		/// <param name="isWidth">If <see langword="true"/> (the default) the width required for the hotkey specifier is returned. Otherwise the height is returned.</param>
+		/// <returns>The number of characters required for the <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/>. If the text direction specified
+		/// by <see cref="TextDirection"/> does not match the <paramref name="isWidth"/> parameter, <c>0</c> is returned.</returns>
+		public int GetHotKeySpecifierLength (bool isWidth = true)
+		{
+			if (isWidth) {
+				return TextFormatter.IsHorizontalDirection (TextDirection) &&
+				    TextFormatter.Text?.Contains (HotKeySpecifier) == true
+				    ? Math.Max (Rune.ColumnWidth (HotKeySpecifier), 0) : 0;
+			} else {
+				return TextFormatter.IsVerticalDirection (TextDirection) &&
+				    TextFormatter.Text?.Contains (HotKeySpecifier) == true
+				    ? Math.Max (Rune.ColumnWidth (HotKeySpecifier), 0) : 0;
+			}
+		}
+
+		/// <summary>
+		/// Gets the dimensions required for <see cref="Text"/> ignoring a <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/>.
+		/// </summary>
+		/// <returns></returns>
+		public Size GetSizeNeededForTextWithoutHotKey ()
+		{
+			return new Size (TextFormatter.Size.Width - GetHotKeySpecifierLength (),
+			    TextFormatter.Size.Height - GetHotKeySpecifierLength (false));
+		}
+
+		/// <summary>
+		/// Gets the dimensions required for <see cref="Text"/> accounting for a <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/> .
+		/// </summary>
+		/// <returns></returns>
+		public Size GetSizeNeededForTextAndHotKey ()
+		{
+			if (ustring.IsNullOrEmpty (TextFormatter.Text)) {
+
+				if (!IsInitialized) return Size.Empty;
+
+				return Bounds.Size;
+			}
+
+			// BUGBUG: This IGNORES what Text is set to, using on only the current View size. This doesn't seem to make sense.
+			// BUGBUG: This uses Frame; in v2 it should be Bounds
+			return new Size (_frame.Size.Width + GetHotKeySpecifierLength (),
+					 _frame.Size.Height + GetHotKeySpecifierLength (false));
+		}
+
+	}
+}

+ 0 - 4
Terminal.Gui/Views/AutocompleteFilepathContext.cs

@@ -17,7 +17,6 @@ namespace Terminal.Gui {
 		}
 	}
 
-
 	internal class FilepathSuggestionGenerator : ISuggestionGenerator {
 
 		FileDialogState state;
@@ -66,7 +65,6 @@ namespace Terminal.Gui {
 				.OrderBy (m => m.Length)
 				.ToArray ();
 
-
 			// nothing to suggest
 			if (validSuggestions.Length == 0 || validSuggestions [0].Length == term.Length) {
 				return Enumerable.Empty<Suggestion> ();
@@ -82,10 +80,8 @@ namespace Terminal.Gui {
 				return false;
 			}
 
-
 			return true;
 		}
 
-
 	}
 }

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

@@ -79,7 +79,7 @@ namespace Terminal.Gui {
 			}
 			if (container != null) {
 				container.Closing -= Container_Closing;
-				container.Resized -= Container_Resized;
+				container.TerminalResized -= Container_Resized;
 			}
 		}
 
@@ -93,7 +93,7 @@ namespace Terminal.Gui {
 			}
 			container = Application.Top;
 			container.Closing += Container_Closing;
-			container.Resized += Container_Resized;
+			container.TerminalResized += Container_Resized;
 			var frame = container.Frame;
 			var position = Position;
 			if (Host != null) {

+ 0 - 2
Terminal.Gui/Views/Dialog.cs

@@ -90,7 +90,6 @@ namespace Terminal.Gui {
 				LayoutButtons ();
 			};
 
-
 		}
 
 		/// <summary>
@@ -146,7 +145,6 @@ namespace Terminal.Gui {
 			Right
 		}
 
-
 		/// <summary>
 		/// Determines how the <see cref="Dialog"/> <see cref="Button"/>s are aligned along the 
 		/// bottom of the dialog. 

+ 0 - 16
Terminal.Gui/Views/FileDialog.cs

@@ -63,7 +63,6 @@ namespace Terminal.Gui {
 			'"','<','>','|','*','?',
 		};
 
-
 		/// <summary>
 		/// The UI selected <see cref="IAllowedType"/> from combo box. May be null.
 		/// </summary>
@@ -125,7 +124,6 @@ namespace Terminal.Gui {
 		/// error handling (e.g. showing a <see cref="MessageBox"/></remarks>
 		public IFileOperations FileOperationsHandler { get; set; } = new DefaultFileOperations ();
 
-
 		/// <summary>
 		/// Initializes a new instance of the <see cref="FileDialog"/> class.
 		/// </summary>
@@ -247,7 +245,6 @@ namespace Terminal.Gui {
 					CycleToNextTableEntryBeginningWith (k);
 				}
 
-
 			};
 
 			this.treeView = new TreeView<object> () {
@@ -276,7 +273,6 @@ namespace Terminal.Gui {
 				this.LayoutSubviews();
 			};
 
-
 			tbFind = new TextField {
 				X = Pos.Right (this.btnToggleSplitterCollapse) + 1,
 				Caption = Style.SearchCaption,
@@ -317,7 +313,6 @@ namespace Terminal.Gui {
 			this.tableView.Style.ShowHorizontalHeaderUnderline = true;
 			this.tableView.Style.ShowHorizontalScrollIndicators = true;
 
-
 			this.SetupTableColumns ();
 
 			this.sorter = new FileDialogSorter (this, this.tableView);
@@ -338,7 +333,6 @@ namespace Terminal.Gui {
 
 			this.treeView.KeyDown += (s, k) => {
 
-
 				var selected = treeView.SelectedObject;
 				if (selected != null) {
 					if (!treeView.CanExpand (selected) || treeView.IsExpanded (selected)) {
@@ -454,7 +448,6 @@ namespace Terminal.Gui {
 		}
 
 
-
 		/// <inheritdoc/>
 		public override bool ProcessHotKey (KeyEvent keyEvent)
 		{
@@ -592,7 +585,6 @@ namespace Terminal.Gui {
 			set => this.tableView.MultiSelect = value;
 		}
 
-
 		/// <summary>
 		/// Gets or Sets a collection of file types that the user can/must select. Only applies
 		/// when <see cref="OpenMode"/> is <see cref="OpenMode.File"/> or <see cref="OpenMode.Mixed"/>.
@@ -615,7 +607,6 @@ namespace Terminal.Gui {
 		public IReadOnlyList<string> MultiSelected { get; private set; }
 			= Enumerable.Empty<string> ().ToList ().AsReadOnly ();
 
-
 		/// <inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
@@ -792,7 +783,6 @@ namespace Terminal.Gui {
 			FinishAccept ();
 		}
 
-
 		private void Accept (IFileInfo f)
 		{
 			if (!this.IsCompatibleWithOpenMode (f.FullName, out var reason)) {
@@ -922,7 +912,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-
 		private bool TableView_KeyUp (KeyEvent keyEvent)
 		{
 			if (keyEvent.Key == Key.Backspace) {
@@ -952,7 +941,6 @@ namespace Terminal.Gui {
 			return false;
 		}
 
-
 		private void SetupTableColumns ()
 		{
 			this.dtFiles = new DataTable ();
@@ -1023,10 +1011,8 @@ namespace Terminal.Gui {
 				}
 			}
 
-
 			var stats = this.RowToStats (obj.Row);
 
-
 			if (stats.FileSystemInfo is IDirectoryInfo d) {
 				this.PushState (d, true);
 				return;
@@ -1282,7 +1268,6 @@ namespace Terminal.Gui {
 			return null;
 		}
 
-
 		private void PathChanged ()
 		{
 			// avoid re-entry
@@ -1545,7 +1530,6 @@ namespace Terminal.Gui {
 							cancel = true;
 						}
 
-
 						if (cancel || finished) {
 							break;
 						}

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

@@ -55,7 +55,6 @@ namespace Terminal.Gui {
 			Border.Data = "Border";
 		}
 
-
 		///<inheritdoc/>
 		public override bool OnEnter (View view)
 		{

+ 0 - 3
Terminal.Gui/Views/GraphView/Annotations.cs

@@ -31,7 +31,6 @@ namespace Terminal.Gui {
 		void Render (GraphView graph);
 	}
 
-
 	/// <summary>
 	/// Displays text at a given position (in screen space or graph space)
 	/// </summary>
@@ -199,7 +198,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-
 		/// <summary>
 		/// Adds an entry into the legend.  Duplicate entries are permissable
 		/// </summary>
@@ -238,7 +236,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool BeforeSeries { get; set; }
 
-
 		/// <summary>
 		/// Draws lines connecting each of the <see cref="Points"/>
 		/// </summary>

+ 0 - 5
Terminal.Gui/Views/GraphView/Axis.cs

@@ -123,7 +123,6 @@ namespace Terminal.Gui {
 		{
 		}
 
-
 		/// <summary>
 		/// Draws the horizontal axis line
 		/// </summary>
@@ -158,7 +157,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-
 		/// <summary>
 		/// Draws a horizontal axis line at the given <paramref name="x"/>, <paramref name="y"/> 
 		/// screen coordinates
@@ -312,7 +310,6 @@ namespace Terminal.Gui {
 	/// </summary>
 	public class VerticalAxis : Axis {
 
-
 		/// <summary>
 		/// Creates a new <see cref="Orientation.Vertical"/> axis
 		/// </summary>
@@ -371,7 +368,6 @@ namespace Terminal.Gui {
 			return graph.Bounds.Height;
 		}
 
-
 		/// <summary>
 		/// Draws axis <see cref="Axis.Increment"/> markers and labels
 		/// </summary>
@@ -501,7 +497,6 @@ namespace Terminal.Gui {
 		}
 	}
 
-
 	/// <summary>
 	/// A location on an axis of a <see cref="GraphView"/> that may
 	/// or may not have a label associated with it

+ 0 - 5
Terminal.Gui/Views/GraphView/GraphView.cs

@@ -27,7 +27,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public List<ISeries> Series { get; } = new List<ISeries> ();
 
-
 		/// <summary>
 		/// Elements drawn into graph after series have been drawn e.g. Legends etc
 		/// </summary>
@@ -162,7 +161,6 @@ namespace Terminal.Gui {
 
 			SetDriverColorToGraphColor ();
 
-
 			Rect drawBounds = new Rect((int)MarginLeft,0, graphScreenWidth, graphScreenHeight);
 			
 			RectangleF graphSpace = ScreenToGraphSpace (drawBounds);
@@ -208,7 +206,6 @@ namespace Terminal.Gui {
 				CellSize.X, CellSize.Y);
 		}
 
-
 		/// <summary>
 		/// Returns the section of the graph that is represented by the screen area
 		/// </summary>
@@ -289,13 +286,11 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 		}
 
-
 		#region Bresenham's line algorithm
 		// https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C.23
 
 		int ipart (decimal x) { return (int)x; }
 
-
 		decimal fpart (decimal x)
 		{
 			if (x < 0) return (1 - (x - Math.Floor (x)));

+ 0 - 3
Terminal.Gui/Views/GraphView/Series.cs

@@ -19,7 +19,6 @@ namespace Terminal.Gui {
 		void DrawSeries (GraphView graph, Rect drawBounds, RectangleF graphBounds);
 	}
 
-
 	/// <summary>
 	/// Series composed of any number of discrete data points 
 	/// </summary>
@@ -56,7 +55,6 @@ namespace Terminal.Gui {
 
 	}
 
-
 	/// <summary>
 	/// Collection of <see cref="BarSeries"/> in which bars are clustered by category
 	/// </summary>
@@ -92,7 +90,6 @@ namespace Terminal.Gui {
 				throw new ArgumentException ("Number of colors must match the number of bars", nameof (numberOfBarsPerCategory));
 			}
 
-
 			for (int i = 0; i < numberOfBarsPerCategory; i++) {
 				subSeries [i] = new BarSeries ();
 				subSeries [i].BarEvery = barsEvery;

+ 2 - 0
Terminal.Gui/Views/Line.cs

@@ -22,6 +22,7 @@ namespace Terminal.Gui {
 
 		}
 		
+		/// <inheritdoc/>
 		public override bool OnDrawFrames()
 		{
 			var screenBounds = ViewToScreen (Bounds);
@@ -39,6 +40,7 @@ namespace Terminal.Gui {
 
 		//}
 
+		/// <inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
 			OnDrawFrames ();

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

@@ -1057,7 +1057,7 @@ namespace Terminal.Gui {
 		public MenuBar () : this (new MenuBarItem [] { }) { }
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="MenuBar"/> class with the specified set of toplevel menu items.
+		/// Initializes a new instance of the <see cref="MenuBar"/> class with the specified set of Toplevel menu items.
 		/// </summary>
 		/// <param name="menus">Individual menu items; a null item will result in a separator being drawn.</param>
 		public MenuBar (MenuBarItem [] menus) : base ()

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

@@ -164,7 +164,6 @@ namespace Terminal.Gui {
 			return QueryFull (false, 0, 0, title, message, defaultButton, wrapMessage, buttons);
 		}
 
-
 		/// <summary>
 		/// Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show to the user.
 		/// </summary>

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

@@ -49,7 +49,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-
 		/// <summary>
 		/// Gets the name of the file the user selected for saving, or null
 		/// if the user canceled the <see cref="SaveDialog"/>.

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

@@ -52,7 +52,6 @@ namespace Terminal.Gui {
 			SetInitialProperties (frame);
 		}
 
-
 		/// <summary>
 		///  Initializes a new instance of the <see cref="Gui.ScrollView"/> class using <see cref="LayoutStyle.Computed"/> positioning.
 		/// </summary>

+ 0 - 23
Terminal.Gui/Views/TabView.cs

@@ -53,14 +53,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		public event EventHandler<TabChangedEventArgs> SelectedTabChanged;
 
-
 		/// <summary>
 		/// Event fired when a <see cref="TabView.Tab"/> is clicked.  Can be used to cancel navigation,
 		/// show context menu (e.g. on right click) etc.
 		/// </summary>
 		public event EventHandler<TabMouseEventArgs> TabClicked;
 
-
 		/// <summary>
 		/// The currently selected member of <see cref="Tabs"/> chosen by the user
 		/// </summary>
@@ -104,7 +102,6 @@ namespace Terminal.Gui {
 		/// <value></value>
 		public TabStyle Style { get; set; } = new TabStyle ();
 
-
 		/// <summary>
 		/// Initializes a <see cref="TabView"/> class using <see cref="LayoutStyle.Computed"/> layout.
 		/// </summary>
@@ -125,7 +122,6 @@ namespace Terminal.Gui {
 			AddCommand (Command.LeftHome, () => { SelectedTab = Tabs.FirstOrDefault (); return true; });
 			AddCommand (Command.RightEnd, () => { SelectedTab = Tabs.LastOrDefault (); return true; });
 
-
 			// Default keybindings for this view
 			AddKeyBinding (Key.CursorLeft, Command.Left);
 			AddKeyBinding (Key.CursorRight, Command.Right);
@@ -176,12 +172,10 @@ namespace Terminal.Gui {
 				tabsBar.Y = Pos.Percent (0);
 			}
 
-
 			SetNeedsDisplay ();
 		}
 
 
-
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
@@ -247,7 +241,6 @@ namespace Terminal.Gui {
 			return base.ProcessKey (keyEvent);
 		}
 
-
 		/// <summary>
 		/// Changes the <see cref="SelectedTab"/> by the given <paramref name="amount"/>.  
 		/// Positive for right, negative for left.  If no tab is currently selected then
@@ -284,7 +277,6 @@ namespace Terminal.Gui {
 			EnsureSelectedTabIsVisible ();
 		}
 
-
 		/// <summary>
 		/// Updates <see cref="TabScrollOffset"/> to be a valid index of <see cref="Tabs"/>
 		/// </summary>
@@ -332,7 +324,6 @@ namespace Terminal.Gui {
 			return Style.ShowTopLine ? 3 : 2;
 		}
 
-
 		/// <summary>
 		/// Returns which tabs to render at each x location
 		/// </summary>
@@ -376,7 +367,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-
 		/// <summary>
 		/// Adds the given <paramref name="tab"/> to <see cref="Tabs"/>
 		/// </summary>
@@ -388,7 +378,6 @@ namespace Terminal.Gui {
 				return;
 			}
 
-
 			tabs.Add (tab);
 
 			if (SelectedTab == null || andSelect) {
@@ -402,7 +391,6 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 		}
 
-
 		/// <summary>
 		/// Removes the given <paramref name="tab"/> from <see cref="Tabs"/>.
 		/// Caller is responsible for disposing the tab's hosted <see cref="Tab.View"/>
@@ -495,7 +483,6 @@ namespace Terminal.Gui {
 				RenderUnderline (tabLocations, width);
 				Driver.SetAttribute (GetNormalColor ());
 
-
 			}
 
 			/// <summary>
@@ -520,7 +507,6 @@ namespace Terminal.Gui {
 					return;
 				}
 
-
 				Move (selected.X - 1, y);
 				Driver.AddRune (host.Style.TabsOnBottom ? Driver.LLCorner : Driver.ULCorner);
 
@@ -556,7 +542,6 @@ namespace Terminal.Gui {
 				}
 
 
-
 				// clear any old text
 				Move (0, y);
 				Driver.AddStr (new string (' ', width));
@@ -585,7 +570,6 @@ namespace Terminal.Gui {
 						}
 					}
 
-
 					Driver.AddStr (toRender.TextToRender);
 					Driver.SetAttribute (GetNormalColor ());
 
@@ -613,7 +597,6 @@ namespace Terminal.Gui {
 						Driver.AddRune (Driver.HLine);
 					}
 
-
 				}
 				var selected = tabLocations.FirstOrDefault (t => t.IsSelected);
 
@@ -628,12 +611,10 @@ namespace Terminal.Gui {
 
 				Driver.AddStr (new string (' ', selected.Width));
 
-
 				Driver.AddRune (selected.X + selected.Width == width - 1 ?
 		     Driver.VLine :
 				(host.Style.TabsOnBottom ? Driver.ULCorner : Driver.LLCorner));
 
-
 				// draw scroll indicators
 
 				// if there are more tabs to the left not visible
@@ -686,7 +667,6 @@ namespace Terminal.Gui {
 					}
 				}
 
-
 				if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) &&
 				!me.Flags.HasFlag (MouseFlags.Button1DoubleClicked) &&
 				!me.Flags.HasFlag (MouseFlags.Button1TripleClicked))
@@ -696,7 +676,6 @@ namespace Terminal.Gui {
 					SetFocus ();
 				}
 
-
 				if (me.Flags.HasFlag (MouseFlags.Button1Clicked) ||
 				me.Flags.HasFlag (MouseFlags.Button1DoubleClicked) ||
 				me.Flags.HasFlag (MouseFlags.Button1TripleClicked)) {
@@ -711,7 +690,6 @@ namespace Terminal.Gui {
 						return true;
 					}
 
-
 					if (hit != null) {
 						host.SelectedTab = hit;
 						SetNeedsDisplay ();
@@ -822,7 +800,6 @@ namespace Terminal.Gui {
 			/// </summary> 
 			public bool ShowTopLine { get; set; } = true;
 
-
 			/// <summary>
 			/// True to show a solid box around the edge of the control.  Defaults to true.
 			/// </summary>

+ 0 - 2
Terminal.Gui/Views/TableView/CellActivatedEventArgs.cs

@@ -3,7 +3,6 @@ using System.Data;
 
 namespace Terminal.Gui {
 
-
 	/// <summary>
 	///  Defines the event arguments for <see cref="TableView.CellActivated"/> event
 	/// </summary>
@@ -14,7 +13,6 @@ namespace Terminal.Gui {
 		/// <value></value>
 		public DataTable Table { get; }
 
-
 		/// <summary>
 		/// The column index of the <see cref="Table"/> cell that is being activated
 		/// </summary>

+ 0 - 5
Terminal.Gui/Views/TableView/SelectedCellChangedEventArgs.cs

@@ -3,7 +3,6 @@ using System.Data;
 
 namespace Terminal.Gui {
 
-
 	/// <summary>
 	/// Defines the event arguments for <see cref="TableView.SelectedCellChanged"/> 
 	/// </summary>
@@ -14,28 +13,24 @@ namespace Terminal.Gui {
 		/// <value></value>
 		public DataTable Table { get; }
 
-
 		/// <summary>
 		/// The previous selected column index.  May be invalid e.g. when the selection has been changed as a result of replacing the existing Table with a smaller one
 		/// </summary>
 		/// <value></value>
 		public int OldCol { get; }
 
-
 		/// <summary>
 		/// The newly selected column index.
 		/// </summary>
 		/// <value></value>
 		public int NewCol { get; }
 
-
 		/// <summary>
 		/// The previous selected row index.  May be invalid e.g. when the selection has been changed as a result of deleting rows from the table
 		/// </summary>
 		/// <value></value>
 		public int OldRow { get; }
 
-
 		/// <summary>
 		/// The newly selected row index.
 		/// </summary>

+ 0 - 13
Terminal.Gui/Views/TableView/TableView.cs

@@ -7,7 +7,6 @@ using System.Linq;
 namespace Terminal.Gui {
 
 
-
 	/// <summary>
 	/// View for tabular data based on a <see cref="DataTable"/>.
 	/// 
@@ -31,7 +30,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public const int DefaultMaxCellWidth = 100;
 
-
 		/// <summary>
 		/// The default minimum cell width for <see cref="ColumnStyle.MinAcceptableWidth"/>
 		/// </summary>
@@ -229,7 +227,6 @@ namespace Terminal.Gui {
 			AddKeyBinding (CellActivationKey, Command.Accept);
 		}
 
-
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
@@ -704,7 +701,6 @@ namespace Terminal.Gui {
 				ClearMultiSelectedRegions (true);
 			}
 
-
 			if (extendExistingSelection) {
 
 				// If we are extending current selection but there isn't one
@@ -776,7 +772,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-
 		/// <summary>
 		/// Moves the <see cref="SelectedRow"/> and <see cref="SelectedColumn"/> by the provided offsets. Optionally starting a box selection (see <see cref="MultiSelect"/>)
 		/// </summary>
@@ -835,7 +830,6 @@ namespace Terminal.Gui {
 			Update ();
 		}
 
-
 		/// <summary>
 		/// Moves or extends the selection to the last cell in the current row
 		/// </summary>
@@ -905,7 +899,6 @@ namespace Terminal.Gui {
 				}
 			} 
 
-
 			// if there are no region selections then it is just the active cell
 
 			// if we are selecting the full row
@@ -1194,7 +1187,6 @@ namespace Terminal.Gui {
 				return null;
 			}
 
-
 			var rowIdx = RowOffset - headerHeight + clientY;
 
 			// if click is off bottom of the rows don't give an
@@ -1275,7 +1267,6 @@ namespace Terminal.Gui {
 			RowOffset = Math.Max (Math.Min (RowOffset, Table.Rows.Count - 1), 0);
 		}
 
-
 		/// <summary>
 		/// Updates <see cref="SelectedColumn"/>, <see cref="SelectedRow"/> and <see cref="MultiSelectedRegions"/> where they are outside the bounds of the table (by adjusting them to the nearest existing cell).  Has no effect if <see cref="Table"/> has not been set.
 		/// </summary>
@@ -1600,7 +1591,6 @@ namespace Terminal.Gui {
 			if (RowOffset < 0)
 				return spaceRequired;
 
-
 			for (int i = RowOffset; i < RowOffset + rowsToRender && i < Table.Rows.Count; i++) {
 
 				//expand required space if cell is bigger than the last biggest cell or header
@@ -1625,7 +1615,6 @@ namespace Terminal.Gui {
 			if (spaceRequired > MaxCellWidth)
 				spaceRequired = MaxCellWidth;
 
-
 			return spaceRequired;
 		}
 
@@ -1726,7 +1715,6 @@ namespace Terminal.Gui {
 			/// <remarks>If <see cref="MaxWidth"/> is 0 then <see cref="Visible"/> will always return false.</remarks>
 			public bool Visible { get => MaxWidth >= 0 && visible; set => visible = value; }
 
-
 			/// <summary>
 			/// Returns the alignment for the cell based on <paramref name="cellValue"/> and <see cref="AlignmentGetter"/>/<see cref="Alignment"/>
 			/// </summary>
@@ -1753,7 +1741,6 @@ namespace Terminal.Gui {
 						return f.ToString (Format, null);
 				}
 
-
 				if (RepresentationGetter != null)
 					return RepresentationGetter (value);
 

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

@@ -486,7 +486,6 @@ namespace Terminal.Gui {
 			if (SelectedLength > 0)
 				return;
 
-
 			// draw autocomplete
 			GenerateSuggestions ();
 

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

@@ -270,7 +270,6 @@ namespace Terminal.Gui {
 			/// </summary>
 			public bool ValidateOnInput { get; set; } = true;
 
-
 			bool Validate (List<Rune> text)
 			{
 				var match = regex.Match (ustring.Make (text).ToString ());

+ 3 - 6
Terminal.Gui/Views/TextView.cs

@@ -1887,7 +1887,7 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// Gets or sets a value indicating whether pressing ENTER in a <see cref="TextView"/>
-		/// creates a new line of text in the view or activates the default button for the toplevel.
+		/// creates a new line of text in the view or activates the default button for the Toplevel.
 		/// </summary>
 		public bool AllowsReturn {
 			get => allowsReturn;
@@ -2705,7 +2705,6 @@ namespace Terminal.Gui {
 			Clipboard.Contents += text;
 		}
 
-
 		/// <summary>
 		/// Inserts the given <paramref name="toAdd"/> text at the current cursor position
 		/// exactly as if the user had just typed it
@@ -2724,7 +2723,6 @@ namespace Terminal.Gui {
 					throw new ArgumentException ($"Cannot insert character '{ch}' because it does not map to a Key");
 				}
 
-
 				InsertText (new KeyEvent () { Key = key });
 			}
 
@@ -3303,7 +3301,7 @@ namespace Terminal.Gui {
 
 		bool MovePreviousView ()
 		{
-			if (Application.MdiTop != null) {
+			if (Application.OverlappedTop != null) {
 				return SuperView?.FocusPrev () == true;
 			}
 
@@ -3312,7 +3310,7 @@ namespace Terminal.Gui {
 
 		bool MoveNextView ()
 		{
-			if (Application.MdiTop != null) {
+			if (Application.OverlappedTop != null) {
 				return SuperView?.FocusNext () == true;
 			}
 
@@ -4477,7 +4475,6 @@ namespace Terminal.Gui {
 		}
 	}
 
-
 	/// <summary>
 	/// Renders an overlay on another view at a given point that allows selecting
 	/// from a range of 'autocomplete' options.

+ 1 - 4
Terminal.Gui/Views/TileView.cs

@@ -380,7 +380,6 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Overridden so no Frames get drawn (BUGBUG: v2 fix this hack)
 		/// </summary>
-		/// <param name="bounds"></param>
 		/// <returns></returns>
 		public override bool OnDrawFrames ()
 		{
@@ -612,7 +611,6 @@ namespace Terminal.Gui {
 					spaceForLast--;
 				}
 
-
 				// don't shrink if it would take us below min size of left panel
 				if (spaceForLast < tiles [idx].MinSize) {
 					return false;
@@ -781,7 +779,6 @@ namespace Terminal.Gui {
 						splitterLines [Math.Min (i, splitterLines.Count - 1)].Visible = false;
 					}
 
-
 				}
 			}
 		}
@@ -941,7 +938,7 @@ namespace Terminal.Gui {
 
 					// Start a Drag
 					SetFocus ();
-					Application.EnsuresTopOnFront ();
+					Application.BringOverlappedTopToFront ();
 
 					if (mouseEvent.Flags == MouseFlags.Button1Pressed) {
 						dragPosition = new Point (mouseEvent.X, mouseEvent.Y);

+ 45 - 122
Terminal.Gui/Views/Toplevel.cs

@@ -21,7 +21,7 @@ namespace Terminal.Gui {
 	///     call <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>.
 	///   </para>
 	/// </remarks>
-	public class Toplevel : View {
+	public partial class Toplevel : View {
 		/// <summary>
 		/// Gets or sets whether the <see cref="MainLoop"/> for this <see cref="Toplevel"/> is running or not. 
 		/// </summary>
@@ -31,18 +31,18 @@ namespace Terminal.Gui {
 		public bool Running { get; set; }
 
 		/// <summary>
-		/// Invoked when the Toplevel <see cref="Application.RunState"/> has begun to be loaded.
+		/// Invoked when the <see cref="Toplevel"/> <see cref="Application.RunState"/> has begun to be loaded.
 		/// A Loaded event handler is a good place to finalize initialization before calling 
 		/// <see cref="Application.RunLoop(Application.RunState, bool)"/>.
 		/// </summary>
 		public event EventHandler Loaded;
 
 		/// <summary>
-		/// Invoked when the Toplevel <see cref="MainLoop"/> has started it's first iteration.
+		/// Invoked when the <see cref="Toplevel"/> <see cref="MainLoop"/> has started it's first iteration.
 		/// Subscribe to this event to perform tasks when the <see cref="Toplevel"/> has been laid out and focus has been set.
 		/// changes. 
 		/// <para>A Ready event handler is a good place to finalize initialization after calling 
-		/// <see cref="Application.Run(Func{Exception, bool})"/> on this Toplevel.</para>
+		/// <see cref="Application.Run(Func{Exception, bool})"/> on this <see cref="Toplevel"/>.</para>
 		/// </summary>
 		public event EventHandler Ready;
 
@@ -98,11 +98,11 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Invoked when the terminal has been resized. The new <see cref="Size"/> of the terminal is provided.
 		/// </summary>
-		public event EventHandler<SizeChangedEventArgs> Resized;
+		public event EventHandler<SizeChangedEventArgs> TerminalResized;
 
-		internal virtual void OnResized (SizeChangedEventArgs size)
+		internal virtual void OnTerminalResized (SizeChangedEventArgs size)
 		{
-			Resized?.Invoke (this, size);
+			TerminalResized?.Invoke (this, size);
 		}
 
 		internal virtual void OnChildUnloaded (Toplevel top)
@@ -133,7 +133,7 @@ namespace Terminal.Gui {
 
 		internal virtual void OnChildClosed (Toplevel top)
 		{
-			if (IsMdiContainer) {
+			if (IsOverlappedContainer) {
 				SetSubViewNeedsDisplay ();
 			}
 			ChildClosed?.Invoke (this, new ToplevelEventArgs (top));
@@ -187,7 +187,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Toplevel"/> class with the specified <see cref="LayoutStyle.Absolute"/> layout.
 		/// </summary>
-		/// <param name="frame">A superview-relative rectangle specifying the location and size for the new Toplevel</param>
+		/// <param name="frame">A Superview-relative rectangle specifying the location and size for the new Toplevel</param>
 		public Toplevel (Rect frame) : base (frame)
 		{
 			SetInitialProperties ();
@@ -252,14 +252,14 @@ namespace Terminal.Gui {
 
 		private void Application_UnGrabbingMouse (object sender, GrabMouseEventArgs e)
 		{
-			if (Application.MouseGrabView == this && dragPosition.HasValue) {
+			if (Application.MouseGrabView == this && _dragPosition.HasValue) {
 				e.Cancel = true;
 			}
 		}
 
 		private void Application_GrabbingMouse (object sender, GrabMouseEventArgs e)
 		{
-			if (Application.MouseGrabView == this && dragPosition.HasValue) {
+			if (Application.MouseGrabView == this && _dragPosition.HasValue) {
 				e.Cancel = true;
 			}
 		}
@@ -362,20 +362,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public virtual StatusBar StatusBar { get; set; }
 
-		/// <summary>
-		/// Gets or sets if this Toplevel is a Mdi container.
-		/// </summary>
-		public bool IsMdiContainer { get; set; }
-
-		/// <summary>
-		/// Gets or sets if this Toplevel is a Mdi child.
-		/// </summary>
-		public bool IsMdiChild {
-			get {
-				return Application.MdiTop != null && Application.MdiTop != this && !Modal;
-			}
-		}
-
 		/// <summary>
 		/// <see langword="true"/> if was already loaded by the <see cref="Application.Begin(Toplevel)"/>
 		/// <see langword="false"/>, otherwise.
@@ -443,31 +429,31 @@ namespace Terminal.Gui {
 
 		private void MovePreviousViewOrTop ()
 		{
-			if (Application.MdiTop == null) {
+			if (Application.OverlappedTop == null) {
 				var top = Modal ? this : Application.Top;
 				top.FocusPrev ();
 				if (top.Focused == null) {
 					top.FocusPrev ();
 				}
 				top.SetNeedsDisplay ();
-				Application.EnsuresTopOnFront ();
+				Application.BringOverlappedTopToFront ();
 			} else {
-				MovePrevious ();
+				Application.OverlappedMovePrevious ();
 			}
 		}
 
 		private void MoveNextViewOrTop ()
 		{
-			if (Application.MdiTop == null) {
+			if (Application.OverlappedTop == null) {
 				var top = Modal ? this : Application.Top;
 				top.FocusNext ();
 				if (top.Focused == null) {
 					top.FocusNext ();
 				}
 				top.SetNeedsDisplay ();
-				Application.EnsuresTopOnFront ();
+				Application.BringOverlappedTopToFront ();
 			} else {
-				MoveNext ();
+				Application.OverlappedMoveNext ();
 			}
 		}
 
@@ -499,8 +485,8 @@ namespace Terminal.Gui {
 
 		private void QuitToplevel ()
 		{
-			if (Application.MdiTop != null) {
-				Application.MdiTop.RequestStop ();
+			if (Application.OverlappedTop != null) {
+				Application.OverlappedTop.RequestStop ();
 			} else {
 				Application.RequestStop ();
 			}
@@ -584,7 +570,7 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Remove (View view)
 		{
-			if (this is Toplevel toplevel && toplevel.MenuBar != null) {
+			if (this is Toplevel Toplevel && Toplevel.MenuBar != null) {
 				RemoveMenuStatusBar (view);
 			}
 			base.Remove (view);
@@ -728,7 +714,7 @@ namespace Terminal.Gui {
 				out int nx, out int ny, out _, out StatusBar sb);
 			bool layoutSubviews = false;
 			if ((superView != top || top?.SuperView != null || (top != Application.Top && top.Modal)
-				|| (top?.SuperView == null && top.IsMdiChild))
+				|| (top?.SuperView == null && top.IsOverlapped))
 				&& (top.Frame.X + top.Frame.Width > Driver.Cols || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
 
 				if ((top.X == null || top.X is Pos.PosAbsolute) && top.Frame.X != nx) {
@@ -770,8 +756,8 @@ namespace Terminal.Gui {
 				LayoutSubviews ();
 				PositionToplevels ();
 
-				if (this == Application.MdiTop) {
-					foreach (var top in Application.MdiChildren.AsEnumerable ().Reverse ()) {
+				if (this == Application.OverlappedTop) {
+					foreach (var top in Application.OverlappedChildren.AsEnumerable ().Reverse ()) {
 						if (top.Frame.IntersectsWith (bounds)) {
 							if (top != this && !top.IsCurrentTop && !OutsideTopFrame (top) && top.Visible) {
 								top.SetNeedsLayout ();
@@ -806,8 +792,8 @@ namespace Terminal.Gui {
 			return false;
 		}
 
-		internal static Point? dragPosition;
-		Point start;
+		internal static Point? _dragPosition;
+		Point _startGrabPoint;
 
 		///<inheritdoc/>
 		public override bool MouseEvent (MouseEvent mouseEvent)
@@ -819,20 +805,20 @@ namespace Terminal.Gui {
 			//System.Diagnostics.Debug.WriteLine ($"dragPosition before: {dragPosition.HasValue}");
 
 			int nx, ny;
-			if (!dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed
+			if (!_dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed
 				|| mouseEvent.Flags == MouseFlags.Button2Pressed
 				|| mouseEvent.Flags == MouseFlags.Button3Pressed)) {
 
 				SetFocus ();
-				Application.EnsuresTopOnFront ();
+				Application.BringOverlappedTopToFront ();
 
 				// Only start grabbing if the user clicks on the title bar.
 				if (mouseEvent.Y == 0 && mouseEvent.Flags == MouseFlags.Button1Pressed) {
-					start = new Point (mouseEvent.X, mouseEvent.Y);
-					dragPosition = new Point ();
+					_startGrabPoint = new Point (mouseEvent.X, mouseEvent.Y);
+					_dragPosition = new Point ();
 					nx = mouseEvent.X - mouseEvent.OfX;
 					ny = mouseEvent.Y - mouseEvent.OfY;
-					dragPosition = new Point (nx, ny);
+					_dragPosition = new Point (nx, ny);
 					Application.GrabMouse (this);
 				}
 
@@ -840,7 +826,7 @@ namespace Terminal.Gui {
 				return true;
 			} else if (mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
 				mouseEvent.Flags == MouseFlags.Button3Pressed) {
-				if (dragPosition.HasValue) {
+				if (_dragPosition.HasValue) {
 					if (SuperView == null) {
 						// Redraw the entire app window using just our Frame. Since we are 
 						// Application.Top, and our Frame always == our Bounds (Location is always (0,0))
@@ -851,11 +837,11 @@ namespace Terminal.Gui {
 					} else {
 						SuperView.SetNeedsDisplay ();
 					}
-					GetLocationThatFits (this, mouseEvent.X + (SuperView == null ? mouseEvent.OfX - start.X : Frame.X - start.X),
-						mouseEvent.Y + (SuperView == null ? mouseEvent.OfY - start.Y : Frame.Y - start.Y),
+					GetLocationThatFits (this, mouseEvent.X + (SuperView == null ? mouseEvent.OfX - _startGrabPoint.X : Frame.X - _startGrabPoint.X),
+						mouseEvent.Y + (SuperView == null ? mouseEvent.OfY - _startGrabPoint.Y : Frame.Y - _startGrabPoint.Y),
 						out nx, out ny, out _, out _);
 
-					dragPosition = new Point (nx, ny);
+					_dragPosition = new Point (nx, ny);
 					X = nx;
 					Y = ny;
 					//System.Diagnostics.Debug.WriteLine ($"Drag: nx:{nx},ny:{ny}");
@@ -865,8 +851,8 @@ namespace Terminal.Gui {
 				}
 			}
 
-			if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && dragPosition.HasValue) {
-				dragPosition = null;
+			if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && _dragPosition.HasValue) {
+				_dragPosition = null;
 				Application.UngrabMouse ();
 			}
 
@@ -875,43 +861,18 @@ namespace Terminal.Gui {
 			return false;
 		}
 
-		/// <summary>
-		/// Invoked by <see cref="Application.Begin"/> as part of  <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> 
-		/// after the views have been laid out, and before the views are drawn for the first time.
-		/// </summary>
-		public virtual void WillPresent ()
-		{
-			FocusFirst ();
-		}
-
-		/// <summary>
-		/// Move to the next Mdi child from the <see cref="Application.MdiTop"/>.
-		/// </summary>
-		public virtual void MoveNext ()
-		{
-			Application.MoveNext ();
-		}
-
-		/// <summary>
-		/// Move to the previous Mdi child from the <see cref="Application.MdiTop"/>.
-		/// </summary>
-		public virtual void MovePrevious ()
-		{
-			Application.MovePrevious ();
-		}
-
 		/// <summary>
 		/// Stops and closes this <see cref="Toplevel"/>. If this Toplevel is the top-most Toplevel, 
 		/// <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to exit.
 		/// </summary>
 		public virtual void RequestStop ()
 		{
-			if (IsMdiContainer && Running
+			if (IsOverlappedContainer && Running
 				&& (Application.Current == this
 				|| Application.Current?.Modal == false
 				|| Application.Current?.Modal == true && Application.Current?.Running == false)) {
 
-				foreach (var child in Application.MdiChildren) {
+				foreach (var child in Application.OverlappedChildren) {
 					var ev = new ToplevelClosingEventArgs (this);
 					if (child.OnClosing (ev)) {
 						return;
@@ -921,13 +882,13 @@ namespace Terminal.Gui {
 				}
 				Running = false;
 				Application.RequestStop (this);
-			} else if (IsMdiContainer && Running && Application.Current?.Modal == true && Application.Current?.Running == true) {
+			} else if (IsOverlappedContainer && Running && Application.Current?.Modal == true && Application.Current?.Running == true) {
 				var ev = new ToplevelClosingEventArgs (Application.Current);
 				if (OnClosing (ev)) {
 					return;
 				}
 				Application.RequestStop (Application.Current);
-			} else if (!IsMdiContainer && Running && (!Modal || (Modal && Application.Current != this))) {
+			} else if (!IsOverlappedContainer && Running && (!Modal || (Modal && Application.Current != this))) {
 				var ev = new ToplevelClosingEventArgs (this);
 				if (OnClosing (ev)) {
 					return;
@@ -943,7 +904,7 @@ namespace Terminal.Gui {
 		/// Stops and closes the <see cref="Toplevel"/> specified by <paramref name="top"/>. If <paramref name="top"/> is the top-most Toplevel, 
 		/// <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to exit.
 		/// </summary>
-		/// <param name="top">The toplevel to request stop.</param>
+		/// <param name="top">The Toplevel to request stop.</param>
 		public virtual void RequestStop (Toplevel top)
 		{
 			top.RequestStop ();
@@ -952,7 +913,7 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void PositionCursor ()
 		{
-			if (!IsMdiContainer) {
+			if (!IsOverlappedContainer) {
 				base.PositionCursor ();
 				if (Focused == null) {
 					EnsureFocus ();
@@ -964,7 +925,7 @@ namespace Terminal.Gui {
 			}
 
 			if (Focused == null) {
-				foreach (var top in Application.MdiChildren) {
+				foreach (var top in Application.OverlappedChildren) {
 					if (top != this && top.Visible) {
 						top.SetFocus ();
 						return;
@@ -977,44 +938,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-		/// <summary>
-		/// Gets the current visible Toplevel Mdi child that matches the arguments pattern.
-		/// </summary>
-		/// <param name="type">The type.</param>
-		/// <param name="exclude">The strings to exclude.</param>
-		/// <returns>The matched view.</returns>
-		public View GetTopMdiChild (Type type = null, string [] exclude = null)
-		{
-			if (Application.MdiTop == null) {
-				return null;
-			}
-
-			foreach (var top in Application.MdiChildren) {
-				if (type != null && top.GetType () == type
-					&& exclude?.Contains (top.Data.ToString ()) == false) {
-					return top;
-				} else if ((type != null && top.GetType () != type)
-					|| (exclude?.Contains (top.Data.ToString ()) == true)) {
-					continue;
-				}
-				return top;
-			}
-			return null;
-		}
-
-		/// <summary>
-		/// Shows the Mdi child indicated by <paramref name="top"/>, setting it as <see cref="Application.Current"/>.
-		/// </summary>
-		/// <param name="top">The Toplevel.</param>
-		/// <returns><c>true</c> if the toplevel can be shown or <c>false</c> if not.</returns>
-		public virtual bool ShowChild (Toplevel top = null)
-		{
-			if (Application.MdiTop != null) {
-				return Application.ShowChild (top == null ? this : top);
-			}
-			return false;
-		}
-
 		///<inheritdoc/>
 		public override bool OnEnter (View view)
 		{
@@ -1030,7 +953,7 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		protected override void Dispose (bool disposing)
 		{
-			dragPosition = null;
+			_dragPosition = null;
 			base.Dispose (disposing);
 		}
 	}
@@ -1077,7 +1000,7 @@ namespace Terminal.Gui {
 
 	/// <summary>
 	/// Implements the <see cref="IComparer{T}"/> to sort the <see cref="Toplevel"/> 
-	/// from the <see cref="Application.MdiChildren"/> if needed.
+	/// from the <see cref="Application.OverlappedChildren"/> if needed.
 	/// </summary>
 	public sealed class ToplevelComparer : IComparer<Toplevel> {
 		/// <summary>Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.</summary>

+ 5 - 5
Terminal.Gui/Views/ToplevelEventArgs.cs

@@ -9,10 +9,10 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Creates a new instance of the <see cref="ToplevelClosingEventArgs"/> class.
 		/// </summary>
-		/// <param name="toplevel"></param>
-		public ToplevelEventArgs (Toplevel toplevel)
+		/// <param name="Toplevel"></param>
+		public ToplevelEventArgs (Toplevel Toplevel)
 		{
-			Toplevel = toplevel;
+			Toplevel = Toplevel;
 		}
 
 		/// <summary>
@@ -31,7 +31,7 @@ namespace Terminal.Gui {
 	/// </summary>
 	public class ToplevelClosingEventArgs : EventArgs {
 		/// <summary>
-		/// The toplevel requesting stop.
+		/// The Toplevel requesting stop.
 		/// </summary>
 		public View RequestingTop { get; }
 		/// <summary>
@@ -40,7 +40,7 @@ namespace Terminal.Gui {
 		public bool Cancel { get; set; }
 
 		/// <summary>
-		/// Initializes the event arguments with the requesting toplevel.
+		/// Initializes the event arguments with the requesting Toplevel.
 		/// </summary>
 		/// <param name="requestingTop">The <see cref="RequestingTop"/>.</param>
 		public ToplevelClosingEventArgs (Toplevel requestingTop)

+ 226 - 0
Terminal.Gui/Views/ToplevelOverlapped.cs

@@ -0,0 +1,226 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+
+namespace Terminal.Gui {
+	public partial class Toplevel {
+		/// <summary>
+		/// Gets or sets if this Toplevel is a container for overlapped children.
+		/// </summary>
+		public bool IsOverlappedContainer { get; set; }
+
+		/// <summary>
+		/// Gets or sets if this Toplevel is in overlapped mode within a Toplevel container.
+		/// </summary>
+		public bool IsOverlapped {
+			get {
+				return Application.OverlappedTop != null && Application.OverlappedTop != this && !Modal;
+			}
+		}
+
+	}
+
+	public static partial class Application {
+
+		/// <summary>
+		/// Gets the list of the Overlapped children which are not modal <see cref="Toplevel"/> from the <see cref="OverlappedTop"/>.
+		/// </summary>
+		public static List<Toplevel> OverlappedChildren {
+			get {
+				if (OverlappedTop != null) {
+					List<Toplevel> _overlappedChildren = new List<Toplevel> ();
+					foreach (var top in _toplevels) {
+						if (top != OverlappedTop && !top.Modal) {
+							_overlappedChildren.Add (top);
+						}
+					}
+					return _overlappedChildren;
+				}
+				return null;
+			}
+		}
+
+		/// <summary>
+		/// The <see cref="Toplevel"/> object used for the application on startup which <see cref="Toplevel.IsOverlappedContainer"/> is true.
+		/// </summary>
+		public static Toplevel OverlappedTop {
+			get {
+				if (Top.IsOverlappedContainer) {
+					return Top;
+				}
+				return null;
+			}
+		}
+
+
+		static View FindDeepestOverlappedView (View start, int x, int y, out int resx, out int resy)
+		{
+			if (start.GetType ().BaseType != typeof (Toplevel)
+				&& !((Toplevel)start).IsOverlappedContainer) {
+				resx = 0;
+				resy = 0;
+				return null;
+			}
+
+			var startFrame = start.Frame;
+
+			if (!startFrame.Contains (x, y)) {
+				resx = 0;
+				resy = 0;
+				return null;
+			}
+
+			int count = _toplevels.Count;
+			for (int i = count - 1; i >= 0; i--) {
+				foreach (var top in _toplevels) {
+					var rx = x - startFrame.X;
+					var ry = y - startFrame.Y;
+					if (top.Visible && top.Frame.Contains (rx, ry)) {
+						var deep = View.FindDeepestView (top, rx, ry, out resx, out resy);
+						if (deep == null)
+							return FindDeepestOverlappedView (top, rx, ry, out resx, out resy);
+						if (deep != OverlappedTop)
+							return deep;
+					}
+				}
+			}
+			resx = x - startFrame.X;
+			resy = y - startFrame.Y;
+			return start;
+		}
+
+		static bool OverlappedChildNeedsDisplay ()
+		{
+			if (OverlappedTop == null) {
+				return false;
+			}
+
+			foreach (var top in _toplevels) {
+				if (top != Current && top.Visible && (!top._needsDisplay.IsEmpty || top._childNeedsDisplay || top.LayoutNeeded)) {
+					OverlappedTop.SetSubViewNeedsDisplay ();
+					return true;
+				}
+			}
+			return false;
+		}
+
+
+		static bool SetCurrentOverlappedAsTop ()
+		{
+			if (OverlappedTop == null && Current != Top && Current?.SuperView == null && Current?.Modal == false) {
+				if (Current.Frame != new Rect (0, 0, Driver.Cols, Driver.Rows)) {
+					Current.Frame = new Rect (0, 0, Driver.Cols, Driver.Rows);
+				}
+				Top = Current;
+				return true;
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Move to the next Overlapped child from the <see cref="OverlappedTop"/>.
+		/// </summary>
+		public static void OverlappedMoveNext ()
+		{
+			if (OverlappedTop != null && !Current.Modal) {
+				lock (_toplevels) {
+					_toplevels.MoveNext ();
+					var isOverlapped = false;
+					while (_toplevels.Peek () == OverlappedTop || !_toplevels.Peek ().Visible) {
+						if (!isOverlapped && _toplevels.Peek () == OverlappedTop) {
+							isOverlapped = true;
+						} else if (isOverlapped && _toplevels.Peek () == OverlappedTop) {
+							MoveCurrent (Top);
+							break;
+						}
+						_toplevels.MoveNext ();
+					}
+					Current = _toplevels.Peek ();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Move to the previous Overlapped child from the <see cref="OverlappedTop"/>.
+		/// </summary>
+		public static void OverlappedMovePrevious ()
+		{
+			if (OverlappedTop != null && !Current.Modal) {
+				lock (_toplevels) {
+					_toplevels.MovePrevious ();
+					var isOverlapped = false;
+					while (_toplevels.Peek () == OverlappedTop || !_toplevels.Peek ().Visible) {
+						if (!isOverlapped && _toplevels.Peek () == OverlappedTop) {
+							isOverlapped = true;
+						} else if (isOverlapped && _toplevels.Peek () == OverlappedTop) {
+							MoveCurrent (Top);
+							break;
+						}
+						_toplevels.MovePrevious ();
+					}
+					Current = _toplevels.Peek ();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Move to the next Overlapped child from the <see cref="OverlappedTop"/> and set it as the <see cref="Top"/> if it is not already.
+		/// </summary>
+		/// <param name="top"></param>
+		/// <returns></returns>
+		public static bool MoveToOverlappedChild (Toplevel top)
+		{
+			if (top.Visible && OverlappedTop != null && Current?.Modal == false) {
+				lock (_toplevels) {
+					_toplevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
+					Current = top;
+				}
+				return true;
+			}
+			return false;
+		}
+
+
+		/// <summary>
+		/// Brings the superview of the most focused overlapped view is on front.
+		/// </summary>
+		public static void BringOverlappedTopToFront ()
+		{
+			if (OverlappedTop != null) {
+				return;
+			}
+			var top = FindTopFromView (Top?.MostFocused);
+			if (top != null && Top.Subviews.Count > 1 && Top.Subviews [Top.Subviews.Count - 1] != top) {
+				Top.BringSubviewToFront (top);
+			}
+		}
+
+
+		/// <summary>
+		/// Gets the current visible Toplevel overlapped child that matches the arguments pattern.
+		/// </summary>
+		/// <param name="type">The type.</param>
+		/// <param name="exclude">The strings to exclude.</param>
+		/// <returns>The matched view.</returns>
+		public static Toplevel GetTopOverlappedChild (Type type = null, string [] exclude = null)
+		{
+			if (Application.OverlappedTop == null) {
+				return null;
+			}
+
+			foreach (var top in Application.OverlappedChildren) {
+				if (type != null && top.GetType () == type
+					&& exclude?.Contains (top.Data.ToString ()) == false) {
+					return top;
+				} else if ((type != null && top.GetType () != type)
+					|| (exclude?.Contains (top.Data.ToString ()) == true)) {
+					continue;
+				}
+				return top;
+			}
+			return null;
+		}
+
+	}
+}

+ 0 - 2
Terminal.Gui/Views/TreeView/Branch.cs

@@ -51,7 +51,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-
 		/// <summary>
 		/// Fetch the children of this branch. This method populates <see cref="ChildBranches"/>.
 		/// </summary>
@@ -164,7 +163,6 @@ namespace Terminal.Gui {
 				availableWidth -= lineBody.Length;
 			}
 
-
 			// default behaviour is for model to use the color scheme
 			// of the tree view
 			var modelColor = textColor;

+ 0 - 1
Terminal.Gui/Views/TreeView/ObjectActivatedEventArgs.cs

@@ -17,7 +17,6 @@
 		/// <value></value>
 		public T ActivatedObject { get; }
 
-
 		/// <summary>
 		/// Creates a new instance documenting activation of the <paramref name="activated"/> object
 		/// </summary>

+ 0 - 3
Terminal.Gui/Views/TreeView/TreeView.cs

@@ -614,7 +614,6 @@ namespace Terminal.Gui {
 			return base.ProcessKey (keyEvent);
 		}
 
-
 		/// <summary>
 		/// <para>Triggers the <see cref="ObjectActivated"/> event with the <see cref="SelectedObject"/>.</para>
 		/// 
@@ -1079,7 +1078,6 @@ namespace Terminal.Gui {
 			GoToEnd ();
 		}
 
-
 		/// <summary>
 		/// Sets the selection to the next branch that matches the <paramref name="predicate"/>.
 		/// </summary>
@@ -1131,7 +1129,6 @@ namespace Terminal.Gui {
 				return;
 			}
 
-
 			/*this -1 allows for possible horizontal scroll bar in the last row of the control*/
 			int leaveSpace = Style.LeaveLastRow ? 1 : 0;
 

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

@@ -68,7 +68,6 @@ namespace Terminal.Gui {
 			AddMenuStatusBar (view);
 		}
 
-
 		/// <inheritdoc/>
 		public override void Remove (View view)
 		{

+ 1 - 2
Terminal.Gui/Views/Wizard/Wizard.cs

@@ -127,7 +127,6 @@ namespace Terminal.Gui {
 			/// <summary>
 			/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
 			/// </summary>
-			/// </remarks>
 			public WizardStep ()
 			{
 				BorderStyle = LineStyle.None;
@@ -700,7 +699,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Determines whether the <see cref="Wizard"/> is displayed as modal pop-up or not.
 		/// 
-		/// The default is <c>true</c>. The Wizard will be shown with a frame with <see cref="Title"/> and will behave like
+		/// The default is <see langword="true"/>. The Wizard will be shown with a frame and title and will behave like
 		/// any <see cref="Toplevel"/> window.
 		/// 
 		/// If set to <c>false</c> the Wizard will have no frame and will behave like any embedded <see cref="View"/>.

+ 1 - 0
Terminal.sln

@@ -14,6 +14,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "Example\Example.
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E143FB1F-0B88-48CB-9086-72CDCECFCD22}"
 	ProjectSection(SolutionItems) = preProject
+		.editorconfig = .editorconfig
 		.gitignore = .gitignore
 		CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
 		CONTRIBUTING.md = CONTRIBUTING.md

+ 0 - 2
UICatalog/KeyBindingsDialog.cs

@@ -7,10 +7,8 @@ using Terminal.Gui;
 
 namespace UICatalog {
 
-
 	class KeyBindingsDialog : Dialog {
 
-
 		static Dictionary<Command,Key> CurrentBindings = new Dictionary<Command,Key>();
 		private Command[] commands;
 		private ListView commandsListView;

+ 0 - 1
UICatalog/Scenarios/AllViewsTester.cs

@@ -49,7 +49,6 @@ namespace UICatalog.Scenarios {
 			Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 		}
 
-
 		public override void Setup ()
 		{
 			var statusBar = new StatusBar (new StatusItem [] {

+ 0 - 3
UICatalog/Scenarios/Animation.cs

@@ -23,7 +23,6 @@ namespace UICatalog.Scenarios {
 		{
 			base.Setup ();
 
-
 			var imageView = new ImageView () {
 				Width = Dim.Fill(),
 				Height = Dim.Fill()-2,
@@ -145,7 +144,6 @@ namespace UICatalog.Scenarios {
 
 			Rect oldSize = Rect.Empty;
 
-
 			internal void SetImage (Image<Rgba32> image)
 			{
 				frameCount = image.Frames.Count;
@@ -201,7 +199,6 @@ namespace UICatalog.Scenarios {
 					brailleCache[currentFrame] = braille = GetBraille(matchSizes[currentFrame]);
 				}
 
-
 				var lines = braille.Split('\n');
 
 				for(int y = 0; y < lines.Length;y++)

+ 21 - 21
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -15,19 +15,19 @@ namespace UICatalog.Scenarios {
 
 		public override void Run ()
 		{
-			Application.Run<MdiMain> ();
+			Application.Run<OverlappedMain> ();
 		}
 
-		class MdiMain : Toplevel {
+		class OverlappedMain : Toplevel {
 			private WorkerApp workerApp;
 			private bool canOpenWorkerApp;
 			MenuBar menu;
 
-			public MdiMain ()
+			public OverlappedMain ()
 			{
-				Data = "MdiMain";
+				Data = "OverlappedMain";
 
-				IsMdiContainer = true;
+				IsOverlappedContainer = true;
 
 				workerApp = new WorkerApp () { Visible = false };
 
@@ -51,19 +51,19 @@ namespace UICatalog.Scenarios {
 				});
 				Add (statusBar);
 
-				Activate += MdiMain_Activate;
-				Deactivate += MdiMain_Deactivate;
+				Activate += OverlappedMain_Activate;
+				Deactivate += OverlappedMain_Deactivate;
 
-				Closed += MdiMain_Closed;
+				Closed += OverlappedMain_Closed;
 
 				Application.Iteration += () => {
-					if (canOpenWorkerApp && !workerApp.Running && Application.MdiTop.Running) {
+					if (canOpenWorkerApp && !workerApp.Running && Application.OverlappedTop.Running) {
 						Application.Run (workerApp);
 					}
 				};
 			}
 
-			private void MdiMain_Closed (object sender, ToplevelEventArgs e)
+			private void OverlappedMain_Closed (object sender, ToplevelEventArgs e)
 			{
 				workerApp.Dispose ();
 				Dispose ();
@@ -82,12 +82,12 @@ namespace UICatalog.Scenarios {
 				}
 			}
 
-			private void MdiMain_Deactivate (object sender, ToplevelEventArgs top)
+			private void OverlappedMain_Deactivate (object sender, ToplevelEventArgs top)
 			{
 				workerApp.WriteLog ($"{top.Toplevel.Data} deactivate.");
 			}
 
-			private void MdiMain_Activate (object sender, ToplevelEventArgs top)
+			private void OverlappedMain_Activate (object sender, ToplevelEventArgs top)
 			{
 				workerApp.WriteLog ($"{top.Toplevel.Data} activate.");
 			}
@@ -99,17 +99,17 @@ namespace UICatalog.Scenarios {
 					Title = "WorkerApp",
 					CheckType = MenuItemCheckStyle.Checked
 				};
-				var top = Application.MdiChildren?.Find ((x) => x.Data.ToString () == "WorkerApp");
+				var top = Application.OverlappedChildren?.Find ((x) => x.Data.ToString () == "WorkerApp");
 				if (top != null) {
 					item.Checked = top.Visible;
 				}
 				item.Action += () => {
-					var top = Application.MdiChildren.Find ((x) => x.Data.ToString () == "WorkerApp");
+					var top = Application.OverlappedChildren.Find ((x) => x.Data.ToString () == "WorkerApp");
 					item.Checked = top.Visible = (bool)!item.Checked;
 					if (top.Visible) {
-						top.ShowChild ();
+						Application.MoveToOverlappedChild (null);
 					} else {
-						Application.MdiTop.SetNeedsDisplay ();
+						Application.OverlappedTop.SetNeedsDisplay ();
 					}
 				};
 				menuItems.Add (item);
@@ -121,9 +121,9 @@ namespace UICatalog.Scenarios {
 			{
 				var index = 1;
 				List<MenuItem> menuItems = new List<MenuItem> ();
-				var sortedChildes = Application.MdiChildren;
-				sortedChildes.Sort (new ToplevelComparer ());
-				foreach (var top in sortedChildes) {
+				var sortedChildren = Application.OverlappedChildren;
+				sortedChildren.Sort (new ToplevelComparer ());
+				foreach (var top in sortedChildren) {
 					if (top.Data.ToString () == "WorkerApp" && !top.Visible) {
 						continue;
 					}
@@ -133,13 +133,13 @@ namespace UICatalog.Scenarios {
 					item.CheckType |= MenuItemCheckStyle.Checked;
 					var topTitle = top is Window ? ((Window)top).Title : top.Data.ToString ();
 					var itemTitle = item.Title.Substring (index.ToString ().Length + 1);
-					if (top == top.GetTopMdiChild () && topTitle == itemTitle) {
+					if (top == Application.GetTopOverlappedChild () && topTitle == itemTitle) {
 						item.Checked = true;
 					} else {
 						item.Checked = false;
 					}
 					item.Action += () => {
-						top.ShowChild ();
+						Application.MoveToOverlappedChild (null);
 					};
 					menuItems.Add (item);
 				}

+ 0 - 2
UICatalog/Scenarios/ClassExplorer.cs

@@ -183,7 +183,6 @@ namespace UICatalog.Scenarios {
 						}
 					}
 
-
 					if (val is ConstructorInfo ctor) {
 						sb.AppendLine ($"Name:{ctor.Name}");
 						sb.AppendLine ($"Parameters:{(ctor.GetParameters ().Any () ? "" : "None")}");
@@ -263,7 +262,6 @@ namespace UICatalog.Scenarios {
 				}
 
 
-
 			} catch (Exception ex) {
 
 				return ex.Message;

+ 0 - 1
UICatalog/Scenarios/ComputedLayout.cs

@@ -254,7 +254,6 @@ namespace UICatalog.Scenarios {
 				Application.Top.LayoutSubviews ();
 			};
 
-
 			// show positioning vertically using Pos.AnchorEnd
 			var centerButton = new Button ("Center") {
 				X = Pos.Center (),

+ 0 - 5
UICatalog/Scenarios/CsvEditor.cs

@@ -156,7 +156,6 @@ namespace UICatalog.Scenarios {
 				return;
 			}
 
-
 			try {
 				tableView.Table.Columns.RemoveAt (tableView.SelectedColumn);
 				tableView.Update ();
@@ -238,7 +237,6 @@ namespace UICatalog.Scenarios {
 
 					var newIdx = Math.Min (Math.Max (0, int.Parse (newOrdinal)), tableView.Table.Rows.Count - 1);
 
-
 					if (newIdx == oldIdx)
 						return;
 
@@ -361,7 +359,6 @@ namespace UICatalog.Scenarios {
 			}
 
 
-
 		}
 
 		private void Save ()
@@ -409,7 +406,6 @@ namespace UICatalog.Scenarios {
 			int lineNumber = 0;
 			currentFile = null;
 
-
 			try {
 				using var reader = new CsvReader (File.OpenText (filename), CultureInfo.InvariantCulture);
 
@@ -499,7 +495,6 @@ namespace UICatalog.Scenarios {
 			tableView.Update ();
 		}
 
-
 		private void CloseExample ()
 		{
 			tableView.Table = null;

+ 0 - 2
UICatalog/Scenarios/Dialogs.cs

@@ -9,7 +9,6 @@ namespace UICatalog.Scenarios {
 	public class Dialogs : Scenario {
 		static int CODE_POINT = '你'; // We know this is a wide char
 
-
 		public override void Setup ()
 		{
 			var frame = new FrameView ("Dialog Options") {
@@ -115,7 +114,6 @@ namespace UICatalog.Scenarios {
 			};
 			frame.Add (styleRadioGroup);
 
-
 			frame.ForceValidatePosDim = true;
 			void Top_Loaded (object sender, EventArgs args)
 			{

+ 0 - 7
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -200,7 +200,6 @@ namespace UICatalog.Scenarios {
 
 				Add (_frmMenu);
 
-
 				var _frmMenuDetails = new DynamicMenuBarDetails ("Menu Details:") {
 					X = Pos.Right (_frmMenu),
 					Y = Pos.Top (_frmMenu),
@@ -286,7 +285,6 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-
 				var _btnOk = new Button ("Ok") {
 					X = Pos.Right (_frmMenu) + 20,
 					Y = Pos.Bottom (_frmMenuDetails),
@@ -493,10 +491,8 @@ namespace UICatalog.Scenarios {
 					SetFrameDetails (null);
 				};
 
-
 				SetFrameDetails ();
 
-
 				var ustringConverter = new UStringValueConverter ();
 				var listWrapperConverter = new ListWrapperConverter ();
 
@@ -504,7 +500,6 @@ namespace UICatalog.Scenarios {
 				var lblParent = new Binding (this, "Parent", _lblParent, "Text", ustringConverter);
 				var lstMenus = new Binding (this, "Menus", _lstMenus, "Source", listWrapperConverter);
 
-
 				void SetFrameDetails (MenuItem menuBarItem = null)
 				{
 					MenuItem menuItem;
@@ -626,7 +621,6 @@ namespace UICatalog.Scenarios {
 					SetFrameDetails (_currentEditMenuBarItem);
 				}
 
-
 				//_frmMenuDetails.Initialized += (s, e) => _frmMenuDetails.Enabled = false;
 			}
 		}
@@ -845,7 +839,6 @@ namespace UICatalog.Scenarios {
 
 			}
 
-
 			public DynamicMenuItem EnterMenuItem ()
 			{
 				var valid = false;

+ 0 - 6
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -133,7 +133,6 @@ namespace UICatalog.Scenarios {
 
 				Add (_frmStatusBar);
 
-
 				var _frmStatusBarDetails = new DynamicStatusBarDetails ("StatusBar Item Details:") {
 					X = Pos.Right (_frmStatusBar),
 					Y = Pos.Top (_frmStatusBar),
@@ -268,16 +267,13 @@ namespace UICatalog.Scenarios {
 					SetFrameDetails (null);
 				};
 
-
 				SetFrameDetails ();
 
-
 				var ustringConverter = new UStringValueConverter ();
 				var listWrapperConverter = new ListWrapperConverter ();
 
 				var lstItems = new Binding (this, "Items", _lstItems, "Source", listWrapperConverter);
 
-
 				void SetFrameDetails (StatusItem statusItem = null)
 				{
 					StatusItem newStatusItem;
@@ -331,7 +327,6 @@ namespace UICatalog.Scenarios {
 					SetFrameDetails (_currentEditStatusItem);
 				}
 
-
 				//_frmStatusBarDetails.Initialized += (s, e) => _frmStatusBarDetails.Enabled = false;
 			}
 
@@ -463,7 +458,6 @@ namespace UICatalog.Scenarios {
 				Add (_btnShortcut);
 			}
 
-
 			public DynamicStatusItem EnterStatusItem ()
 			{
 				var valid = false;

+ 0 - 1
UICatalog/Scenarios/FileDialogExamples.cs

@@ -33,7 +33,6 @@ namespace UICatalog.Scenarios {
 			cbMustExist = new CheckBox ("Must Exist") { Checked = true, Y = y++, X = x };
 			Win.Add (cbMustExist);
 
-
 			cbUnicode = new CheckBox ("UseUnicode") { Checked = FileDialogStyle.DefaultUseUnicodeCharacters, Y = y++, X = x };
 			Win.Add (cbUnicode);
 

+ 0 - 11
UICatalog/Scenarios/GraphViewExample.cs

@@ -35,7 +35,6 @@ namespace UICatalog.Scenarios {
 				 ()=>MultiBarGraph()                     //7
 			};
 
-
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("Scatter _Plot", "",()=>graphs[currentGraph = 0]()),
@@ -67,10 +66,8 @@ namespace UICatalog.Scenarios {
 				Height = 20,
 			};
 
-
 			Win.Add (graphView);
 
-
 			var frameRight = new FrameView ("About") {
 				X = Pos.Right (graphView) + 1,
 				Y = 0,
@@ -78,7 +75,6 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (),
 			};
 
-
 			frameRight.Add (about = new TextView () {
 				Width = Dim.Fill (),
 				Height = Dim.Fill ()
@@ -86,7 +82,6 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (frameRight);
 
-
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 				new StatusItem(Key.CtrlMask | Key.G, "~^G~ Next", ()=>graphs[currentGraph++%graphs.Length]()),
@@ -135,7 +130,6 @@ namespace UICatalog.Scenarios {
 			graphView.AxisX.ShowLabelsEvery = 0;
 			graphView.AxisX.Minimum = 0;
 
-
 			graphView.AxisY.Minimum = 0;
 
 			var legend = new LegendAnnotation (new Rect (graphView.Bounds.Width - 20, 0, 20, 5));
@@ -179,14 +173,12 @@ namespace UICatalog.Scenarios {
 			graphView.Series.Add (points);
 			graphView.Annotations.Add (line);
 
-
 			randomPoints = new List<PointF> ();
 
 			for (int i = 0; i < 10; i++) {
 				randomPoints.Add (new PointF (r.Next (100), r.Next (100)));
 			}
 
-
 			var points2 = new ScatterSeries () {
 				Points = randomPoints,
 				Fill = new GraphCellToRender ('x', red)
@@ -471,7 +463,6 @@ namespace UICatalog.Scenarios {
 			};
 			graphView.Series.Add (malesSeries);
 
-
 			// Females
 			var femalesSeries = new BarSeries () {
 				Orientation = Orientation.Horizontal,
@@ -501,7 +492,6 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-
 			var softStiple = new GraphCellToRender ('\u2591');
 			var mediumStiple = new GraphCellToRender ('\u2592');
 
@@ -587,7 +577,6 @@ namespace UICatalog.Scenarios {
 				}
 				graphView.SetNeedsDisplay ();
 
-
 				// while the equaliser is showing
 				return graphView.Series.Contains (series);
 			};

+ 0 - 1
UICatalog/Scenarios/LineCanvasExperiment.cs

@@ -7,7 +7,6 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("LineCanvas")]
 	public class LineCanvasExperiment : Scenario {
 
-
 		public override void Init ()
 		{
 			Application.Init ();

+ 0 - 2
UICatalog/Scenarios/LineDrawing.cs

@@ -26,7 +26,6 @@ namespace UICatalog.Scenarios {
 				Width = Dim.Fill ()
 			};
 
-
 			tools.ColorChanged += (c) => canvas.SetColor (c);
 			tools.SetStyle += (b) => canvas.BorderStyle = b;
 
@@ -183,7 +182,6 @@ namespace UICatalog.Scenarios {
 							length = end.X - start.X;
 						}
 
-
 						canvases [currentColor].AddLine (
 							start,
 							length,

+ 0 - 4
UICatalog/Scenarios/LineViewExample.cs

@@ -26,7 +26,6 @@ namespace UICatalog.Scenarios {
 			});
 			Application.Top.Add (menu);
 
-
 			Win.Add (new Label ("Regular Line") { Y = 0 });
 
 			// creates a horizontal line
@@ -56,7 +55,6 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (shortLine);
 
-
 			Win.Add (new Label ("Arrow Line") { Y = 6 });
 
 			// creates a horizontal line
@@ -69,7 +67,6 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (arrowLine);
 
-
 			Win.Add (new Label ("Vertical Line") { Y = 9,X=11 });
 
 			// creates a horizontal line
@@ -79,7 +76,6 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (verticalLine);
 
-
 			Win.Add (new Label ("Vertical Arrow") { Y = 11, X = 28 });
 
 			// creates a horizontal line

+ 0 - 1
UICatalog/Scenarios/ListsAndCombos.cs

@@ -109,7 +109,6 @@ namespace UICatalog.Scenarios {
 				scrollBarCbx.Refresh ();
 			};
 
-
 			var btnMoveUp = new Button ("Move _Up") {
 				X = 1,
 				Y = Pos.Bottom(lbListView),

+ 0 - 2
UICatalog/Scenarios/Snake.cs

@@ -25,7 +25,6 @@ namespace UICatalog.Scenarios {
 				Height = state.Height
 			};
 
-
 			Win.Add (snakeView);
 
 			Stopwatch sw = new Stopwatch ();
@@ -115,7 +114,6 @@ namespace UICatalog.Scenarios {
 					AddRune (p.Key.X, p.Key.Y, p.Value);
 				}
 
-
 				Driver.SetAttribute (red);
 				AddRune (State.Apple.X, State.Apple.Y, 'A');
 				Driver.SetAttribute (white);

+ 0 - 3
UICatalog/Scenarios/SyntaxHighlighting.cs

@@ -48,7 +48,6 @@ namespace UICatalog.Scenarios {
 				new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
 			});
 
-
 			Application.Top.Add (statusBar);
 		}
 
@@ -70,7 +69,6 @@ namespace UICatalog.Scenarios {
 			private Attribute white;
 			private Attribute magenta;
 
-
 			public void Init ()
 			{
 				keywords.Add ("select");
@@ -194,7 +192,6 @@ namespace UICatalog.Scenarios {
 					new string (line.Select (r => (char)r).ToArray ()),
 					"\\b");
 
-
 				int count = 0;
 				string current = null;
 

+ 0 - 2
UICatalog/Scenarios/TabViewExample.cs

@@ -59,7 +59,6 @@ namespace UICatalog.Scenarios {
 				Height = 20,
 			};
 
-
 			tabView.AddTab (new TabView.Tab ("Tab1", new Label ("hodor!")), false);
 			tabView.AddTab (new TabView.Tab ("Tab2", new Label ("durdur")), false);
 			tabView.AddTab (new TabView.Tab ("Interactive Tab", GetInteractiveTab ()), false);
@@ -150,7 +149,6 @@ namespace UICatalog.Scenarios {
 			return interactiveTab;
 		}
 
-
 		private View GetBigTextFileTab ()
 		{
 

+ 0 - 2
UICatalog/Scenarios/TableEditor.cs

@@ -79,7 +79,6 @@ namespace UICatalog.Scenarios {
 				}),
 			});
 
-
 			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
@@ -365,7 +364,6 @@ namespace UICatalog.Scenarios {
 				e.Handled = true;
 			}
 
-
 		}
 
 		private void ClearColumnStyles ()

+ 0 - 2
UICatalog/Scenarios/Text.cs

@@ -8,7 +8,6 @@ using Terminal.Gui;
 using Terminal.Gui.TextValidateProviders;
 
 
-
 namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Text Input Controls", Description: "Tests all text input controls")]
 	[ScenarioCategory ("Controls")]
@@ -234,7 +233,6 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-
 			Win.Add (labelAppendAutocomplete);
 			Win.Add (appendAutocompleteTextField);
 		}

+ 0 - 3
UICatalog/Scenarios/TextAlignmentsAndDirection.cs

@@ -97,7 +97,6 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (container);
 
-
 			// Edit Text
 
 			var editText = new TextView () {
@@ -131,7 +130,6 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (editText);
 
-
 			// JUSTIFY CHECKBOX
 
 			var justifyCheckbox = new CheckBox ("Justify") {
@@ -162,7 +160,6 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (justifyCheckbox);
 
-
 			// Direction Options
 
 			var directionsEnum = Enum.GetValues (typeof (Terminal.Gui.TextDirection)).Cast<Terminal.Gui.TextDirection> ().ToList ();

+ 0 - 1
UICatalog/Scenarios/TextViewAutocompletePopup.cs

@@ -27,7 +27,6 @@ namespace UICatalog.Scenarios {
 			var text = " jamp jemp jimp jomp jump";
 
 
-
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
 					miMultiline =  new MenuItem ("_Multiline", "", () => Multiline()){CheckType = MenuItemCheckStyle.Checked},

Some files were not shown because too many files changed in this diff