Browse Source

Merge pull request #2385 from BDisp/v2_layout-improvements

Fixes #2358 - BREAKING CHANGE: Pos.Combine is incorrect for scenarios involving PosAbsolute.
Tig 2 years ago
parent
commit
b7d206bf56
39 changed files with 1771 additions and 1520 deletions
  1. 13 13
      Terminal.Gui/Configuration/ConfigurationManager.cs
  2. 10 2
      Terminal.Gui/Configuration/Scope.cs
  3. 7 7
      Terminal.Gui/Configuration/ThemeScope.cs
  4. 0 5
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  5. 0 5
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  6. 0 6
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  7. 0 5
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  8. 4 13
      Terminal.Gui/Core/Application.cs
  9. 2 1
      Terminal.Gui/Core/Autocomplete/Autocomplete.cs
  10. 132 50
      Terminal.Gui/Core/Border.cs
  11. 0 6
      Terminal.Gui/Core/ConsoleDriver.cs
  12. 2 2
      Terminal.Gui/Core/PosDim.cs
  13. 20 9
      Terminal.Gui/Core/TextFormatter.cs
  14. 8 4
      Terminal.Gui/Core/Toplevel.cs
  15. 54 69
      Terminal.Gui/Core/View.cs
  16. 4 5
      Terminal.Gui/Core/Window.cs
  17. 1 1
      Terminal.Gui/Terminal.Gui.csproj
  18. 22 30
      Terminal.Gui/Views/FrameView.cs
  19. 1 0
      Terminal.Gui/Windows/Dialog.cs
  20. 0 10
      UICatalog/Properties/launchSettings.json
  21. 313 0
      UICatalog/Scenarios/ASCIICustomButton.cs
  22. 6 7
      UICatalog/Scenarios/Borders.cs
  23. 3 3
      UICatalog/Scenarios/BordersComparisons.cs
  24. 386 0
      UICatalog/Scenarios/BordersOnContainers.cs
  25. 12 376
      UICatalog/Scenarios/BordersOnFrameView.cs
  26. 12 376
      UICatalog/Scenarios/BordersOnToplevel.cs
  27. 12 375
      UICatalog/Scenarios/BordersOnWindow.cs
  28. 12 10
      UICatalog/UICatalog.cs
  29. 4 4
      UnitTests/Configuration/ConfigurationMangerTests.cs
  30. 68 16
      UnitTests/Core/BorderTests.cs
  31. 16 4
      UnitTests/Core/LayoutTests.cs
  32. 141 45
      UnitTests/Core/ViewTests.cs
  33. 184 6
      UnitTests/Text/TextFormatterTests.cs
  34. 6 6
      UnitTests/TopLevels/MessageBoxTests.cs
  35. 57 9
      UnitTests/TopLevels/ToplevelTests.cs
  36. 25 24
      UnitTests/Types/DimTests.cs
  37. 1 1
      UnitTests/UnitTests.csproj
  38. 10 10
      UnitTests/Views/ScrollBarViewTests.cs
  39. 223 5
      UnitTests/Views/ScrollViewTests.cs

+ 13 - 13
Terminal.Gui/Configuration/ConfigurationManager.cs

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

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

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

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

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

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

