소스 검색

Fixes #2569. Borders scenarios needed to be refactored. (#2570)

* Fixes #2569. Borders scenarios needed to be refactored.

* Fix border title with width equal to 4 and thickness top grater than 1.

* Improves border manipulation on borders scenarios.

* Prevents null value on the margin, border and padding thickness on the border scenarios.

* Remove NStack using dependence and fix prior commit.

* Refactoring the Frames scenario.

* Deleted borders scenarios.

* I did not realize that it was changed to SetSubViewNeedsDisplay.

* Re-layout; fixed colorpicker; fixed radio group

* Remove Thickness.IsEmpty as requested.

* Change the Frames scenario as requested.

---------

Co-authored-by: Tig Kindel <[email protected]>
BDisp 2 년 전
부모
커밋
105ff9ab54

+ 10 - 9
Terminal.Gui/Application.cs

@@ -56,7 +56,7 @@ namespace Terminal.Gui {
 
 		// For Unit testing - ignores UseSystemConsole
 		internal static bool _forceFakeConsole;
-		
+
 		private static bool? _enableConsoleScrolling;
 		/// <summary>
 		/// The current <see cref="ConsoleDriver.EnableConsoleScrolling"/> used in the terminal.
@@ -119,7 +119,7 @@ namespace Terminal.Gui {
 			   File.Exists (Path.Combine (assemblyLocation, cultureInfo.Name, resourceFilename))
 			).ToList ();
 		}
-		
+
 		#region Initialization (Init/Shutdown)
 
 		/// <summary>
@@ -357,7 +357,7 @@ namespace Terminal.Gui {
 			/// For debug (see DEBUG_IDISPOSABLE define) purposes; the runstate instances that have been created
 			/// </summary>
 			public static List<RunState> Instances = new List<RunState> ();
-			
+
 			/// <summary>
 			/// Creates a new RunState object.
 			/// </summary>
@@ -618,7 +618,7 @@ namespace Terminal.Gui {
 				}
 #endif
 			}
-		}		
+		}
 
 		/// <summary>
 		/// Triggers a refresh of the entire display.
