瀏覽代碼

Border feature (#1436)

* Allowing pass the view to the RootMouseEvent.

* Allowing bring the current window to front on dragging.

* Improving the ConsoleDriver to get the Attribute colors based on the value.

* Added a PanelView to deal with borders.

* Added a Border feature to all views.
BDisp 3 年之前
父節點
當前提交
8bfa2f0425

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

@@ -6,6 +6,7 @@
 //
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using NStack;
@@ -111,11 +112,11 @@ namespace Terminal.Gui {
 
 		public override void UpdateScreen () => window.redrawwin ();
 
-		int currentAttribute;
+		Attribute currentAttribute;
 
 		public override void SetAttribute (Attribute c)
 		{
-			currentAttribute = c.Value;
+			currentAttribute = c;
 			Curses.attrset (currentAttribute);
 		}
 
@@ -1118,6 +1119,28 @@ namespace Terminal.Gui {
 			}
 			keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
 		}
+
+		public override bool GetColors (int value, out Color foreground, out Color background)
+		{
+			bool hasColor = false;
+			foreground = default;
+			background = default;
+			int back = -1;
+			IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
+			      .OfType<ConsoleColor> ()
+			      .Select (s => (int)s);
+			if (values.Contains ((value >> 12) & 0xffff)) {
+				hasColor = true;
+				back = (value >> 12) & 0xffff;
+				background = MapCursesColor (back);
+			}
+			if (values.Contains ((value - (back << 12)) >> 8)) {
+				hasColor = true;
+				foreground = MapCursesColor ((value - (back << 12)) >> 8);
+			}
+			return hasColor;
+
+		}
 	}
 
 	internal static class Platform {

+ 36 - 17
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -266,23 +266,6 @@ namespace Terminal.Gui {
 			FakeConsole.CursorLeft = savedCol;
 		}
 
-		public override void UpdateCursor ()
-		{
-			//
-		}
-
-		public override void StartReportingMouseMoves ()
-		{
-		}
-
-		public override void StopReportingMouseMoves ()
-		{
-		}
-
-		public override void Suspend ()
-		{
-		}
-
 		Attribute currentAttribute;
 		public override void SetAttribute (Attribute c)
 		{
@@ -568,7 +551,43 @@ namespace Terminal.Gui {
 			} catch (IndexOutOfRangeException) { }
 		}
 
+		public override bool GetColors (int value, out Color foreground, out Color background)
+		{
+			bool hasColor = false;
+			foreground = default;
+			background = default;
+			IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
+			      .OfType<ConsoleColor> ()
+			      .Select (s => (int)s);
+			if (values.Contains (value & 0xffff)) {
+				hasColor = true;
+				background = (Color)(ConsoleColor)(value & 0xffff);
+			}
+			if (values.Contains ((value >> 16) & 0xffff)) {
+				hasColor = true;
+				foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff);
+			}
+			return hasColor;
+		}
+
 		#region Unused
+		public override void UpdateCursor ()
+		{
+			//
+		}
+
+		public override void StartReportingMouseMoves ()
+		{
+		}
+
+		public override void StopReportingMouseMoves ()
+		{
+		}
+
+		public override void Suspend ()
+		{
+		}
+
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
 		}

+ 38 - 19
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -1497,10 +1497,10 @@ namespace Terminal.Gui {
 		{
 		}
 
-		int currentAttribute;
+		Attribute currentAttribute;
 		public override void SetAttribute (Attribute c)
 		{
-			currentAttribute = c.Value;
+			currentAttribute = c;
 		}
 
 		Key MapKey (ConsoleKeyInfo keyInfo)
@@ -1798,23 +1798,6 @@ namespace Terminal.Gui {
 			return currentAttribute;
 		}
 
-		#region Unused
-		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
-		{
-		}
-
-		public override void SetColors (short foregroundColorId, short backgroundColorId)
-		{
-		}
-
-		public override void CookMouse ()
-		{
-		}
-
-		public override void UncookMouse ()
-		{
-		}
-
 		/// <inheritdoc/>
 		public override bool GetCursorVisibility (out CursorVisibility visibility)
 		{
@@ -1851,6 +1834,42 @@ namespace Terminal.Gui {
 				ProcessInput (input);
 			} catch (OverflowException) { }
 		}
+
+		public override bool GetColors (int value, out Color foreground, out Color background)
+		{
+			bool hasColor = false;
+			foreground = default;
+			background = default;
+			IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
+			      .OfType<ConsoleColor> ()
+			      .Select (s => (int)s);
+			if (values.Contains (value & 0xffff)) {
+				hasColor = true;
+				background = (Color)(ConsoleColor)(value & 0xffff);
+			}
+			if (values.Contains ((value >> 16) & 0xffff)) {
+				hasColor = true;
+				foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff);
+			}
+			return hasColor;
+		}
+
+		#region Unused
+		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
+		{
+		}
+
+		public override void SetColors (short foregroundColorId, short backgroundColorId)
+		{
+		}
+
+		public override void CookMouse ()
+		{
+		}
+
+		public override void UncookMouse ()
+		{
+		}
 		#endregion
 
 		//

+ 52 - 31
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -27,7 +27,9 @@
 //
 using NStack;
 using System;
+using System.Collections.Generic;
 using System.ComponentModel;
+using System.Linq;
 using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
@@ -1387,11 +1389,11 @@ namespace Terminal.Gui {
 				AddRune (rune);
 		}
 
-		int currentAttribute;
+		Attribute currentAttribute;
 
 		public override void SetAttribute (Attribute c)
 		{
-			currentAttribute = c.Value;
+			currentAttribute = c;
 		}
 
 		Attribute MakeColor (ConsoleColor f, ConsoleColor b)
@@ -1492,35 +1494,6 @@ namespace Terminal.Gui {
 			return winConsole.EnsureCursorVisibility ();
 		}
 
-		#region Unused
-		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
-		{
-		}
-
-		public override void SetColors (short foregroundColorId, short backgroundColorId)
-		{
-		}
-
-		public override void Suspend ()
-		{
-		}
-
-		public override void StartReportingMouseMoves ()
-		{
-		}
-
-		public override void StopReportingMouseMoves ()
-		{
-		}
-
-		public override void UncookMouse ()
-		{
-		}
-
-		public override void CookMouse ()
-		{
-		}
-
 		public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
 		{
 			WindowsConsole.InputRecord input = new WindowsConsole.InputRecord ();
@@ -1573,6 +1546,54 @@ namespace Terminal.Gui {
 				ProcessInput (input);
 			}
 		}
+
+		public override bool GetColors (int value, out Color foreground, out Color background)
+		{
+			bool hasColor = false;
+			foreground = default;
+			background = default;
+			IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
+			      .OfType<ConsoleColor> ()
+			      .Select (s => (int)s);
+			if (values.Contains ((value >> 4) & 0xffff)) {
+				hasColor = true;
+				background = (Color)(ConsoleColor)((value >> 4) & 0xffff);
+			}
+			if (values.Contains (value - ((int)background << 4))) {
+				hasColor = true;
+				foreground = (Color)(ConsoleColor)(value - ((int)background << 4));
+			}
+			return hasColor;
+		}
+
+		#region Unused
+		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
+		{
+		}
+
+		public override void SetColors (short foregroundColorId, short backgroundColorId)
+		{
+		}
+
+		public override void Suspend ()
+		{
+		}
+
+		public override void StartReportingMouseMoves ()
+		{
+		}
+
+		public override void StopReportingMouseMoves ()
+		{
+		}
+
+		public override void UncookMouse ()
+		{
+		}
+
+		public override void CookMouse ()
+		{
+		}
 		#endregion
 	}
 

+ 3 - 1
Terminal.Gui/Core/Application.cs