@@ -21,11 +21,6 @@ namespace Terminal.Gui {
 		public override int Left => 0;
 		public override int Top => 0;
 		public override bool EnableConsoleScrolling { get; set; }
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public override bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
 		public override IClipboard Clipboard { get => clipboard; }
 
 		CursorVisibility? initialCursorVisibility = null;

+ 0 - 5
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -46,11 +46,6 @@ namespace Terminal.Gui {
 		public override int Left => 0;
 		public override int Top => 0;
 		public override bool EnableConsoleScrolling { get; set; }
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public override bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
 		private IClipboard clipboard = null;
 		public override IClipboard Clipboard => clipboard;
 

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

@@ -621,12 +621,6 @@ namespace Terminal.Gui {
 		public override int Left => left;
 		public override int Top => top;
 		public override bool EnableConsoleScrolling { get; set; }
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public override bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
-
 		public NetWinVTConsole NetWinConsole { get; }
 		public bool IsWinPlatform { get; }
 		public override IClipboard Clipboard { get; }

+ 0 - 5
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -716,11 +716,6 @@ namespace Terminal.Gui {
 		public override int Left => left;
 		public override int Top => top;
 		public override bool EnableConsoleScrolling { get; set; }
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public override bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
 		public override IClipboard Clipboard => clipboard;
 		public override int [,,] Contents => contents;
 

+ 4 - 13
Terminal.Gui/Core/Application.cs

@@ -133,7 +133,7 @@ namespace Terminal.Gui {
 		/// </para>
 		/// This API was previously named 'HeightAsBuffer` but was renamed to make its purpose more clear.
 		/// </remarks>
-		[SerializableConfigurationProperty (Scope = typeof(SettingsScope))]
+		[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
 		public static bool EnableConsoleScrolling {
 			get {
 				if (Driver == null) {
@@ -150,21 +150,12 @@ namespace Terminal.Gui {
 			}
 		}
 
-		/// <summary>
-		/// This API is deprecated; use <see cref="EnableConsoleScrolling"/> instead.
-		/// </summary>
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public static bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
-
 		static Key alternateForwardKey = Key.PageDown | Key.CtrlMask;
 
 		/// <summary>
 		/// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.
 		/// </summary>
-		[SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter(typeof(KeyJsonConverter))]
+		[SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
 		public static Key AlternateForwardKey {
 			get => alternateForwardKey;
 			set {
@@ -188,7 +179,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.
 		/// </summary>
-		[SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
+		[SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
 		public static Key AlternateBackwardKey {
 			get => alternateBackwardKey;
 			set {
@@ -212,7 +203,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Gets or sets the key to quit the application.
 		/// </summary>
-		[SerializableConfigurationProperty (Scope = typeof(SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
+		[SerializableConfigurationProperty (Scope = typeof (SettingsScope)), JsonConverter (typeof (KeyJsonConverter))]
 		public static Key QuitKey {
 			get => quitKey;
 			set {

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

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

+ 132 - 50
Terminal.Gui/Core/Border.cs

@@ -331,6 +331,7 @@ namespace Terminal.Gui {
 		private Point effect3DOffset = new Point (1, 1);
 		private Attribute? effect3DBrush;
 		private ustring title = ustring.Empty;
+		private View child;
 
 		/// <summary>
 		/// Specifies the <see cref="Gui.BorderStyle"/> for a view.
@@ -448,7 +449,47 @@ namespace Terminal.Gui {
 		/// Gets or sets the single child element of a <see cref="View"/>.
 		/// </summary>
 		[JsonIgnore]
-		public View Child { get; set; }
+		public View Child {
+			get => child;
+			set {
+				child = value;
+				if (child != null && Parent != null) {
+					Parent.Initialized += Parent_Initialized;
+					Parent.Removed += Parent_Removed;
+				}
+			}
+		}
+
+		private void Parent_Removed (View obj)
+		{
+			BorderBrush = default;
+			Background = default;
+			child.Removed -= Parent_Removed;
+		}
+
+		private void Parent_Initialized (object s, EventArgs e)
+		{
+			SetMarginFrameTitleBrush ();
+			child.Initialized -= Parent_Initialized;
+		}
+
+		private void SetMarginFrameTitleBrush ()
+		{
+			if (child != null) {
+				var view = Parent?.Border != null ? Parent : child;
+				if (view.ColorScheme != null) {
+					if (borderBrush == default) {
+						BorderBrush = view.GetNormalColor ().Foreground;
+					}
+					if (background == default) {
+						Background = view.GetNormalColor ().Background;
+					}
+					return;
+				}
+			}
+			BorderBrush = default;
+			Background = default;
+		}
 
 		/// <summary>
 		/// Gets the parent <see cref="Child"/> parent if any.
@@ -611,7 +652,7 @@ namespace Terminal.Gui {
 				Child.Clear (borderRect);
 			}
 
-			driver.SetAttribute (savedAttribute);
+			driver.SetAttribute (new Attribute (BorderBrush, Background));
 
 			// Draw margin frame
 			if (DrawMarginFrame) {
@@ -635,6 +676,7 @@ namespace Terminal.Gui {
 					driver.DrawWindowFrame (borderRect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill: true, this);
 				}
 			}
+			driver.SetAttribute (savedAttribute);
 		}
 
 		private void DrawChildBorder (Rect frame, bool fill = true)
@@ -651,9 +693,13 @@ namespace Terminal.Gui {
 
 			// Draw the upper BorderThickness
 			for (int r = frame.Y - drawMarginFrame - sumThickness.Top;
-				r > 0 && r < frame.Y - drawMarginFrame - padding.Top; r++) {
+				r < frame.Y - drawMarginFrame - padding.Top; r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
-					c > 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -661,9 +707,13 @@ namespace Terminal.Gui {
 
 			// Draw the left BorderThickness
 			for (int r = frame.Y - drawMarginFrame - padding.Top;
-				r > 0 && r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
-					c > 0 && c < frame.X - drawMarginFrame - padding.Left; c++) {
+					c < frame.X - drawMarginFrame - padding.Left; c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -671,9 +721,13 @@ namespace Terminal.Gui {
 
 			// Draw the right BorderThickness
 			for (int r = frame.Y - drawMarginFrame - padding.Top;
-				r > 0 && r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = frame.Right + drawMarginFrame + padding.Right;
-					c > 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -681,9 +735,9 @@ namespace Terminal.Gui {
 
 			// Draw the lower BorderThickness
 			for (int r = frame.Bottom + drawMarginFrame + padding.Bottom;
-				r > 0 && r > 0 && r < Math.Min (frame.Bottom + drawMarginFrame + sumThickness.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom + drawMarginFrame + sumThickness.Bottom, driver.Rows); r++) {
 				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
-					c > 0 && c > 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -693,9 +747,13 @@ namespace Terminal.Gui {
 
 			// Draw the upper Padding
 			for (int r = frame.Y - drawMarginFrame - padding.Top;
-				r > 0 && r < frame.Y - drawMarginFrame; r++) {
+				r < frame.Y - drawMarginFrame; r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = frame.X - drawMarginFrame - padding.Left;
-					c > 0 && c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -703,9 +761,9 @@ namespace Terminal.Gui {
 
 			// Draw the left Padding
 			for (int r = frame.Y - drawMarginFrame;
-				r > 0 && r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) {
 				for (int c = frame.X - drawMarginFrame - padding.Left;
-					c > 0 && c < frame.X - drawMarginFrame; c++) {
+					c < frame.X - drawMarginFrame; c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -713,9 +771,9 @@ namespace Terminal.Gui {
 
 			// Draw the right Padding
 			for (int r = frame.Y - drawMarginFrame;
-				r > 0 && r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) {
 				for (int c = frame.Right + drawMarginFrame;
-					c > 0 && c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -723,15 +781,15 @@ namespace Terminal.Gui {
 
 			// Draw the lower Padding
 			for (int r = frame.Bottom + drawMarginFrame;
-				r > 0 && r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) {
 				for (int c = frame.X - drawMarginFrame - padding.Left;
-					c > 0 && c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
 			}
 
-			driver.SetAttribute (savedAttribute);
+			driver.SetAttribute (new Attribute (BorderBrush, Background));
 
 			// Draw the MarginFrame
 			if (DrawMarginFrame) {
@@ -825,9 +883,12 @@ namespace Terminal.Gui {
 
 			// Draw the upper BorderThickness
 			for (int r = frame.Y;
-				r > 0 && r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) {
+				r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) {
+				if (r < 0) {
+					continue;
+				}
 				for (int c = frame.X;
-					c > 0 && c < Math.Min (frame.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -835,9 +896,13 @@ namespace Terminal.Gui {
 
 			// Draw the left BorderThickness
 			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
-				r > 0 && r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = frame.X;
-					c > 0 && c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) {
+					c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -845,9 +910,13 @@ namespace Terminal.Gui {
 
 			// Draw the right BorderThickness
 			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
-				r > 0 && r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = Math.Max (frame.Right - borderThickness.Right, frame.X);
-					c > 0 && c < Math.Min (frame.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -855,9 +924,9 @@ namespace Terminal.Gui {
 
 			// Draw the lower BorderThickness
 			for (int r = Math.Max (frame.Bottom - borderThickness.Bottom, frame.Y);
-				r > 0 && r < Math.Min (frame.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom, driver.Rows); r++) {
 				for (int c = frame.X;
-					c > 0 && c < Math.Min (frame.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -867,9 +936,13 @@ namespace Terminal.Gui {
 
 			// Draw the upper Padding
 			for (int r = frame.Y + borderThickness.Top;
-				r > 0 && r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) {
+				r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = frame.X + borderThickness.Left;
-					c > 0 && c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -877,9 +950,13 @@ namespace Terminal.Gui {
 
 			// Draw the left Padding
 			for (int r = frame.Y + sumThickness.Top;
-				r > 0 && r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = frame.X + borderThickness.Left;
-					c > 0 && c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) {
+					c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -887,9 +964,13 @@ namespace Terminal.Gui {
 
 			// Draw the right Padding
 			for (int r = frame.Y + sumThickness.Top;
-				r > 0 && r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
+
+				if (r < 0) {
+					continue;
+				}
 				for (int c = Math.Max (frame.Right - sumThickness.Right, frame.X + sumThickness.Left);
-					c > 0 && c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) {
+					c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
@@ -897,15 +978,15 @@ namespace Terminal.Gui {
 
 			// Draw the lower Padding
 			for (int r = Math.Max (frame.Bottom - sumThickness.Bottom, frame.Y + borderThickness.Top);
-				r > 0 && r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
+				r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
 				for (int c = frame.X + borderThickness.Left;
-					c > 0 && c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) {
+					c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) {
 
 					AddRuneAt (driver, c, r, ' ');
 				}
 			}
 
-			driver.SetAttribute (savedAttribute);
+			driver.SetAttribute (new Attribute (BorderBrush, Background));
 
 			// Draw the MarginFrame
 			if (DrawMarginFrame) {
@@ -926,9 +1007,9 @@ namespace Terminal.Gui {
 
 				// Draw the upper Effect3D
 				for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0);
-					r > 0 && r < frame.Y; r++) {
+					r < frame.Y; r++) {
 					for (int c = Math.Max (frame.X + effect3DOffset.X, 0);
-						c > 0 && c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
+						c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
 
 						AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]);
 					}
@@ -936,9 +1017,9 @@ namespace Terminal.Gui {
 
 				// Draw the left Effect3D
 				for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0);
-					r > 0 && r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
+					r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
 					for (int c = Math.Max (frame.X + effect3DOffset.X, 0);
-						c > 0 && c < frame.X; c++) {
+						c < frame.X; c++) {
 
 						AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]);
 					}
@@ -946,9 +1027,9 @@ namespace Terminal.Gui {
 
 				// Draw the right Effect3D
 				for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0);
-					r > 0 && r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
+					r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
 					for (int c = frame.Right;
-						c > 0 && c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
+						c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
 
 						AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]);
 					}
@@ -956,9 +1037,9 @@ namespace Terminal.Gui {
 
 				// Draw the lower Effect3D
 				for (int r = frame.Bottom;
-					r > 0 && r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
+					r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) {
 					for (int c = Math.Max (frame.X + effect3DOffset.X, 0);
-						c > 0 && c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
+						c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) {
 
 						AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]);
 					}
@@ -987,9 +1068,10 @@ namespace Terminal.Gui {
 		{
 			var driver = Application.Driver;
 			if (DrawMarginFrame) {
-				driver.SetAttribute (Child.GetNormalColor ());
-				if (Child.HasFocus)
-					driver.SetAttribute (Child.ColorScheme.HotNormal);
+				driver.SetAttribute (new Attribute (BorderBrush, Background));
+				if (view.HasFocus) {
+					driver.SetAttribute (new Attribute (Child.ColorScheme.HotNormal.Foreground, Background));
+				}
 				var padding = view.Border.GetSumThickness ();
 				Rect scrRect;
 				if (view == Child) {
@@ -998,7 +1080,7 @@ namespace Terminal.Gui {
 					driver.DrawWindowTitle (scrRect, Title, 0, 0, 0, 0);
 				} else {
 					scrRect = view.ViewToScreen (new Rect (0, 0, view.Frame.Width, view.Frame.Height));
-					driver.DrawWindowTitle (scrRect, Title,
+					driver.DrawWindowTitle (scrRect, Parent.Border.Title,
 						padding.Left, padding.Top, padding.Right, padding.Bottom);
 				}
 			}
@@ -1014,9 +1096,9 @@ namespace Terminal.Gui {
 		{
 			var driver = Application.Driver;
 			if (DrawMarginFrame) {
-				driver.SetAttribute (view.GetNormalColor ());
+				driver.SetAttribute (new Attribute (BorderBrush, Background));
 				if (view.HasFocus) {
-					driver.SetAttribute (view.ColorScheme.HotNormal);
+					driver.SetAttribute (new Attribute (view.ColorScheme.HotNormal.Foreground, Background));
 				}
 				var padding = Parent.Border.GetSumThickness ();
 				var scrRect = Parent.ViewToScreen (new Rect (0, 0, rect.Width, rect.Height));

+ 0 - 6
Terminal.Gui/Core/ConsoleDriver.cs

@@ -713,12 +713,6 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public abstract bool EnableConsoleScrolling { get; set; }
 
-		/// <summary>
-		/// This API is deprecated; use <see cref="EnableConsoleScrolling"/> instead.
-		/// </summary>
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public abstract bool HeightAsBuffer { get; set; }
-
 		/// <summary>
 		/// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
 		/// </summary>

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

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

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

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

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

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

+ 54 - 69
Terminal.Gui/Core/View.cs

@@ -447,11 +447,10 @@ namespace Terminal.Gui {
 		public virtual Rect Frame {
 			get => frame;
 			set {
-				var rect = GetMaxNeedDisplay (frame, value);
 				frame = new Rect (value.X, value.Y, Math.Max (value.Width, 0), Math.Max (value.Height, 0));
 				TextFormatter.Size = GetBoundsTextFormatterSize ();
 				SetNeedsLayout ();
-				SetNeedsDisplay (rect);
+				SetNeedsDisplay ();
 			}
 		}
 
@@ -809,7 +808,6 @@ namespace Terminal.Gui {
 		{
 			var actX = x is Pos.PosAbsolute ? x.Anchor (0) : frame.X;
 			var actY = y is Pos.PosAbsolute ? y.Anchor (0) : frame.Y;
-			Rect oldFrame = frame;
 
 			if (AutoSize) {
 				var s = GetAutoSize ();
@@ -824,21 +822,7 @@ namespace Terminal.Gui {
 			}
 			TextFormatter.Size = GetBoundsTextFormatterSize ();
 			SetNeedsLayout ();
-			SetNeedsDisplay (GetMaxNeedDisplay (oldFrame, frame));
-		}
-
-		Rect GetMaxNeedDisplay (Rect oldFrame, Rect newFrame)
-		{
-			var rect = new Rect () {
-				X = Math.Min (oldFrame.X, newFrame.X),
-				Y = Math.Min (oldFrame.Y, newFrame.Y),
-				Width = Math.Max (oldFrame.Width, newFrame.Width),
-				Height = Math.Max (oldFrame.Height, newFrame.Height)
-			};
-			rect.Width += Math.Max (oldFrame.X - newFrame.X, 0);
-			rect.Height += Math.Max (oldFrame.Y - newFrame.Y, 0);
-
-			return rect;
+			SetNeedsDisplay ();
 		}
 
 		void TextFormatter_HotKeyChanged (Key obj)
@@ -1110,15 +1094,8 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public void Clear ()
 		{
-			Rect containerBounds = GetContainerBounds ();
-			Rect viewBounds = Bounds;
-			if (!containerBounds.IsEmpty) {
-				viewBounds.Width = Math.Min (viewBounds.Width, containerBounds.Width);
-				viewBounds.Height = Math.Min (viewBounds.Height, containerBounds.Height);
-			}
-
-			var h = viewBounds.Height;
-			var w = viewBounds.Width;
+			var h = Frame.Height;
+			var w = Frame.Width;
 			for (var line = 0; line < h; line++) {
 				Move (0, line);
 				for (var col = 0; col < w; col++)
@@ -1381,10 +1358,10 @@ namespace Terminal.Gui {
 		public virtual void OnAdded (View view)
 		{
 			view.IsAdded = true;
-			view.x = view.x ?? view.frame.X;
-			view.y = view.y ?? view.frame.Y;
-			view.width = view.width ?? view.frame.Width;
-			view.height = view.height ?? view.frame.Height;
+			view.x ??= view.frame.X;
+			view.y ??= view.frame.Y;
+			view.width ??= view.frame.Width;
+			view.height ??= view.frame.Height;
 
 			view.Added?.Invoke (this);
 		}
@@ -1524,8 +1501,6 @@ namespace Terminal.Gui {
 			var boundsAdjustedForBorder = Bounds;
 			if (!IgnoreBorderPropertyOnRedraw && Border != null) {
 				Border.DrawContent (this);
-				boundsAdjustedForBorder = new Rect (bounds.X + 1, bounds.Y + 1, Math.Max (0, bounds.Width - 2), Math.Max(0, bounds.Height - 2));
-				boundsAdjustedForBorder = Bounds;// new Rect (bounds.X + 1, bounds.Y + 1, Math.Max (bounds.Width, bounds.Width - 2), Math.Max (bounds.Height, bounds.Height - 2));
 			} else if (ustring.IsNullOrEmpty (TextFormatter.Text) &&
 				(GetType ().IsNestedPublic && !IsOverridden (this, "Redraw") || GetType ().Name == "View") &&
 				(!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded)) {
@@ -1536,15 +1511,17 @@ namespace Terminal.Gui {
 
 			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
 				Rect containerBounds = GetContainerBounds ();
-				Clear (ViewToScreen (GetNeedDisplay (containerBounds)));
-				SetChildNeedsDisplay ();
-				// Draw any Text
-				if (TextFormatter != null) {
-					TextFormatter.NeedsFormat = true;
+				if (!containerBounds.IsEmpty) {
+					Clear (GetNeedDisplay (containerBounds));
+					SetChildNeedsDisplay ();
+					// Draw any Text
+					if (TextFormatter != null) {
+						TextFormatter.NeedsFormat = true;
+					}
+					TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
+					    HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+					    containerBounds);
 				}
-				TextFormatter?.Draw (ViewToScreen (boundsAdjustedForBorder), HasFocus ? ColorScheme.Focus : GetNormalColor (),
-				    HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled,
-				    containerBounds);
 			}
 
 			// Invoke DrawContentEvent
@@ -1553,7 +1530,7 @@ namespace Terminal.Gui {
 			if (subviews != null) {
 				foreach (var view in subviews) {
 					if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay || view.LayoutNeeded) {
-						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (boundsAdjustedForBorder) || boundsAdjustedForBorder.X < 0 || bounds.Y < 0)) {
+						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
 							if (view.LayoutNeeded) {
 								view.LayoutSubviews ();
 							}
@@ -1582,7 +1559,7 @@ namespace Terminal.Gui {
 
 		Rect GetNeedDisplay (Rect containerBounds)
 		{
-			Rect rect = NeedDisplay;
+			Rect rect = ViewToScreen (NeedDisplay);
 			if (!containerBounds.IsEmpty) {
 				rect.Width = Math.Min (NeedDisplay.Width, containerBounds.Width);
 				rect.Height = Math.Min (NeedDisplay.Height, containerBounds.Height);
@@ -2223,8 +2200,7 @@ namespace Terminal.Gui {
 					newLocation = pos.Anchor (superviewDimension - newDimension);
 					break;
 
-				case Pos.PosCombine:
-					var combine = pos as Pos.PosCombine;
+				case Pos.PosCombine combine:
 					int left, right;
 					(left, newDimension) = GetNewLocationAndDimension (superviewLocation, superviewDimension, combine.left, dim, autosizeDimension);
 					(right, newDimension) = GetNewLocationAndDimension (superviewLocation, superviewDimension, combine.right, dim, autosizeDimension);
@@ -2251,7 +2227,7 @@ namespace Terminal.Gui {
 
 			// Recursively calculates the new dimension (width or height) of the given Dim given:
 			//   the current location (x or y)
-			//   the current dimennsion (width or height)
+			//   the current dimension (width or height)
 			int CalculateNewDimension (Dim d, int location, int dimension, int autosize)
 			{
 				int newDimension;
@@ -2269,12 +2245,12 @@ namespace Terminal.Gui {
 					}
 					newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
 					break;
-					
+
 				case Dim.DimFactor factor when !factor.IsFromRemaining ():
 					newDimension = d.Anchor (dimension);
 					newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
 					break;
-					
+
 				case Dim.DimFill:
 				default:
 					newDimension = Math.Max (d.Anchor (dimension - location), 0);
@@ -2286,11 +2262,11 @@ namespace Terminal.Gui {
 			}
 
 
-			// horiztonal
-			(newX, newW) = GetNewLocationAndDimension (superviewFrame.X, superviewFrame.Width, x, Width, autosize.Width);
+			// horizontal
+			(newX, newW) = GetNewLocationAndDimension (superviewFrame.X, superviewFrame.Width, x, width, autosize.Width);
 
 			// vertical
-			(newY, newH) = GetNewLocationAndDimension (superviewFrame.Y, superviewFrame.Height, y, Height, autosize.Height);
+			(newY, newH) = GetNewLocationAndDimension (superviewFrame.Y, superviewFrame.Height, y, height, autosize.Height);
 
 			var r = new Rect (newX, newY, newW, newH);
 			if (Frame != r) {
@@ -2438,7 +2414,7 @@ namespace Terminal.Gui {
 
 			if (edges.Any ()) {
 				(var from, var to) = edges.First ();
-				if (from != Application.Top) {
+				if (from != superView?.GetTopSuperView (to, from)) {
 					if (!ReferenceEquals (from, to)) {
 						throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {from} linked with {to}. Did you forget to add it to {superView}?");
 					} else {
@@ -2449,8 +2425,8 @@ namespace Terminal.Gui {
 			// return L (a topologically sorted order)
 			return result;
 		} // TopologicalSort
-		
-		
+
+
 		/// <summary>
 		/// Invoked when a view starts executing or when the dimensions of the view have changed, for example in
 		/// response to the container view or terminal resizing.
@@ -2475,20 +2451,16 @@ namespace Terminal.Gui {
 			CollectAll (this, ref nodes, ref edges);
 			var ordered = View.TopologicalSort (SuperView, nodes, edges);
 			foreach (var v in ordered) {
-				if (v.LayoutStyle == LayoutStyle.Computed) {
-					v.SetRelativeLayout (Frame);
-				}
-
-				v.LayoutSubviews ();
-				v.LayoutNeeded = false;
+				LayoutSubview (v, Frame);
 			}
 
-			// If our SuperView is Application.Top and the layoutstyle is Computed it's a special-cass.
-			// Use SetRelativeaLayout with the Frame of the Application.Top
-			if (SuperView != null && SuperView == Application.Top && LayoutNeeded
-			    && ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) {
-				Debug.Assert (Application.Top.Frame.Location == Point.Empty);
-				SetRelativeLayout (Application.Top.Frame);
+			// If the 'to' is rooted to 'from' and the layoutstyle is Computed it's a special-case.
+			// Use LayoutSubview with the Frame of the 'from' 
+			if (SuperView != null && GetTopSuperView () != null && LayoutNeeded
+			    && ordered.Count == 0 && edges.Count > 0 && LayoutStyle == LayoutStyle.Computed) {
+
+				(var from, var to) = edges.First ();
+				LayoutSubview (to, from.Frame);
 			}
 
 			LayoutNeeded = false;
@@ -2496,6 +2468,16 @@ namespace Terminal.Gui {
 			OnLayoutComplete (new LayoutEventArgs () { OldBounds = oldBounds });
 		}
 
+		private void LayoutSubview (View v, Rect hostFrame)
+		{
+			if (v.LayoutStyle == LayoutStyle.Computed) {
+				v.SetRelativeLayout (hostFrame);
+			}
+
+			v.LayoutSubviews ();
+			v.LayoutNeeded = false;
+		}
+
 		ustring text;
 
 		/// <summary>
@@ -3165,11 +3147,14 @@ namespace Terminal.Gui {
 		/// Get the top superview of a given <see cref="View"/>.
 		/// </summary>
 		/// <returns>The superview view.</returns>
-		public View GetTopSuperView ()
+		public View GetTopSuperView (View view = null, View superview = null)
 		{
-			View top = Application.Top;
-			for (var v = this?.SuperView; v != null; v = v.SuperView) {
+			View top = superview ?? Application.Top;
+			for (var v = view?.SuperView ?? (this?.SuperView); v != null; v = v.SuperView) {
 				top = v;
+				if (top == superview) {
+					break;
+				}
 			}
 
 			return top;

+ 4 - 5
Terminal.Gui/Core/Window.cs

@@ -7,7 +7,7 @@
 //  - FrameView Does not support padding (but should)
 //  - FrameView Does not support mouse dragging
 //  - FrameView Does not support IEnumerable
-// Any udpates done here should probably be done in FrameView as well; TODO: Merge these classes
+// Any updates done here should probably be done in FrameView as well; TODO: Merge these classes
 
 using System;
 using System.Collections;
@@ -308,7 +308,6 @@ namespace Terminal.Gui {
 			ClearNeedsDisplay ();
 
 			Driver.SetAttribute (GetNormalColor ());
-			Border.Title = Title; //  not sure why Title is getting un-set
 			Border.DrawContent (this, false);
 		}
 
@@ -346,7 +345,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Event arguments for <see cref="Title"/> chane events.
+		/// Event arguments for <see cref="Title"/> change events.
 		/// </summary>
 		public class TitleEventArgs : EventArgs {
 			/// <summary>
@@ -360,7 +359,7 @@ namespace Terminal.Gui {
 			public ustring OldTitle { get; set; }
 
 			/// <summary>
-			/// Flag which allows cancelling the Title change.
+			/// Flag which allows canceling the Title change.
 			/// </summary>
 			public bool Cancel { get; set; }
 
@@ -380,7 +379,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="oldTitle">The <see cref="Window.Title"/> that is/has been replaced.</param>
 		/// <param name="newTitle">The new <see cref="Window.Title"/> to be replaced.</param>
-		/// <returns>`true` if an event handler cancelled the Title change.</returns>
+		/// <returns>`true` if an event handler canceled the Title change.</returns>
 		public virtual bool OnTitleChanging (ustring oldTitle, ustring newTitle)
 		{
 			var args = new TitleEventArgs (oldTitle, newTitle);

+ 1 - 1
Terminal.Gui/Terminal.Gui.csproj

@@ -30,7 +30,7 @@
     <PackageReference Include="System.Text.Json" Version="7.0.1" />
     <PackageReference Include="System.Management" Version="7.0.0" />
     <InternalsVisibleTo Include="UnitTests" />
-  </ItemGroup>
+   </ItemGroup>
   <!-- Uncomment the RestoreSources element to have dotnet restore pull NStack from a local dir for testing -->
   <PropertyGroup>
     <!-- See https://stackoverflow.com/a/44463578/297526 -->

+ 22 - 30
Terminal.Gui/Views/FrameView.cs

@@ -162,7 +162,8 @@ namespace Terminal.Gui {
 			this.Title = title;
 			if (border == null) {
 				Border = new Border () {
-					BorderStyle = DefaultBorderStyle
+					BorderStyle = DefaultBorderStyle,
+					Title = title
 				};
 			} else {
 				Border = border;
@@ -267,12 +268,8 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
-			var padding = Border.GetSumThickness ();
-			var scrRect = ViewToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
-
 			if (!NeedDisplay.IsEmpty) {
 				Driver.SetAttribute (GetNormalColor ());
-				//Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: true);
 				Clear ();
 			}
 
@@ -280,17 +277,12 @@ namespace Terminal.Gui {
 			contentView.Redraw (!NeedDisplay.IsEmpty ? contentView.Bounds : bounds);
 			Driver.Clip = savedClip;
 
+			ClearLayoutNeeded ();
 			ClearNeedsDisplay ();
 
 			if (!IgnoreBorderPropertyOnRedraw) {
 				Driver.SetAttribute (GetNormalColor ());
-				//Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false);
 				Border.DrawContent (this, false);
-				if (HasFocus)
-					Driver.SetAttribute (ColorScheme.HotNormal);
-				if (Border.DrawMarginFrame)
-					Driver.DrawWindowTitle (scrRect, Title, padding.Left, padding.Top, padding.Right, padding.Bottom);
-				Driver.SetAttribute (GetNormalColor ());
 			} else {
 				var lc = new LineCanvas ();
 
@@ -312,28 +304,28 @@ namespace Terminal.Gui {
 
 				//}
 
-				Driver.SetAttribute (ColorScheme.Normal);
-				foreach (var p in lc.GenerateImage (bounds)) {
-					this.AddRune (p.Key.X, p.Key.Y, p.Value);
-				}
+				//Driver.SetAttribute (ColorScheme.Normal);
+				//foreach (var p in lc.GenerateImage (bounds)) {
+				//	this.AddRune (p.Key.X, p.Key.Y, p.Value);
+				//}
 
-				// Redraw the lines so that focus/drag symbol renders
-				foreach (var subview in contentView.Subviews) {
-					//	line.DrawSplitterSymbol ();
-				}
+				//// Redraw the lines so that focus/drag symbol renders
+				//foreach (var subview in contentView.Subviews) {
+				//	//	line.DrawSplitterSymbol ();
+				//}
 
 
-				// Draw Titles over Border
-				foreach (var subview in contentView.Subviews) {
-					// TODO: Use reflection to see if subview has a Title property
-					if (subview is FrameView viewWithTite) {
-						var rect = viewWithTite.Frame;
-						rect.X = rect.X + 1;
-						rect.Y = rect.Y + 2;
-						// TODO: Do focus color correctly
-						Driver.DrawWindowTitle (rect, viewWithTite.Title, padding.Left, padding.Top, padding.Right, padding.Bottom);
-					}
-				}
+				//// Draw Titles over Border
+				//foreach (var subview in contentView.Subviews) {
+				//	// TODO: Use reflection to see if subview has a Title property
+				//	if (subview is FrameView viewWithTite) {
+				//		var rect = viewWithTite.Frame;
+				//		rect.X = rect.X + 1;
+				//		rect.Y = rect.Y + 2;
+				//		// TODO: Do focus color correctly
+				//		Driver.DrawWindowTitle (rect, viewWithTite.Title, padding.Left, padding.Top, padding.Right, padding.Bottom);
+				//	}
+				//}
 			}
 		}
 

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

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

+ 0 - 10
UICatalog/Properties/launchSettings.json

@@ -50,16 +50,6 @@
     "Windows & FrameViews": {
       "commandName": "Project",
       "commandLineArgs": "\"Windows & FrameViews\""
-    },
-    "WSL : UICatalog": {
-      "commandName": "Executable",
-      "executablePath": "wsl",
-      "commandLineArgs": "dotnet UICatalog.dll",
-      "distributionName": ""
-    },
-    "Tile View Experiments": {
-      "commandName": "Project",
-      "commandLineArgs": "\"Tile View Experiments\""
     }
   }
 }

+ 313 - 0
UICatalog/Scenarios/ASCIICustomButton.cs

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

+ 6 - 7
UICatalog/Scenarios/Borders.cs

@@ -19,6 +19,8 @@ namespace UICatalog.Scenarios {
 			var effect3D = true;
 
 			var smartPanel = new PanelView () {
+				X = Pos.Center () - 20,
+				Y = Pos.Center () + 2,
 				Width = 24,
 				Height = 13,
 				Border = new Border () {
@@ -70,6 +72,8 @@ namespace UICatalog.Scenarios {
 			//};
 
 			var smartLabel = new Label () {
+				X = Pos.Center () + 16,
+				Y = Pos.Center () + 2,
 				Border = new Border () {
 					BorderStyle = borderStyle,
 					DrawMarginFrame = drawMarginFrame,
@@ -184,7 +188,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (paddingBottomEdit);
 
 			var replacePadding = new Button ("Replace all based on top") {
-				X = Pos.Left(paddingLeftEdit),
+				X = Pos.Left (paddingLeftEdit),
 				Y = 5
 			};
 			replacePadding.Clicked += () => {
@@ -303,7 +307,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (borderBottomEdit);
 
 			var replaceBorder = new Button ("Replace all based on top") {
-				X = Pos.Left(borderLeftEdit),
+				X = Pos.Left (borderLeftEdit),
 				Y = 5
 			};
 			replaceBorder.Clicked += () => {
@@ -457,14 +461,9 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (rbBorderBrush);
 
-			smartPanel.X = Pos.Left (paddingLeftEdit);
-			smartPanel.Y = Pos.Top (smartLabel);
 			Win.Add (smartPanel);
-			smartLabel.X = Pos.Left (borderLeftEdit);
-			smartLabel.Y = Pos.Bottom (cbUseUsePanelFrame) + 5;
 			Win.Add (smartLabel);
 			Win.BringSubviewToFront (smartPanel);
-
 		}
 	}
 }

+ 3 - 3
UICatalog/Scenarios/BordersComparisons.cs

@@ -38,7 +38,7 @@ namespace UICatalog.Scenarios {
 			button.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a Window?", "Yes", "No");
 			var label = new Label ("I'm a Window") {
 				X = Pos.Center (),
-				Y = Pos.Center () - 3,
+				Y = Pos.Center () - 1,
 			};
 			var tv = new TextView () {
 				Y = Pos.AnchorEnd (2),
@@ -77,7 +77,7 @@ namespace UICatalog.Scenarios {
 			button2.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a Toplevel?", "Yes", "No");
 			var label2 = new Label ("I'm a Toplevel") {
 				X = Pos.Center (),
-				Y = Pos.Center () - 3,
+				Y = Pos.Center () - 1,
 			};
 			var tv2 = new TextView () {
 				Y = Pos.AnchorEnd (2),
@@ -113,7 +113,7 @@ namespace UICatalog.Scenarios {
 			button3.Clicked += () => MessageBox.Query (20, 7, "Hi", "I'm a FrameView?", "Yes", "No");
 			var label3 = new Label ("I'm a FrameView") {
 				X = Pos.Center (),
-				Y = Pos.Center () - 3,
+				Y = Pos.Center () - 1,
 			};
 			var tv3 = new TextView () {
 				Y = Pos.AnchorEnd (2),

+ 386 - 0
UICatalog/Scenarios/BordersOnContainers.cs

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

+ 12 - 376
UICatalog/Scenarios/BordersOnFrameView.cs

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

+ 12 - 376
UICatalog/Scenarios/BordersOnToplevel.cs

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

+ 12 - 375
UICatalog/Scenarios/BordersOnWindow.cs

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

+ 12 - 10
UICatalog/UICatalog.cs

@@ -195,9 +195,9 @@ namespace UICatalog {
 			// Run UI Catalog UI. When it exits, if _selectedScenario is != null then
 			// a Scenario was selected. Otherwise, the user wants to exit UI Catalog.
 			Application.Init ();
-			
-			//Application.EnableConsoleScrolling = _enableConsoleScrolling;
-			
+
+			Application.EnableConsoleScrolling = _enableConsoleScrolling;
+
 			Application.Run<UICatalogTopLevel> ();
 			Application.Shutdown ();
 
@@ -219,6 +219,7 @@ namespace UICatalog {
 
 		static bool _useSystemConsole = false;
 		static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
+		static bool _enableConsoleScrolling = false;
 		static bool _isFirstRunning = true;
 		static string _topLevelColorScheme;
 
@@ -355,6 +356,8 @@ namespace UICatalog {
 			{
 				ConfigChanged ();
 
+				miIsMouseDisabled.Checked = Application.IsMouseDisabled;
+				miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling;
 				DriverName.Title = $"Driver: {Driver.GetType ().Name}";
 				OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}";
 
@@ -409,13 +412,12 @@ namespace UICatalog {
 
 			List<MenuItem []> CreateDiagnosticMenuItems ()
 			{
-				List<MenuItem []> menuItems = new List<MenuItem []> {
-					CreateDiagnosticFlagsMenuItems (),
-					new MenuItem [] { null },
-					CreateEnableConsoleScrollingMenuItems (),
-					CreateDisabledEnabledMouseItems (),
-					CreateKeybindingsMenuItems ()
-				};
+				List<MenuItem []> menuItems = new List<MenuItem []> ();
+				menuItems.Add (CreateDiagnosticFlagsMenuItems ());
+				menuItems.Add (new MenuItem [] { null });
+				menuItems.Add (CreateEnableConsoleScrollingMenuItems ());
+				menuItems.Add (CreateDisabledEnabledMouseItems ());
+				menuItems.Add (CreateKeybindingsMenuItems ());
 				return menuItems;
 			}
 

+ 4 - 4
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -182,12 +182,13 @@ namespace Terminal.Gui.ConfigurationTests {
 		/// Save the `config.json` file; this can be used to update the file in `Terminal.Gui.Resources.config.json'.
 		/// </summary>
 		/// <remarks>
-		/// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Conifg Properties
-		/// are all satic and thus can be overwritten by other tests.</remarks>
+		/// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Config Properties
+		/// are all static and thus can be overwritten by other tests.</remarks>
 		[Fact]
 		public void SaveDefaults ()
 		{
 			ConfigurationManager.Initialize ();
+			ConfigurationManager.Reset ();
 
 			// Get the hard coded settings
 			ConfigurationManager.GetHardCodedDefaults ();
@@ -316,12 +317,11 @@ namespace Terminal.Gui.ConfigurationTests {
 		[Fact, AutoInitShutdown]
 		public void TestConfigurationManagerToJson ()
 		{
+			ConfigurationManager.Reset ();
 			ConfigurationManager.GetHardCodedDefaults ();
 			var stream = ConfigurationManager.ToStream ();
-
 			
 			ConfigurationManager.Settings.Update (stream, "TestConfigurationManagerToJson");
-
 		}
 
 		[Fact, AutoInitShutdown (configLocation: ConfigLocations.None)]

+ 68 - 16
UnitTests/Core/BorderTests.cs

@@ -1,15 +1,19 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Reflection.Emit;
 using Xunit;
+using Xunit.Abstractions;
 using Rune = System.Rune;
 
 namespace Terminal.Gui.CoreTests {
 	public class BorderTests {
-		[Fact]
-		[AutoInitShutdown]
+		readonly ITestOutputHelper output;
+
+		public BorderTests (ITestOutputHelper output)
+		{
+			this.output = output;
+		}
+
+		[Fact, AutoInitShutdown]
 		public void Constructor_Defaults ()
 		{
 			var b = new Border ();
@@ -45,8 +49,7 @@ namespace Terminal.Gui.CoreTests {
 			Assert.False (b.DrawMarginFrame);
 		}
 
-		[Fact]
-		[AutoInitShutdown]
+		[Fact, AutoInitShutdown]
 		public void ActualWidth_ActualHeight ()
 		{
 			var v = new View (new Rect (5, 10, 60, 20), "", new Border ());
@@ -81,8 +84,7 @@ namespace Terminal.Gui.CoreTests {
 			Assert.Equal (new Thickness (5, 5, 5, 5), b.GetSumThickness ());
 		}
 
-		[Fact]
-		[AutoInitShutdown]
+		[Fact, AutoInitShutdown]
 		public void DrawContent_With_Child_Border ()
 		{
 			var top = Application.Top;
@@ -228,7 +230,7 @@ namespace Terminal.Gui.CoreTests {
 					var rune = (Rune)driver.Contents [r, c, 0];
 					if (r == frame.Y - drawMarginFrame || r == frame.Bottom + drawMarginFrame - 1
 						|| c == frame.X - drawMarginFrame || c == frame.Right + drawMarginFrame - 1) {
-						Assert.Equal (Color.Black, color.Background);  // because of #2345 - Border: can't change border color in window by Border.BorderBrush. 
+						Assert.Equal (Color.BrightGreen, color.Background);
 					} else {
 						Assert.Equal (Color.Black, color.Background);
 					}
@@ -308,8 +310,7 @@ namespace Terminal.Gui.CoreTests {
 			}
 		}
 
-		[Fact]
-		[AutoInitShutdown]
+		[Fact, AutoInitShutdown]
 		public void DrawContent_With_Parent_Border ()
 		{
 			var top = Application.Top;
@@ -463,7 +464,7 @@ namespace Terminal.Gui.CoreTests {
 					var rune = (Rune)driver.Contents [r, c, 0];
 					if (r == frame.Y + sumThickness.Top || r == frame.Bottom - sumThickness.Bottom - 1
 						|| c == frame.X + sumThickness.Left || c == frame.Right - sumThickness.Right - 1) {
-						Assert.Equal (Color.Black, color.Background);  // because of #2345 - Border: can't change border color in window by Border.BorderBrush. 
+						Assert.Equal (Color.BrightGreen, color.Background);
 					} else {
 						Assert.Equal (Color.Black, color.Background);
 					}
@@ -550,8 +551,7 @@ namespace Terminal.Gui.CoreTests {
 			}
 		}
 
-		[Fact]
-		[AutoInitShutdown]
+		[Fact, AutoInitShutdown]
 		public void BorderOnControlWithNoChildren ()
 		{
 			var label = new TextField ("Loading...") {
@@ -567,5 +567,57 @@ namespace Terminal.Gui.CoreTests {
 
 			Assert.Null (Record.Exception (() => label.Redraw (label.Bounds)));
 		}
+
+		[Fact, AutoInitShutdown]
+		public void BorderStyle_And_DrawMarginFrame_Gets_Sets ()
+		{
+			var lblTop = new Label ("At 0,0");
+			var lblFrame = new Label ("Centered") { X = Pos.Center (), Y = Pos.Center () };
+			var frame = new FrameView () { Y = 1, Width = 20, Height = 3 };
+			var lblFill = new Label () { Width = Dim.Fill(),Height = Dim.Fill(), Visible = false };
+			var fillText = new System.Text.StringBuilder ();
+			for (int i = 0; i < frame.Bounds.Height; i++) {
+				if (i > 0) {
+					fillText.AppendLine ("");
+				}
+				for (int j = 0; j < frame.Bounds.Width; j++) {
+					fillText.Append ("█");
+				}
+			}
+			lblFill.Text = fillText.ToString ();
+			frame.Add (lblFill, lblFrame);
+			var lblBottom = new Label ("At 0,4") { Y = 4 };
+			Application.Top.Add (lblTop, frame, lblBottom);
+			Application.Begin (Application.Top);
+
+			Assert.Equal (BorderStyle.Single, frame.Border.BorderStyle);
+			Assert.True (frame.Border.DrawMarginFrame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+At 0,0              
+┌──────────────────┐
+│     Centered     │
+└──────────────────┘
+At 0,4              ", output);
+
+			frame.Border.BorderStyle = BorderStyle.None;
+			Application.Refresh ();
+			Assert.True (frame.Border.DrawMarginFrame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+At 0,0        
+              
+      Centered
+              
+At 0,4        ", output);
+
+			frame.Border.DrawMarginFrame = false;
+			lblFill.Visible = true;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+At 0,0              
+████████████████████
+██████Centered██████
+████████████████████
+At 0,4              ", output);
+		}
 	}
 }

+ 16 - 4
UnitTests/Core/LayoutTests.cs

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

+ 141 - 45
UnitTests/Core/ViewTests.cs

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

+ 184 - 6
UnitTests/Text/TextFormatterTests.cs

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

+ 6 - 6
UnitTests/TopLevels/MessageBoxTests.cs

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

+ 57 - 9
UnitTests/TopLevels/ToplevelTests.cs

@@ -900,9 +900,9 @@ namespace Terminal.Gui.TopLevelTests {
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
  File      
            
-    ┌────
-    │     
-    └────
+    ┌────┐ 
+    │    │ 
+    └────┘ 
            
            
            
@@ -910,7 +910,7 @@ namespace Terminal.Gui.TopLevelTests {
  CTRL-N New", output);
 
 					Assert.Equal (win, Application.MouseGrabView);
-					Assert.Equal (new Rect (4, 2, 7, 3), Application.MouseGrabView.Frame);
+					Assert.Equal (new Rect (4, 2, 6, 3), Application.MouseGrabView.Frame);
 
 				} else if (iterations == 3) {
 					Assert.Equal (win, Application.MouseGrabView);
@@ -931,10 +931,10 @@ namespace Terminal.Gui.TopLevelTests {
 
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
  File      
-    ┌────
-    │     
-    └─────┘
-           
+    ┌────┐ 
+    │    │ 
+    │    │ 
+    └────┘ 
            
            
            
@@ -942,7 +942,7 @@ namespace Terminal.Gui.TopLevelTests {
  CTRL-N New", output);
 
 					Assert.Equal (win, Application.MouseGrabView);
-					Assert.Equal (new Rect (4, 1, 7, 3), Application.MouseGrabView.Frame);
+					Assert.Equal (new Rect (4, 1, 6, 4), Application.MouseGrabView.Frame);
 
 				} else if (iterations == 5) {
 					Assert.Equal (win, Application.MouseGrabView);
@@ -1033,5 +1033,53 @@ namespace Terminal.Gui.TopLevelTests {
 			Application.Driver.GetCursorVisibility (out cursor);
 			Assert.Equal (CursorVisibility.Invisible, cursor);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void IsLoaded_Application_Begin ()
+		{
+			var top = Application.Top;
+			Assert.False (top.IsLoaded);
+
+			Application.Begin (top);
+			Assert.True (top.IsLoaded);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void IsLoaded_With_Sub_Toplevel_Application_Begin_NeedDisplay ()
+		{
+			var top = Application.Top;
+			var subTop = new Toplevel ();
+			var view = new View (new Rect (0, 0, 20, 10));
+			subTop.Add (view);
+			top.Add (subTop);
+
+			Assert.False (top.IsLoaded);
+			Assert.False (subTop.IsLoaded);
+			Assert.Equal (new Rect (0, 0, 20, 10), view.Frame);
+			Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay);
+
+			view.LayoutStarted += view_LayoutStarted;
+
+			void view_LayoutStarted (View.LayoutEventArgs e)
+			{
+				Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay);
+				view.LayoutStarted -= view_LayoutStarted;
+			}
+
+			Application.Begin (top);
+
+			Assert.True (top.IsLoaded);
+			Assert.True (subTop.IsLoaded);
+			Assert.Equal (new Rect (0, 0, 20, 10), view.Frame);
+
+			view.Frame = new Rect (1, 3, 10, 5);
+			Assert.Equal (new Rect (1, 3, 10, 5), view.Frame);
+			Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay);
+
+			view.Redraw (view.Bounds);
+			view.Frame = new Rect (1, 3, 10, 5);
+			Assert.Equal (new Rect (1, 3, 10, 5), view.Frame);
+			Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay);
+		}
 	}
 }

+ 25 - 24
UnitTests/Types/DimTests.cs

@@ -83,11 +83,11 @@ namespace Terminal.Gui.TypeTests {
 			var testVal = Rect.Empty;
 			testVal = Rect.Empty;
 			dim = Dim.Width (new View (testVal));
-			Assert.Equal ($"DimView(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
+			Assert.Equal ($"View(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
 
 			testVal = new Rect (1, 2, 3, 4);
 			dim = Dim.Width (new View (testVal));
-			Assert.Equal ($"DimView(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
+			Assert.Equal ($"View(Width,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
 		}
 
 		[Fact]
@@ -141,11 +141,11 @@ namespace Terminal.Gui.TypeTests {
 			var testVal = Rect.Empty;
 			testVal = Rect.Empty;
 			dim = Dim.Height (new View (testVal));
-			Assert.Equal ($"DimView(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
+			Assert.Equal ($"View(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
 
 			testVal = new Rect (1, 2, 3, 4);
 			dim = Dim.Height (new View (testVal));
-			Assert.Equal ($"DimView(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
+			Assert.Equal ($"View(Height,View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ());
 		}
 
 		// TODO: Other Dim.Height tests (e.g. Equal?)
@@ -435,12 +435,12 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (49, f2.Frame.Width); // 50-1=49
 				Assert.Equal (5, f2.Frame.Height);
 	
-				Assert.Equal ("Combine(DimView(Width,FrameView()({X=0,Y=0,Width=49,Height=5}))-Absolute(2))", v1.Width.ToString ());
+				Assert.Equal ("Combine(View(Width,FrameView()({X=0,Y=0,Width=49,Height=5}))-Absolute(2))", v1.Width.ToString ());
 				Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
 				Assert.Equal (47, v1.Frame.Width); // 49-2=47
 				Assert.Equal (89, v1.Frame.Height); // 98-5-2-2=89
 
-				Assert.Equal ("Combine(DimView(Width,FrameView()({X=49,Y=0,Width=49,Height=5}))-Absolute(2))", v2.Width.ToString ());
+				Assert.Equal ("Combine(View(Width,FrameView()({X=49,Y=0,Width=49,Height=5}))-Absolute(2))", v2.Width.ToString ());
 				Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
 				Assert.Equal (47, v2.Frame.Width); // 49-2=47
 				Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89
@@ -455,8 +455,8 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (50, v4.Frame.Width);
 				Assert.Equal (50, v4.Frame.Height);
 
-				Assert.Equal ("Combine(DimView(Width,Button()({X=2,Y=7,Width=47,Height=89}))-DimView(Width,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Width.ToString ());
-				Assert.Equal ("Combine(DimView(Height,Button()({X=2,Y=7,Width=47,Height=89}))-DimView(Height,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Height.ToString ());
+				Assert.Equal ("Combine(View(Width,Button()({X=2,Y=7,Width=47,Height=89}))-View(Width,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Width.ToString ());
+				Assert.Equal ("Combine(View(Height,Button()({X=2,Y=7,Width=47,Height=89}))-View(Height,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Height.ToString ());
 				Assert.Equal (38, v5.Frame.Width); // 47-9=38
 				Assert.Equal (80, v5.Frame.Height); // 89-9=80
 
@@ -466,6 +466,7 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (18, v6.Frame.Height); // 89*20%=18
 
 				w.Width = 200;
+				Assert.True (t.LayoutNeeded);
 				w.Height = 200;
 				t.LayoutSubviews ();
 
@@ -477,32 +478,32 @@ namespace Terminal.Gui.TypeTests {
 				f1.Text = "Frame1";
 				Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ());
 				Assert.Equal ("Absolute(5)", f1.Height.ToString ());
-				Assert.Equal (49, f1.Frame.Width); // 100*0.5=49
+				Assert.Equal (99, f1.Frame.Width); // 100-1=99
 				Assert.Equal (5, f1.Frame.Height);
 
 				f2.Text = "Frame2";
 				Assert.Equal ("Fill(0)", f2.Width.ToString ());
 				Assert.Equal ("Absolute(5)", f2.Height.ToString ());
-				Assert.Equal (49, f2.Frame.Width); // f2.X = Pos.Right(f1), thus 50-1=49
+				Assert.Equal (99, f2.Frame.Width); // 100-1=99
 				Assert.Equal (5, f2.Frame.Height);
 
 				v1.Text = "Button1";
-				Assert.Equal ("Combine(DimView(Width,FrameView()({X=0,Y=0,Width=49,Height=5}))-Absolute(2))", v1.Width.ToString ());
+				Assert.Equal ("Combine(View(Width,FrameView()({X=0,Y=0,Width=99,Height=5}))-Absolute(2))", v1.Width.ToString ());
 				Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
-				Assert.Equal (47, v1.Frame.Width); // 49-2=47
-				Assert.Equal (89, v1.Frame.Height); // 98-2-7=89
+				Assert.Equal (97, v1.Frame.Width); // 99-2=97
+				Assert.Equal (189, v1.Frame.Height); // 198-2-7=189
 
 				v2.Text = "Button2";
-				Assert.Equal ("Combine(DimView(Width,FrameView()({X=49,Y=0,Width=49,Height=5}))-Absolute(2))", v2.Width.ToString ());
+				Assert.Equal ("Combine(View(Width,FrameView()({X=99,Y=0,Width=99,Height=5}))-Absolute(2))", v2.Width.ToString ());
 				Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
-				Assert.Equal (47, v2.Frame.Width); // 49-2=47
-				Assert.Equal (89, v2.Frame.Height); // 98-2-7=89
+				Assert.Equal (97, v2.Frame.Width); // 99-2=97
+				Assert.Equal (189, v2.Frame.Height); // 198-2-7=189
 
 				v3.Text = "Button3";
 				Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ());
 				Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ());
-				Assert.Equal (9, v3.Frame.Width); // 98*10%=9 * Percent is related to the super-view if it isn't null otherwise the view width
-				Assert.Equal (9, v3.Frame.Height); // 99*10%=9
+				Assert.Equal (19, v3.Frame.Width); // 198*10%=19 * Percent is related to the super-view if it isn't null otherwise the view width
+				Assert.Equal (19, v3.Frame.Height); // 199*10%=19
 
 				v4.Text = "Button4";
 				v4.AutoSize = false;
@@ -517,16 +518,16 @@ namespace Terminal.Gui.TypeTests {
 				Assert.Equal (1, v4.Frame.Height); // 1 because is Dim.DimAbsolute
 
 				v5.Text = "Button5";
-				Assert.Equal ("Combine(DimView(Width,Button()({X=2,Y=7,Width=47,Height=89}))-DimView(Width,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Width.ToString ());
-				Assert.Equal ("Combine(DimView(Height,Button()({X=2,Y=7,Width=47,Height=89}))-DimView(Height,Button()({X=0,Y=0,Width=9,Height=9})))", v5.Height.ToString ());
-				Assert.Equal (38, v5.Frame.Width); // 47-9=38
-				Assert.Equal (80, v5.Frame.Height); // 89-9=80
+				Assert.Equal ("Combine(View(Width,Button()({X=2,Y=7,Width=97,Height=189}))-View(Width,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Width.ToString ());
+				Assert.Equal ("Combine(View(Height,Button()({X=2,Y=7,Width=97,Height=189}))-View(Height,Button()({X=0,Y=0,Width=19,Height=19})))", v5.Height.ToString ());
+				Assert.Equal (78, v5.Frame.Width); // 97-9=78
+				Assert.Equal (170, v5.Frame.Height); // 189-19=170
 
 				v6.Text = "Button6";
 				Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ());
 				Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ());
-				Assert.Equal (9, v6.Frame.Width); // 49*20%=9
-				Assert.Equal (18, v6.Frame.Height); // 98-7*20=18
+				Assert.Equal (19, v6.Frame.Width); // 99*20%=19
+				Assert.Equal (38, v6.Frame.Height); // 198-7*20=18
 			};
 
 			Application.Iteration += () => Application.RequestStop ();

+ 1 - 1
UnitTests/UnitTests.csproj

@@ -22,7 +22,7 @@
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
-    <PackageReference Include="ReportGenerator" Version="5.1.17" />
+    <PackageReference Include="ReportGenerator" Version="5.1.18" />
     <PackageReference Include="System.Collections" Version="4.3.0" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">

+ 10 - 10
UnitTests/Views/ScrollBarViewTests.cs

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

+ 223 - 5
UnitTests/Views/ScrollViewTests.cs

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