@@ -630,6 +630,7 @@ namespace Terminal.Gui {
 			foreach (var v in _toplevels.Reverse ()) {
 				if (v.Visible) {
 					v.SetNeedsDisplay ();
+					v.SetSubViewNeedsDisplay ();
 					v.Redraw (v.Bounds);
 				}
 				last = v;
@@ -763,7 +764,7 @@ namespace Terminal.Gui {
 			firstIteration = false;
 
 			if (state.Toplevel != Top
-				&& (!Top._needsDisplay.IsEmpty || Top._childNeedsDisplay || Top.LayoutNeeded)) {
+				&& (!Top._needsDisplay.IsEmpty || Top._subViewNeedsDisplay || Top.LayoutNeeded)) {
 				state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
 				Top.Redraw (Top.Bounds);
 				foreach (var top in _toplevels.Reverse ()) {
@@ -775,14 +776,14 @@ namespace Terminal.Gui {
 			}
 			if (_toplevels.Count == 1 && state.Toplevel == Top
 				&& (Driver.Cols != state.Toplevel.Frame.Width || Driver.Rows != state.Toplevel.Frame.Height)
-				&& (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._childNeedsDisplay || state.Toplevel.LayoutNeeded)) {
+				&& (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._subViewNeedsDisplay || state.Toplevel.LayoutNeeded)) {
 
 				Driver.SetAttribute (Colors.TopLevel.Normal);
 				state.Toplevel.Clear (new Rect (0, 0, Driver.Cols, Driver.Rows));
 
 			}
 
-			if (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._childNeedsDisplay || state.Toplevel.LayoutNeeded
+			if (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._subViewNeedsDisplay || state.Toplevel.LayoutNeeded
 				|| OverlappedChildNeedsDisplay ()) {
 				state.Toplevel.Redraw (state.Toplevel.Bounds);
 				//if (state.Toplevel.SuperView != null) {
@@ -796,7 +797,7 @@ namespace Terminal.Gui {
 				Driver.UpdateCursor ();
 			}
 			if (state.Toplevel != Top && !state.Toplevel.Modal
-				&& (!Top._needsDisplay.IsEmpty || Top._childNeedsDisplay || Top.LayoutNeeded)) {
+				&& (!Top._needsDisplay.IsEmpty || Top._subViewNeedsDisplay || Top.LayoutNeeded)) {
 				Top.Redraw (Top.Bounds);
 			}
 		}
@@ -892,7 +893,7 @@ namespace Terminal.Gui {
 				NotifyStopRunState?.Invoke (top, new ToplevelEventArgs (top));
 			}
 		}
-		
+
 		/// <summary>
 		/// Building block API: completes the execution of a <see cref="Toplevel"/> that was started with <see cref="Begin(Toplevel)"/> .
 		/// </summary>

+ 20 - 0
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -344,6 +344,26 @@ namespace Terminal.Gui {
 		/// </summary>
 		internal string schemeBeingSet = "";
 
+		/// <summary>
+		/// Creates a new instance.
+		/// </summary>
+		public ColorScheme() { }
+
+		/// <summary>
+		/// Creates a new instance, initialized with the values from <paramref name="scheme"/>.
+		/// </summary>
+		/// <param name="scheme">The scheme to initlize the new instance with.</param>
+		public ColorScheme (ColorScheme scheme) : base()
+		{
+			if (scheme != null) {
+				_normal = scheme.Normal;
+				_focus = scheme.Focus;
+				_hotNormal = scheme.HotNormal;
+				_disabled = scheme.Disabled;
+				_hotFocus = scheme.HotFocus;
+			}
+		}
+
 		/// <summary>
 		/// The foreground and background color for text when the view is not focused, hot, or disabled.
 		/// </summary>

+ 68 - 44
Terminal.Gui/View/Frame.cs

@@ -124,9 +124,13 @@ namespace Terminal.Gui {
 			if (Thickness == Thickness.Empty) return;
 
 			if (ColorScheme != null) {
-				Driver.SetAttribute (ColorScheme.Normal);
+				Driver.SetAttribute (GetNormalColor ());
 			} else {
-				Driver.SetAttribute (Parent.GetNormalColor ());
+				if (Id == "Padding") {
+					Driver.SetAttribute (new Attribute (Parent.ColorScheme.HotNormal.Background, Parent.ColorScheme.HotNormal.Foreground));
+				} else {
+					Driver.SetAttribute (Parent.GetNormalColor ());
+				}
 			}
 
 			//Driver.SetAttribute (Colors.Error.Normal);
@@ -154,43 +158,51 @@ namespace Terminal.Gui {
 			var topTitleLineY = borderBounds.Y;
 			var titleY = borderBounds.Y;
 			var titleBarsLength = 0; // the little vertical thingies
-			var maxTitleWidth = Math.Min (Parent.Title.ConsoleWidth, screenBounds.Width - 4);
+			var maxTitleWidth = Math.Min (Parent.Title.ConsoleWidth, Math.Min (screenBounds.Width - 4, borderBounds.Width - 4));
 			var sideLineLength = borderBounds.Height;
+			var canDrawBorder = borderBounds.Width > 0 && borderBounds.Height > 0;
 
-			if (Thickness.Top == 2) {
-				topTitleLineY = borderBounds.Y - 1;
-				titleY = topTitleLineY + 1;
-				titleBarsLength = 2;
-			}
+			if (!ustring.IsNullOrEmpty (Parent?.Title)) {
+				if (Thickness.Top == 2) {
+					topTitleLineY = borderBounds.Y - 1;
+					titleY = topTitleLineY + 1;
+					titleBarsLength = 2;
+				}
 
-			// ┌────┐
-			//┌┘View└
-			//│
-			if (Thickness.Top == 3) {
-				topTitleLineY = borderBounds.Y - (Thickness.Top - 1);
-				titleY = topTitleLineY + 1;
-				titleBarsLength = 3;
-				sideLineLength++;
-			}
+				// ┌────┐
+				//┌┘View└
+				//│
+				if (Thickness.Top == 3) {
+					topTitleLineY = borderBounds.Y - (Thickness.Top - 1);
+					titleY = topTitleLineY + 1;
+					titleBarsLength = 3;
+					sideLineLength++;
+				}
+
+				// ┌────┐
+				//┌┘View└
+				//│
+				if (Thickness.Top > 3) {
+					topTitleLineY = borderBounds.Y - 2;
+					titleY = topTitleLineY + 1;
+					titleBarsLength = 3;
+					sideLineLength++;
+				}
 
-			// ┌────┐
-			//┌┘View└
-			//│
-			if (Thickness.Top > 3) {
-				topTitleLineY = borderBounds.Y - 2;
-				titleY = topTitleLineY + 1;
-				titleBarsLength = 3;
-				sideLineLength++;
 			}
 
-			if (Id == "Border" && Thickness.Top > 0 && maxTitleWidth > 0 && !ustring.IsNullOrEmpty (Parent?.Title)) {
+			if (Id == "Border" && canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && !ustring.IsNullOrEmpty (Parent?.Title)) {
 				var prevAttr = Driver.GetAttribute ();
-				Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
-				DrawTitle (new Rect (borderBounds.X, titleY, Math.Min (borderBounds.Width - 4, Parent.Title.ConsoleWidth), 1), Parent?.Title);
+				if (ColorScheme != null) {
+					Driver.SetAttribute (HasFocus ? GetHotNormalColor () : GetNormalColor ());
+				} else {
+					Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
+				}
+				DrawTitle (new Rect (borderBounds.X, titleY, maxTitleWidth, 1), Parent?.Title);
 				Driver.SetAttribute (prevAttr);
 			}
 
-			if (Id == "Border" && BorderStyle != LineStyle.None) {
+			if (Id == "Border" && canDrawBorder && BorderStyle != LineStyle.None) {
 				LineCanvas lc = Parent?.LineCanvas;
 
 				var drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height > 1;
@@ -198,48 +210,56 @@ namespace Terminal.Gui {
 				var drawBottom = Thickness.Bottom > 0 && Frame.Width > 1;
 				var drawRight = Thickness.Right > 0 && (Frame.Height > 1 || Thickness.Top == 0);
 
+				var prevAttr = Driver.GetAttribute ();
+				if (ColorScheme != null) {
+					Driver.SetAttribute (GetNormalColor ());
+				} else {
+					Driver.SetAttribute (Parent.GetNormalColor ());
+				}
+
 				if (drawTop) {
 					// ╔╡Title╞═════╗
 					// ╔╡╞═════╗
-					if (Frame.Width < 4 || ustring.IsNullOrEmpty (Parent?.Title)) {
+					if (borderBounds.Width < 4 || ustring.IsNullOrEmpty (Parent?.Title)) {
 						// ╔╡╞╗ should be ╔══╗
-						lc.AddLine (new Point (borderBounds.Location.X, titleY), borderBounds.Width, Orientation.Horizontal, BorderStyle);
+						lc.AddLine (new Point (borderBounds.Location.X, titleY), borderBounds.Width, Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
 					} else {
 
 						// ┌────┐
 						//┌┘View└
 						//│
 						if (Thickness.Top == 2) {
-							lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle);
+							lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
 						}
 						// ┌────┐
 						//┌┘View└
 						//│
-						if (Thickness.Top > 2) {
-							lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle);
-							lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY + 2), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle);
+						if (borderBounds.Width >= 4 && Thickness.Top > 2) {
+							lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
+							lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY + 2), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
 						}
 
 						// ╔╡Title╞═════╗
 						// Add a short horiz line for ╔╡
-						lc.AddLine (new Point (borderBounds.Location.X, titleY), 2, Orientation.Horizontal, BorderStyle);
+						lc.AddLine (new Point (borderBounds.Location.X, titleY), 2, Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
 						// Add a vert line for ╔╡
-						lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), titleBarsLength, Orientation.Vertical, LineStyle.Single);
+						lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), titleBarsLength, Orientation.Vertical, LineStyle.Single, Driver.GetAttribute ());
 						// Add a vert line for ╞
-						lc.AddLine (new Point (borderBounds.X + 1 + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2) - 1, topTitleLineY), titleBarsLength, Orientation.Vertical, LineStyle.Single);
+						lc.AddLine (new Point (borderBounds.X + 1 + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2) - 1, topTitleLineY), titleBarsLength, Orientation.Vertical, LineStyle.Single, Driver.GetAttribute ());
 						// Add the right hand line for ╞═════╗
-						lc.AddLine (new Point (borderBounds.X + 1 + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2) - 1, titleY), borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle);
+						lc.AddLine (new Point (borderBounds.X + 1 + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2) - 1, titleY), borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
 					}
 				}
 				if (drawLeft) {
-					lc.AddLine (new Point (borderBounds.Location.X, titleY), sideLineLength, Orientation.Vertical, BorderStyle);
+					lc.AddLine (new Point (borderBounds.Location.X, titleY), sideLineLength, Orientation.Vertical, BorderStyle, Driver.GetAttribute ());
 				}
 				if (drawBottom) {
-					lc.AddLine (new Point (borderBounds.X, borderBounds.Y + borderBounds.Height - 1), borderBounds.Width, Orientation.Horizontal, BorderStyle);
+					lc.AddLine (new Point (borderBounds.X, borderBounds.Y + borderBounds.Height - 1), borderBounds.Width, Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
 				}
 				if (drawRight) {
-					lc.AddLine (new Point (borderBounds.X + borderBounds.Width - 1, titleY), sideLineLength, Orientation.Vertical, BorderStyle);
+					lc.AddLine (new Point (borderBounds.X + borderBounds.Width - 1, titleY), sideLineLength, Orientation.Vertical, BorderStyle, Driver.GetAttribute ());
 				}
+				Driver.SetAttribute (prevAttr);
 
 				// TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
 				if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler) {
@@ -251,8 +271,12 @@ namespace Terminal.Gui {
 
 					// Redraw title 
 					if (drawTop && Id == "Border" && maxTitleWidth > 0 && !ustring.IsNullOrEmpty (Parent?.Title)) {
-						var prevAttr = Driver.GetAttribute ();
-						Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
+						prevAttr = Driver.GetAttribute ();
+						if (ColorScheme != null) {
+							Driver.SetAttribute (HasFocus ? GetHotNormalColor () : GetNormalColor ());
+						} else {
+							Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
+						}
 						DrawTitle (new Rect (borderBounds.X, titleY, Parent.Title.ConsoleWidth, 1), Parent?.Title);
 						Driver.SetAttribute (prevAttr);
 					}

+ 1 - 0
Terminal.Gui/View/Layout/PosDim.cs

@@ -345,6 +345,7 @@ namespace Terminal.Gui {
 				case 3: tside = "bottom"; break;
 				default: tside = "unknown"; break;
 				}
+				// Note: We do not checkt `Target` for null here to intentionally throw if so
 				return $"View({tside},{Target.ToString ()})";
 			}
 

+ 10 - 10
Terminal.Gui/View/ViewDrawing.cs

@@ -76,12 +76,12 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Removes the <see cref="_needsDisplay"/> and the <see cref="_childNeedsDisplay"/> setting on this view.
+		/// Removes the <see cref="_needsDisplay"/> and the <see cref="_subViewNeedsDisplay"/> setting on this view.
 		/// </summary>
 		protected void ClearNeedsDisplay ()
 		{
 			_needsDisplay = Rect.Empty;
-			_childNeedsDisplay = false;
+			_subViewNeedsDisplay = false;
 		}
 
 		// The view-relative region that needs to be redrawn
@@ -102,9 +102,9 @@ namespace Terminal.Gui {
 		/// <param name="region">The view-relative region that needs to be redrawn.</param>
 		public void SetNeedsDisplay (Rect region)
 		{
-			if (_needsDisplay.IsEmpty)
+			if (_needsDisplay.IsEmpty) {
 				_needsDisplay = region;
-			else {
+			} else {
 				var x = Math.Min (_needsDisplay.X, region.X);
 				var y = Math.Min (_needsDisplay.Y, region.Y);
 				var w = Math.Max (_needsDisplay.Width, region.Width);
@@ -125,18 +125,18 @@ namespace Terminal.Gui {
 				}
 		}
 
-		internal bool _childNeedsDisplay { get; private set; }
+		internal bool _subViewNeedsDisplay { get; private set; }
 
 		/// <summary>
 		/// Indicates that any Subviews (in the <see cref="Subviews"/> list) need to be repainted.
 		/// </summary>
 		public void SetSubViewNeedsDisplay ()
 		{
-			if (_childNeedsDisplay) {
+			if (_subViewNeedsDisplay) {
 				return;
 			}
-			_childNeedsDisplay = true;
-			if (_superView != null && !_superView._childNeedsDisplay)
+			_subViewNeedsDisplay = true;
+			if (_superView != null && !_superView._subViewNeedsDisplay)
 				_superView.SetSubViewNeedsDisplay ();
 		}
 
@@ -410,8 +410,8 @@ namespace Terminal.Gui {
 				LineCanvas.Clear ();
 			}
 
-			if (Subviews.Select (s => s.SuperViewRendersLineCanvas).Count () > 0) {
-				foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas)) {
+			if (Subviews.Where (s => s.SuperViewRendersLineCanvas).Count () > 0) {
+				foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas == true)) {
 					// Combine the LineCavas'
 					LineCanvas.Merge (subview.LineCanvas);
 					subview.LineCanvas.Clear ();

+ 12 - 2
Terminal.Gui/View/ViewLayout.cs

@@ -379,6 +379,10 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public bool GetMinimumBounds (out Size size)
 		{
+			if (!IsInitialized) {
+				size = new Size (0, 0);
+				return false;
+			}
 			size = Bounds.Size;
 
 			if (!AutoSize && !ustring.IsNullOrEmpty (TextFormatter.Text)) {
@@ -962,7 +966,7 @@ namespace Terminal.Gui {
 
 			var aSize = true;
 			var nBoundsSize = GetAutoSize ();
-			if (nBoundsSize != Bounds.Size) {
+			if (IsInitialized && nBoundsSize != Bounds.Size) {
 				if (ForceValidatePosDim) {
 					aSize = SetWidthHeight (nBoundsSize);
 				} else {
@@ -1007,7 +1011,13 @@ namespace Terminal.Gui {
 		/// <returns>The <see cref="Size"/> required to fit the text.</returns>
 		public Size GetAutoSize ()
 		{
-			var rect = TextFormatter.CalcRect (Bounds.X, Bounds.Y, TextFormatter.Text, TextFormatter.Direction);
+			int x = 0;
+			int y = 0;
+			if (IsInitialized) {
+				x = Bounds.X;
+				y = Bounds.Y; 
+			}
+			var rect = TextFormatter.CalcRect (x, y,TextFormatter.Text, TextFormatter.Direction);
 			var newWidth = rect.Size.Width - GetHotKeySpecifierLength () + Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal;
 			var newHeight = rect.Size.Height - GetHotKeySpecifierLength (false) + Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical;
 			return new Size (newWidth, newHeight);

+ 106 - 71
Terminal.Gui/Views/ColorPicker.cs

@@ -1,34 +1,79 @@
 using System;
+using System.Reflection;
 using NStack;
+using static Terminal.Gui.SpinnerStyle;
 
 namespace Terminal.Gui {
 
+	/// <summary>
+	/// Event arguments for the <see cref="Color"/> events.
+	/// </summary>
+	public class ColorEventArgs : EventArgs {
+
+		/// <summary>
+		/// Initializes a new instance of <see cref="ColorEventArgs"/>
+		/// </summary>
+		public ColorEventArgs ()
+		{
+		}
+
+		/// <summary>
+		/// The new Thickness.
+		/// </summary>
+		public Color Color { get; set; }
+
+		/// <summary>
+		/// The previous Thickness.
+		/// </summary>
+		public Color PreviousColor { get; set; }
+	}
+
 	/// <summary>
 	/// The <see cref="ColorPicker"/> <see cref="View"/> Color picker.
 	/// </summary>
 	public class ColorPicker : View {
+		private int _selectColorIndex = (int)Color.Black;
+
 		/// <summary>
-		/// Number of colors on a line.
+		/// Columns of color boxes
 		/// </summary>
-		private static readonly int colorsPerLine = 8;
+		private int _cols = 8;
 
 		/// <summary>
-		/// Number of color lines.
+		/// Rows of color boxes
 		/// </summary>
-		private static readonly int lineCount = 2;
+		private int _rows = 2;
 
 		/// <summary>
-		/// Horizontal zoom.
+		/// Width of a color box
 		/// </summary>
-		private static readonly int horizontalZoom = 4;
+		public int BoxWidth {
+			get => _boxWidth;
+			set {
+				if (_boxWidth != value) {
+					_boxWidth = value;
+					SetNeedsLayout ();
+				}
+			}
+		}
+		private int _boxWidth = 4;
 
 		/// <summary>
-		/// Vertical zoom.
+		/// Height of a color box
 		/// </summary>
-		private static readonly int verticalZoom = 2;
+		public int BoxHeight {
+			get => _boxHeight;
+			set {
+				if (_boxHeight != value) {
+					_boxHeight = value;
+					SetNeedsLayout ();
+				}
+			}
+		}
+		private int _boxHeight = 2;
 
 		// Cursor runes.
-		private static readonly Rune [] cursorRunes = new Rune []
+		private static readonly Rune [] _cursorRunes = new Rune []
 		{
 			0x250C, 0x2500, 0x2500, 0x2510,
 			0x2514, 0x2500, 0x2500, 0x2518
@@ -39,11 +84,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Point Cursor {
 			get {
-				return new Point (selectColorIndex % colorsPerLine, selectColorIndex / colorsPerLine);
+				return new Point (_selectColorIndex % _cols, _selectColorIndex / _cols);
 			}
 
 			set {
-				var colorIndex = value.Y * colorsPerLine + value.X;
+				var colorIndex = value.Y * _cols + value.X;
 				SelectedColor = (Color)colorIndex;
 			}
 		}
@@ -51,21 +96,23 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Fired when a color is picked.
 		/// </summary>
-		public event EventHandler ColorChanged;
-
-		private int selectColorIndex = (int)Color.Black;
+		public event EventHandler<ColorEventArgs> ColorChanged;
 
 		/// <summary>
 		/// Selected color.
 		/// </summary>
 		public Color SelectedColor {
 			get {
-				return (Color)selectColorIndex;
+				return (Color)_selectColorIndex;
 			}
 
 			set {
-				selectColorIndex = (int)value;
-				ColorChanged?.Invoke (this, EventArgs.Empty);
+				Color prev = (Color)_selectColorIndex;
+				_selectColorIndex = (int)value;
+				ColorChanged?.Invoke (this, new ColorEventArgs () {
+					PreviousColor = prev,
+					Color = value,
+				});
 				SetNeedsDisplay ();
 			}
 		}
@@ -73,48 +120,19 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Initializes a new instance of <see cref="ColorPicker"/>.
 		/// </summary>
-		public ColorPicker () : base ("Color Picker")
+		public ColorPicker ()
 		{
-			Initialize ();
+			SetInitialProperties ();
 		}
 
-		/// <summary>
-		/// Initializes a new instance of <see cref="ColorPicker"/>.
-		/// </summary>
-		/// <param name="title">Title.</param>
-		public ColorPicker (ustring title) : base (title)
-		{
-			Initialize ();
-		}
-
-		/// <summary>
-		/// Initializes a new instance of <see cref="ColorPicker"/>.
-		/// </summary>
-		/// <param name="point">Location point.</param>
-		/// <param name="title">Title.</param>
-		public ColorPicker (Point point, ustring title) : this (point.X, point.Y, title)
-		{
-		}
-
-		/// <summary>
-		/// Initializes a new instance of <see cref="ColorPicker"/>.
-		/// </summary>
-		/// <param name="x">X location.</param>
-		/// <param name="y">Y location.</param>
-		/// <param name="title">Title</param>
-		public ColorPicker (int x, int y, ustring title) : base (x, y, title)
-		{
-			Initialize ();
-		}
-
-		private void Initialize()
+		private void SetInitialProperties ()
 		{
 			CanFocus = true;
-			Width = colorsPerLine * horizontalZoom;
-			Height = lineCount * verticalZoom + 1;
-
 			AddCommands ();
 			AddKeyBindings ();
+			LayoutStarted += (o, a) => {
+				Bounds = new Rect (Bounds.Location, new Size (_cols * BoxWidth, _rows * BoxHeight));
+			};
 		}
 
 		/// <summary>
@@ -147,9 +165,9 @@ namespace Terminal.Gui {
 			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : GetNormalColor ());
 			var colorIndex = 0;
 
-			for (var y = 0; y < (Height.Anchor (0) - 1) / verticalZoom; y++) {
-				for (var x = 0; x < Width.Anchor (0) / horizontalZoom; x++) {
-					var foregroundColorIndex = y == 0 ? colorIndex + colorsPerLine : colorIndex - colorsPerLine;
+			for (var y = 0; y < (Bounds.Height / BoxHeight); y++) {
+				for (var x = 0; x < (Bounds.Width / BoxWidth); x++) {
+					var foregroundColorIndex = y == 0 ? colorIndex + _cols : colorIndex - _cols;
 					Driver.SetAttribute (Driver.MakeAttribute ((Color)foregroundColorIndex, (Color)colorIndex));
 					var selected = x == Cursor.X && y == Cursor.Y;
 					DrawColorBox (x, y, selected);
@@ -168,20 +186,36 @@ namespace Terminal.Gui {
 		{
 			var index = 0;
 
-			for (var zommedY = 0; zommedY < verticalZoom; zommedY++) {
-				for (var zommedX = 0; zommedX < horizontalZoom; zommedX++) {
-					Move (x * horizontalZoom + zommedX, y * verticalZoom + zommedY + 1);
-
-					if (selected) {
-						var character = cursorRunes [index];
-						Driver.AddRune (character);
-					} else {
-						Driver.AddRune (' ');
-					}
-
+			for (var zoomedY = 0; zoomedY < BoxHeight; zoomedY++) {
+				for (var zoomedX = 0; zoomedX < BoxWidth; zoomedX++) {
+					Move (x * BoxWidth + zoomedX, y * BoxHeight + zoomedY);
+					Driver.AddRune (' ');
 					index++;
 				}
 			}
+
+			if (selected) {
+				DrawFocusRect (new Rect (x * BoxWidth, y * BoxHeight, BoxWidth, BoxHeight));
+			}
+		}
+
+		private void DrawFocusRect (Rect rect)
+		{
+			var lc = new LineCanvas ();
+			if (rect.Width == 1) {
+				lc.AddLine (rect.Location, rect.Height, Orientation.Vertical, LineStyle.Dotted);
+			} else if (rect.Height == 1) {
+				lc.AddLine (rect.Location, rect.Width, Orientation.Horizontal, LineStyle.Dotted);
+			} else {
+				lc.AddLine (rect.Location, rect.Width, Orientation.Horizontal, LineStyle.Dotted);
+				lc.AddLine (new Point (rect.Location.X, rect.Location.Y + rect.Height - 1), rect.Width, Orientation.Horizontal, LineStyle.Dotted);
+
+				lc.AddLine (rect.Location, rect.Height, Orientation.Vertical, LineStyle.Dotted);
+				lc.AddLine (new Point (rect.Location.X + rect.Width - 1, rect.Location.Y), rect.Height, Orientation.Vertical, LineStyle.Dotted);
+			}
+			foreach (var p in lc.GetMap ()) {
+				AddRune (p.Key.X, p.Key.Y, p.Value);
+			}
 		}
 
 		/// <summary>
@@ -200,7 +234,7 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public virtual bool MoveRight ()
 		{
-			if (Cursor.X < colorsPerLine - 1) SelectedColor++;
+			if (Cursor.X < _cols - 1) SelectedColor++;
 			return true;
 		}
 
@@ -210,7 +244,7 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public virtual bool MoveUp ()
 		{
-			if (Cursor.Y > 0) SelectedColor -= colorsPerLine;
+			if (Cursor.Y > 0) SelectedColor -= _cols;
 			return true;
 		}
 
@@ -220,7 +254,7 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public virtual bool MoveDown ()
 		{
-			if (Cursor.Y < lineCount - 1) SelectedColor += colorsPerLine;
+			if (Cursor.Y < _rows - 1) SelectedColor += _cols;
 			return true;
 		}
 
@@ -237,11 +271,12 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override bool MouseEvent (MouseEvent me)
 		{
-			if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) || !CanFocus)
+			if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) || !CanFocus) {
 				return false;
+			}
 
 			SetFocus ();
-			Cursor = new Point (me.X / horizontalZoom, (me.Y - 1) / verticalZoom);
+			Cursor = new Point ((me.X - GetFramesThickness ().Left) / _boxWidth, (me.Y - GetFramesThickness ().Top) / _boxHeight);
 
 			return true;
 		}

+ 24 - 33
Terminal.Gui/Views/RadioGroup.cs

@@ -26,7 +26,7 @@ namespace Terminal.Gui {
 		/// <param name="selected">The index of the item to be selected, the value is clamped to the number of items.</param>
 		public RadioGroup (ustring [] radioLabels, int selected = 0) : base ()
 		{
-			Initialize (Rect.Empty, radioLabels, selected);
+			SetInitalProperties (Rect.Empty, radioLabels, selected);
 		}
 
 		/// <summary>
@@ -37,7 +37,7 @@ namespace Terminal.Gui {
 		/// <param name="selected">The index of item to be selected, the value is clamped to the number of items.</param>
 		public RadioGroup (Rect rect, ustring [] radioLabels, int selected = 0) : base (rect)
 		{
-			Initialize (rect, radioLabels, selected);
+			SetInitalProperties (rect, radioLabels, selected);
 		}
 
 		/// <summary>
@@ -52,7 +52,7 @@ namespace Terminal.Gui {
 			this (MakeRect (x, y, radioLabels != null ? radioLabels.ToList () : null), radioLabels, selected)
 		{ }
 
-		void Initialize (Rect rect, ustring [] radioLabels, int selected)
+		void SetInitalProperties (Rect rect, ustring [] radioLabels, int selected)
 		{
 			if (radioLabels == null) {
 				this.radioLabels = new List<ustring> ();
@@ -61,11 +61,7 @@ namespace Terminal.Gui {
 			}
 
 			this.selected = selected;
-			if (rect == Rect.Empty) {
-				SetWidthHeight (this.radioLabels);
-			} else {
-				Frame = rect;
-			}
+			Frame = rect;
 			CanFocus = true;
 			HotKeySpecifier = new Rune ('_');
 
@@ -82,6 +78,13 @@ namespace Terminal.Gui {
 			AddKeyBinding (Key.Home, Command.TopHome);
 			AddKeyBinding (Key.End, Command.BottomEnd);
 			AddKeyBinding (Key.Space, Command.Accept);
+
+			LayoutStarted += RadioGroup_LayoutStarted;
+		}
+
+		private void RadioGroup_LayoutStarted (object sender, EventArgs e)
+		{
+			SetWidthHeight (radioLabels);
 		}
 
 		/// <summary>
@@ -118,13 +121,9 @@ namespace Terminal.Gui {
 			switch (displayMode) {
 			case DisplayModeLayout.Vertical:
 				var r = MakeRect (0, 0, radioLabels);
-				if (IsAdded && LayoutStyle == LayoutStyle.Computed) {
-					Width = r.Width;
-					Height = radioLabels.Count;
-				} else {
-					Frame = new Rect (Frame.Location, new Size (r.Width, radioLabels.Count));
-				}
+				Bounds = new Rect (Bounds.Location, new Size (r.Width, radioLabels.Count));
 				break;
+
 			case DisplayModeLayout.Horizontal:
 				CalculateHorizontalPositions ();
 				var length = 0;
@@ -136,7 +135,7 @@ namespace Terminal.Gui {
 					Width = hr.Width;
 					Height = 1;
 				} else {
-					Frame = new Rect (Frame.Location, new Size (hr.Width, radioLabels.Count));
+					Bounds = new Rect (Bounds.Location, new Size (hr.Width, radioLabels.Count));
 				}
 				break;
 			}
@@ -150,8 +149,9 @@ namespace Terminal.Gui {
 
 			int width = 0;
 
-			foreach (var s in radioLabels)
-				width = Math.Max (s.ConsoleWidth + 3, width);
+			foreach (var s in radioLabels) {
+				width = Math.Max (s.ConsoleWidth + 2, width);
+			}
 			return new Rect (x, y, width, radioLabels.Count);
 		}
 
@@ -189,24 +189,12 @@ namespace Terminal.Gui {
 			}
 		}
 
-		//// Redraws the RadioGroup 
-		//void Update(List<ustring> newRadioLabels)
-		//{
-		//	for (int i = 0; i < radioLabels.Count; i++) {
-		//		Move(0, i);
-		//		Driver.SetAttribute(ColorScheme.Normal);
-		//		Driver.AddStr(ustring.Make(new string (' ', radioLabels[i].ConsoleWidth + 4)));
-		//	}
-		//	if (newRadioLabels.Count != radioLabels.Count) {
-		//		SetWidthHeight(newRadioLabels);
-		//	}
-		//}
-
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
+			base.Redraw (bounds);
+
 			Driver.SetAttribute (GetNormalColor ());
-			Clear ();
 			for (int i = 0; i < radioLabels.Count; i++) {
 				switch (DisplayMode) {
 				case DisplayModeLayout.Vertical:
@@ -387,11 +375,14 @@ namespace Terminal.Gui {
 			}
 			SetFocus ();
 
-			var pos = displayMode == DisplayModeLayout.Horizontal ? me.X : me.Y;
+			int boundsX = me.X - GetFramesThickness ().Left;
+			int boundsY = me.Y - GetFramesThickness ().Top;
+
+			var pos = displayMode == DisplayModeLayout.Horizontal ? boundsX : boundsY;
 			var rCount = displayMode == DisplayModeLayout.Horizontal ? horizontal.Last ().pos + horizontal.Last ().length : radioLabels.Count;
 
 			if (pos < rCount) {
-				var c = displayMode == DisplayModeLayout.Horizontal ? horizontal.FindIndex ((x) => x.pos <= me.X && x.pos + x.length - 2 >= me.X) : me.Y;
+				var c = displayMode == DisplayModeLayout.Horizontal ? horizontal.FindIndex ((x) => x.pos <= boundsX && x.pos + x.length - 2 >= boundsX) : boundsY;
 				if (c > -1) {
 					cursor = SelectedItem = c;
 					SetNeedsDisplay ();

+ 18 - 10
Terminal.Gui/Views/Toplevel.cs

@@ -633,14 +633,14 @@ namespace Terminal.Gui {
 				maxWidth = top.SuperView.Bounds.Width;
 				superView = top.SuperView;
 			}
-
-			// BUGBUG: v2 hack for now
-			var mfLength = top.Border?.Thickness.Top > 0 ? 2 : 1;
+			if (superView.Margin != null && superView == top.SuperView) {
+				maxWidth -= superView.GetFramesThickness ().Left + superView.GetFramesThickness ().Right;
+			}
 			if (top.Frame.Width <= maxWidth) {
 				nx = Math.Max (targetX, 0);
 				nx = nx + top.Frame.Width > maxWidth ? Math.Max (maxWidth - top.Frame.Width, 0) : nx;
-				if (nx + mfLength > top.Frame.X + top.Frame.Width) {
-					nx = Math.Max (top.Frame.Right - mfLength, 0);
+				if (nx > top.Frame.X + top.Frame.Width) {
+					nx = Math.Max (top.Frame.Right, 0);
 				}
 			} else {
 				nx = targetX;
@@ -680,11 +680,14 @@ namespace Terminal.Gui {
 			} else {
 				maxWidth = statusVisible ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
 			}
+			if (superView.Margin != null && superView == top.SuperView) {
+				maxWidth -= superView.GetFramesThickness ().Top + superView.GetFramesThickness ().Bottom;
+			}
 			ny = Math.Min (ny, maxWidth);
 			if (top.Frame.Height <= maxWidth) {
-				ny = ny + top.Frame.Height >= maxWidth ? Math.Max (maxWidth - top.Frame.Height, menuVisible ? 1 : 0) : ny;
-				if (ny + mfLength > top.Frame.Y + top.Frame.Height) {
-					ny = Math.Max (top.Frame.Bottom - mfLength, 0);
+				ny = ny + top.Frame.Height > maxWidth ? Math.Max (maxWidth - top.Frame.Height, menuVisible ? 1 : 0) : ny;
+				if (ny > top.Frame.Y + top.Frame.Height) {
+					ny = Math.Max (top.Frame.Bottom, 0);
 				}
 			}
 			//System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
@@ -713,9 +716,13 @@ namespace Terminal.Gui {
 			var superView = GetLocationThatFits (top, top.Frame.X, top.Frame.Y,
 				out int nx, out int ny, out _, out StatusBar sb);
 			bool layoutSubviews = false;
+			var maxWidth = 0;
+			if (superView.Margin != null && superView == top.SuperView) {
+				maxWidth -= superView.GetFramesThickness ().Left + superView.GetFramesThickness ().Right;
+			}
 			if ((superView != top || top?.SuperView != null || (top != Application.Top && top.Modal)
 				|| (top?.SuperView == null && top.IsOverlapped))
-				&& (top.Frame.X + top.Frame.Width > Driver.Cols || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
+				&& (top.Frame.X + top.Frame.Width > maxWidth || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
 
 				if ((top.X == null || top.X is Pos.PosAbsolute) && top.Frame.X != nx) {
 					top.X = nx;
@@ -750,7 +757,7 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			if (!_needsDisplay.IsEmpty || _childNeedsDisplay || LayoutNeeded) {
+			if (!_needsDisplay.IsEmpty || _subViewNeedsDisplay || LayoutNeeded) {
 				Driver.SetAttribute (GetNormalColor ());
 				Clear (ViewToScreen (bounds));
 				LayoutSubviews ();
@@ -773,6 +780,7 @@ namespace Terminal.Gui {
 					if (view.Frame.IntersectsWith (bounds) && !OutsideTopFrame (this)) {
 						view.SetNeedsLayout ();
 						view.SetNeedsDisplay (view.Bounds);
+						view.SetSubViewNeedsDisplay ();
 					}
 				}
 				base.Redraw (Bounds);

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

@@ -97,7 +97,7 @@ namespace Terminal.Gui {
 			}
 
 			foreach (var top in _toplevels) {
-				if (top != Current && top.Visible && (!top._needsDisplay.IsEmpty || top._childNeedsDisplay || top.LayoutNeeded)) {
+				if (top != Current && top.Visible && (!top._needsDisplay.IsEmpty || top._subViewNeedsDisplay || top.LayoutNeeded)) {
 					OverlappedTop.SetSubViewNeedsDisplay ();
 					return true;
 				}

+ 0 - 113
UICatalog/Scenarios/BordersComparisons.cs

@@ -1,113 +0,0 @@
-using Terminal.Gui;
-
-namespace UICatalog.Scenarios {
-	[ScenarioMetadata (Name: "Borders Comparisons", Description: "Compares Window, Toplevel and FrameView borders.")]
-	[ScenarioCategory ("Layout")]
-	[ScenarioCategory ("Borders")]
-	public class BordersComparisons : Scenario {
-		public override void Init ()
-		{
-			Application.Init ();
-
-			var borderStyle = LineStyle.Double;
-			var borderThickness = new Thickness (1, 2, 3, 4);
-			var padding = 1;
-
-			Application.Top.Text = $"Border Thickness: {borderThickness}\nPadding: {padding}";
-
-			var win = new Window (new Rect (5, 5, 40, 20)) { Title = "Window" };
-
-			var tf1 = new TextField ("1234567890") { Width = 10 };
-
-			var button = new Button ("Press me!") {
-				X = Pos.Center (),
-				Y = Pos.Center (),
-			};
-			button.Clicked += (s,e) => 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 () - 1,
-			};
-			var tv = new TextView () {
-				Y = Pos.AnchorEnd (2),
-				Width = 10,
-				Height = Dim.Fill (),
-				Text = "1234567890"
-			};
-			var tf2 = new TextField ("1234567890") {
-				X = Pos.AnchorEnd (10),
-				Y = Pos.AnchorEnd (1),
-				Width = 10
-			};
-			win.Add (tf1, button, label, tv, tf2);
-			Application.Top.Add (win);
-
-			var topLevel = new Toplevel (new Rect (50, 5, 40, 20));
-			//topLevel.Border.Thickness = borderThickness;
-			//topLevel.Border.BorderStyle = borderStyle;
-			//topLevel.Padding.Thickness = paddingThickness;
-
-			var tf3 = new TextField ("1234567890") { Width = 10 };
-
-			var button2 = new Button ("Press me!") {
-				X = Pos.Center (),
-				Y = Pos.Center (),
-			};
-			button2.Clicked += (s,e) => 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 () - 1,
-			};
-			var tv2 = new TextView () {
-				Y = Pos.AnchorEnd (2),
-				Width = 10,
-				Height = Dim.Fill (),
-				Text = "1234567890"
-			};
-			var tf4 = new TextField ("1234567890") {
-				X = Pos.AnchorEnd (10),
-				Y = Pos.AnchorEnd (1),
-				Width = 10
-			};
-			topLevel.Add (tf3, button2, label2, tv2, tf4);
-			Application.Top.Add (topLevel);
-
-			var frameView = new FrameView (new Rect (95, 5, 40, 20), "FrameView", null);
-			frameView.Border.Thickness = borderThickness;
-			frameView.Border.BorderStyle = borderStyle;
-			//frameView.Padding.Thickness = paddingThickness;
-
-			var tf5 = new TextField ("1234567890") { Width = 10 };
-
-			var button3 = new Button ("Press me!") {
-				X = Pos.Center (),
-				Y = Pos.Center (),
-			};
-			button3.Clicked += (s,e) => 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 () - 1,
-			};
-			var tv3 = new TextView () {
-				Y = Pos.AnchorEnd (2),
-				Width = 10,
-				Height = Dim.Fill (),
-				Text = "1234567890"
-			};
-			var tf6 = new TextField ("1234567890") {
-				X = Pos.AnchorEnd (10),
-				Y = Pos.AnchorEnd (1),
-				Width = 10
-			};
-			frameView.Add (tf5, button3, label3, tv3, tf6);
-			Application.Top.Add (frameView);
-
-			Application.Run ();
-		}
-
-		public override void Run ()
-		{
-			// Do nothing
-		}
-	}
-}

+ 0 - 301
UICatalog/Scenarios/BordersOnContainers.cs

@@ -1,301 +0,0 @@
-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 = LineStyle.Double;
-			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;
-
-			smartView.X = Pos.Center ();
-			smartView.Y = 0;
-			smartView.Width = 40;
-			smartView.Height = 20;
-			smartView.BorderStyle = borderStyle;
-			
-			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 += (s, e) => 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 += (s, e) => {
-				try {
-					smartView.Padding.Thickness = new Thickness (smartView.Padding.Thickness.Left,
-						int.Parse (e.NewText.ToString ()), smartView.Padding.Thickness.Right,
-						smartView.Padding.Thickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingTopEdit.Text = $"{smartView.Padding.Thickness.Top}";
-
-			Add (paddingTopEdit);
-
-			var paddingLeftEdit = new TextField ("") {
-				X = Pos.Center () - 30,
-				Y = 2,
-				Width = 5
-			};
-			paddingLeftEdit.TextChanging += (s, e) => {
-				try {
-					smartView.Padding.Thickness = new Thickness (int.Parse (e.NewText.ToString ()),
-						smartView.Padding.Thickness.Top, smartView.Padding.Thickness.Right,
-						smartView.Padding.Thickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingLeftEdit.Text = $"{smartView.Padding.Thickness.Left}";
-			Add (paddingLeftEdit);
-
-			var paddingRightEdit = new TextField ("") {
-				X = Pos.Center () - 15,
-				Y = 2,
-				Width = 5
-			};
-			paddingRightEdit.TextChanging += (s, e) => {
-				try {
-					smartView.Padding.Thickness = new Thickness (smartView.Padding.Thickness.Left,
-						smartView.Padding.Thickness.Top, int.Parse (e.NewText.ToString ()),
-						smartView.Padding.Thickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingRightEdit.Text = $"{smartView.Padding.Thickness.Right}";
-			Add (paddingRightEdit);
-
-			var paddingBottomEdit = new TextField ("") {
-				X = Pos.Center () - 22,
-				Y = 3,
-				Width = 5
-			};
-			paddingBottomEdit.TextChanging += (s, e) => {
-				try {
-					smartView.Padding.Thickness = new Thickness (smartView.Padding.Thickness.Left,
-						smartView.Padding.Thickness.Top, smartView.Padding.Thickness.Right,
-						int.Parse (e.NewText.ToString ()));
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			paddingBottomEdit.Text = $"{smartView.Padding.Thickness.Bottom}";
-			Add (paddingBottomEdit);
-
-			var replacePadding = new Button ("Replace all based on top") {
-				X = Pos.Left (paddingLeftEdit),
-				Y = 5
-			};
-			replacePadding.Clicked += (s, e) => {
-				smartView.Padding.Thickness = new Thickness (smartView.Padding.Thickness.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 += (s, e) => {
-				try {
-					smartView.Border.Thickness = new Thickness (smartView.Border.Thickness.Left,
-						int.Parse (e.NewText.ToString ()), smartView.Border.Thickness.Right,
-						smartView.Border.Thickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderTopEdit.Text = $"{smartView.Border.Thickness.Top}";
-
-			Add (borderTopEdit);
-
-			var borderLeftEdit = new TextField ("") {
-				X = Pos.Center () + 5,
-				Y = 2,
-				Width = 5
-			};
-			borderLeftEdit.TextChanging += (s, e) => {
-				try {
-					smartView.Border.Thickness = new Thickness (int.Parse (e.NewText.ToString ()),
-						smartView.Border.Thickness.Top, smartView.Border.Thickness.Right,
-						smartView.Border.Thickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderLeftEdit.Text = $"{smartView.Border.Thickness.Left}";
-			Add (borderLeftEdit);
-
-			var borderRightEdit = new TextField ("") {
-				X = Pos.Center () + 19,
-				Y = 2,
-				Width = 5
-			};
-			borderRightEdit.TextChanging += (s, e) => {
-				try {
-					smartView.Border.Thickness = new Thickness (smartView.Border.Thickness.Left,
-						smartView.Border.Thickness.Top, int.Parse (e.NewText.ToString ()),
-						smartView.Border.Thickness.Bottom);
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderRightEdit.Text = $"{smartView.Border.Thickness.Right}";
-			Add (borderRightEdit);
-
-			var borderBottomEdit = new TextField ("") {
-				X = Pos.Center () + 12,
-				Y = 3,
-				Width = 5
-			};
-			borderBottomEdit.TextChanging += (s, e) => {
-				try {
-					smartView.Border.Thickness = new Thickness (smartView.Border.Thickness.Left,
-						smartView.Border.Thickness.Top, smartView.Border.Thickness.Right,
-						int.Parse (e.NewText.ToString ()));
-				} catch {
-					if (!e.NewText.IsEmpty) {
-						e.Cancel = true;
-					}
-				}
-			};
-			borderBottomEdit.Text = $"{smartView.Border.Thickness.Bottom}";
-			Add (borderBottomEdit);
-
-			var replaceBorder = new Button ("Replace all based on top") {
-				X = Pos.Left (borderLeftEdit),
-				Y = 5
-			};
-			replaceBorder.Clicked += (s, e) => {
-				smartView.Border.Thickness = new Thickness (smartView.Border.Thickness.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 (LineStyle)).Cast<LineStyle> ().ToList ();
-			var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
-				e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
-
-				X = 2,
-				Y = 1,
-				SelectedItem = (int)smartView.BorderStyle
-			};
-			Add (rbBorderStyle);
-
-			rbBorderStyle.SelectedItemChanged += (s, e) => {
-				smartView.BorderStyle = (LineStyle)e.SelectedItem;
-				smartView.SetNeedsDisplay ();
-			};
-
-			Add (new Label ("Background:") {
-				Y = 12
-			});
-
-			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 = 13,
-				//SelectedItem = (int)smartView.BorderFrame.BackgroundColor
-			};
-			rbBackground.SelectedItemChanged += (s, e) => {
-//				smartView.Border.BackgroundColor = (Color)e.SelectedItem;
-				smartView.Border.ColorScheme = new ColorScheme() { 
-					Normal = new Terminal.Gui.Attribute(Color.Red, Color.White),
-					HotNormal = new Terminal.Gui.Attribute (Color.Magenta, Color.White),
-					Disabled = new Terminal.Gui.Attribute (Color.Gray, Color.White),
-					Focus = new Terminal.Gui.Attribute (Color.Blue, Color.White),
-					HotFocus = new Terminal.Gui.Attribute (Color.BrightBlue, Color.White),	
-				};
-			};
-			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.ForgroundColor
-			};
-			rbBorderBrush.SelectedItemChanged += (s, e) => {
-				//smartView.Border.ForgroundColor = (Color)e.SelectedItem;
-			};
-			Add (rbBorderBrush);
-
-			Add (smartView);
-
-			Title = title;
-		}
-	}
-}

+ 0 - 25
UICatalog/Scenarios/BordersOnFrameView.cs

@@ -1,25 +0,0 @@
-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 Init ()
-		{
-			Application.Init ();
-
-			var boc = new BordersOnContainers (
-				$"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
-				"FrameView",
-				new FrameView ());
-
-			Application.Run (boc);
-			Application.Shutdown ();
-		}
-
-		public override void Run ()
-		{
-		}
-	}
-}

+ 0 - 25
UICatalog/Scenarios/BordersOnWindow.cs

@@ -1,25 +0,0 @@
-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 Init ()
-		{
-			Application.Init ();
-
-			var boc = new BordersOnContainers (
-				$"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
-				"Window",
-				new Window ());
-
-			Application.Run (boc);
-			Application.Shutdown ();
-		}
-
-		public override void Run ()
-		{
-		}
-	}
-}

+ 41 - 20
UICatalog/Scenarios/ColorPicker.cs

@@ -19,17 +19,17 @@ namespace UICatalog.Scenarios {
 		/// <summary>
 		/// Foreground color label.
 		/// </summary>
-		private Label foregroundColorLabel;
+		private Label _foregroundColorLabel;
 
 		/// <summary>
 		/// Background color Label.
 		/// </summary>
-		private Label backgroundColorLabel;
+		private Label _backgroundColorLabel;
 
 		/// <summary>
 		/// Demo label.
 		/// </summary>
-		private Label demoLabel;
+		private View _demoView;
 
 		/// <summary>
 		/// Setup the scenario.
@@ -40,36 +40,57 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 
 			// Foreground ColorPicker.
-			foregroundColorPicker = new ColorPicker ("Foreground Color");
+			foregroundColorPicker = new ColorPicker () {
+				Title = "Foreground Color",
+				X = 0,
+				Y = 0,
+				BoxHeight = 3,
+				BoxWidth = 6,
+				BorderStyle = LineStyle.Single
+			};
 			foregroundColorPicker.ColorChanged += ForegroundColor_ColorChanged;
 			Win.Add (foregroundColorPicker);
 
-			foregroundColorLabel = new Label {
+			_foregroundColorLabel = new Label {
 				X = Pos.Left (foregroundColorPicker),
 				Y = Pos.Bottom (foregroundColorPicker) + 1
 			};
-			Win.Add (foregroundColorLabel);
+			Win.Add (_foregroundColorLabel);
 
 			// Background ColorPicker.
-			backgroundColorPicker = new ColorPicker ("Background Color");
+			backgroundColorPicker = new ColorPicker () { 
+				Title = "Background Color",
+				Y = 0,
+				BoxHeight = 1,
+				BoxWidth = 4,
+				BorderStyle = LineStyle.Single
+			};
 			backgroundColorPicker.X = Pos.AnchorEnd () - (Pos.Right (backgroundColorPicker) - Pos.Left (backgroundColorPicker));
 			backgroundColorPicker.ColorChanged += BackgroundColor_ColorChanged;
 			Win.Add (backgroundColorPicker);
 
-			backgroundColorLabel = new Label ();
-			backgroundColorLabel.X = Pos.AnchorEnd () - (Pos.Right (backgroundColorLabel) - Pos.Left (backgroundColorLabel));
-			backgroundColorLabel.Y = Pos.Bottom (backgroundColorPicker) + 1;
-			Win.Add (backgroundColorLabel);
+			_backgroundColorLabel = new Label ();
+			_backgroundColorLabel.X = Pos.AnchorEnd () - (Pos.Right (_backgroundColorLabel) - Pos.Left (_backgroundColorLabel));
+			_backgroundColorLabel.Y = Pos.Bottom (backgroundColorPicker) + 1;
+			Win.Add (_backgroundColorLabel);
 
 			// Demo Label.
-			demoLabel = new Label ("Lorem Ipsum");
-			demoLabel.X = Pos.Center ();
-			demoLabel.Y = 1;
-			Win.Add (demoLabel);
+			_demoView = new View () {
+				Title = "Color Sample",
+				Text = "Lorem Ipsum",
+				TextAlignment = TextAlignment.Centered,
+				VerticalTextAlignment = VerticalTextAlignment.Middle,
+				BorderStyle = LineStyle.Heavy,
+				X = Pos.Center (),
+				Y = Pos.Center (),
+				Height = 5,
+				Width = 20
+			};
+			Win.Add (_demoView);
 
 			// Set default colors.
-			backgroundColorPicker.SelectedColor = demoLabel.SuperView.ColorScheme.Normal.Background;
-			foregroundColorPicker.SelectedColor = demoLabel.SuperView.ColorScheme.Normal.Foreground;
+			backgroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Background;
+			foregroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Foreground;
 		}
 
 		/// <summary>
@@ -77,7 +98,7 @@ namespace UICatalog.Scenarios {
 		/// </summary>
 		private void ForegroundColor_ColorChanged (object sender, EventArgs e)
 		{
-			UpdateColorLabel (foregroundColorLabel, foregroundColorPicker);
+			UpdateColorLabel (_foregroundColorLabel, foregroundColorPicker);
 			UpdateDemoLabel ();
 		}
 
@@ -86,7 +107,7 @@ namespace UICatalog.Scenarios {
 		/// </summary>
 		private void BackgroundColor_ColorChanged (object sender, EventArgs e)
 		{
-			UpdateColorLabel (backgroundColorLabel, backgroundColorPicker);
+			UpdateColorLabel (_backgroundColorLabel, backgroundColorPicker);
 			UpdateDemoLabel ();
 		}
 
@@ -102,7 +123,7 @@ namespace UICatalog.Scenarios {
 		/// <summary>
 		/// Update Demo Label.
 		/// </summary>
-		private void UpdateDemoLabel () => demoLabel.ColorScheme = new ColorScheme () {
+		private void UpdateDemoLabel () => _demoView.ColorScheme = new ColorScheme () {
 			Normal = new Terminal.Gui.Attribute (foregroundColorPicker.SelectedColor, backgroundColorPicker.SelectedColor)
 		};
 	}

+ 267 - 182
UICatalog/Scenarios/Frames.cs

@@ -1,4 +1,5 @@
-using System;
+using NStack;
+using System;
 using System.Linq;
 using Terminal.Gui;
 
@@ -7,245 +8,330 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Layout")]
 	[ScenarioCategory ("Borders")]
 	public class Frames : Scenario {
+		public class FrameEditor : View {
+			private Thickness _thickness;
+			private TextField _topEdit;
+			private TextField _leftEdit;
+			private TextField _rightEdit;
+			private TextField _bottomEdit;
+			private bool _isUpdating;
 
-		public class ThicknessEditor : View {
-			private Thickness thickness;
+			private ColorPicker _foregroundColorPicker;
+			private ColorPicker _backgroundColorPicker;
+
+			public Terminal.Gui.Attribute Color { get; set; }
 
 			public Thickness Thickness {
-				get => thickness;
+				get => _thickness;
 				set {
-					thickness = value;
-					ThicknessChanged?.Invoke (this, new ThicknessEventArgs () {  Thickness = Thickness });
+					if (_isUpdating) {
+						return;
+					}
+					_thickness = value;
+					ThicknessChanged?.Invoke (this, new ThicknessEventArgs () { Thickness = Thickness });
+					if (IsInitialized) {
+						_isUpdating = true;
+						if (_topEdit.Text != _thickness.Top.ToString ()) {
+							_topEdit.Text = _thickness.Top.ToString ();
+						}
+						if (_leftEdit.Text != _thickness.Left.ToString ()) {
+							_leftEdit.Text = _thickness.Left.ToString ();
+						}
+						if (_rightEdit.Text != _thickness.Right.ToString ()) {
+							_rightEdit.Text = _thickness.Right.ToString ();
+						}
+						if (_bottomEdit.Text != _thickness.Bottom.ToString ()) {
+							_bottomEdit.Text = _thickness.Bottom.ToString ();
+						}
+						_isUpdating = false;
+					}
 				}
 			}
 
 			public event EventHandler<ThicknessEventArgs> ThicknessChanged;
+			public event EventHandler<Terminal.Gui.Attribute> AttributeChanged;
 
-			public ThicknessEditor ()
+			public FrameEditor ()
 			{
 				Margin.Thickness = new Thickness (0);
-				BorderStyle = LineStyle.Single;
+				BorderStyle = LineStyle.Double;
+				Initialized += FrameEditor_Initialized; ;
 			}
 
-			public override void BeginInit ()
+			void FrameEditor_Initialized (object sender, EventArgs e)
 			{
-				base.BeginInit ();
+				var editWidth = 3;
 
-				var topEdit = new TextField ("") {
+				_topEdit = new TextField ("") {
 					X = Pos.Center (),
 					Y = 0,
-					Width = 5
-				};
-				topEdit.TextChanging += (s, e) => {
-					try {
-						Thickness = new Thickness (Thickness.Left,
-							int.Parse (e.NewText.ToString ()), Thickness.Right,
-							Thickness.Bottom);
-					} catch {
-						if (!e.NewText.IsEmpty) {
-							e.Cancel = true;
-						}
-					}
+					Width = editWidth
 				};
-				topEdit.Text = $"{Thickness.Top}";
-
-				Add (topEdit);
+				_topEdit.TextChanging += Edit_TextChanging;
+				Add (_topEdit);
 
-				var leftEdit = new TextField ("") {
-					X = 0,
-					Y = Pos.Bottom (topEdit),
-					Width = 5
-				};
-				leftEdit.TextChanging += (s, e) => {
-					try {
-						Thickness = new Thickness (int.Parse (e.NewText.ToString ()),
-							Thickness.Top, Thickness.Right,
-							Thickness.Bottom);
-					} catch {
-						if (!e.NewText.IsEmpty) {
-							e.Cancel = true;
-						}
-					}
+				_leftEdit = new TextField ("") {
+					X = Pos.Left (_topEdit) - editWidth,
+					Y = Pos.Bottom (_topEdit),
+					Width = editWidth
 				};
-				leftEdit.Text = $"{Thickness.Left}";
-				Add (leftEdit);
+				_leftEdit.TextChanging += Edit_TextChanging;
+				Add (_leftEdit);
 
-				var rightEdit = new TextField ("") {
-					X = Pos.Right (topEdit),
-					Y = Pos.Bottom (topEdit),
-					Width = 5
-				};
-				rightEdit.TextChanging += (s, e) => {
-					try {
-						Thickness = new Thickness (Thickness.Left,
-							Thickness.Top, int.Parse (e.NewText.ToString ()),
-							Thickness.Bottom);
-					} catch {
-						if (!e.NewText.IsEmpty) {
-							e.Cancel = true;
-						}
-					}
+				_rightEdit = new TextField ("") {
+					X = Pos.Right (_topEdit),
+					Y = Pos.Bottom (_topEdit),
+					Width = editWidth
 				};
-				rightEdit.Text = $"{Thickness.Right}";
-				Add (rightEdit);
+				_rightEdit.TextChanging += Edit_TextChanging;
+				Add (_rightEdit);
 
-				var bottomEdit = new TextField ("") {
+				_bottomEdit = new TextField ("") {
 					X = Pos.Center (),
-					Y = Pos.Bottom (leftEdit),
-					Width = 5
-				};
-				bottomEdit.TextChanging += (s, e) => {
-					try {
-						Thickness = new Thickness (Thickness.Left,
-							Thickness.Top, Thickness.Right,
-							int.Parse (e.NewText.ToString ()));
-					} catch {
-						if (!e.NewText.IsEmpty) {
-							e.Cancel = true;
-						}
-					}
+					Y = Pos.Bottom (_leftEdit),
+					Width = editWidth
 				};
-				bottomEdit.Text = $"{Thickness.Bottom}";
-				Add (bottomEdit);
+				_bottomEdit.TextChanging += Edit_TextChanging;
+				Add (_bottomEdit);
 
 				var copyTop = new Button ("Copy Top") {
-					X = Pos.Center (),
-					Y = Pos.AnchorEnd (1)
+					X = Pos.Center () + 1,
+					Y = Pos.Bottom (_bottomEdit)
 				};
 				copyTop.Clicked += (s, e) => {
 					Thickness = new Thickness (Thickness.Top);
-					if (topEdit.Text.IsEmpty) {
-						topEdit.Text = "0";
+					if (_topEdit.Text.IsEmpty) {
+						_topEdit.Text = "0";
 					}
-					bottomEdit.Text = leftEdit.Text = rightEdit.Text = topEdit.Text;
+					_bottomEdit.Text = _leftEdit.Text = _rightEdit.Text = _topEdit.Text;
 				};
 				Add (copyTop);
 
+				// Foreground ColorPicker.
+				_foregroundColorPicker = new ColorPicker () {
+					Title = "FG",
+					BoxWidth = 1,
+					BoxHeight = 1,
+					X = -1,
+					Y = Pos.Bottom (copyTop) + 1,
+					BorderStyle = LineStyle.Single,
+					SuperViewRendersLineCanvas = true
+				};
+				_foregroundColorPicker.ColorChanged += (o, a) =>
+					AttributeChanged?.Invoke (this,
+						new Terminal.Gui.Attribute (_foregroundColorPicker.SelectedColor, _backgroundColorPicker.SelectedColor));
+				Add (_foregroundColorPicker);
+
+				// Background ColorPicker.
+				_backgroundColorPicker = new ColorPicker () {
+					Title = "BG",
+					BoxWidth = 1,
+					BoxHeight = 1,
+					X = Pos.Right (_foregroundColorPicker) - 1,
+					Y = Pos.Top (_foregroundColorPicker),
+					BorderStyle = LineStyle.Single,
+					SuperViewRendersLineCanvas = true
+				};
+
+				_backgroundColorPicker.ColorChanged += (o, a) =>
+					AttributeChanged?.Invoke (this,
+						new Terminal.Gui.Attribute (
+							_foregroundColorPicker.SelectedColor,
+							_backgroundColorPicker.SelectedColor));
+				Add (_backgroundColorPicker);
+
+				_topEdit.Text = $"{Thickness.Top}";
+				_leftEdit.Text = $"{Thickness.Left}";
+				_rightEdit.Text = $"{Thickness.Right}";
+				_bottomEdit.Text = $"{Thickness.Bottom}";
+
 				LayoutSubviews ();
-				Height = Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical + 4;
-				Width = 20;
+				Height = GetFramesThickness ().Vertical + 4 + 4;
+				Width = GetFramesThickness ().Horizontal + _foregroundColorPicker.Frame.Width * 2 - 3;
+			}
+
+			private void Edit_TextChanging (object sender, TextChangingEventArgs e)
+			{
+				try {
+					if (string.IsNullOrEmpty (e.NewText.ToString ())) {
+						e.Cancel = true;
+						((TextField)sender).Text = "0";
+						return;
+					}
+					switch (sender.ToString ()) {
+					case var s when s == _topEdit.ToString ():
+						Thickness = new Thickness (Thickness.Left,
+							int.Parse (e.NewText.ToString ()), Thickness.Right,
+							Thickness.Bottom);
+						break;
+					case var s when s == _leftEdit.ToString ():
+						Thickness = new Thickness (int.Parse (e.NewText.ToString ()),
+							Thickness.Top, Thickness.Right,
+							Thickness.Bottom);
+						break;
+					case var s when s == _rightEdit.ToString ():
+						Thickness = new Thickness (Thickness.Left,
+							Thickness.Top, int.Parse (e.NewText.ToString ()),
+							Thickness.Bottom);
+						break;
+					case var s when s == _bottomEdit.ToString ():
+						Thickness = new Thickness (Thickness.Left,
+							Thickness.Top, Thickness.Right,
+							int.Parse (e.NewText.ToString ()));
+						break;
+					}
+				} catch {
+					if (!e.NewText.IsEmpty) {
+						e.Cancel = true;
+					}
+				}
 			}
 		}
 
 		public class FramesEditor : Window {
-			public FramesEditor (NStack.ustring title, View viewToEdit)
+			private View _viewToEdit;
+			private FrameEditor _marginEditor;
+			private FrameEditor _borderEditor;
+			private FrameEditor _paddingEditor;
+
+			public FramesEditor (ustring title, View viewToEdit)
 			{
-				viewToEdit.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
-				var marginEditor = new ThicknessEditor () {
-					X = 20,
+				this._viewToEdit = viewToEdit;
+
+				viewToEdit.Margin.ColorScheme = new ColorScheme (Colors.ColorSchemes ["Toplevel"]);
+				_marginEditor = new FrameEditor () {
+					X = 0,
 					Y = 0,
 					Title = "Margin",
 					Thickness = viewToEdit.Margin.Thickness,
+					SuperViewRendersLineCanvas = true
 				};
-				marginEditor.ThicknessChanged += (s, a) => {
-					try {
-						viewToEdit.Margin.Thickness = a.Thickness;
-					} catch {
-
-						viewToEdit.Margin.Thickness = a.PreviousThickness;
-					}
-				};
-				Add (marginEditor);
-
-				viewToEdit.Border.ColorScheme = Colors.ColorSchemes ["Base"];
-				var borderEditor = new ThicknessEditor () {
-					X = Pos.Right(marginEditor) - 1,
-					Y = 0,
+				_marginEditor.ThicknessChanged += Editor_ThicknessChanged;
+				_marginEditor.AttributeChanged += Editor_AttributeChanged; ;
+				Add (_marginEditor);
+
+				viewToEdit.Border.ColorScheme = new ColorScheme (Colors.ColorSchemes ["Base"]);
+				_borderEditor = new FrameEditor () {
+					X = Pos.Left (_marginEditor),
+					Y = Pos.Bottom (_marginEditor),
 					Title = "Border",
 					Thickness = viewToEdit.Border.Thickness,
+					SuperViewRendersLineCanvas = true
 				};
-				borderEditor.ThicknessChanged += (s, a) => {
-					try {
-						viewToEdit.Border.Thickness = a.Thickness;
-					} catch {
-						viewToEdit.Border.Thickness = a.PreviousThickness;
-					}
-				};
-				Add (borderEditor);
+				_borderEditor.ThicknessChanged += Editor_ThicknessChanged;
+				_borderEditor.AttributeChanged += Editor_AttributeChanged;
+				Add (_borderEditor);
 
-				viewToEdit.Padding.ColorScheme = Colors.ColorSchemes ["Error"];
-				var paddingEditor = new ThicknessEditor () {
-					X = Pos.Right (borderEditor) - 1,
-					Y = 0,
-					Title = "Padding",
-					Thickness = viewToEdit.Padding.Thickness,
-				};
-				paddingEditor.ThicknessChanged += (s, a) => {
-					try {
-						viewToEdit.Padding.Thickness = a.Thickness;
-					} catch {
-						viewToEdit.Padding.Thickness = a.PreviousThickness;
-					}
-				};
-				Add (paddingEditor);
-
-				viewToEdit.Y = Pos.Center () + 4;
-
-				Add (new Label ("BorderStyle:"));
+				viewToEdit.Padding.ColorScheme = new ColorScheme (Colors.ColorSchemes ["Error"]);
+				var colorEnum = Enum.GetValues (typeof (Color)).Cast<Color> ().ToList ();
 
 				var borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast<LineStyle> ().ToList ();
 				var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
 					e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
 
-					X = 2,
-					Y = 1,
-					SelectedItem = (int)viewToEdit.Border.BorderStyle
+					X = Pos.Right (_borderEditor) - 1,
+					Y = Pos.Top (_borderEditor),
+					SelectedItem = (int)viewToEdit.Border.BorderStyle,
+					BorderStyle = LineStyle.Double,
+					Title = "Border Style",
+					SuperViewRendersLineCanvas = true
 				};
 				Add (rbBorderStyle);
 
 				rbBorderStyle.SelectedItemChanged += (s, e) => {
+					var prevBorderStyle = viewToEdit.BorderStyle;
 					viewToEdit.Border.BorderStyle = (LineStyle)e.SelectedItem;
+					if (viewToEdit.Border.BorderStyle == LineStyle.None) {
+						viewToEdit.Border.Thickness = new Thickness (0);
+					} else if (prevBorderStyle == LineStyle.None && viewToEdit.Border.BorderStyle != LineStyle.None) {
+						viewToEdit.Border.Thickness = new Thickness (1);
+					}
+					_borderEditor.Thickness = new Thickness (viewToEdit.Border.Thickness.Left, viewToEdit.Border.Thickness.Top,
+						viewToEdit.Border.Thickness.Right, viewToEdit.Border.Thickness.Bottom);
 					viewToEdit.SetNeedsDisplay ();
 				};
 
-				//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)viewToEdit.Border.BackgroundColor
-				//};
-				//rbBackground.SelectedItemChanged += (s, e) => {
-				//	if (viewToEdit.Border != null) {
-				//		viewToEdit.Border.BackgroundColor = (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)viewToEdit.Border.ForgroundColor
-				//};
-				//rbBorderBrush.SelectedItemChanged += (s, e) => {
-				//	if (viewToEdit.Border != null) {
-				//		viewToEdit.Border.ForgroundColor = (Color)e.SelectedItem;
-				//	}
-				//};
-				//Add (rbBorderBrush);
-
-				viewToEdit.X = Pos.Center ();
-				viewToEdit.Y = Pos.Bottom (marginEditor);
-				viewToEdit.Width = 60;
-				viewToEdit.Height = 25;
+				var ckbTitle = new CheckBox ("Show Title") {
+					BorderStyle = LineStyle.Double,
+					X = Pos.Left (_borderEditor),
+					Y = Pos.Bottom (_borderEditor) - 1,
+					Width = Dim.Width (_borderEditor),
+					Checked = true,
+					SuperViewRendersLineCanvas = true
+				};
+				Add (ckbTitle);
+
+				_paddingEditor = new FrameEditor () {
+					X = Pos.Left (_borderEditor),
+					Y = Pos.Bottom (rbBorderStyle),
+					Title = "Padding",
+					Thickness = viewToEdit.Padding.Thickness,
+					SuperViewRendersLineCanvas = true
+				};
+				_paddingEditor.ThicknessChanged += Editor_ThicknessChanged;
+				_paddingEditor.AttributeChanged += Editor_AttributeChanged;
+				Add (_paddingEditor);
+
+				viewToEdit.X = Pos.Right (rbBorderStyle);
+				viewToEdit.Y = 0;
+				viewToEdit.Width = Dim.Fill ();
+				viewToEdit.Height = Dim.Fill ();
 				Add (viewToEdit);
 
-				LayoutSubviews ();
+				viewToEdit.LayoutComplete += (s, e) => {
+					if (ckbTitle.Checked == true) {
+						viewToEdit.Title = viewToEdit.ToString ();
+					} else {
+						viewToEdit.Title = string.Empty;
+					}
+				};
 
 				Title = title;
 			}
+
+			private void Editor_AttributeChanged (object sender, Terminal.Gui.Attribute attr)
+			{
+				switch (sender.ToString ()) {
+				case var s when s == _marginEditor.ToString ():
+					_viewToEdit.Margin.ColorScheme = new ColorScheme (_viewToEdit.Margin.ColorScheme) { Normal = attr };
+					break;
+				case var s when s == _borderEditor.ToString ():
+					_viewToEdit.Border.ColorScheme = new ColorScheme (_viewToEdit.Border.ColorScheme) { Normal = attr };
+					break;
+				case var s when s == _paddingEditor.ToString ():
+					_viewToEdit.Padding.ColorScheme = new ColorScheme (_viewToEdit.Padding.ColorScheme) { Normal = attr };
+					break;
+				}
+			}
+
+			private void Editor_ThicknessChanged (object sender, ThicknessEventArgs e)
+			{
+				try {
+					switch (sender.ToString ()) {
+					case var s when s == _marginEditor.ToString ():
+						_viewToEdit.Margin.Thickness = e.Thickness;
+						break;
+					case var s when s == _borderEditor.ToString ():
+						_viewToEdit.Border.Thickness = e.Thickness;
+						break;
+					case var s when s == _paddingEditor.ToString ():
+						_viewToEdit.Padding.Thickness = e.Thickness;
+						break;
+					}
+				} catch {
+					switch (sender.ToString ()) {
+					case var s when s == _marginEditor.ToString ():
+						_viewToEdit.Margin.Thickness = e.PreviousThickness;
+						break;
+					case var s when s == _borderEditor.ToString ():
+						_viewToEdit.Border.Thickness = e.PreviousThickness;
+						break;
+					case var s when s == _paddingEditor.ToString ():
+						_viewToEdit.Padding.Thickness = e.PreviousThickness;
+						break;
+					}
+				}
+			}
 		}
 
 		public override void Init ()
@@ -256,34 +342,33 @@ namespace UICatalog.Scenarios {
 			Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 
 			var view = new Window ();
-			var tf1 = new TextField ("1234567890") { Width = 10 };
+			var tf1 = new TextField ("TextField") { Width = 10 };
 
 			var button = new Button ("Press me!") {
 				X = Pos.Center (),
 				Y = Pos.Center (),
 			};
-			button.Clicked += (s, e) => MessageBox.Query (20, 7, "Hi", $"I'm a {view.GetType().Name}?", "Yes", "No");
+			button.Clicked += (s, e) => MessageBox.Query (20, 7, "Hi", $"Am I a {view.GetType ().Name}?", "Yes", "No");
 			var label = new Label ($"I'm a {view.GetType ().Name}") {
 				X = Pos.Center (),
 				Y = Pos.Center () - 1,
 			};
-			var tf2 = new TextField ("1234567890") {
+			var tf2 = new Button ("Button") {
 				X = Pos.AnchorEnd (10),
 				Y = Pos.AnchorEnd (1),
 				Width = 10
 			};
-			var tv = new TextView () {
+			var tv = new Label () {
 				Y = Pos.AnchorEnd (2),
-				Width = 10,
+				Width = 25,
 				Height = Dim.Fill (),
-				Text = "1234567890"
+				Text = "Label\nY=AnchorEnd(2),Height=Dim.Fill()"
 			};
 
 			view.Margin.Thickness = new Thickness (3);
-			view.Margin.ColorScheme = Colors.ColorSchemes ["Dialog"];
+			view.Padding.Thickness = new Thickness (1);
 
 			view.Add (tf1, button, label, tf2, tv);
-			view.LayoutComplete += (s, e) => view.Title = view.ToString ();
 
 			var editor = new FramesEditor (
 				$"{Application.QuitKey} to Quit - Scenario: {GetName ()}",

+ 3 - 242
UICatalog/Scenarios/ViewExperiments.cs

@@ -3,253 +3,20 @@ using System.Linq;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios {
-	[ScenarioMetadata (Name: "_ View Experiments", Description: "v2 View Experiments")]
+	[ScenarioMetadata (Name: "View Experiments", Description: "v2 View Experiments")]
 	[ScenarioCategory ("Controls")]
 	public class ViewExperiments : Scenario {
 
-		public class ThicknessEditor : View {
-			private Thickness thickness;
-
-			public Thickness Thickness {
-				get => thickness;
-				set {
-					thickness = value;
-					ThicknessChanged?.Invoke (this, new ThicknessEventArgs () { Thickness = Thickness });
-				}
-			}
-
-			public event EventHandler<ThicknessEventArgs> ThicknessChanged;
-
-			public ThicknessEditor ()
-			{
-				Margin.Thickness = new Thickness (0);
-				Border.Thickness = new Thickness (1);
-			}
-
-			public override void BeginInit ()
-			{
-				base.BeginInit ();
-
-				var topEdit = new TextField ("") {
-					X = Pos.Center (),
-					Y = 0,
-					Width = 5
-				};
-				topEdit.TextChanging += (s, e) => {
-					try {
-						Thickness = new Thickness (Thickness.Left,
-							int.Parse (e.NewText.ToString ()), Thickness.Right,
-							Thickness.Bottom);
-					} catch {
-						if (!e.NewText.IsEmpty) {
-							e.Cancel = true;
-						}
-					}
-				};
-				topEdit.Text = $"{Thickness.Top}";
-
-				Add (topEdit);
-
-				var leftEdit = new TextField ("") {
-					X = 0,
-					Y = Pos.Bottom (topEdit),
-					Width = 5
-				};
-				leftEdit.TextChanging += (s, e) => {
-					try {
-						Thickness = new Thickness (int.Parse (e.NewText.ToString ()),
-							Thickness.Top, Thickness.Right,
-							Thickness.Bottom);
-					} catch {
-						if (!e.NewText.IsEmpty) {
-							e.Cancel = true;
-						}
-					}
-				};
-				leftEdit.Text = $"{Thickness.Left}";
-				Add (leftEdit);
-
-				var rightEdit = new TextField ("") {
-					X = Pos.Right (topEdit),
-					Y = Pos.Bottom (topEdit),
-					Width = 5
-				};
-				rightEdit.TextChanging += (s, e) => {
-					try {
-						Thickness = new Thickness (Thickness.Left,
-							Thickness.Top, int.Parse (e.NewText.ToString ()),
-							Thickness.Bottom);
-					} catch {
-						if (!e.NewText.IsEmpty) {
-							e.Cancel = true;
-						}
-					}
-				};
-				rightEdit.Text = $"{Thickness.Right}";
-				Add (rightEdit);
-
-				var bottomEdit = new TextField ("") {
-					X = Pos.Center (),
-					Y = Pos.Bottom (leftEdit),
-					Width = 5
-				};
-				bottomEdit.TextChanging += (s, e) => {
-					try {
-						Thickness = new Thickness (Thickness.Left,
-							Thickness.Top, Thickness.Right,
-							int.Parse (e.NewText.ToString ()));
-					} catch {
-						if (!e.NewText.IsEmpty) {
-							e.Cancel = true;
-						}
-					}
-				};
-				bottomEdit.Text = $"{Thickness.Bottom}";
-				Add (bottomEdit);
-
-				var copyTop = new Button ("Copy Top") {
-					X = Pos.Center (),
-					Y = Pos.AnchorEnd (1)
-				};
-				copyTop.Clicked += (s, e) => {
-					Thickness = new Thickness (Thickness.Top);
-					if (topEdit.Text.IsEmpty) {
-						topEdit.Text = "0";
-					}
-					bottomEdit.Text = leftEdit.Text = rightEdit.Text = topEdit.Text;
-				};
-				Add (copyTop);
-
-				//LayoutSubviews ();
-				Height = Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical + 4;
-				Width = 20;
-			}
-		}
-
-		public class FramesEditor : Window {
-			public FramesEditor (NStack.ustring title, View viewToEdit)
-			{
-				viewToEdit.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
-				var marginEditor = new ThicknessEditor () {
-					X = 0,
-					Y = 0,
-					Title = "Margin",
-					Thickness = viewToEdit.Margin.Thickness,
-				};
-				marginEditor.Margin.Thickness = new Thickness (0, 0, 1, 0);
-				marginEditor.ThicknessChanged += (s, a) => {
-					viewToEdit.Margin.Thickness = a.Thickness;
-				};
-				Add (marginEditor);
-
-				viewToEdit.Border.ColorScheme = Colors.ColorSchemes ["Base"];
-				var borderEditor = new ThicknessEditor () {
-					X = Pos.Right (marginEditor),
-					Y = 0,
-					Title = "Border",
-					Thickness = viewToEdit.Border.Thickness,
-				};
-				borderEditor.Margin.Thickness = new Thickness (0, 0, 1, 0);
-				borderEditor.ThicknessChanged += (s, a) => {
-					viewToEdit.Border.Thickness = a.Thickness;
-				};
-				Add (borderEditor);
-
-				var styleLabel = new Label ("BorderStyle: ") {
-					X = Pos.Right (borderEditor),
-					Y = 0
-				};
-				Add (styleLabel);
-
-				var borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast<LineStyle> ().ToList ();
-				var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
-					e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
-					X = Pos.Left (styleLabel),
-					Y = Pos.Bottom (styleLabel),
-					SelectedItem = (int)viewToEdit.Border.BorderStyle
-				};
-
-				rbBorderStyle.SelectedItemChanged += (s, e) => {
-					viewToEdit.Border.BorderStyle = (LineStyle)e.SelectedItem;
-					viewToEdit.SetNeedsDisplay ();
-				};
-				Add (rbBorderStyle);
-
-				viewToEdit.Padding.ColorScheme = Colors.ColorSchemes ["Error"];
-				var paddingEditor = new ThicknessEditor () {
-					X = Pos.Right (styleLabel),
-					Y = 0,
-					Title = "Padding",
-					Thickness = viewToEdit.Padding.Thickness,
-				};
-				paddingEditor.ThicknessChanged += (s, a) => {
-					viewToEdit.Padding.Thickness = a.Thickness;
-				};
-				Add (paddingEditor);
-
-				viewToEdit.Y = Pos.Center () + 4;
-
-
-				//rbBorderStyle.SelectedItemChanged += (e) => {
-				//	viewToEdit.Border.BorderStyle = (BorderStyle)e.SelectedItem;
-				//	viewToEdit.SetNeedsDisplay ();
-				//};
-
-				//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)viewToEdit.Border.BackgroundColor
-				//};
-				//rbBackground.SelectedItemChanged += (e) => {
-				//	if (viewToEdit.Border != null) {
-				//		viewToEdit.Border.BackgroundColor = (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)viewToEdit.Border.ForgroundColor
-				//};
-				//rbBorderBrush.SelectedItemChanged += (e) => {
-				//	if (viewToEdit.Border != null) {
-				//		viewToEdit.Border.ForgroundColor = (Color)e.SelectedItem;
-				//	}
-				//};
-				//Add (rbBorderBrush);
-
-				Height = 8;
-				Title = title;
-			}
-		}
-
 		public override void Init ()
 		{
 			Application.Init ();
 			ConfigurationManager.Themes.Theme = Theme;
 			ConfigurationManager.Apply ();
 			Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
-
 		}
 
 		public override void Setup ()
 		{
-			//ConsoleDriver.Diagnostics |= ConsoleDriver.DiagnosticFlags.FramePadding;
 			var containerLabel = new Label () {
 				X = 0,
 				Y = 0,
@@ -270,11 +37,10 @@ namespace UICatalog.Scenarios {
 
 			//Application.Top.Add (view);
 
-			//view.InitializeFrames ();
 			view.Margin.Thickness = new Thickness (2, 2, 2, 2);
 			view.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
 			view.Margin.Data = "Margin";
-			view.Border.Thickness = new Thickness (2);
+			view.Border.Thickness = new Thickness (3);
 			view.Border.BorderStyle = LineStyle.Single;
 			view.Border.ColorScheme = view.ColorScheme;
 			view.Border.Data = "Border";
@@ -454,18 +220,13 @@ namespace UICatalog.Scenarios {
 
 			view.X = Pos.Center ();
 
-			var editor = new FramesEditor ($"Frame Editor", view) {
+			var editor = new Frames.FramesEditor ($"Frames Editor", view) {
 				X = 0,
 				Y = Pos.Bottom (containerLabel),
 				Width = Dim.Fill (),
 			};
 
 			Application.Top.Add (editor);
-
-			view.Y = Pos.Bottom (editor);
-			view.Width = Dim.Fill ();
-			view.Height = Dim.Fill ();
-			Application.Top.Add (view);
 		}
 	}
 }

+ 1 - 0
UnitTests/Dialogs/DialogTests.cs

@@ -181,6 +181,7 @@ namespace Terminal.Gui.DialogTests {
 
 					// This is because of PostionTopLevels and EnsureVisibleBounds
 					Assert.Equal (new Point (3, 2), d.Frame.Location);
+					Assert.Equal (new Size (17, 8), d.Frame.Size);
 					TestHelpers.AssertDriverContentsWithFrameAre (@"
 ╔══════════════════╗
 ║                  ║

+ 2 - 3
UnitTests/Drawing/ThicknessTests.cs

@@ -288,7 +288,7 @@ namespace Terminal.Gui.DrawingTests {
 		}
 
 		[Fact ()]
-		public void GetInsideTests_Positive_Thickness_Non_Empty_Size()
+		public void GetInsideTests_Positive_Thickness_Non_Empty_Size ()
 		{
 
 			var t = new Thickness (1, 1, 1, 1);
@@ -493,7 +493,7 @@ namespace Terminal.Gui.DrawingTests {
 
 			Application.Top.Add (f);
 			Application.Begin (Application.Top);
-			
+
 			((FakeDriver)Application.Driver).SetBufferSize (45, 20);
 			var t = new Thickness (0, 0, 0, 0);
 			var r = new Rect (2, 2, 40, 15);
@@ -633,4 +633,3 @@ namespace Terminal.Gui.DrawingTests {
 		}
 	}
 }
-

+ 335 - 3
UnitTests/View/FrameTests.cs

@@ -50,7 +50,7 @@ namespace Terminal.Gui.ViewTests {
 		[InlineData (3)]
 		public void Border_With_Title_Size_Height (int height)
 		{
-			var win = new Window () { 
+			var win = new Window () {
 				Title = "1234",
 				Width = Dim.Fill (),
 				Height = Dim.Fill ()
@@ -93,7 +93,7 @@ namespace Terminal.Gui.ViewTests {
 			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 
 		}
-		
+
 		[Theory, AutoInitShutdown]
 		[InlineData (0)]
 		[InlineData (1)]
@@ -195,7 +195,6 @@ namespace Terminal.Gui.ViewTests {
 				break;
 			}
 			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-
 		}
 
 		[Fact, AutoInitShutdown]
@@ -270,5 +269,338 @@ namespace Terminal.Gui.ViewTests {
 
 			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData (0)]
+		[InlineData (1)]
+		[InlineData (2)]
+		[InlineData (3)]
+		[InlineData (4)]
+		[InlineData (5)]
+		[InlineData (6)]
+		[InlineData (7)]
+		[InlineData (8)]
+		[InlineData (9)]
+		[InlineData (10)]
+		public void Border_With_Title_Border_Double_Thickness_Top_Two_Size_Width (int width)
+		{
+			var win = new Window () {
+				Title = "1234",
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				BorderStyle = LineStyle.Double,
+			};
+			win.Border.Thickness.Top = 2;
+
+			var rs = Application.Begin (win);
+			bool firstIteration = false;
+
+			((FakeDriver)Application.Driver).SetBufferSize (width, 4);
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			var expected = string.Empty;
+
+			switch (width) {
+			case 1:
+				Assert.Equal (new Rect (0, 0, 1, 4), win.Frame);
+				expected = @"
+║
+║
+║";
+				break;
+			case 2:
+				Assert.Equal (new Rect (0, 0, 2, 4), win.Frame);
+				expected = @"
+╔╗
+║║
+╚╝";
+				break;
+			case 3:
+				Assert.Equal (new Rect (0, 0, 3, 4), win.Frame);
+				expected = @"
+╔═╗
+║ ║
+╚═╝";
+				break;
+			case 4:
+				Assert.Equal (new Rect (0, 0, 4, 4), win.Frame);
+				expected = @"
+ ╒╕ 
+╔╛╘╗
+║  ║
+╚══╝";
+				break;
+			case 5:
+				Assert.Equal (new Rect (0, 0, 5, 4), win.Frame);
+				expected = @"
+ ╒═╕ 
+╔╛1╘╗
+║   ║
+╚═══╝";
+				break;
+			case 6:
+				Assert.Equal (new Rect (0, 0, 6, 4), win.Frame);
+				expected = @"
+ ╒══╕ 
+╔╛12╘╗
+║    ║
+╚════╝";
+				break;
+			case 7:
+				Assert.Equal (new Rect (0, 0, 7, 4), win.Frame);
+				expected = @"
+ ╒═══╕ 
+╔╛123╘╗
+║     ║
+╚═════╝";
+				break;
+			case 8:
+				Assert.Equal (new Rect (0, 0, 8, 4), win.Frame);
+				expected = @"
+ ╒════╕ 
+╔╛1234╘╗
+║      ║
+╚══════╝";
+				break;
+			case 9:
+				Assert.Equal (new Rect (0, 0, 9, 4), win.Frame);
+				expected = @"
+ ╒════╕  
+╔╛1234╘═╗
+║       ║
+╚═══════╝";
+				break;
+			case 10:
+				Assert.Equal (new Rect (0, 0, 10, 4), win.Frame);
+				expected = @"
+ ╒════╕   
+╔╛1234╘══╗
+║        ║
+╚════════╝";
+				break;
+			}
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData (0)]
+		[InlineData (1)]
+		[InlineData (2)]
+		[InlineData (3)]
+		[InlineData (4)]
+		[InlineData (5)]
+		[InlineData (6)]
+		[InlineData (7)]
+		[InlineData (8)]
+		[InlineData (9)]
+		[InlineData (10)]
+		public void Border_With_Title_Border_Double_Thickness_Top_Three_Size_Width (int width)
+		{
+			var win = new Window () {
+				Title = "1234",
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				BorderStyle = LineStyle.Double,
+			};
+			win.Border.Thickness.Top = 3;
+
+			var rs = Application.Begin (win);
+			bool firstIteration = false;
+
+			((FakeDriver)Application.Driver).SetBufferSize (width, 4);
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			var expected = string.Empty;
+
+			switch (width) {
+			case 1:
+				Assert.Equal (new Rect (0, 0, 1, 4), win.Frame);
+				expected = @"
+║
+║
+║";
+				break;
+			case 2:
+				Assert.Equal (new Rect (0, 0, 2, 4), win.Frame);
+				expected = @"
+╔╗
+║║
+╚╝";
+				break;
+			case 3:
+				Assert.Equal (new Rect (0, 0, 3, 4), win.Frame);
+				expected = @"
+╔═╗
+║ ║
+╚═╝";
+				break;
+			case 4:
+				Assert.Equal (new Rect (0, 0, 4, 4), win.Frame);
+				expected = @"
+ ╒╕ 
+╔╡╞╗
+║╘╛║
+╚══╝";
+				break;
+			case 5:
+				Assert.Equal (new Rect (0, 0, 5, 4), win.Frame);
+				expected = @"
+ ╒═╕ 
+╔╡1╞╗
+║╘═╛║
+╚═══╝";
+				break;
+			case 6:
+				Assert.Equal (new Rect (0, 0, 6, 4), win.Frame);
+				expected = @"
+ ╒══╕ 
+╔╡12╞╗
+║╘══╛║
+╚════╝";
+				break;
+			case 7:
+				Assert.Equal (new Rect (0, 0, 7, 4), win.Frame);
+				expected = @"
+ ╒═══╕ 
+╔╡123╞╗
+║╘═══╛║
+╚═════╝";
+				break;
+			case 8:
+				Assert.Equal (new Rect (0, 0, 8, 4), win.Frame);
+				expected = @"
+ ╒════╕ 
+╔╡1234╞╗
+║╘════╛║
+╚══════╝";
+				break;
+			case 9:
+				Assert.Equal (new Rect (0, 0, 9, 4), win.Frame);
+				expected = @"
+ ╒════╕  
+╔╡1234╞═╗
+║╘════╛ ║
+╚═══════╝";
+				break;
+			case 10:
+				Assert.Equal (new Rect (0, 0, 10, 4), win.Frame);
+				expected = @"
+ ╒════╕   
+╔╡1234╞══╗
+║╘════╛  ║
+╚════════╝";
+				break;
+			}
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData (0)]
+		[InlineData (1)]
+		[InlineData (2)]
+		[InlineData (3)]
+		[InlineData (4)]
+		[InlineData (5)]
+		[InlineData (6)]
+		[InlineData (7)]
+		[InlineData (8)]
+		[InlineData (9)]
+		[InlineData (10)]
+		public void Border_With_Title_Border_Double_Thickness_Top_Four_Size_Width (int width)
+		{
+			var win = new Window () {
+				Title = "1234",
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				BorderStyle = LineStyle.Double,
+			};
+			win.Border.Thickness.Top = 4;
+
+			var rs = Application.Begin (win);
+			bool firstIteration = false;
+
+			((FakeDriver)Application.Driver).SetBufferSize (width, 5);
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			var expected = string.Empty;
+
+			switch (width) {
+			case 1:
+				Assert.Equal (new Rect (0, 0, 1, 5), win.Frame);
+				expected = @"
+║
+║
+║";
+				break;
+			case 2:
+				Assert.Equal (new Rect (0, 0, 2, 5), win.Frame);
+				expected = @"
+╔╗
+║║
+╚╝";
+				break;
+			case 3:
+				Assert.Equal (new Rect (0, 0, 3, 5), win.Frame);
+				expected = @"
+╔═╗
+║ ║
+╚═╝";
+				break;
+			case 4:
+				Assert.Equal (new Rect (0, 0, 4, 5), win.Frame);
+				expected = @"
+ ╒╕ 
+╔╡╞╗
+║╘╛║
+╚══╝";
+				break;
+			case 5:
+				Assert.Equal (new Rect (0, 0, 5, 5), win.Frame);
+				expected = @"
+ ╒═╕ 
+╔╡1╞╗
+║╘═╛║
+╚═══╝";
+				break;
+			case 6:
+				Assert.Equal (new Rect (0, 0, 6, 5), win.Frame);
+				expected = @"
+ ╒══╕ 
+╔╡12╞╗
+║╘══╛║
+╚════╝";
+				break;
+			case 7:
+				Assert.Equal (new Rect (0, 0, 7, 5), win.Frame);
+				expected = @"
+ ╒═══╕ 
+╔╡123╞╗
+║╘═══╛║
+╚═════╝";
+				break;
+			case 8:
+				Assert.Equal (new Rect (0, 0, 8, 5), win.Frame);
+				expected = @"
+ ╒════╕ 
+╔╡1234╞╗
+║╘════╛║
+╚══════╝";
+				break;
+			case 9:
+				Assert.Equal (new Rect (0, 0, 9, 5), win.Frame);
+				expected = @"
+ ╒════╕  
+╔╡1234╞═╗
+║╘════╛ ║
+╚═══════╝";
+				break;
+			case 10:
+				Assert.Equal (new Rect (0, 0, 10, 5), win.Frame);
+				expected = @"
+ ╒════╕   
+╔╡1234╞══╗
+║╘════╛  ║
+╚════════╝";
+				break;
+			}
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+		}
 	}
 }

+ 1 - 1
UnitTests/View/ViewTests.cs

@@ -563,7 +563,7 @@ namespace Terminal.Gui.ViewTests {
 			// BUGBUG: v2 - _needsDisplay needs debugging - test disabled for now.
 			//Assert.Equal (new Rect (new Point (0, 0), rect.Size), view._needsDisplay);
 			Assert.True (view.LayoutNeeded);
-			Assert.False (view._childNeedsDisplay);
+			Assert.False (view._subViewNeedsDisplay);
 			Assert.False (view._addingView);
 			view._addingView = true;
 			Assert.True (view._addingView);

+ 4 - 12
UnitTests/Views/ColorPickerTests.cs

@@ -14,19 +14,11 @@ namespace Terminal.Gui.ViewsTests {
 			Assert.Equal (Color.Black, colorPicker.SelectedColor);
 			Assert.Equal (new Point (0, 0), colorPicker.Cursor);
 			Assert.True (colorPicker.CanFocus);
-			Assert.Equal (new Rect (0, 0, 32, 5), colorPicker.Frame);
 
-			colorPicker = new ColorPicker (5, 10, "Title");
-			Assert.Equal (Color.Black, colorPicker.SelectedColor);
-			Assert.Equal (new Point (0, 0), colorPicker.Cursor);
-			Assert.True (colorPicker.CanFocus);
-			Assert.Equal (new Rect (5, 10, 32, 5), colorPicker.Frame);
-
-			colorPicker = new ColorPicker (new Point (10, 15), "Title");
-			Assert.Equal (Color.Black, colorPicker.SelectedColor);
-			Assert.Equal (new Point (0, 0), colorPicker.Cursor);
-			Assert.True (colorPicker.CanFocus);
-			Assert.Equal (new Rect (10, 15, 32, 5), colorPicker.Frame);
+			colorPicker.BeginInit ();
+			colorPicker.EndInit ();
+			colorPicker.LayoutSubviews ();
+			Assert.Equal (new Rect (0, 0, 32, 4), colorPicker.Frame);
 		}
 
 		[Fact]

+ 8 - 9
UnitTests/Views/RadioGroupTests.cs

@@ -35,7 +35,7 @@ namespace Terminal.Gui.ViewsTests {
 			Assert.Null (rg.Y);
 			Assert.Null (rg.Width);
 			Assert.Null (rg.Height);
-			Assert.Equal (new Rect (0, 0, 7, 1), rg.Frame);
+			Assert.Equal (new Rect (0, 0, 0, 0), rg.Frame);
 			Assert.Equal (0, rg.SelectedItem);
 
 			rg = new RadioGroup (new Rect (1, 2, 20, 5), new NStack.ustring [] { "Test" });
@@ -57,7 +57,7 @@ namespace Terminal.Gui.ViewsTests {
 			Assert.Null (rg.Y);
 			Assert.Null (rg.Width);
 			Assert.Null (rg.Height);
-			Assert.Equal (new Rect (1, 2, 7, 1), rg.Frame);
+			Assert.Equal (new Rect (1, 2, 6, 1), rg.Frame);
 			Assert.Equal (0, rg.SelectedItem);
 		}
 
@@ -76,8 +76,7 @@ namespace Terminal.Gui.ViewsTests {
 			var rg = new RadioGroup (new NStack.ustring [] { "Test", "New Test 你" });
 			var win = new Window () {
 				Width = Dim.Fill (),
-				Height = Dim.Fill (),
-				Title = "Test Demo 你"
+				Height = Dim.Fill ()
 			};
 			win.Add (rg);
 			Application.Top.Add (win);
@@ -89,10 +88,10 @@ namespace Terminal.Gui.ViewsTests {
 			Assert.Equal (2, rg.RadioLabels.Length);
 			Assert.Equal (0, rg.X);
 			Assert.Equal (0, rg.Y);
-			Assert.Equal (14, rg.Width);
-			Assert.Equal (2, rg.Height);
+			Assert.Equal (13, rg.Frame.Width);
+			Assert.Equal (2, rg.Frame.Height);
 			var expected = @"
-┌┤Test Demo 你├──────────────┐
+┌────────────────────────────┐
 │● Test                      │
 │◌ New Test 你               │
 │                            │
@@ -113,7 +112,7 @@ namespace Terminal.Gui.ViewsTests {
 			Assert.Equal (1, rg.Height);
 
 			expected = @"
-┌┤Test Demo 你├──────────────┐
+┌────────────────────────────┐
 │● Test  ◌ New Test 你       │
 │                            │
 │                            │
@@ -133,7 +132,7 @@ namespace Terminal.Gui.ViewsTests {
 			Assert.Equal (23, rg.Width);
 			Assert.Equal (1, rg.Height);
 			expected = @"
-┌┤Test Demo 你├──────────────┐
+┌────────────────────────────┐
 │● Test    ◌ New Test 你     │
 │                            │
 │                            │