@@ -528,7 +528,9 @@ namespace Terminal.Gui {
 				wantContinuousButtonPressedView = view;
 			else
 				wantContinuousButtonPressedView = null;
-
+			if (view != null) {
+				me.View = view;
+			}
 			RootMouseEvent?.Invoke (me);
 			if (mouseGrabView != null) {
 				var newxy = mouseGrabView.ScreenToView (me.X, me.Y);

+ 853 - 0
Terminal.Gui/Core/Border.cs

@@ -0,0 +1,853 @@
+using System;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Specifies the border style for a <see cref="View"/> and to be used by the <see cref="Border"/> class.
+	/// </summary>
+	public enum BorderStyle {
+		/// <summary>
+		/// No border is drawn.
+		/// </summary>
+		None,
+		/// <summary>
+		/// The border is drawn with a single line limits.
+		/// </summary>
+		Single,
+		/// <summary>
+		/// The border is drawn with a double line limits.
+		/// </summary>
+		Double
+	}
+
+	/// <summary>
+	/// Describes the thickness of a frame around a rectangle. Four <see cref="int"/> values describe
+	///  the <see cref="Left"/>, <see cref="Top"/>, <see cref="Right"/>, and <see cref="Bottom"/> sides
+	///  of the rectangle, respectively.
+	/// </summary>
+	public struct Thickness {
+		/// <summary>
+		/// Gets or sets the width, in integers, of the left side of the bounding rectangle.
+		/// </summary>
+		public int Left;
+		/// <summary>
+		/// Gets or sets the width, in integers, of the upper side of the bounding rectangle.
+		/// </summary>
+		public int Top;
+		/// <summary>
+		/// Gets or sets the width, in integers, of the right side of the bounding rectangle.
+		/// </summary>
+		public int Right;
+		/// <summary>
+		/// Gets or sets the width, in integers, of the lower side of the bounding rectangle.
+		/// </summary>
+		public int Bottom;
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Thickness"/> structure that has the
+		///  specified uniform length on each side.
+		/// </summary>
+		/// <param name="length"></param>
+		public Thickness (int length)
+		{
+			if (length < 0) {
+				throw new ArgumentException ("Invalid value for this property.");
+			}
+
+			Left = Top = Right = Bottom = length;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Thickness"/> structure that has specific
+		///  lengths (supplied as a <see cref="int"/>) applied to each side of the rectangle.
+		/// </summary>
+		/// <param name="left"></param>
+		/// <param name="top"></param>
+		/// <param name="right"></param>
+		/// <param name="bottom"></param>
+		public Thickness (int left, int top, int right, int bottom)
+		{
+			if (left < 0 || top < 0 || right < 0 || bottom < 0) {
+				throw new ArgumentException ("Invalid value for this property.");
+			}
+
+			Left = left;
+			Top = top;
+			Right = right;
+			Bottom = bottom;
+		}
+	}
+
+	/// <summary>
+	/// Draws a border, background, or both around another element.
+	/// </summary>
+	public class Border {
+		private int marginFrame => DrawMarginFrame ? 1 : 0;
+
+		/// <summary>
+		/// A sealed <see cref="Toplevel"/> derived class to implement <see cref="Border"/> feature.
+		/// This is only a wrapper to get borders on a toplevel and is recommended using another
+		/// derived, like <see cref="Window"/> where is possible to have borders with or without
+		/// border line or spacing around.
+		/// </summary>
+		public sealed class ToplevelContainer : Toplevel {
+			/// <inheritdoc/>
+			public override Border Border {
+				get => base.Border;
+				set {
+					if (base.Border != null && base.Border.Child != null && value.Child == null) {
+						value.Child = base.Border.Child;
+					}
+					base.Border = value;
+					if (value == null) {
+						return;
+					}
+					Rect frame;
+					if (Border.Child != null && (Border.Child.Width is Dim || Border.Child.Height is Dim)) {
+						frame = Rect.Empty;
+					} else {
+						frame = Frame;
+					}
+					AdjustContentView (frame);
+
+					Border.BorderChanged += Border_BorderChanged;
+				}
+			}
+
+			void Border_BorderChanged (Border border)
+			{
+				Rect frame;
+				if (Border.Child != null && (Border.Child.Width is Dim || Border.Child.Height is Dim)) {
+					frame = Rect.Empty;
+				} else {
+					frame = Frame;
+				}
+				AdjustContentView (frame);
+			}
+
+			/// <summary>
+			/// Initializes with default null values.
+			/// </summary>
+			public ToplevelContainer () : this (null, null) { }
+
+			/// <summary>
+			/// Initializes a <see cref="ToplevelContainer"/> with a <see cref="LayoutStyle.Computed"/>
+			/// </summary>
+			/// <param name="border">The border.</param>
+			/// <param name="title">The title.</param>
+			public ToplevelContainer (Border border, string title = null)
+			{
+				Initialize (Rect.Empty, border, title);
+			}
+
+			/// <summary>
+			/// Initializes a <see cref="ToplevelContainer"/> with a <see cref="LayoutStyle.Absolute"/>
+			/// </summary>
+			/// <param name="frame">The frame.</param>
+			/// <param name="border">The border.</param>
+			/// <param name="title">The title.</param>
+			public ToplevelContainer (Rect frame, Border border, string title = null) : base (frame)
+			{
+				Initialize (frame, border, title);
+			}
+
+			private void Initialize (Rect frame, Border border, string title = null)
+			{
+				ColorScheme = Colors.TopLevel;
+				Text = title ?? "";
+				if (border == null) {
+					Border = new Border () {
+						BorderStyle = BorderStyle.Single,
+						BorderBrush = ColorScheme.Normal.Background
+					};
+				} else {
+					Border = border;
+				}
+			}
+
+			void AdjustContentView (Rect frame)
+			{
+				var borderLength = Border.DrawMarginFrame ? 1 : 0;
+				var sumPadding = Border.GetSumThickness ();
+				var wb = new Size ();
+				if (frame == Rect.Empty) {
+					wb.Width = borderLength + sumPadding.Right;
+					wb.Height = borderLength + sumPadding.Bottom;
+					if (Border.Child == null) {
+						Border.Child = new ChildContentView (this) {
+							X = borderLength + sumPadding.Left,
+							Y = borderLength + sumPadding.Top,
+							Width = Dim.Fill (wb.Width),
+							Height = Dim.Fill (wb.Height)
+						};
+					} else {
+						Border.Child.X = borderLength + sumPadding.Left;
+						Border.Child.Y = borderLength + sumPadding.Top;
+						Border.Child.Width = Dim.Fill (wb.Width);
+						Border.Child.Height = Dim.Fill (wb.Height);
+					}
+				} else {
+					wb.Width = (2 * borderLength) + sumPadding.Right + sumPadding.Left;
+					wb.Height = (2 * borderLength) + sumPadding.Bottom + sumPadding.Top;
+					var cFrame = new Rect (borderLength + sumPadding.Left, borderLength + sumPadding.Top, frame.Width - wb.Width, frame.Height - wb.Height);
+					if (Border.Child == null) {
+						Border.Child = new ChildContentView (cFrame, this);
+					} else {
+						Border.Child.Frame = cFrame;
+					}
+				}
+				base.Add (Border.Child);
+				Border.ChildContainer = this;
+			}
+
+			/// <inheritdoc/>
+			public override void Add (View view)
+			{
+				Border.Child.Add (view);
+				if (view.CanFocus) {
+					CanFocus = true;
+				}
+				AddMenuStatusBar (view);
+			}
+
+			/// <inheritdoc/>
+			public override void Remove (View view)
+			{
+				if (view == null) {
+					return;
+				}
+
+				SetNeedsDisplay ();
+				var touched = view.Frame;
+				Border.Child.Remove (view);
+
+				if (Border.Child.InternalSubviews.Count < 1) {
+					CanFocus = false;
+				}
+				RemoveMenuStatusBar (view);
+			}
+
+			/// <inheritdoc/>
+			public override void RemoveAll ()
+			{
+				Border.Child.RemoveAll ();
+			}
+
+			/// <inheritdoc/>
+			public override void Redraw (Rect bounds)
+			{
+				if (!NeedDisplay.IsEmpty) {
+					Driver.SetAttribute (GetNormalColor ());
+					Border.DrawContent ();
+				}
+				var savedClip = Border.Child.ClipToBounds ();
+				Border.Child.Redraw (Border.Child.Bounds);
+				Driver.Clip = savedClip;
+
+				ClearLayoutNeeded ();
+				ClearNeedsDisplay ();
+
+				if (Border.BorderStyle != BorderStyle.None) {
+					Driver.SetAttribute (GetNormalColor ());
+					Border.DrawTitle (this, this.Frame);
+				}
+
+				// Checks if there are any SuperView view which intersect with this window.
+				if (SuperView != null) {
+					SuperView.SetNeedsLayout ();
+					SuperView.SetNeedsDisplay ();
+				}
+			}
+		}
+
+		private class ChildContentView : View {
+			View instance;
+
+			public ChildContentView (Rect frame, View instance) : base (frame)
+			{
+				this.instance = instance;
+			}
+			public ChildContentView (View instance)
+			{
+				this.instance = instance;
+			}
+
+			public override bool MouseEvent (MouseEvent mouseEvent)
+			{
+				return instance.MouseEvent (mouseEvent);
+			}
+		}
+
+		/// <summary>
+		/// Event to be invoked when any border property change.
+		/// </summary>
+		public event Action<Border> BorderChanged;
+
+		private BorderStyle borderStyle;
+		private bool drawMarginFrame;
+		private Thickness borderThickness;
+		private Thickness padding;
+
+		/// <summary>
+		/// Specifies the <see cref="Gui.BorderStyle"/> for a view.
+		/// </summary>
+		public BorderStyle BorderStyle {
+			get => borderStyle;
+			set {
+				if (value != BorderStyle.None && !drawMarginFrame) {
+					// Ensures drawn the border lines.
+					drawMarginFrame = true;
+				}
+				borderStyle = value;
+				OnBorderChanged ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets if a margin frame is drawn around the <see cref="Child"/> regardless the <see cref="BorderStyle"/>
+		/// </summary>
+		public bool DrawMarginFrame {
+			get => drawMarginFrame;
+			set {
+				if (borderStyle != BorderStyle.None
+					&& (!value || !drawMarginFrame)) {
+					// Ensures drawn the border lines.
+					drawMarginFrame = true;
+				} else {
+					drawMarginFrame = value;
+				}
+				OnBorderChanged ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the relative <see cref="Thickness"/> of a <see cref="Border"/>.
+		/// </summary>
+		public Thickness BorderThickness {
+			get => borderThickness;
+			set {
+				borderThickness = value;
+				OnBorderChanged ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the <see cref="Color"/> that draws the outer border color.
+		/// </summary>
+		public Color BorderBrush { get; set; }
+
+		/// <summary>
+		/// Gets or sets the <see cref="Color"/> that fills the area between the bounds of a <see cref="Border"/>.
+		/// </summary>
+		public Color Background { get; set; }
+
+		/// <summary>
+		/// Gets or sets a <see cref="Thickness"/> value that describes the amount of space between a
+		///  <see cref="Border"/> and its child element.
+		/// </summary>
+		public Thickness Padding {
+			get => padding;
+			set {
+				padding = value;
+				OnBorderChanged ();
+			}
+		}
+
+		/// <summary>
+		/// Gets the rendered width of this element.
+		/// </summary>
+		public int ActualWidth {
+			get {
+				if (Parent?.Border == null) {
+					return Child?.Frame.Width + (2 * marginFrame) + Padding.Right
+						+ BorderThickness.Right + Padding.Left + BorderThickness.Left ?? 0;
+				}
+				return Parent.Frame.Width;
+			}
+		}
+		/// <summary>
+		/// Gets the rendered height of this element.
+		/// </summary>
+		public int ActualHeight {
+			get {
+				if (Parent?.Border == null) {
+					return Child?.Frame.Height + (2 * marginFrame) + Padding.Bottom
+						+ BorderThickness.Bottom + Padding.Top + BorderThickness.Top ?? 0;
+				}
+				return Parent.Frame.Height;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the single child element of a <see cref="View"/>.
+		/// </summary>
+		public View Child { get; set; }
+
+		/// <summary>
+		/// Gets the parent <see cref="Child"/> parent if any.
+		/// </summary>
+		public View Parent { get => Child?.SuperView; }
+
+		/// <summary>
+		/// Gets or private sets by the <see cref="ToplevelContainer"/>
+		/// </summary>
+		public ToplevelContainer ChildContainer { get; private set; }
+
+		/// <summary>
+		/// Gets or sets the 3D effect around the <see cref="Border"/>.
+		/// </summary>
+		public bool Effect3D { get; set; }
+
+		/// <summary>
+		/// Get or sets the offset start position for the <see cref="Effect3D"/>
+		/// </summary>
+		public Point Effect3DOffset { get; set; } = new Point (1, 1);
+
+		/// <summary>
+		/// Gets or sets the color for the <see cref="Border"/>
+		/// </summary>
+		public Color Effect3DBrush { get; set; } = Color.DarkGray;
+
+		/// <summary>
+		/// Calculate the sum of the <see cref="Padding"/> and the <see cref="BorderThickness"/>
+		/// </summary>
+		/// <returns>The total of the <see cref="Border"/> <see cref="Thickness"/></returns>
+		public Thickness GetSumThickness ()
+		{
+			return new Thickness () {
+				Left = Padding.Left + BorderThickness.Left,
+				Top = Padding.Top + BorderThickness.Top,
+				Right = Padding.Right + BorderThickness.Right,
+				Bottom = Padding.Bottom + BorderThickness.Bottom
+			};
+		}
+
+		/// <summary>
+		/// Drawn the <see cref="BorderThickness"/> more the <see cref="Padding"/>
+		///  more the <see cref="Border.BorderStyle"/> and the <see cref="Effect3D"/>.
+		/// </summary>
+		public void DrawContent ()
+		{
+			if (Parent?.Border != null) {
+				DrawParentBorder (Parent.ViewToScreen (new Rect (0, 0, Parent.Frame.Width, Parent.Frame.Height)));
+			} else {
+				DrawChildBorder (Child.ViewToScreen (new Rect (0, 0, Child.Frame.Width, Child.Frame.Height)));
+			}
+		}
+
+		/// <summary>
+		/// Same as <see cref="DrawContent"/> but drawing full frames for all borders.
+		/// </summary>
+		public void DrawFullContent ()
+		{
+			var borderThickness = BorderThickness;
+			var padding = Padding;
+			var marginFrame = DrawMarginFrame ? 1 : 0;
+			var driver = Application.Driver;
+			Rect scrRect;
+			if (Parent?.Border != null) {
+				scrRect = Parent.ViewToScreen (new Rect (0, 0, Parent.Frame.Width, Parent.Frame.Height));
+			} else {
+				scrRect = Child.ViewToScreen (new Rect (0, 0, Child.Frame.Width, Child.Frame.Height));
+			}
+			Rect borderRect;
+			if (Parent?.Border != null) {
+				borderRect = scrRect;
+			} else {
+				borderRect = new Rect () {
+					X = scrRect.X - marginFrame - padding.Left - borderThickness.Left,
+					Y = scrRect.Y - marginFrame - padding.Top - borderThickness.Top,
+					Width = ActualWidth,
+					Height = ActualHeight
+				};
+			}
+			var savedAttribute = driver.GetAttribute ();
+
+			// Draw 3D effects
+			if (Effect3D) {
+				driver.SetAttribute (new Attribute (Effect3DBrush));
+				var effectBorder = new Rect () {
+					X = borderRect.X + Effect3DOffset.X,
+					Y = borderRect.Y + Effect3DOffset.Y,
+					Width = ActualWidth,
+					Height = ActualHeight
+				};
+				Child.Clear (effectBorder);
+			}
+
+			// Draw border thickness
+			driver.SetAttribute (new Attribute (BorderBrush));
+			Child.Clear (borderRect);
+
+			borderRect = new Rect () {
+				X = borderRect.X + borderThickness.Left,
+				Y = borderRect.Y + borderThickness.Top,
+				Width = Math.Max (borderRect.Width - borderThickness.Right - borderThickness.Left, 0),
+				Height = Math.Max (borderRect.Height - borderThickness.Bottom - borderThickness.Top, 0)
+			};
+			if (borderRect != scrRect) {
+				// Draw padding
+				driver.SetAttribute (new Attribute (Background));
+				Child.Clear (borderRect);
+			}
+
+			driver.SetAttribute (savedAttribute);
+
+			// Draw margin frame
+			if (Parent?.Border != null) {
+				var sumPadding = GetSumThickness ();
+				borderRect = new Rect () {
+					X = scrRect.X + sumPadding.Left,
+					Y = scrRect.Y + sumPadding.Top,
+					Width = Math.Max (scrRect.Width - sumPadding.Right - sumPadding.Left, 0),
+					Height = Math.Max (scrRect.Height - sumPadding.Bottom - sumPadding.Top, 0)
+				};
+			} else {
+				borderRect = new Rect () {
+					X = borderRect.X + padding.Left,
+					Y = borderRect.Y + padding.Top,
+					Width = Math.Max (borderRect.Width - padding.Right - padding.Left, 0),
+					Height = Math.Max (borderRect.Height - padding.Bottom - padding.Top, 0)
+				};
+			}
+			if (borderRect.Width > 0 && borderRect.Height > 0) {
+				driver.DrawWindowFrame (borderRect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill: true, this);
+			}
+		}
+
+		private void DrawChildBorder (Rect frame)
+		{
+			var drawMarginFrame = DrawMarginFrame ? 1 : 0;
+			var sumThickness = GetSumThickness ();
+			var padding = Padding;
+			var effect3DOffset = Effect3DOffset;
+			var driver = Application.Driver;
+
+			var savedAttribute = driver.GetAttribute ();
+
+			driver.SetAttribute (new Attribute (BorderBrush));
+
+			// Draw the upper BorderThickness
+			for (int r = frame.Y - drawMarginFrame - sumThickness.Top;
+				r < frame.Y - drawMarginFrame - padding.Top; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
+					c < frame.Right + drawMarginFrame + sumThickness.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the left BorderThickness
+			for (int r = frame.Y - drawMarginFrame - padding.Top;
+				r < frame.Bottom + drawMarginFrame + padding.Bottom; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
+					c < frame.X - drawMarginFrame - padding.Left; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the right BorderThickness
+			for (int r = frame.Y - drawMarginFrame - padding.Top;
+				r < frame.Bottom + drawMarginFrame + padding.Bottom; r++) {
+				for (int c = frame.Right + drawMarginFrame + padding.Right;
+					c < frame.Right + drawMarginFrame + sumThickness.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the lower BorderThickness
+			for (int r = frame.Bottom + drawMarginFrame + padding.Bottom;
+				r < frame.Bottom + drawMarginFrame + sumThickness.Bottom; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
+					c < frame.Right + drawMarginFrame + sumThickness.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			driver.SetAttribute (new Attribute (Background));
+
+			// Draw the upper Padding
+			for (int r = frame.Y - drawMarginFrame - padding.Top;
+				r < frame.Y - drawMarginFrame; r++) {
+				for (int c = frame.X - drawMarginFrame - padding.Left;
+					c < frame.Right + drawMarginFrame + padding.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the left Padding
+			for (int r = frame.Y - drawMarginFrame;
+				r < frame.Bottom + drawMarginFrame; r++) {
+				for (int c = frame.X - drawMarginFrame - padding.Left;
+					c < frame.X - drawMarginFrame; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the right Padding
+			for (int r = frame.Y - drawMarginFrame;
+				r < frame.Bottom + drawMarginFrame; r++) {
+				for (int c = frame.Right + drawMarginFrame;
+					c < frame.Right + drawMarginFrame + padding.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the lower Padding
+			for (int r = frame.Bottom + drawMarginFrame;
+				r < frame.Bottom + drawMarginFrame + padding.Bottom; r++) {
+				for (int c = frame.X - drawMarginFrame - padding.Left;
+					c < frame.Right + drawMarginFrame + padding.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			driver.SetAttribute (savedAttribute);
+
+			// Draw the MarginFrame
+			var rect = new Rect () {
+				X = frame.X - drawMarginFrame,
+				Y = frame.Y - drawMarginFrame,
+				Width = frame.Width + (2 * drawMarginFrame),
+				Height = frame.Height + (2 * drawMarginFrame)
+			};
+			if (rect.Width > 0 && rect.Height > 0) {
+				driver.DrawWindowFrame (rect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill: true, this);
+			}
+
+			if (Effect3D) {
+				driver.SetAttribute (new Attribute (Effect3DBrush));
+
+				// Draw the upper Effect3D
+				for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y;
+					r < frame.Y - drawMarginFrame - sumThickness.Top; r++) {
+					for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X;
+						c < frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X; c++) {
+
+						AddRuneAt (driver, c, r, ' ');
+					}
+				}
+
+				// Draw the left Effect3D
+				for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y;
+					r < frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y; r++) {
+					for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X;
+						c < frame.X - drawMarginFrame - sumThickness.Left; c++) {
+
+						AddRuneAt (driver, c, r, ' ');
+					}
+				}
+
+				// Draw the right Effect3D
+				for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y;
+					r < frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y; r++) {
+					for (int c = frame.Right + drawMarginFrame + sumThickness.Right;
+						c < frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X; c++) {
+
+						AddRuneAt (driver, c, r, ' ');
+					}
+				}
+
+				// Draw the lower Effect3D
+				for (int r = frame.Bottom + drawMarginFrame + sumThickness.Bottom;
+					r < frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y; r++) {
+					for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X;
+						c < frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X; c++) {
+
+						AddRuneAt (driver, c, r, ' ');
+					}
+				}
+			}
+		}
+
+		private void DrawParentBorder (Rect frame)
+		{
+			var drawMarginFrame = DrawMarginFrame ? 1 : 0;
+			var sumThickness = GetSumThickness ();
+			var borderThickness = BorderThickness;
+			var effect3DOffset = Effect3DOffset;
+			var driver = Application.Driver;
+
+			var savedAttribute = driver.GetAttribute ();
+
+			driver.SetAttribute (new Attribute (BorderBrush));
+
+			// Draw the upper BorderThickness
+			for (int r = frame.Y;
+				r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) {
+				for (int c = frame.X;
+					c < frame.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the left BorderThickness
+			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
+				r < frame.Bottom - borderThickness.Bottom; r++) {
+				for (int c = frame.X;
+					c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the right BorderThickness
+			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
+				r < frame.Bottom - borderThickness.Bottom; r++) {
+				for (int c = Math.Max (frame.Right - borderThickness.Right, frame.X);
+					c < frame.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the lower BorderThickness
+			for (int r = Math.Max (frame.Bottom - borderThickness.Bottom, frame.Y);
+				r < frame.Bottom; r++) {
+				for (int c = frame.X;
+					c < frame.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			driver.SetAttribute (new Attribute (Background));
+
+			// Draw the upper Padding
+			for (int r = frame.Y + borderThickness.Top;
+				r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) {
+				for (int c = frame.X + borderThickness.Left;
+					c < frame.Right - borderThickness.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the left Padding
+			for (int r = frame.Y + sumThickness.Top;
+				r < frame.Bottom - sumThickness.Bottom; r++) {
+				for (int c = frame.X + borderThickness.Left;
+					c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the right Padding
+			for (int r = frame.Y + sumThickness.Top;
+				r < frame.Bottom - sumThickness.Bottom; r++) {
+				for (int c = Math.Max (frame.Right - sumThickness.Right, frame.X + sumThickness.Left);
+					c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			// Draw the lower Padding
+			for (int r = Math.Max (frame.Bottom - sumThickness.Bottom, frame.Y + borderThickness.Top);
+				r < frame.Bottom - borderThickness.Bottom; r++) {
+				for (int c = frame.X + borderThickness.Left;
+					c < frame.Right - borderThickness.Right; c++) {
+
+					AddRuneAt (driver, c, r, ' ');
+				}
+			}
+
+			driver.SetAttribute (savedAttribute);
+
+			// Draw the MarginFrame
+			var rect = new Rect () {
+				X = frame.X + sumThickness.Left,
+				Y = frame.Y + sumThickness.Top,
+				Width = Math.Max (frame.Width - sumThickness.Right - sumThickness.Left, 0),
+				Height = Math.Max (frame.Height - sumThickness.Bottom - sumThickness.Top, 0)
+			};
+			if (rect.Width > 0 && rect.Height > 0) {
+				driver.DrawWindowFrame (rect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill: true, this);
+			}
+
+			if (Effect3D) {
+				driver.SetAttribute (new Attribute (Effect3DBrush));
+
+				// Draw the upper Effect3D
+				for (int r = frame.Y + effect3DOffset.Y;
+					r < frame.Y; r++) {
+					for (int c = frame.X + effect3DOffset.X;
+						c < frame.Right + effect3DOffset.X; c++) {
+
+						AddRuneAt (driver, c, r, ' ');
+					}
+				}
+
+				// Draw the left Effect3D
+				for (int r = frame.Y + effect3DOffset.Y;
+					r < frame.Bottom + effect3DOffset.Y; r++) {
+					for (int c = frame.X + effect3DOffset.X;
+						c < frame.X; c++) {
+
+						AddRuneAt (driver, c, r, ' ');
+					}
+				}
+
+				// Draw the right Effect3D
+				for (int r = frame.Y + effect3DOffset.Y;
+					r < frame.Bottom + effect3DOffset.Y; r++) {
+					for (int c = frame.Right;
+						c < frame.Right + effect3DOffset.X; c++) {
+
+						AddRuneAt (driver, c, r, ' ');
+					}
+				}
+
+				// Draw the lower Effect3D
+				for (int r = frame.Bottom;
+					r < frame.Bottom + effect3DOffset.Y; r++) {
+					for (int c = frame.X + effect3DOffset.X;
+						c < frame.Right + effect3DOffset.X; c++) {
+
+						AddRuneAt (driver, c, r, ' ');
+					}
+				}
+			}
+		}
+
+		private void AddRuneAt (ConsoleDriver driver, int col, int row, Rune ch)
+		{
+			driver.Move (col, row);
+			driver.AddRune (ch);
+		}
+
+		/// <summary>
+		/// Drawn the view text from a <see cref="View"/>.
+		/// </summary>
+		public void DrawTitle (View view, Rect rect)
+		{
+			var driver = Application.Driver;
+			if (BorderStyle != BorderStyle.None) {
+				driver.SetAttribute (view.GetNormalColor ());
+				if (view.HasFocus) {
+					driver.SetAttribute (view.ColorScheme.HotNormal);
+				}
+				var padding = GetSumThickness ();
+				driver.DrawWindowTitle (rect, view.Text,
+					padding.Left, padding.Top, padding.Right, padding.Bottom);
+			}
+			driver.SetAttribute (view.GetNormalColor ());
+		}
+
+		/// <summary>
+		/// Invoke the <see cref="BorderChanged"/> event.
+		/// </summary>
+		public virtual void OnBorderChanged ()
+		{
+			BorderChanged?.Invoke (this);
+		}
+	}
+}

+ 80 - 12
Terminal.Gui/Core/ConsoleDriver.cs

@@ -104,13 +104,31 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Color Background { get; }
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Attribute"/> struct with only the value passed to
+		///   and trying to get the colors if defined.
+		/// </summary>
+		/// <param name="value">Value.</param>
+		public Attribute (int value)
+		{
+			Color foreground = default;
+			Color background = default;
+
+			if (Application.Driver != null) {
+				Application.Driver.GetColors (value, out foreground, out background);
+			}
+			Value = value;
+			Foreground = foreground;
+			Background = background;
+		}
+
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Attribute"/> struct.
 		/// </summary>
 		/// <param name="value">Value.</param>
 		/// <param name="foreground">Foreground</param>
 		/// <param name="background">Background</param>
-		public Attribute (int value, Color foreground = new Color (), Color background = new Color ())
+		public Attribute (int value, Color foreground, Color background)
 		{
 			Value = value;
 			Foreground = foreground;
@@ -129,6 +147,13 @@ namespace Terminal.Gui {
 			Background = background;
 		}
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Attribute"/> struct
+		///  with the same colors for the foreground and background.
+		/// </summary>
+		/// <param name="color">The color.</param>
+		public Attribute (Color color) : this (color, color) { }
+
 		/// <summary>
 		/// Implicit conversion from an <see cref="Attribute"/> to the underlying Int32 representation
 		/// </summary>
@@ -751,6 +776,15 @@ namespace Terminal.Gui {
 		/// <param name="backgroundColorId">Background color identifier.</param>
 		public abstract void SetColors (short foregroundColorId, short backgroundColorId);
 
+		/// <summary>
+		/// Gets the foreground and background colors based on the value.
+		/// </summary>
+		/// <param name="value">The value.</param>
+		/// <param name="foreground">The foreground.</param>
+		/// <param name="background">The background.</param>
+		/// <returns></returns>
+		public abstract bool GetColors (int value, out Color foreground, out Color background);
+
 		/// <summary>
 		/// Allows sending keys without typing on a keyboard.
 		/// </summary>
@@ -803,12 +837,12 @@ namespace Terminal.Gui {
 			/// </summary>
 			Off = 0b_0000_0000,
 			/// <summary>
-			/// When enabled, <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/> will draw a 
+			/// When enabled, <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool, Border)"/> will draw a 
 			/// ruler in the frame for any side with a padding value greater than 0.
 			/// </summary>
 			FrameRuler = 0b_0000_0001,
 			/// <summary>
-			/// When Enabled, <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/> will use
+			/// When Enabled, <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool, Border)"/> will use
 			/// 'L', 'R', 'T', and 'B' for padding instead of ' '.
 			/// </summary>
 			FramePadding = 0b_0000_0010,
@@ -829,7 +863,9 @@ namespace Terminal.Gui {
 		/// <param name="paddingBottom">Number of rows to pad on the bottom (if 0 the border will not appear on the bottom).</param>
 		/// <param name="border">If set to <c>true</c> and any padding dimension is > 0 the border will be drawn.</param>
 		/// <param name="fill">If set to <c>true</c> it will clear the content area (the area inside the padding) with the current color, otherwise the content area will be left untouched.</param>
-		public virtual void DrawWindowFrame (Rect region, int paddingLeft = 0, int paddingTop = 0, int paddingRight = 0, int paddingBottom = 0, bool border = true, bool fill = false)
+		/// <param name="borderContent">The <see cref="Border"/> to be used if defined.</param>
+		public virtual void DrawWindowFrame (Rect region, int paddingLeft = 0, int paddingTop = 0, int paddingRight = 0,
+			int paddingBottom = 0, bool border = true, bool fill = false, Border borderContent = null)
 		{
 			char clearChar = ' ';
 			char leftChar = clearChar;
@@ -866,15 +902,17 @@ namespace Terminal.Gui {
 			// ftop is location of top frame line
 			int ftop = region.Y + paddingTop - 1;
 
-			// fbottom is locaiton of bottom frame line
+			// fbottom is location of bottom frame line
 			int fbottom = ftop + fheight + 1;
 
-			Rune hLine = border ? HLine : clearChar;
-			Rune vLine = border ? VLine : clearChar;
-			Rune uRCorner = border ? URCorner : clearChar;
-			Rune uLCorner = border ? ULCorner : clearChar;
-			Rune lLCorner = border ? LLCorner : clearChar;
-			Rune lRCorner = border ? LRCorner : clearChar;
+			var borderStyle = borderContent == null ? BorderStyle.Single : borderContent.BorderStyle;
+
+			Rune hLine = border ? (borderStyle == BorderStyle.Single ? HLine : HDLine) : clearChar;
+			Rune vLine = border ? (borderStyle == BorderStyle.Single ? VLine : VDLine) : clearChar;
+			Rune uRCorner = border ? (borderStyle == BorderStyle.Single ? URCorner : URDCorner) : clearChar;
+			Rune uLCorner = border ? (borderStyle == BorderStyle.Single ? ULCorner : ULDCorner) : clearChar;
+			Rune lLCorner = border ? (borderStyle == BorderStyle.Single ? LLCorner : LLDCorner) : clearChar;
+			Rune lRCorner = border ? (borderStyle == BorderStyle.Single ? LRCorner : LRDCorner) : clearChar;
 
 			// Outside top
 			if (paddingTop > 1) {
@@ -985,7 +1023,7 @@ namespace Terminal.Gui {
 		/// <param name="region">Screen relative region where the frame will be drawn.</param>
 		/// <param name="padding">Padding to add on the sides.</param>
 		/// <param name="fill">If set to <c>true</c> it will clear the contents with the current color, otherwise the contents will be left untouched.</param>
-		/// <remarks>This API has been superseded by <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/>.</remarks>
+		/// <remarks>This API has been superseded by <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool, Border)"/>.</remarks>
 		/// <remarks>This API is equivalent to calling <c>DrawWindowFrame(Rect, p - 1, p - 1, p - 1, p - 1)</c>. In other words,
 		/// A padding value of 0 means there is actually a one cell border.
 		/// </remarks>
@@ -1164,6 +1202,36 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Rune ContinuousMeterSegment = '\u2588';
 
+		/// <summary>
+		/// Horizontal double line character.
+		/// </summary>
+		public Rune HDLine = '\u2550';
+
+		/// <summary>
+		/// Vertical double line character.
+		/// </summary>
+		public Rune VDLine = '\u2551';
+
+		/// <summary>
+		/// Upper left double corner
+		/// </summary>
+		public Rune ULDCorner = '\u2554';
+
+		/// <summary>
+		/// Lower left double corner
+		/// </summary>
+		public Rune LLDCorner = '\u255a';
+
+		/// <summary>
+		/// Upper right double corner
+		/// </summary>
+		public Rune URDCorner = '\u2557';
+
+		/// <summary>
+		/// Lower right double corner
+		/// </summary>
+		public Rune LRDCorner = '\u255d';
+
 		/// <summary>
 		/// Make the attribute for the foreground and background colors.
 		/// </summary>

+ 1 - 0
Terminal.Gui/Core/Toplevel.cs

@@ -663,6 +663,7 @@ namespace Terminal.Gui {
 					nx = mouseEvent.X - mouseEvent.OfX;
 					ny = mouseEvent.Y - mouseEvent.OfY;
 					dragPosition = new Point (nx, ny);
+					SuperView?.BringSubviewToFront (this);
 					Application.GrabMouse (this);
 				}
 

+ 30 - 8
Terminal.Gui/Core/View.cs

@@ -664,9 +664,10 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// <param name="rect">Location.</param>
 		/// <param name="text">text to initialize the <see cref="Text"/> property with.</param>
-		public View (Rect rect, ustring text)
+		/// <param name="border">The <see cref="Border"/>.</param>
+		public View (Rect rect, ustring text, Border border = null)
 		{
-			Initialize (text, rect, LayoutStyle.Absolute);
+			Initialize (text, rect, LayoutStyle.Absolute, TextDirection.LeftRight_TopBottom, border);
 		}
 
 		/// <summary>
@@ -684,19 +685,22 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// <param name="text">text to initialize the <see cref="Text"/> property with.</param>
 		/// <param name="direction">The text direction.</param>
-		public View (ustring text, TextDirection direction = TextDirection.LeftRight_TopBottom)
+		/// <param name="border">The <see cref="Border"/>.</param>
+		public View (ustring text, TextDirection direction = TextDirection.LeftRight_TopBottom, Border border = null)
 		{
-			Initialize (text, Rect.Empty, LayoutStyle.Computed, direction);
+			Initialize (text, Rect.Empty, LayoutStyle.Computed, direction, border);
 		}
 
 		void Initialize (ustring text, Rect rect, LayoutStyle layoutStyle = LayoutStyle.Computed,
-			TextDirection direction = TextDirection.LeftRight_TopBottom)
+			TextDirection direction = TextDirection.LeftRight_TopBottom, Border border = null)
 		{
 			textFormatter = new TextFormatter ();
 			TextDirection = direction;
-
+			Border = border;
+			if (Border != null) {
+				Border.Child = this;
+			}
 			shortcutHelper = new ShortcutHelper ();
-
 			CanFocus = false;
 			TabIndex = -1;
 			TabStop = false;
@@ -1361,10 +1365,15 @@ namespace Terminal.Gui {
 
 			var clipRect = new Rect (Point.Empty, frame.Size);
 
+			//if (ColorScheme != null && !(this is Toplevel)) {
 			if (ColorScheme != null) {
 				Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
 			}
 
+			if (Border != null) {
+				Border.DrawContent ();
+			}
+
 			if (!ustring.IsNullOrEmpty (Text) || (this is Label && !AutoSize)) {
 				Clear ();
 				// Draw any Text
@@ -2167,6 +2176,19 @@ namespace Terminal.Gui {
 			}
 		}
 
+		Border border;
+
+		/// <inheritdoc/>
+		public virtual Border Border {
+			get => border;
+			set {
+				if (border != value) {
+					border = value;
+					SetNeedsDisplay ();
+				}
+			}
+		}
+
 		/// <summary>
 		/// Pretty prints the View
 		/// </summary>
@@ -2494,7 +2516,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <returns><see cref="ColorScheme.Normal"/> if <see cref="Enabled"/> is <see langword="true"/>
 		/// or <see cref="ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/></returns>
-		protected Attribute GetNormalColor ()
+		public Attribute GetNormalColor ()
 		{
 			return Enabled ? ColorScheme.Normal : ColorScheme.Disabled;
 		}

+ 115 - 34
Terminal.Gui/Core/Window.cs

@@ -23,7 +23,6 @@ namespace Terminal.Gui {
 	public class Window : Toplevel {
 		View contentView;
 		ustring title;
-		int padding;
 
 		/// <summary>
 		/// The title to be displayed for this window.
@@ -37,14 +36,62 @@ namespace Terminal.Gui {
 			}
 		}
 
+		/// <inheritdoc/>
+		public override Border Border {
+			get => base.Border;
+			set {
+				if (base.Border != null && base.Border.Child != null && value.Child == null) {
+					value.Child = base.Border.Child;
+				}
+				base.Border = value;
+				if (value == null) {
+					return;
+				}
+				Rect frame;
+				if (contentView != null && (contentView.Width is Dim || contentView.Height is Dim)) {
+					frame = Rect.Empty;
+				} else {
+					frame = Frame;
+				}
+				AdjustContentView (frame);
+
+				Border.BorderChanged += Border_BorderChanged;
+			}
+		}
+
+		void Border_BorderChanged (Border border)
+		{
+			Rect frame;
+			if (contentView != null && (contentView.Width is Dim || contentView.Height is Dim)) {
+				frame = Rect.Empty;
+			} else {
+				frame = Frame;
+			}
+			AdjustContentView (frame);
+		}
+
+
 		/// <summary>
 		/// ContentView is an internal implementation detail of Window. It is used to host Views added with <see cref="Add(View)"/>. 
 		/// Its ONLY reason for being is to provide a simple way for Window to expose to those SubViews that the Window's Bounds 
 		/// are actually deflated due to the border. 
 		/// </summary>
 		class ContentView : View {
-			public ContentView (Rect frame) : base (frame) { }
-			public ContentView () : base () { }
+			Window instance;
+
+			public ContentView (Rect frame, Window instance) : base (frame)
+			{
+				this.instance = instance;
+			}
+			public ContentView (Window instance) : base ()
+			{
+				this.instance = instance;
+			}
+
+			public override bool MouseEvent (MouseEvent mouseEvent)
+			{
+				return instance.MouseEvent (mouseEvent);
+			}
 		}
 
 		/// <summary>
@@ -56,7 +103,7 @@ namespace Terminal.Gui {
 		/// This constructor initializes a Window with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use constructors
 		/// that do not take <c>Rect</c> parameters to initialize a Window with <see cref="LayoutStyle.Computed"/>. 
 		/// </remarks>
-		public Window (Rect frame, ustring title = null) : this (frame, title, padding: 0)
+		public Window (Rect frame, ustring title = null) : this (frame, title, padding: 0, border: null)
 		{
 		}
 
@@ -68,7 +115,7 @@ namespace Terminal.Gui {
 		///   This constructor initializes a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
 		///   Use <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and <see cref="View.Height"/> properties to dynamically control the size and location of the view.
 		/// </remarks>
-		public Window (ustring title = null) : this (title, padding: 0)
+		public Window (ustring title = null) : this (title, padding: 0, border: null)
 		{
 		}
 
@@ -82,52 +129,82 @@ namespace Terminal.Gui {
 		/// and an optional title.
 		/// </summary>
 		/// <param name="frame">Superview-relative rectangle specifying the location and size</param>
-		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
 		/// <param name="title">Title</param>
+		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
+		/// <param name="border">The <see cref="Border"/>.</param>
 		/// <remarks>
 		/// This constructor initializes a Window with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use constructors
 		/// that do not take <c>Rect</c> parameters to initialize a Window with  <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/> 
 		/// </remarks>
-		public Window (Rect frame, ustring title = null, int padding = 0) : base (frame)
+		public Window (Rect frame, ustring title = null, int padding = 0, Border border = null) : base (frame)
 		{
-			Initialize (title, frame, padding);
+			Initialize (title, frame, padding, border);
 		}
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Window"/> using <see cref="LayoutStyle.Computed"/> positioning,
 		/// and an optional title.
 		/// </summary>
-		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
 		/// <param name="title">Title.</param>
+		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
+		/// <param name="border">The <see cref="Border"/>.</param>
 		/// <remarks>
 		///   This constructor initializes a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
 		///   Use <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and <see cref="View.Height"/> properties to dynamically control the size and location of the view.
 		/// </remarks>
-		public Window (ustring title = null, int padding = 0) : base ()
+		public Window (ustring title = null, int padding = 0, Border border = null) : base ()
 		{
-			Initialize (title, Rect.Empty, padding);
+			Initialize (title, Rect.Empty, padding, border);
 		}
 
-		void Initialize (ustring title, Rect frame, int padding = 0)
+		void Initialize (ustring title, Rect frame, int padding = 0, Border border = null)
 		{
 			ColorScheme = Colors.Base;
 			Title = title;
-			int wb;
-			if (frame == Rect.Empty) {
-				wb = 1 + padding;
-				contentView = new ContentView () {
-					X = wb,
-					Y = wb,
-					Width = Dim.Fill (wb),
-					Height = Dim.Fill (wb)
+			if (border == null) {
+				Border = new Border () {
+					BorderStyle = BorderStyle.Single,
+					Padding = new Thickness (padding),
+					BorderBrush = ColorScheme.Normal.Background
 				};
 			} else {
-				wb = 2 * (1 + padding);
-				var cFrame = new Rect (1 + padding, 1 + padding, frame.Width - wb, frame.Height - wb);
-				contentView = new ContentView (cFrame);
+				Border = border;
+			}
+		}
+
+		void AdjustContentView (Rect frame)
+		{
+			var borderLength = Border.DrawMarginFrame ? 1 : 0;
+			var sumPadding = Border.GetSumThickness ();
+			var wb = new Size ();
+			if (frame == Rect.Empty) {
+				wb.Width = borderLength + sumPadding.Right;
+				wb.Height = borderLength + sumPadding.Bottom;
+				if (contentView == null) {
+					contentView = new ContentView (this) {
+						X = borderLength + sumPadding.Left,
+						Y = borderLength + sumPadding.Top,
+						Width = Dim.Fill (wb.Width),
+						Height = Dim.Fill (wb.Height)
+					};
+				} else {
+					contentView.X = borderLength + sumPadding.Left;
+					contentView.Y = borderLength + sumPadding.Top;
+					contentView.Width = Dim.Fill (wb.Width);
+					contentView.Height = Dim.Fill (wb.Height);
+				}
+			} else {
+				wb.Width = (2 * borderLength) + sumPadding.Right + sumPadding.Left;
+				wb.Height = (2 * borderLength) + sumPadding.Bottom + sumPadding.Top;
+				var cFrame = new Rect (borderLength + sumPadding.Left, borderLength + sumPadding.Top, frame.Width - wb.Width, frame.Height - wb.Height);
+				if (contentView == null) {
+					contentView = new ContentView (cFrame, this);
+				} else {
+					contentView.Frame = cFrame;
+				}
 			}
-			this.padding = padding;
 			base.Add (contentView);
+			Border.Child = contentView;
 		}
 
 		///// <summary>
@@ -176,16 +253,18 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
-			//var padding = 0;
+			var padding = Border.GetSumThickness ();
 			var scrRect = ViewToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
+			//var borderLength = Border.DrawMarginFrame ? 1 : 0;
 
 			// BUGBUG: Why do we draw the frame twice? This call is here to clear the content area, I think. Why not just clear that area?
 			if (!NeedDisplay.IsEmpty) {
 				Driver.SetAttribute (GetNormalColor ());
-				Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: true);
+				//Driver.DrawWindowFrame (scrRect, padding.Left + borderLength, padding.Top + borderLength, padding.Right + borderLength, padding.Bottom + borderLength,
+				//	Border.BorderStyle != BorderStyle.None, fill: true, Border);
+				Border.DrawContent ();
 			}
-
-			var savedClip = ClipToBounds ();
+			var savedClip = contentView.ClipToBounds ();
 
 			// Redraw our contentView
 			// TODO: smartly constrict contentView.Bounds to just be what intersects with the 'bounds' we were passed
@@ -194,12 +273,14 @@ namespace Terminal.Gui {
 
 			ClearLayoutNeeded ();
 			ClearNeedsDisplay ();
-			Driver.SetAttribute (GetNormalColor ());
-			Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false);
-
-			if (HasFocus)
-				Driver.SetAttribute (ColorScheme.HotNormal);
-			Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
+			if (Border.BorderStyle != BorderStyle.None) {
+				Driver.SetAttribute (GetNormalColor ());
+				//Driver.DrawWindowFrame (scrRect, padding.Left + borderLength, padding.Top + borderLength, padding.Right + borderLength, padding.Bottom + borderLength,
+				//	Border.BorderStyle != BorderStyle.None, fill: true, Border.BorderStyle);
+				if (HasFocus)
+					Driver.SetAttribute (ColorScheme.HotNormal);
+				Driver.DrawWindowTitle (scrRect, Title, padding.Left, padding.Top, padding.Right, padding.Bottom);
+			}
 			Driver.SetAttribute (GetNormalColor ());
 
 			// Checks if there are any SuperView view which intersect with this window.

+ 93 - 33
Terminal.Gui/Views/FrameView.cs

@@ -34,6 +34,40 @@ namespace Terminal.Gui {
 			}
 		}
 
+		/// <inheritdoc/>
+		public override Border Border {
+			get => base.Border;
+			set {
+				if (base.Border != null && base.Border.Child != null && value.Child == null) {
+					value.Child = base.Border.Child;
+				}
+				base.Border = value;
+				if (value == null) {
+					return;
+				}
+				Rect frame;
+				if (contentView != null && (contentView.Width is Dim || contentView.Height is Dim)) {
+					frame = Rect.Empty;
+				} else {
+					frame = Frame;
+				}
+				AdjustContentView (frame);
+
+				Border.BorderChanged += Border_BorderChanged;
+			}
+		}
+
+		void Border_BorderChanged (Border border)
+		{
+			Rect frame;
+			if (contentView != null && (contentView.Width is Dim || contentView.Height is Dim)) {
+				frame = Rect.Empty;
+			} else {
+				frame = Frame;
+			}
+			AdjustContentView (frame);
+		}
+
 		/// <summary>
 		/// ContentView is an internal implementation detail of Window. It is used to host Views added with <see cref="Add(View)"/>. 
 		/// Its ONLY reason for being is to provide a simple way for Window to expose to those SubViews that the Window's Bounds 
@@ -49,30 +83,22 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="frame">Frame.</param>
 		/// <param name="title">Title.</param>
-		public FrameView (Rect frame, ustring title = null) : base (frame)
-		{
-			var cFrame = new Rect (1, 1, Math.Max (frame.Width - 2, 0), Math.Max (frame.Height - 2, 0));
-			Initialize (title, cFrame);
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Gui.FrameView"/> class using <see cref="LayoutStyle.Computed"/> layout.
-		/// </summary>
-		/// <param name="frame">Frame.</param>
-		/// <param name="title">Title.</param>
 		/// <param name="views">Views.</param>
-		public FrameView (Rect frame, ustring title, View [] views) : this (frame, title)
+		/// <param name="border">The <see cref="Border"/>.</param>
+		public FrameView (Rect frame, ustring title = null, View [] views = null, Border border = null) : base (frame)
 		{
-			Initialize (title, frame, views);
+			//var cFrame = new Rect (1, 1, Math.Max (frame.Width - 2, 0), Math.Max (frame.Height - 2, 0));
+			Initialize (frame, title, views, border);
 		}
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Gui.FrameView"/> class using <see cref="LayoutStyle.Computed"/> layout.
 		/// </summary>
 		/// <param name="title">Title.</param>
-		public FrameView (ustring title)
+		/// <param name="border">The <see cref="Border"/>.</param>
+		public FrameView (ustring title, Border border = null)
 		{
-			Initialize (title, Rect.Empty);
+			Initialize (Rect.Empty, title, null, border);
 		}
 
 		/// <summary>
@@ -80,19 +106,49 @@ namespace Terminal.Gui {
 		/// </summary>
 		public FrameView () : this (title: string.Empty) { }
 
-		void Initialize (ustring title, Rect frame, View [] views = null)
+		void Initialize (Rect frame, ustring title, View [] views = null, Border border = null)
 		{
 			this.title = title;
-			if (frame == Rect.Empty) {
-				const int wb = 1;
-				contentView = new ContentView () {
-					X = wb,
-					Y = wb,
-					Width = Dim.Fill (wb),
-					Height = Dim.Fill (wb)
+			if (border == null) {
+				Border = new Border () {
+					BorderStyle = BorderStyle.Single
 				};
 			} else {
-				contentView = new ContentView (frame);
+				Border = border;
+			}
+			AdjustContentView (frame, views);
+		}
+
+		void AdjustContentView (Rect frame, View [] views = null)
+		{
+			var borderLength = Border.DrawMarginFrame ? 1 : 0;
+			var sumPadding = Border.GetSumThickness ();
+			var wb = new Size ();
+			if (frame == Rect.Empty) {
+				wb.Width = borderLength + sumPadding.Right;
+				wb.Height = borderLength + sumPadding.Bottom;
+				if (contentView == null) {
+					contentView = new ContentView () {
+						X = borderLength + sumPadding.Left,
+						Y = borderLength + sumPadding.Top,
+						Width = Dim.Fill (wb.Width),
+						Height = Dim.Fill (wb.Height)
+					};
+				} else {
+					contentView.X = borderLength + sumPadding.Left;
+					contentView.Y = borderLength + sumPadding.Top;
+					contentView.Width = Dim.Fill (wb.Width);
+					contentView.Height = Dim.Fill (wb.Height);
+				}
+			} else {
+				wb.Width = (2 * borderLength) + sumPadding.Right + sumPadding.Left;
+				wb.Height = (2 * borderLength) + sumPadding.Bottom + sumPadding.Top;
+				var cFrame = new Rect (borderLength + sumPadding.Left, borderLength + sumPadding.Top, frame.Width - wb.Width, frame.Height - wb.Height);
+				if (contentView == null) {
+					contentView = new ContentView (cFrame);
+				} else {
+					contentView.Frame = cFrame;
+				}
 			}
 			if (views != null) {
 				foreach (var view in views) {
@@ -103,6 +159,7 @@ namespace Terminal.Gui {
 				base.Add (contentView);
 				contentView.Text = base.Text;
 			}
+			Border.Child = contentView;
 		}
 
 		void DrawFrame ()
@@ -153,25 +210,28 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
-			var padding = 0;
+			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);
+				//Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: true);
+				Border.DrawContent ();
 			}
 
-			var savedClip = ClipToBounds ();
+			var savedClip = contentView.ClipToBounds ();
 			contentView.Redraw (contentView.Bounds);
 			Driver.Clip = savedClip;
 
 			ClearNeedsDisplay ();
-			Driver.SetAttribute (GetNormalColor ());
-			Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false);
-
-			if (HasFocus)
-				Driver.SetAttribute (ColorScheme.HotNormal);
-			Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
+			if (Border.BorderStyle != BorderStyle.None) {
+				Driver.SetAttribute (GetNormalColor ());
+				//Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false);
+				if (HasFocus)
+					Driver.SetAttribute (ColorScheme.HotNormal);
+				//Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
+				Driver.DrawWindowTitle (scrRect, Title, padding.Left, padding.Top, padding.Right, padding.Bottom);
+			}
 			Driver.SetAttribute (GetNormalColor ());
 		}
 

+ 219 - 0
Terminal.Gui/Views/PanelView.cs

@@ -0,0 +1,219 @@
+using System;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// A container for single <see cref="Child"/> that will allow to drawn <see cref="Border"/> in
+	///  two ways. If <see cref="UsePanelFrame"/> the borders and the child will be accommodated in the available
+	///  panel size, otherwise the panel will be resized based on the child and borders thickness sizes.
+	/// </summary>
+	public class PanelView : View {
+		ChildContentView childContentView;
+
+		private class ChildContentView : View { }
+
+		private class SavedPosDim {
+			public Pos X;
+			public Pos Y;
+			public Dim Width;
+			public Dim Height;
+		}
+
+		private SavedPosDim savedPanel;
+		private SavedPosDim savedChild;
+
+		private View child;
+		private bool usePanelFrame;
+
+		/// <summary>
+		/// Initializes a panel with a null child.
+		/// </summary>
+		public PanelView () : this (null) { }
+
+		/// <summary>
+		/// Initializes a panel with a valid child.
+		/// </summary>
+		/// <param name="child"></param>
+		public PanelView (View child)
+		{
+			childContentView = new ChildContentView ();
+			base.Add (childContentView);
+			CanFocus = false;
+			Child = child;
+			if (child != null) {
+				Visible = child.Visible;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets if the panel size will used, otherwise the child size.
+		/// </summary>
+		public bool UsePanelFrame {
+			get => usePanelFrame;
+			set {
+				usePanelFrame = value;
+				AdjustContainer ();
+			}
+		}
+
+		/// <summary>
+		/// The child that will use this panel.
+		/// </summary>
+		public View Child {
+			get => child;
+			set {
+				if (child != null && value == null) {
+					childContentView.Remove (child);
+					child = value;
+					return;
+				}
+				child = value;
+				savedChild = new SavedPosDim () {
+					X = child?.X,
+					Y = child?.Y,
+					Width = child?.Width,
+					Height = child?.Height
+				};
+				if (child == null) {
+					Visible = false;
+					return;
+				}
+				child.X = 0;
+				child.Y = 0;
+				AdjustContainer ();
+				if (child?.Border != null) {
+					child.Border.BorderChanged += Border_BorderChanged;
+					Border = child.Border;
+					Border.Child = childContentView;
+				} else {
+					if (Border == null) {
+						Border = new Border ();
+					}
+					Border.BorderChanged += Border_BorderChanged;
+					Border.Child = childContentView;
+				}
+				if (!child.IsInitialized) {
+					child.Initialized += Child_Initialized;
+				}
+				childContentView.Add (Child);
+			}
+		}
+
+		private void Child_Initialized (object sender, EventArgs e)
+		{
+			savedPanel = new SavedPosDim () {
+				X = X,
+				Y = Y,
+				Width = Width,
+				Height = Height
+			};
+			AdjustContainer ();
+			Child.Initialized -= Child_Initialized;
+		}
+
+		private void Border_BorderChanged (Border obj)
+		{
+			AdjustContainer ();
+		}
+
+		private void AdjustContainer ()
+		{
+			if (Child?.IsInitialized == true) {
+				var borderLength = Child.Border != null
+					? Child.Border.DrawMarginFrame ? 1 : 0
+					: 0;
+				var sumPadding = Child.Border != null
+					? Child.Border.GetSumThickness ()
+					: new Thickness ();
+				if (!UsePanelFrame) {
+					X = savedChild.X;
+					childContentView.X = borderLength + sumPadding.Left;
+					Y = savedChild.Y;
+					childContentView.Y = borderLength + sumPadding.Top;
+					if (savedChild.Width is Dim.DimFill) {
+						var margin = -savedChild.Width.Anchor (0);
+						Width = Dim.Fill (margin);
+						childContentView.Width = Dim.Fill (margin + borderLength + sumPadding.Right);
+					} else {
+						Width = savedChild.Width + (2 * borderLength) + sumPadding.Right + sumPadding.Left;
+						childContentView.Width = Dim.Fill (borderLength + sumPadding.Right);
+					}
+					if (savedChild.Height is Dim.DimFill) {
+						var margin = -savedChild.Height.Anchor (0);
+						Height = Dim.Fill (margin);
+						childContentView.Height = Dim.Fill (margin + borderLength + sumPadding.Bottom);
+					} else {
+						Height = savedChild.Height + (2 * borderLength) + sumPadding.Bottom + sumPadding.Top;
+						childContentView.Height = Dim.Fill (borderLength + sumPadding.Bottom);
+					}
+				} else {
+					X = savedPanel.X;
+					childContentView.X = borderLength + sumPadding.Left;
+					Y = savedPanel.Y;
+					childContentView.Y = borderLength + sumPadding.Top;
+					Width = savedPanel.Width;
+					Height = savedPanel.Height;
+					if (Width is Dim.DimFill) {
+						var margin = -savedPanel.Width.Anchor (0);
+						childContentView.Width = Dim.Fill (margin + borderLength + sumPadding.Right);
+					} else {
+						childContentView.Width = Dim.Fill (borderLength + sumPadding.Right);
+					}
+					if (Height is Dim.DimFill) {
+						var margin = -savedPanel.Height.Anchor (0);
+						childContentView.Height = Dim.Fill (margin + borderLength + sumPadding.Bottom);
+					} else {
+						childContentView.Height = Dim.Fill (borderLength + sumPadding.Bottom);
+					}
+				}
+				Visible = Child.Visible;
+			} else {
+				Visible = false;
+			}
+		}
+
+		/// <inheritdoc/>
+		public override void Add (View view)
+		{
+			if (Child != null) {
+				Child = null;
+			}
+			Child = view;
+		}
+
+		/// <inheritdoc/>
+		public override void Remove (View view)
+		{
+			if (view == childContentView) {
+				base.Remove (view);
+				return;
+			}
+			childContentView.Remove (view);
+			if (Child != null) {
+				Child = null;
+			}
+		}
+
+		/// <inheritdoc/>
+		public override void RemoveAll ()
+		{
+			if (Child != null) {
+				Child = null;
+			}
+		}
+
+		/// <inheritdoc/>
+		public override void Redraw (Rect bounds)
+		{
+			if (!NeedDisplay.IsEmpty) {
+				Driver.SetAttribute (Child.GetNormalColor ());
+				Border.DrawContent ();
+			}
+			var savedClip = childContentView.ClipToBounds ();
+			childContentView.Redraw (childContentView.Bounds);
+			Driver.Clip = savedClip;
+
+			ClearLayoutNeeded ();
+			ClearNeedsDisplay ();
+		}
+	}
+}

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

@@ -54,6 +54,7 @@ namespace Terminal.Gui {
 
 			ColorScheme = Colors.Dialog;
 			Modal = true;
+			Border.Effect3D = true;
 
 			if (buttons != null) {
 				foreach (var b in buttons) {

+ 50 - 0
UICatalog/Scenarios/BasicColors.cs

@@ -43,6 +43,56 @@ namespace UICatalog {
 				x = 30;
 				y++;
 			}
+
+			Win.Add (new Label ("Mouse over to get the view color:") {
+				X = Pos.AnchorEnd (35)
+			});
+			Win.Add (new Label ("Foreground:") {
+				X = Pos.AnchorEnd (34),
+				Y = 2
+			});
+
+			var lblForeground = new Label () {
+				X = Pos.AnchorEnd (20),
+				Y = 2
+			};
+			Win.Add (lblForeground);
+
+			var viewForeground = new View ("  ") {
+				X = Pos.AnchorEnd (2),
+				Y = 2,
+				ColorScheme = new ColorScheme ()
+			};
+			Win.Add (viewForeground);
+
+			Win.Add (new Label ("Background:") {
+				X = Pos.AnchorEnd (34),
+				Y = 4
+			});
+
+			var lblBackground = new Label () {
+				X = Pos.AnchorEnd (20),
+				Y = 4
+			};
+			Win.Add (lblBackground);
+
+			var viewBackground = new View ("  ") {
+				X = Pos.AnchorEnd (2),
+				Y = 4,
+				ColorScheme = new ColorScheme ()
+			};
+			Win.Add (viewBackground);
+
+			Application.RootMouseEvent = (e) => {
+				if (e.View != null) {
+					var colorValue = e.View.GetNormalColor ().Value;
+					Application.Driver.GetColors (colorValue, out Color fore, out Color back);
+					lblForeground.Text = fore.ToString ();
+					viewForeground.ColorScheme.Normal = new Attribute (fore, fore);
+					lblBackground.Text = back.ToString ();
+					viewBackground.ColorScheme.Normal = new Attribute (back, back);
+				}
+			};
 		}
 	}
 }

+ 466 - 0
UICatalog/Scenarios/Borders.cs

@@ -0,0 +1,466 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Borders with/without PanelView", Description: "Demonstrate with/without PanelView borders manipulation.")]
+	[ScenarioCategory ("Border")]
+	class Borders : Scenario {
+		public override void Setup ()
+		{
+			var borderStyle = BorderStyle.Single;
+			var drawMarginFrame = true;
+			var borderThickness = new Thickness (2);
+			var borderBrush = Color.Red;
+			var padding = new Thickness (2);
+			var background = Color.BrightGreen;
+			var effect3D = true;
+
+			var smartPanel = new PanelView () {
+				X = Pos.Center () - 38,
+				Y = Pos.Center () - 3,
+				Width = 24,
+				Height = 13
+			};
+			smartPanel.Add (new Label () { // Or smartPanel.Child = 
+				X = Pos.Center () - 38,
+				Y = Pos.Center () - 3,
+				Width = 24,
+				Height = 13,
+				Border = new Border () {
+					BorderStyle = borderStyle,
+					DrawMarginFrame = drawMarginFrame,
+					BorderThickness = borderThickness,
+					BorderBrush = borderBrush,
+					Padding = padding,
+					Background = background,
+					Effect3D = effect3D
+				},
+				ColorScheme = Colors.TopLevel,
+				Text = "This is a test\nwith a \nPanelView",
+				TextAlignment = TextAlignment.Centered
+			});
+
+			// Can be initialized this way too.
+
+			//var smartPanel = new PanelView (new Label () {
+			//	X = Pos.Center () - 38,
+			//	Y = Pos.Center () - 3,
+			//	Width = 24,
+			//	Height = 13,
+			//	Border = new Border () {
+			//		BorderStyle = borderStyle,
+			//		DrawMarginFrame = drawMarginFrame,
+			//		BorderThickness = borderThickness,
+			//		BorderBrush = borderBrush,
+			//		Padding = padding,
+			//		Background = background,
+			//		Effect3D = effect3D
+			//	},
+			//	ColorScheme = Colors.TopLevel,
+			//	Text = "This is a test\nwith a \nPanelView",
+			//	TextAlignment = TextAlignment.Centered
+			//}) {
+			//	X = Pos.Center () - 38,
+			//	Y = Pos.Center () - 3,
+			//	Width = 24,
+			//	Height = 13
+			//};
+
+			var smartView = new Label () {
+				X = Pos.Center () + 10,
+				Y = Pos.Center () + 2,
+				Border = new Border () {
+					BorderStyle = borderStyle,
+					DrawMarginFrame = drawMarginFrame,
+					BorderThickness = borderThickness,
+					BorderBrush = borderBrush,
+					Padding = padding,
+					Background = background,
+					Effect3D = effect3D
+				},
+				ColorScheme = Colors.TopLevel,
+				Text = "This is a test\nwithout a \nPanelView",
+				TextAlignment = TextAlignment.Centered
+			};
+			smartView.Border.Child = smartView;
+
+			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 {
+					smartPanel.Child.Border.Padding = new Thickness (smartPanel.Child.Border.Padding.Left,
+						int.Parse (e.NewText.ToString ()), smartPanel.Child.Border.Padding.Right,
+						smartPanel.Child.Border.Padding.Bottom);
+
+					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 {
+					smartPanel.Child.Border.Padding = new Thickness (int.Parse (e.NewText.ToString ()),
+						smartPanel.Child.Border.Padding.Top, smartPanel.Child.Border.Padding.Right,
+						smartPanel.Child.Border.Padding.Bottom);
+
+					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 {
+					smartPanel.Child.Border.Padding = new Thickness (smartPanel.Child.Border.Padding.Left,
+						smartPanel.Child.Border.Padding.Top, int.Parse (e.NewText.ToString ()),
+						smartPanel.Child.Border.Padding.Bottom);
+
+					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 {
+					smartPanel.Child.Border.Padding = new Thickness (smartPanel.Child.Border.Padding.Left,
+						smartPanel.Child.Border.Padding.Top, smartPanel.Child.Border.Padding.Right,
+						int.Parse (e.NewText.ToString ()));
+
+					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.Center () - 35,
+				Y = 5
+			};
+			replacePadding.Clicked += () => {
+				smartPanel.Child.Border.Padding = new Thickness (smartPanel.Child.Border.Padding.Top);
+				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);
+
+			var cbUseUsePanelFrame = new CheckBox ("UsePanelFrame") {
+				X = Pos.X (replacePadding),
+				Y = Pos.Y (replacePadding) + 3,
+				Checked = smartPanel.UsePanelFrame
+			};
+			cbUseUsePanelFrame.Toggled += (e) => smartPanel.UsePanelFrame = !e;
+			Win.Add (cbUseUsePanelFrame);
+
+			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 {
+					smartPanel.Child.Border.BorderThickness = new Thickness (smartPanel.Child.Border.BorderThickness.Left,
+						int.Parse (e.NewText.ToString ()), smartPanel.Child.Border.BorderThickness.Right,
+						smartPanel.Child.Border.BorderThickness.Bottom);
+
+					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 {
+					smartPanel.Child.Border.BorderThickness = new Thickness (int.Parse (e.NewText.ToString ()),
+						smartPanel.Child.Border.BorderThickness.Top, smartPanel.Child.Border.BorderThickness.Right,
+						smartPanel.Child.Border.BorderThickness.Bottom);
+
+					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 {
+					smartPanel.Child.Border.BorderThickness = new Thickness (smartPanel.Child.Border.BorderThickness.Left,
+						smartPanel.Child.Border.BorderThickness.Top, int.Parse (e.NewText.ToString ()),
+						smartPanel.Child.Border.BorderThickness.Bottom);
+
+					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 {
+					smartPanel.Child.Border.BorderThickness = new Thickness (smartPanel.Child.Border.BorderThickness.Left,
+						smartPanel.Child.Border.BorderThickness.Top, smartPanel.Child.Border.BorderThickness.Right,
+						int.Parse (e.NewText.ToString ()));
+
+					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.Center () + 1,
+				Y = 5
+			};
+			replaceBorder.Clicked += () => {
+				smartPanel.Child.Border.BorderThickness = new Thickness (smartPanel.Child.Border.BorderThickness.Top);
+				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:"));
+
+			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 {
+					smartPanel.Child.Border.DrawMarginFrame = cbDrawMarginFrame.Checked;
+					smartView.Border.DrawMarginFrame = cbDrawMarginFrame.Checked;
+					if (cbDrawMarginFrame.Checked != smartView.Border.DrawMarginFrame) {
+						cbDrawMarginFrame.Checked = smartView.Border.DrawMarginFrame;
+					}
+				} catch { }
+			};
+			Win.Add (cbDrawMarginFrame);
+
+			rbBorderStyle.SelectedItemChanged += (e) => {
+				smartPanel.Child.Border.BorderStyle = (BorderStyle)e.SelectedItem;
+				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 {
+					smartPanel.Child.Border.Effect3DOffset = new Point (int.Parse (e.NewText.ToString ()),
+						smartPanel.Child.Border.Effect3DOffset.Y);
+
+					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 {
+					smartPanel.Child.Border.Effect3DOffset = new Point (smartPanel.Child.Border.Effect3DOffset.X,
+						int.Parse (e.NewText.ToString ()));
+
+					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 {
+					smartPanel.Child.Border.Effect3D = smartView.Border.Effect3D = effect3DOffsetX.Enabled =
+						effect3DOffsetY.Enabled = 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) => {
+				smartPanel.Child.Border.Background = 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) => {
+				smartPanel.Child.Border.BorderBrush = smartView.Border.BorderBrush = (Color)e.SelectedItem;
+			};
+			Win.Add (rbBorderBrush);
+
+			Win.Add (smartPanel);
+			Win.Add (smartView);
+
+			Win.BringSubviewToFront (smartPanel);
+		}
+	}
+}

+ 144 - 0
UICatalog/Scenarios/BordersComparisons.cs

@@ -0,0 +1,144 @@
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Borders Comparisons", Description: "Compares Window, Toplevel and FrameView borders.")]
+	[ScenarioCategory ("Border")]
+	class BordersComparisons : Scenario {
+		public override void Init (Toplevel top, ColorScheme colorScheme)
+		{
+			top.Dispose ();
+			Application.Init ();
+
+			top = Application.Top;
+
+			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;
+
+			var win = new Window (new Rect (5, 5, 40, 20), "Test", 8,
+				new Border () {
+					BorderStyle = borderStyle,
+					DrawMarginFrame = drawMarginFrame,
+					BorderThickness = borderThickness,
+					BorderBrush = borderBrush,
+					Padding = padding,
+					Background = background,
+					Effect3D = effect3D
+				});
+
+			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 (),
+				ColorScheme = Colors.Dialog,
+				Text = "1234567890"
+			};
+			win.Add (tf1, button, label, tf2, tv);
+			top.Add (win);
+
+			var top2 = new Border.ToplevelContainer (new Rect (50, 5, 40, 20),
+				new Border () {
+					BorderStyle = borderStyle,
+					DrawMarginFrame = drawMarginFrame,
+					BorderThickness = borderThickness,
+					BorderBrush = borderBrush,
+					Padding = padding,
+					Background = background,
+					Effect3D = effect3D
+				},
+				"Test2") {
+				ColorScheme = Colors.Base,
+			};
+
+			var tf3 = new TextField ("1234567890") { Width = 10 };
+
+			var button2 = new Button ("Press me!") {
+				X = Pos.Center (),
+				Y = Pos.Center (),
+			};
+			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,
+			};
+			var tf4 = new TextField ("1234567890") {
+				X = Pos.AnchorEnd (10),
+				Y = Pos.AnchorEnd (1),
+				Width = 10
+			};
+			var tv2 = new TextView () {
+				Y = Pos.AnchorEnd (2),
+				Width = 10,
+				Height = Dim.Fill (),
+				ColorScheme = Colors.Dialog,
+				Text = "1234567890"
+			};
+			top2.Add (tf3, button2, label2, tf4, tv2);
+			top.Add (top2);
+
+			var frm = new FrameView (new Rect (95, 5, 40, 20), "Test3", null,
+				new Border () {
+					BorderStyle = borderStyle,
+					DrawMarginFrame = drawMarginFrame,
+					BorderThickness = borderThickness,
+					BorderBrush = borderBrush,
+					Padding = padding,
+					Background = background,
+					Effect3D = effect3D
+				}) { ColorScheme = Colors.Base };
+
+			var tf5 = new TextField ("1234567890") { Width = 10 };
+
+			var button3 = new Button ("Press me!") {
+				X = Pos.Center (),
+				Y = Pos.Center (),
+			};
+			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,
+			};
+			var tf6 = new TextField ("1234567890") {
+				X = Pos.AnchorEnd (10),
+				Y = Pos.AnchorEnd (1),
+				Width = 10
+			};
+			var tv3 = new TextView () {
+				Y = Pos.AnchorEnd (2),
+				Width = 10,
+				Height = Dim.Fill (),
+				ColorScheme = Colors.Dialog,
+				Text = "1234567890"
+			};
+			frm.Add (tf5, button3, label3, tf6, tv3);
+			top.Add (frm);
+
+			Application.Run ();
+		}
+
+		public override void Run ()
+		{
+			// Do nothing
+		}
+	}
+}

+ 386 - 0
UICatalog/Scenarios/BordersOnFrameView.cs

@@ -0,0 +1,386 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Borders on FrameView", Description: "Demonstrate FrameView borders manipulation.")]
+	[ScenarioCategory ("Border")]
+	class BordersOnFrameView : Scenario {
+		public override void Setup ()
+		{
+			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;
+
+			var smartView = new FrameView () {
+				X = Pos.Center (),
+				Y = Pos.Center () - 7,
+				Width = 40,
+				Height = 20,
+				Border = new Border () {
+					BorderStyle = borderStyle,
+					DrawMarginFrame = drawMarginFrame,
+					BorderThickness = borderThickness,
+					BorderBrush = borderBrush,
+					Padding = padding,
+					Background = background,
+					Effect3D = effect3D
+				},
+				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 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 (),
+				ColorScheme = Colors.Dialog,
+				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.Center () - 35,
+				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.Center () + 1,
+				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:"));
+
+			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 = 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 = 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);
+
+			Win.Add (smartView);
+		}
+	}
+}

+ 386 - 0
UICatalog/Scenarios/BordersOnToplevel.cs

@@ -0,0 +1,386 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Borders on Toplevel", Description: "Demonstrate Toplevel borders manipulation.")]
+	[ScenarioCategory ("Border")]
+	class BordersOnToplevel : Scenario {
+		public override void Setup ()
+		{
+			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;
+
+			var smartView = new Border.ToplevelContainer () {
+				X = Pos.Center (),
+				Y = Pos.Center () - 7,
+				Width = 40,
+				Height = 20,
+				Border = new Border () {
+					BorderStyle = borderStyle,
+					DrawMarginFrame = drawMarginFrame,
+					BorderThickness = borderThickness,
+					BorderBrush = borderBrush,
+					Padding = padding,
+					Background = background,
+					Effect3D = effect3D
+				},
+				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 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 (),
+				ColorScheme = Colors.Dialog,
+				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.Center () - 35,
+				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.Center () + 1,
+				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:"));
+
+			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 = 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 = 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);
+
+			Win.Add (smartView);
+		}
+	}
+}

+ 386 - 0
UICatalog/Scenarios/BordersOnWindow.cs

@@ -0,0 +1,386 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Borders on Window", Description: "Demonstrate Window borders manipulation.")]
+	[ScenarioCategory ("Border")]
+	class BordersOnWindow : Scenario {
+		public override void Setup ()
+		{
+			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;
+
+			var smartView = new Window () {
+				X = Pos.Center (),
+				Y = Pos.Center () - 7,
+				Width = 40,
+				Height = 20,
+				Border = new Border () {
+					BorderStyle = borderStyle,
+					DrawMarginFrame = drawMarginFrame,
+					BorderThickness = borderThickness,
+					BorderBrush = borderBrush,
+					Padding = padding,
+					Background = background,
+					Effect3D = effect3D
+				},
+				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 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 (),
+				ColorScheme = Colors.Dialog,
+				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.Center () - 35,
+				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.Center () + 1,
+				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:"));
+
+			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 = 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 = 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);
+
+			Win.Add (smartView);
+		}
+	}
+}

+ 22 - 2
UnitTests/AttributeTests.cs

@@ -43,6 +43,14 @@ namespace Terminal.Gui.ConsoleDrivers {
 			Assert.Equal (fg, attr.Foreground);
 			Assert.Equal (bg, attr.Background);
 
+			attr = new Attribute (fg);
+			Assert.Equal (fg, attr.Foreground);
+			Assert.Equal (fg, attr.Background);
+
+			attr = new Attribute (bg);
+			Assert.Equal (bg, attr.Foreground);
+			Assert.Equal (bg, attr.Background);
+
 			driver.End ();
 			Application.Shutdown ();
 		}
@@ -63,12 +71,12 @@ namespace Terminal.Gui.ConsoleDrivers {
 			var bg = new Color ();
 			bg = Color.Blue;
 
-			// Test converstion to int
+			// Test conversion to int
 			attr = new Attribute (value, fg, bg);
 			int value_implicit = (int)attr.Value;
 			Assert.Equal (value, value_implicit);
 
-			// Test converstion from int
+			// Test conversion from int
 			attr = value;
 			Assert.Equal (value, attr.Value);
 
@@ -143,5 +151,17 @@ namespace Terminal.Gui.ConsoleDrivers {
 			driver.End ();
 			Application.Shutdown ();
 		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void GetColors_Based_On_Value ()
+		{
+			var driver = Application.Driver;
+			var attrValue = new Attribute (Color.Red, Color.Green).Value;
+			driver.GetColors (attrValue, out Color fg, out Color bg);
+
+			Assert.Equal (Color.Red, fg);
+			Assert.Equal (Color.Green, bg);
+		}
 	}
 }

+ 540 - 0
UnitTests/BorderTests.cs

@@ -0,0 +1,540 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+using Rune = System.Rune;
+
+namespace Terminal.Gui.Core {
+	public class BorderTests {
+		[Fact]
+		public void Constructor_Defaults ()
+		{
+			var b = new Border ();
+			Assert.Equal (BorderStyle.None, b.BorderStyle);
+			Assert.False (b.DrawMarginFrame);
+			Assert.Equal (default, b.BorderThickness);
+			Assert.Equal (default, b.BorderBrush);
+			Assert.Equal (default, b.Background);
+			Assert.Equal (default, b.Padding);
+			Assert.Equal (0, b.ActualWidth);
+			Assert.Equal (0, b.ActualHeight);
+			Assert.Null (b.Child);
+			Assert.Null (b.ChildContainer);
+			Assert.False (b.Effect3D);
+			Assert.Equal (new Point (1, 1), b.Effect3DOffset);
+			Assert.Equal (Color.DarkGray, b.Effect3DBrush);
+		}
+
+		[Fact]
+		public void BorderStyle_Different_None_Ensures_DrawMarginFrame_To_True ()
+		{
+			var b = new Border () {
+				BorderStyle = BorderStyle.Single,
+				DrawMarginFrame = false
+			};
+
+			Assert.True (b.DrawMarginFrame);
+
+			b.BorderStyle = BorderStyle.None;
+			Assert.True (b.DrawMarginFrame);
+			b.DrawMarginFrame = false;
+			Assert.False (b.DrawMarginFrame);
+		}
+
+		[Fact]
+		public void ActualWidth_ActualHeight ()
+		{
+			var v = new View (new Rect (5, 10, 60, 20), "", new Border ());
+
+			Assert.Equal (60, v.Border.ActualWidth);
+			Assert.Equal (20, v.Border.ActualHeight);
+		}
+
+		[Fact]
+		public void ToplevelContainer_LayoutStyle_Computed_Constuctor_ ()
+		{
+			var tc = new Border.ToplevelContainer (new Border ());
+
+			Assert.Equal (LayoutStyle.Computed, tc.LayoutStyle);
+		}
+
+		[Fact]
+		public void ToplevelContainer_LayoutStyle_Absolute_Constuctor_ ()
+		{
+			var tc = new Border.ToplevelContainer (new Rect (1, 2, 3, 4), new Border ());
+
+			Assert.Equal (LayoutStyle.Absolute, tc.LayoutStyle);
+		}
+
+		[Fact]
+		public void GetSumThickness_Test ()
+		{
+			var b = new Border () {
+				BorderThickness = new Thickness (1, 2, 3, 4),
+				Padding = new Thickness (4, 3, 2, 1)
+			};
+			Assert.Equal (new Thickness (5, 5, 5, 5), b.GetSumThickness ());
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void DrawContent_With_Child_Border ()
+		{
+			var top = Application.Top;
+			var driver = (FakeDriver)Application.Driver;
+
+			var label = new Label () {
+				X = Pos.Center (),
+				Y = Pos.Center (),
+				Border = new Border () {
+					BorderStyle = BorderStyle.Single,
+					Padding = new Thickness (2),
+					BorderThickness = new Thickness (2),
+					BorderBrush = Color.Red,
+					Background = Color.BrightGreen,
+					Effect3D = true,
+					Effect3DOffset = new Point (2, -3)
+				},
+				ColorScheme = Colors.TopLevel,
+				Text = "This is a test"
+			};
+			label.Border.Child = label;
+			top.Add (label);
+
+			top.LayoutSubviews ();
+			label.Redraw (label.Bounds);
+
+			var frame = label.Frame;
+			var drawMarginFrame = label.Border.DrawMarginFrame ? 1 : 0;
+			var sumThickness = label.Border.GetSumThickness ();
+			var padding = label.Border.Padding;
+			var effect3DOffset = label.Border.Effect3DOffset;
+			var borderStyle = label.Border.BorderStyle;
+
+			// Check the upper BorderThickness
+			for (int r = frame.Y - drawMarginFrame - sumThickness.Top;
+				r < frame.Y - drawMarginFrame - padding.Top; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
+					c < frame.Right + drawMarginFrame + sumThickness.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.Red, color.Background);
+				}
+			}
+
+			// Check the left BorderThickness
+			for (int r = frame.Y - drawMarginFrame - padding.Top;
+				r < frame.Bottom + drawMarginFrame + padding.Bottom; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
+					c < frame.X - drawMarginFrame - padding.Left; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.Red, color.Background);
+				}
+			}
+
+			// Check the right BorderThickness
+			for (int r = frame.Y - drawMarginFrame - padding.Top;
+				r < frame.Bottom + drawMarginFrame + padding.Bottom; r++) {
+				for (int c = frame.Right + drawMarginFrame + padding.Right;
+					c < frame.Right + drawMarginFrame - sumThickness.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.Red, color.Background);
+				}
+			}
+
+			// Check the lower BorderThickness
+			for (int r = frame.Bottom + drawMarginFrame + padding.Bottom;
+				r < frame.Bottom + drawMarginFrame + sumThickness.Bottom; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left;
+					c < frame.Right + drawMarginFrame + sumThickness.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.Red, color.Background);
+				}
+			}
+
+			// Check the upper Padding
+			for (int r = frame.Y - drawMarginFrame - padding.Top;
+				r < frame.Y - drawMarginFrame; r++) {
+				for (int c = frame.X - drawMarginFrame - padding.Left;
+					c < frame.Right + drawMarginFrame + padding.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Background);
+				}
+			}
+
+			// Check the left Padding
+			for (int r = frame.Y - drawMarginFrame;
+				r < frame.Bottom + drawMarginFrame; r++) {
+				for (int c = frame.X - drawMarginFrame - padding.Left;
+					c < frame.X - drawMarginFrame; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Background);
+				}
+			}
+
+			// Check the right Padding
+			for (int r = frame.Y - drawMarginFrame;
+				r < frame.Bottom + drawMarginFrame; r++) {
+				for (int c = frame.Right + drawMarginFrame;
+					c < frame.Right + drawMarginFrame - padding.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Background);
+				}
+			}
+
+			// Check the lower Padding
+			for (int r = frame.Bottom + drawMarginFrame;
+				r < frame.Bottom + drawMarginFrame + padding.Bottom; r++) {
+				for (int c = frame.X - drawMarginFrame - padding.Left;
+					c < frame.Right + drawMarginFrame + padding.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Background);
+				}
+			}
+
+			Rune hLine = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.HLine : (borderStyle == BorderStyle.Double ? driver.HDLine : ' ')) : ' ';
+			Rune vLine = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.VLine : (borderStyle == BorderStyle.Double ? driver.VDLine : ' ')) : ' ';
+			Rune uRCorner = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.URCorner : (borderStyle == BorderStyle.Double ? driver.URDCorner : ' ')) : ' ';
+			Rune uLCorner = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.ULCorner : (borderStyle == BorderStyle.Double ? driver.ULDCorner : ' ')) : ' ';
+			Rune lLCorner = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.LLCorner : (borderStyle == BorderStyle.Double ? driver.LLDCorner : ' ')) : ' ';
+			Rune lRCorner = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.LRCorner : (borderStyle == BorderStyle.Double ? driver.LRDCorner : ' ')) : ' ';
+
+			var text = "";
+			// Check the MarginFrame
+			for (int r = frame.Y - drawMarginFrame;
+				r < frame.Bottom + drawMarginFrame; r++) {
+				for (int c = frame.X - drawMarginFrame;
+					c <= frame.Right + drawMarginFrame - 1; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					var rune = (Rune)driver.Contents [r, c, 0];
+					Assert.Equal (Color.Black, color.Background);
+					if (c == frame.X - drawMarginFrame && r == frame.Y - drawMarginFrame) {
+						Assert.Equal (uLCorner, rune);
+					} else if (c == frame.Right && r == frame.Y - drawMarginFrame) {
+						Assert.Equal (uRCorner, rune);
+					} else if (c == frame.X - drawMarginFrame && r == frame.Bottom) {
+						Assert.Equal (lLCorner, rune);
+					} else if (c == frame.Right && r == frame.Bottom) {
+						Assert.Equal (lRCorner, rune);
+					} else if (c >= frame.X && (r == frame.Y - drawMarginFrame
+						|| r == frame.Bottom)) {
+						Assert.Equal (hLine, rune);
+					} else if ((c == frame.X - drawMarginFrame || c == frame.Right)
+						&& r >= frame.Y && r <= frame.Bottom - drawMarginFrame) {
+						Assert.Equal (vLine, rune);
+					} else {
+						text += rune.ToString ();
+					}
+				}
+			}
+			Assert.Equal ("This is a test", text.Trim ());
+
+			// Check the upper Effect3D
+			for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y;
+				r < frame.Y - drawMarginFrame - sumThickness.Top; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X;
+					c < frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.DarkGray, color.Background);
+				}
+			}
+
+			// Check the left Effect3D
+			for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y;
+				r < frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X;
+					c < frame.X - drawMarginFrame - sumThickness.Left; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.DarkGray, color.Background);
+				}
+			}
+
+			// Check the right Effect3D
+			for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y;
+				r < frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y; r++) {
+				for (int c = frame.Right + drawMarginFrame + sumThickness.Right;
+					c < frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.DarkGray, color.Background);
+				}
+			}
+
+			// Check the lower Effect3D
+			for (int r = frame.Bottom + drawMarginFrame + sumThickness.Bottom;
+				r < frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y; r++) {
+				for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X;
+					c < frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.DarkGray, color.Background);
+				}
+			}
+
+			// Check the Child frame
+			for (int r = frame.Y; r < frame.Y + frame.Height; r++) {
+				for (int c = frame.X; c < frame.X + frame.Width; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Foreground);
+					Assert.Equal (Color.Black, color.Background);
+				}
+			}
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void DrawContent_With_Parent_Border ()
+		{
+			var top = Application.Top;
+			var driver = (FakeDriver)Application.Driver;
+
+			var frameView = new FrameView () {
+				X = Pos.Center (),
+				Y = Pos.Center (),
+				Width = 24,
+				Height = 13,
+				Border = new Border () {
+					BorderStyle = BorderStyle.Single,
+					Padding = new Thickness (2),
+					BorderThickness = new Thickness (2),
+					BorderBrush = Color.Red,
+					Background = Color.BrightGreen,
+					Effect3D = true,
+					Effect3DOffset = new Point (2, -3)
+				}
+			};
+			frameView.Add (new Label () {
+				ColorScheme = Colors.TopLevel,
+				Text = "This is a test"
+			});
+			//frameView.Border.Child = frameView;
+			top.Add (frameView);
+
+			top.LayoutSubviews ();
+			frameView.Redraw (frameView.Bounds);
+
+			var frame = frameView.Frame;
+			var drawMarginFrame = frameView.Border.DrawMarginFrame ? 1 : 0;
+			var sumThickness = frameView.Border.GetSumThickness ();
+			var borderThickness = frameView.Border.BorderThickness;
+			var padding = frameView.Border.Padding;
+
+			var effect3DOffset = frameView.Border.Effect3DOffset;
+			var borderStyle = frameView.Border.BorderStyle;
+
+			// Check the upper BorderThickness
+			for (int r = frame.Y;
+				r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) {
+				for (int c = frame.X;
+					c < frame.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.Red, color.Background);
+				}
+			}
+
+			// Check the left BorderThickness
+			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
+				r < frame.Bottom - borderThickness.Bottom; r++) {
+				for (int c = frame.X;
+					c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.Red, color.Background);
+				}
+			}
+
+			// Check the right BorderThickness
+			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
+				r < frame.Bottom - borderThickness.Bottom; r++) {
+				for (int c = Math.Max (frame.Right - borderThickness.Right, frame.X);
+					c < frame.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.Red, color.Background);
+				}
+			}
+
+			// Check the lower BorderThickness
+			for (int r = Math.Max (frame.Bottom - borderThickness.Bottom, frame.Y);
+				r < frame.Bottom; r++) {
+				for (int c = frame.X;
+					c < frame.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.Red, color.Background);
+				}
+			}
+
+			// Check the upper Padding
+			for (int r = frame.Y + borderThickness.Top;
+				r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) {
+				for (int c = frame.X + borderThickness.Left;
+					c < frame.Right - borderThickness.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Background);
+				}
+			}
+
+			// Check the left Padding
+			for (int r = frame.Y + sumThickness.Top;
+							r < frame.Bottom - sumThickness.Bottom; r++) {
+				for (int c = frame.X + borderThickness.Left;
+					c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Background);
+				}
+			}
+
+			// Check the right Padding
+			// Draw the right Padding
+			for (int r = frame.Y + sumThickness.Top;
+				r < frame.Bottom - sumThickness.Bottom; r++) {
+				for (int c = Math.Max (frame.Right - sumThickness.Right, frame.X + sumThickness.Left);
+					c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) {
+
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Background);
+				}
+			}
+
+			// Check the lower Padding
+			for (int r = Math.Max (frame.Bottom - sumThickness.Bottom, frame.Y + borderThickness.Top);
+				r < frame.Bottom - borderThickness.Bottom; r++) {
+				for (int c = frame.X + borderThickness.Left;
+					c < frame.Right - borderThickness.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Background);
+				}
+			}
+
+			Rune hLine = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.HLine : (borderStyle == BorderStyle.Double ? driver.HDLine : ' ')) : ' ';
+			Rune vLine = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.VLine : (borderStyle == BorderStyle.Double ? driver.VDLine : ' ')) : ' ';
+			Rune uRCorner = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.URCorner : (borderStyle == BorderStyle.Double ? driver.URDCorner : ' ')) : ' ';
+			Rune uLCorner = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.ULCorner : (borderStyle == BorderStyle.Double ? driver.ULDCorner : ' ')) : ' ';
+			Rune lLCorner = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.LLCorner : (borderStyle == BorderStyle.Double ? driver.LLDCorner : ' ')) : ' ';
+			Rune lRCorner = drawMarginFrame > 0 ? (borderStyle == BorderStyle.Single
+				? driver.LRCorner : (borderStyle == BorderStyle.Double ? driver.LRDCorner : ' ')) : ' ';
+
+			var text = "";
+			// Check the MarginFrame
+			for (int r = frame.Y + sumThickness.Top;
+				r < frame.Bottom - sumThickness.Bottom; r++) {
+				for (int c = frame.X + sumThickness.Left;
+					c <= frame.Right - sumThickness.Right - 1; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					var rune = (Rune)driver.Contents [r, c, 0];
+					Assert.Equal (Color.Black, color.Background);
+					if (c == frame.X + sumThickness.Left && r == frame.Y + sumThickness.Top) {
+						Assert.Equal (uLCorner, rune);
+					} else if (c == frame.Right - drawMarginFrame - sumThickness.Right
+						&& r == frame.Y + sumThickness.Top) {
+						Assert.Equal (uRCorner, rune);
+					} else if (c == frame.X + sumThickness.Left
+						&& r == frame.Bottom - drawMarginFrame - sumThickness.Bottom) {
+						Assert.Equal (lLCorner, rune);
+					} else if (c == frame.Right - drawMarginFrame - sumThickness.Right
+						&& r == frame.Bottom - drawMarginFrame - sumThickness.Bottom) {
+						Assert.Equal (lRCorner, rune);
+					} else if (c > frame.X + sumThickness.Left
+						&& (r == frame.Y + sumThickness.Top
+						|| r == frame.Bottom - drawMarginFrame - sumThickness.Bottom)) {
+						Assert.Equal (hLine, rune);
+					} else if ((c == frame.X + sumThickness.Left
+						|| c == frame.Right - drawMarginFrame - sumThickness.Right)
+						&& r >= frame.Y + drawMarginFrame + sumThickness.Top) {
+						Assert.Equal (vLine, rune);
+					} else {
+						text += rune.ToString ();
+					}
+				}
+			}
+			Assert.Equal ("This is a test", text.Trim ());
+
+			// Check the upper Effect3D
+			for (int r = frame.Y + effect3DOffset.Y;
+				r < frame.Y; r++) {
+				for (int c = frame.X + effect3DOffset.X;
+					c < frame.Right + effect3DOffset.X; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.DarkGray, color.Background);
+				}
+			}
+
+			// Check the left Effect3D
+			for (int r = frame.Y + effect3DOffset.Y;
+				r < frame.Bottom + effect3DOffset.Y; r++) {
+				for (int c = frame.X + effect3DOffset.X;
+					c < frame.X; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.DarkGray, color.Background);
+				}
+			}
+
+			// Check the right Effect3D
+			for (int r = frame.Y + effect3DOffset.Y;
+				r < frame.Bottom + effect3DOffset.Y; r++) {
+				for (int c = frame.Right;
+					c < frame.Right + effect3DOffset.X; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.DarkGray, color.Background);
+				}
+			}
+
+			// Check the lower Effect3D
+			for (int r = frame.Bottom;
+				r < frame.Bottom + effect3DOffset.Y; r++) {
+				for (int c = frame.X + effect3DOffset.X;
+					c < frame.Right + effect3DOffset.X; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.DarkGray, color.Background);
+				}
+			}
+
+			// Check the Child frame
+			for (int r = frame.Y + drawMarginFrame + sumThickness.Top;
+				r < frame.Bottom - drawMarginFrame - sumThickness.Bottom; r++) {
+				for (int c = frame.X + drawMarginFrame + sumThickness.Left;
+					c < frame.Right - drawMarginFrame - sumThickness.Right; c++) {
+
+					var color = (Attribute)driver.Contents [r, c, 1];
+					Assert.Equal (Color.BrightGreen, color.Foreground);
+					Assert.Equal (Color.Black, color.Background);
+				}
+			}
+		}
+	}
+}

+ 261 - 0
UnitTests/PanelViewTests.cs

@@ -0,0 +1,261 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Terminal.Gui.Views {
+	public class PanelViewTests {
+		[Fact]
+		public void Constructor_Defaults ()
+		{
+			var pv = new PanelView ();
+
+			Assert.False (pv.CanFocus);
+			Assert.False (pv.Visible);
+			Assert.False (pv.UsePanelFrame);
+			Assert.Null (pv.Child);
+
+			pv = new PanelView (new Label ("This is a test."));
+
+			Assert.False (pv.CanFocus);
+			Assert.True (pv.Visible);
+			Assert.False (pv.UsePanelFrame);
+			Assert.NotNull (pv.Child);
+			Assert.NotNull (pv.Border);
+			Assert.Null (pv.Child.Border);
+		}
+
+		[Fact]
+		public void Child_Sets_To_Null_Remove_From_Subviews_PanelView ()
+		{
+			var pv = new PanelView (new Label ("This is a test."));
+			Assert.NotNull (pv.Child);
+			Assert.Equal (1, pv.Subviews[0].Subviews.Count);
+
+			pv.Child = null;
+			Assert.Null (pv.Child);
+			Assert.Equal (0, pv.Subviews [0].Subviews.Count);
+		}
+
+		[Fact]
+		public void Add_View_Also_Sets_Child ()
+		{
+			var pv = new PanelView ();
+			Assert.Null (pv.Child);
+			Assert.Equal (0, pv.Subviews [0].Subviews.Count);
+
+			pv.Add (new Label ("This is a test."));
+			Assert.NotNull (pv.Child);
+			Assert.Equal (1, pv.Subviews [0].Subviews.Count);
+		}
+
+		[Fact]
+		public void Add_More_Views_Remove_Last_Child_Before__Only_One_Is_Allowed ()
+		{
+			var pv = new PanelView (new Label ("This is a test."));
+			Assert.NotNull (pv.Child);
+			Assert.Equal (1, pv.Subviews [0].Subviews.Count);
+			Assert.IsType<Label> (pv.Child);
+
+			pv.Add (new TextField ("This is a test."));
+			Assert.NotNull (pv.Child);
+			Assert.Equal (1, pv.Subviews [0].Subviews.Count);
+			Assert.IsNotType<Label> (pv.Child);
+			Assert.IsType<TextField> (pv.Child);
+		}
+
+		[Fact]
+		public void Remove_RemoveAll_View_Also_Sets_Child_To_Null ()
+		{
+			var pv = new PanelView (new Label ("This is a test."));
+			Assert.NotNull (pv.Child);
+			Assert.Equal (1, pv.Subviews [0].Subviews.Count);
+
+			pv.Remove (pv.Child);
+			Assert.Null (pv.Child);
+			Assert.Equal (0, pv.Subviews [0].Subviews.Count);
+
+			pv = new PanelView (new Label ("This is a test."));
+			Assert.NotNull (pv.Child);
+			Assert.Equal (1, pv.Subviews [0].Subviews.Count);
+
+			pv.RemoveAll ();
+			Assert.Null (pv.Child);
+			Assert.Equal (0, pv.Subviews [0].Subviews.Count);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void AdjustContainer_Without_Border ()
+		{
+			var top = Application.Top;
+			var win = new Window ();
+			var pv = new PanelView (new Label ("This is a test."));
+			win.Add (pv);
+			top.Add (win);
+
+			Application.Begin (top);
+
+			Assert.Equal (new Rect (0, 0, 15, 1), pv.Frame);
+			Assert.Equal (new Rect (0, 0, 15, 1), pv.Child.Frame);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void AdjustContainer_With_Border_Absolute_Values ()
+		{
+			var top = Application.Top;
+			var win = new Window ();
+			var pv = new PanelView (new Label ("This is a test.") {
+				Border = new Border () {
+					BorderStyle = BorderStyle.Double,
+					BorderThickness = new Thickness (1, 2, 3, 4),
+					Padding = new Thickness (1, 2, 3, 4)
+				}
+			});
+			win.Add (pv);
+			top.Add (win);
+
+			Application.Begin (top);
+
+			Assert.Equal (new Rect (0, 0, 25, 15), pv.Frame);
+			Assert.Equal (new Rect (0, 0, 15, 1), pv.Child.Frame);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void AdjustContainer_With_Border_Computed_Values ()
+		{
+			var top = Application.Top;
+			var win = new Window ();
+			var pv = new PanelView (new TextView () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Border = new Border () {
+					BorderStyle = BorderStyle.Double,
+					BorderThickness = new Thickness (1, 2, 3, 4),
+					Padding = new Thickness (1, 2, 3, 4)
+				}
+			});
+
+			var pv1 = new PanelView (new TextView () {
+				Width = Dim.Fill (1),
+				Height = Dim.Fill (1),
+				Border = new Border () {
+					BorderStyle = BorderStyle.Double,
+					BorderThickness = new Thickness (1, 2, 3, 4),
+					Padding = new Thickness (1, 2, 3, 4)
+				}
+			});
+
+			var pv2 = new PanelView (new TextView () {
+				Width = Dim.Fill (2),
+				Height = Dim.Fill (2),
+				Border = new Border () {
+					BorderStyle = BorderStyle.Double,
+					BorderThickness = new Thickness (1, 2, 3, 4),
+					Padding = new Thickness (1, 2, 3, 4)
+				}
+			});
+
+			win.Add (pv, pv1, pv2);
+			top.Add (win);
+
+			Application.Begin (top);
+
+			Assert.Equal (new Rect (0, 0, 78, 23), pv.Frame);
+			Assert.Equal (new Rect (0, 0, 68, 9), pv.Child.Frame);
+			Assert.Equal (new Rect (0, 0, 77, 22), pv1.Frame);
+			Assert.Equal (new Rect (0, 0, 65, 6), pv1.Child.Frame);
+			Assert.Equal (new Rect (0, 0, 76, 21), pv2.Frame);
+			Assert.Equal (new Rect (0, 0, 62, 3), pv2.Child.Frame);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void UsePanelFrame_False_PanelView_Always_Respect_The_Child_Upper_Left_Corner_Position_And_Size ()
+		{
+			var top = Application.Top;
+			var win = new Window ();
+			var pv = new PanelView (new Label ("This is a test.")) {
+				X = 2,
+				Y = 4,
+				Width = 20,
+				Height = 10
+			};
+			var pv1 = new PanelView (new TextField (3, 4, 15, "This is a test.")) {
+				X = 2,
+				Y = 4,
+				Width = 20,
+				Height = 10
+			};
+			var pv2 = new PanelView (new TextView () {
+				X = 5,
+				Y = 6,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			}) {
+				X = 2,
+				Y = 4,
+				Width = 20,
+				Height = 10
+			};
+
+			win.Add (pv, pv1, pv2);
+			top.Add (win);
+
+			Application.Begin (top);
+
+			Assert.Equal (new Rect (0, 0, 15, 1), pv.Frame);
+			Assert.Equal (new Rect (0, 0, 15, 1), pv.Child.Frame);
+			Assert.Equal (new Rect (3, 4, 15, 1), pv1.Frame);
+			Assert.Equal (new Rect (0, 0, 15, 1), pv1.Child.Frame);
+			Assert.Equal (new Rect (5, 6, 73, 17), pv2.Frame);
+			Assert.Equal (new Rect (0, 0, 73, 17), pv2.Child.Frame);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void UsePanelFrame_True_PanelView_Position_And_Size_Are_Used ()
+		{
+			var top = Application.Top;
+			var win = new Window ();
+			var pv = new PanelView (new TextView () {
+				X = 2,
+				Y = 4,
+				Width = 20,
+				Height = 10
+			}) {
+				X = 5,
+				Y = 6,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				UsePanelFrame = true
+			};
+			var pv1 = new PanelView (new TextView () {
+				X = 5,
+				Y = 6,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			}) {
+				X = 2,
+				Y = 4,
+				Width = 20,
+				Height = 10,
+				UsePanelFrame = true
+			};
+
+			win.Add (pv, pv1);
+			top.Add (win);
+
+			Application.Begin (top);
+
+			Assert.Equal (new Rect (5, 6, 73, 17), pv.Frame);
+			Assert.Equal (new Rect (0, 0, 20, 10), pv.Child.Frame);
+			Assert.Equal (new Rect (2, 4, 20, 10), pv1.Frame);
+			Assert.Equal (new Rect (0, 0, 20, 10), pv1.Child.Frame);
+		}
+	}
+}