소스 검색

Merge pull request #2498 from tig/v2_ruler

Fixes #2496. Adds simple `Ruler` class for drawing rulers; Thickness and Frame now use
Tig 2 년 전
부모
커밋
c2e774fc33

+ 46 - 6
Terminal.Gui/Core/Frame.cs

@@ -85,8 +85,8 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Redraws the Frames that comprise the <see cref="Frame"/>.
 		/// </summary>
-		/// <param name="clipRect"></param>
-		public override void Redraw (Rect clipRect)
+		/// <param name="bounds"></param>
+		public override void Redraw (Rect bounds)
 		{
 			if (Thickness == Thickness.Empty) return;
 
@@ -114,7 +114,13 @@ namespace Terminal.Gui {
 
 			if (Id == "BorderFrame" && BorderStyle != BorderStyle.None) {
 				var lc = new LineCanvas ();
-				if (Thickness.Top > 0 && Frame.Width > 1 && Frame.Height > 1) {
+				
+				var drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height > 1;
+				var drawLeft = Thickness.Left > 0 && (Frame.Height > 1 || Thickness.Top == 0);
+				var drawBottom = Thickness.Bottom > 0 && Frame.Width > 1;
+				var drawRight = Thickness.Right > 0 && (Frame.Height > 1 || Thickness.Top == 0);
+
+				if (drawTop) {
 					// ╔╡Title╞═════╗
 					// ╔╡╞═════╗
 					if (Frame.Width < 4 || ustring.IsNullOrEmpty (Parent?.Title)) {
@@ -133,19 +139,53 @@ namespace Terminal.Gui {
 						lc.AddLine (new Point (screenBounds.X + 1 + (titleWidth + 1), screenBounds.Location.Y), Frame.Width - (titleWidth + 3), Orientation.Horizontal, BorderStyle);
 					}
 				}
-				if (Thickness.Left > 0 && (Frame.Height > 1 || Thickness.Top == 0)) {
+				if (drawLeft) {
 					lc.AddLine (screenBounds.Location, Frame.Height - 1, Orientation.Vertical, BorderStyle);
 				}
-				if (Thickness.Bottom > 0 && Frame.Width > 1) {
+				if (drawBottom) {
 					lc.AddLine (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1), screenBounds.Width - 1, Orientation.Horizontal, BorderStyle);
 				}
-				if (Thickness.Right > 0 && (Frame.Height > 1 || Thickness.Top == 0)) {
+				if (drawRight) {
 					lc.AddLine (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y), screenBounds.Height - 1, Orientation.Vertical, BorderStyle);
 				}
 				foreach (var p in lc.GenerateImage (screenBounds)) {
 					Driver.Move (p.Key.X, p.Key.Y);
 					Driver.AddRune (p.Value);
 				}
+
+				// TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
+				if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler) {
+					// Top
+					var hruler = new Ruler () { Length = screenBounds.Width, Orientation = Orientation.Horizontal };
+					if (drawTop) {
+						hruler.Draw (new Point (screenBounds.X, screenBounds.Y));
+					}
+
+					// Redraw title 
+					if (drawTop && Id == "BorderFrame" && !ustring.IsNullOrEmpty (Parent?.Title)) {
+						var prevAttr = Driver.GetAttribute ();
+						Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
+						Driver.DrawWindowTitle (screenBounds, Parent?.Title, 0, 0, 0, 0);
+						Driver.SetAttribute (prevAttr);
+					}
+
+					//Left
+					var vruler = new Ruler () { Length = screenBounds.Height - 2, Orientation = Orientation.Vertical };
+					if (drawLeft) {
+						vruler.Draw (new Point (screenBounds.X, screenBounds.Y + 1), 1);
+					}
+
+					// Bottom
+					if (drawBottom) {
+						hruler.Draw (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1));
+					}
+
+					// Right
+					if (drawRight) {
+						vruler.Draw (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
+					}
+
+				}
 			}
 
 

+ 16 - 37
Terminal.Gui/Core/Thickness.cs

@@ -4,6 +4,7 @@ using System.Collections.Generic;
 using System.Text;
 using System.Text.Json.Serialization;
 using Terminal.Gui.Configuration;
+using Terminal.Gui.Graphs;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -159,16 +160,6 @@ namespace Terminal.Gui {
 				}
 			}
 
-			ustring hrule = ustring.Empty;
-			ustring vrule = ustring.Empty;
-			if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler) {
-
-				string h = "0123456789";
-				hrule = h.Repeat ((int)Math.Ceiling ((double)(rect.Width) / (double)h.Length)) [0..(rect.Width)];
-				string v = "0123456789";
-				vrule = v.Repeat ((int)Math.Ceiling ((double)(rect.Height * 2) / (double)v.Length)) [0..(rect.Height * 2)];
-			};
-
 			// Draw the Top side
 			if (Top > 0) {
 				Application.Driver.FillRect (new Rect (rect.X, rect.Y, rect.Width, Math.Min (rect.Height, Top)), topChar);
@@ -192,20 +183,25 @@ namespace Terminal.Gui {
 			// TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
 			if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler) {
 				// Top
-				Application.Driver.Move (rect.X, rect.Y);
-				Application.Driver.AddStr (hrule);
+				var hruler = new Ruler () { Length = rect.Width, Orientation = Orientation.Horizontal };
+				if (Top > 0) {
+					hruler.Draw (new Point (rect.X, rect.Y));
+				}
+
 				//Left
-				for (var r = rect.Y; r < rect.Y + rect.Height; r++) {
-					Application.Driver.Move (rect.X, r);
-					Application.Driver.AddRune (vrule [r - rect.Y]);
+				var vruler = new Ruler () { Length = rect.Height - 2, Orientation = Orientation.Vertical };
+				if (Left > 0) {
+					vruler.Draw (new Point (rect.X, rect.Y + 1), 1);
 				}
+
 				// Bottom
-				Application.Driver.Move (rect.X, rect.Y + rect.Height - Bottom + 1);
-				Application.Driver.AddStr (hrule);
+				if (Bottom > 0) {
+					hruler.Draw (new Point (rect.X, rect.Y + rect.Height - 1));
+				}
+
 				// Right
-				for (var r = rect.Y + 1; r < rect.Y + rect.Height; r++) {
-					Application.Driver.Move (rect.X + rect.Width - Right + 1, r);
-					Application.Driver.AddRune (vrule [r - rect.Y]);
+				if (Right > 0) {
+					vruler.Draw (new Point (rect.X + rect.Width - 1, rect.Y + 1), 1);
 				}
 			}
 
@@ -281,21 +277,4 @@ namespace Terminal.Gui {
 			return !(left == right);
 		}
 	}
-
-	internal static class StringExtensions {
-		public static string Repeat (this string instr, int n)
-		{
-			if (n <= 0) {
-				return null;
-			}
-
-			if (string.IsNullOrEmpty (instr) || n == 1) {
-				return instr;
-			}
-
-			return new StringBuilder (instr.Length * n)
-				.Insert (0, instr, n)
-				.ToString ();
-		}
-	}
 }

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

@@ -0,0 +1,86 @@
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Text;
+using System.Text.Json.Serialization;
+using Terminal.Gui.Configuration;
+using Terminal.Gui.Graphs;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Draws a ruler on the screen.
+	/// </summary>
+	/// <remarks>
+	/// <para>
+	/// </para>
+	/// </remarks>
+	public class Ruler {
+
+		/// <summary>
+		/// Gets or sets whether the ruler is drawn horizontally or vertically. The default is horizontally.
+		/// </summary>
+		public Orientation Orientation { get; set; }
+
+		/// <summary>
+		/// Gets or sets the lenght of the ruler. The default is 0.
+		/// </summary>
+		public int Length { get; set; }
+
+		/// <summary>
+		/// Gets or sets the foreground and backgrond color to use.
+		/// </summary>
+		public Attribute Attribute { get; set; }
+
+		string _hTemplate { get; set; } = "|123456789";
+		string _vTemplate { get; set; } = "-123456789";
+
+
+		/// <summary>
+		/// Draws the <see cref="Ruler"/>. 
+		/// </summary>
+		/// <param name="location">The location to start drawing the ruler, in screen-relative coordinates.</param>
+		/// <param name="start">The start value of the ruler.</param>
+		public void Draw (Point location, int start = 0)
+		{
+			if (start < 0) {
+				throw new ArgumentException ("start must be greater than or equal to 0");
+			}
+
+			if (Length < 1) {
+				return;
+			}
+
+			if (Orientation == Orientation.Horizontal) {
+				var hrule = _hTemplate.Repeat ((int)Math.Ceiling ((double)Length + 2 / (double)_hTemplate.Length)) [start..(Length + start)];
+				// Top
+				Application.Driver.Move (location.X, location.Y);
+				Application.Driver.AddStr (hrule);
+
+			} else {
+				var vrule = _vTemplate.Repeat ((int)Math.Ceiling ((double)(Length + 2) / (double)_vTemplate.Length)) [start..(Length + start)];
+				for (var r = location.Y; r < location.Y + Length; r++) {
+					Application.Driver.Move (location.X, r);
+					Application.Driver.AddRune (vrule [r - location.Y]);
+				}
+			}
+		}
+	}
+
+	internal static class StringExtensions {
+		public static string Repeat (this string instr, int n)
+		{
+			if (n <= 0) {
+				return null;
+			}
+
+			if (string.IsNullOrEmpty (instr) || n == 1) {
+				return instr;
+			}
+
+			return new StringBuilder (instr.Length * n)
+				.Insert (0, instr, n)
+				.ToString ();
+		}
+	}
+}

+ 13 - 0
UnitTests/Application/ApplicationTests.cs

@@ -461,6 +461,19 @@ namespace Terminal.Gui.ApplicationTests {
 		}
 		#endregion
 
+		[Fact, AutoInitShutdown]
+		public void Begin_Sets_Application_Top_To_Console_Size()
+		{
+			Assert.Equal (new Rect (0, 0, 80, 25), Application.Top.Frame);
+
+			((FakeDriver)Application.Driver).SetBufferSize (5, 5);
+			Application.Begin (Application.Top);
+			// BUGBUG: v2 - 
+			Assert.Equal (new Rect (0, 0, 80, 25), Application.Top.Frame);
+			((FakeDriver)Application.Driver).SetBufferSize (5, 5);
+			Assert.Equal (new Rect (0, 0, 5, 5), Application.Top.Frame);
+		}
+
 		[Fact]
 		[AutoInitShutdown]
 		public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()

+ 131 - 0
UnitTests/Core/ThicknessTests.cs

@@ -485,6 +485,137 @@ namespace Terminal.Gui.CoreTests {
 
 		}
 
+		[Fact (), AutoInitShutdown]
+		public void DrawTests_Ruler ()
+		{
+			// Add a frame so we can see the ruler
+			var f = new FrameView () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+			};
+
+
+			Application.Top.Add (f);
+			Application.Begin (Application.Top);
+			
+			((FakeDriver)Application.Driver).SetBufferSize (45, 20);
+			var t = new Thickness (0, 0, 0, 0);
+			var r = new Rect (2, 2, 40, 15);
+			Application.Refresh ();
+			ConsoleDriver.Diagnostics |= ConsoleDriver.DiagnosticFlags.FrameRuler;
+			t.Draw (r, "Test");
+			ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.Off;
+			TestHelpers.AssertDriverContentsAre (@"
+┌───────────────────────────────────────────┐
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+└───────────────────────────────────────────┘", output);
+
+
+			t = new Thickness (1, 1, 1, 1);
+			r = new Rect (1, 1, 40, 15);
+			Application.Refresh ();
+			ConsoleDriver.Diagnostics |= ConsoleDriver.DiagnosticFlags.FrameRuler;
+			t.Draw (r, "Test");
+			ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.Off;
+			TestHelpers.AssertDriverContentsAre (@"
+┌───────────────────────────────────────────┐
+│|123456789|123456789|123456789|123456789   │
+│1                                      1   │
+│2                                      2   │
+│3                                      3   │
+│4                                      4   │
+│5                                      5   │
+│6                                      6   │
+│7                                      7   │
+│8                                      8   │
+│9                                      9   │
+│-                                      -   │
+│1                                      1   │
+│2                                      2   │
+│3                                      3   │
+│|123456789|123456789|123456789|123456789   │
+│                                           │
+│                                           │
+│                                           │
+└───────────────────────────────────────────┘", output);
+
+			t = new Thickness (1, 2, 3, 4);
+			r = new Rect (2, 2, 40, 15);
+			Application.Refresh ();
+			ConsoleDriver.Diagnostics |= ConsoleDriver.DiagnosticFlags.FrameRuler;
+			t.Draw (r, "Test");
+			ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.Off;
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌───────────────────────────────────────────┐
+│                                           │
+│ |123456789|123456789|123456789|123456789  │
+│ 1                                      1  │
+│ 2                                      2  │
+│ 3                                      3  │
+│ 4                                      4  │
+│ 5                                      5  │
+│ 6                                      6  │
+│ 7                                      7  │
+│ 8                                      8  │
+│ 9                                      9  │
+│ -                                      -  │
+│ 1                                      1  │
+│ 2                                      2  │
+│ 3                                      3  │
+│ |123456789|123456789|123456789|123456789  │
+│                                           │
+│                                           │
+└───────────────────────────────────────────┘", output);
+
+
+			t = new Thickness (-1, 1, 1, 1);
+			r = new Rect (5, 5, 40, 15);
+			Application.Refresh ();
+			ConsoleDriver.Diagnostics |= ConsoleDriver.DiagnosticFlags.FrameRuler;
+			t.Draw (r, "Test");
+			ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.Off;
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌───────────────────────────────────────────┐
+│                                           │
+│                                           │
+│                                           │
+│                                           │
+│    |123456789|123456789|123456789|123456789
+│                                           1
+│                                           2
+│                                           3
+│                                           4
+│                                           5
+│                                           6
+│                                           7
+│                                           8
+│                                           9
+│                                           -
+│                                           1
+│                                           2
+│                                           3
+└────|123456789|123456789|123456789|123456789", output);
+
+		}
 		[Fact ()]
 		public void EqualsTest ()
 		{

+ 368 - 0
UnitTests/Drawing/RulerTests.cs

@@ -0,0 +1,368 @@
+using Terminal.Gui;
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Xml.Linq;
+using Terminal.Gui.Graphs;
+using Xunit;
+using Xunit.Abstractions;
+//using GraphViewTests = Terminal.Gui.Views.GraphViewTests;
+
+// Alias Console to MockConsole so we don't accidentally use Console
+using Console = Terminal.Gui.FakeConsole;
+
+namespace Terminal.Gui.DrawingTests {
+	public class RulerTests {
+
+		readonly ITestOutputHelper output;
+
+		public RulerTests (ITestOutputHelper output)
+		{
+			this.output = output;
+		}
+
+		[Fact ()]
+		public void Constructor_Defaults ()
+		{
+			var r = new Ruler ();
+			Assert.Equal (0, r.Length);
+			Assert.Equal (Orientation.Horizontal, r.Orientation);
+			Assert.Equal (default, r.Attribute);
+		}
+
+
+		[Fact ()]
+		public void Orientation_set ()
+		{
+			var r = new Ruler ();
+			Assert.Equal (Orientation.Horizontal, r.Orientation);
+			r.Orientation = Orientation.Vertical;
+			Assert.Equal (Orientation.Vertical, r.Orientation);
+		}
+
+		[Fact ()]
+		public void Length_set ()
+		{
+			var r = new Ruler ();
+			Assert.Equal (0, r.Length);
+			r.Length = 42;
+			Assert.Equal (42, r.Length);
+		}
+
+		[Fact ()]
+		public void Attribute_set ()
+		{
+			var newAttribute = new Attribute (Color.Red, Color.Green);
+
+			var r = new Ruler ();
+			Assert.Equal (default, r.Attribute);
+			r.Attribute = newAttribute;
+			Assert.Equal (newAttribute, r.Attribute);
+		}
+
+		[Fact (), AutoInitShutdown]
+		public void Draw_Default ()
+		{
+			((FakeDriver)Application.Driver).SetBufferSize (25, 25);
+
+			var r = new Ruler ();
+			r.Draw (new Point (0, 0));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"", output);
+		}
+
+		[Fact (), AutoInitShutdown]
+		public void Draw_Horizontal ()
+		{
+			var len = 15;
+
+			// Add a frame so we can see the ruler
+			var f = new FrameView () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+			};
+			Application.Top.Add (f);
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (len + 5, 5);
+			Assert.Equal (new Rect (0, 0, len + 5, 5), f.Frame);
+
+			var r = new Ruler ();
+			Assert.Equal (Orientation.Horizontal, r.Orientation);
+
+			r.Length = len;
+			r.Draw (new Point (0, 0));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+|123456789|1234────┐
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			// Postive offset
+			Application.Refresh ();
+			r.Draw (new Point (1, 1));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│|123456789|1234   │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			// Negative offset
+			Application.Refresh ();
+			r.Draw (new Point (-1, 1));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+123456789|1234     │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			// Clip
+			Application.Refresh ();
+			r.Draw (new Point (10, 1));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│         |123456789
+│                  │
+│                  │
+└──────────────────┘", output);
+		}
+
+		[Fact (), AutoInitShutdown]
+		public void Draw_Horizontal_Start ()
+		{
+			var len = 15;
+
+			// Add a frame so we can see the ruler
+			var f = new FrameView () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+			};
+			Application.Top.Add (f);
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (len + 5, 5);
+			Assert.Equal (new Rect (0, 0, len + 5, 5), f.Frame);
+
+			var r = new Ruler ();
+			Assert.Equal (Orientation.Horizontal, r.Orientation);
+
+			r.Length = len;
+			r.Draw (new Point (0, 0), 1);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+123456789|12345────┐
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			Application.Refresh ();
+			r.Length = len;
+			r.Draw (new Point (1, 0), 1);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌123456789|12345───┐
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+		}
+
+		[Fact (), AutoInitShutdown]
+		public void Draw_Vertical ()
+		{
+			var len = 15;
+
+			// Add a frame so we can see the ruler
+			var f = new FrameView () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+			};
+
+
+			Application.Top.Add (f);
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (5, len + 5);
+			Assert.Equal (new Rect (0, 0, 5, len + 5), f.Frame);
+
+			var r = new Ruler ();
+			r.Orientation = Orientation.Vertical;
+			r.Length = len;
+			r.Draw (new Point (0, 0));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+-───┐
+1   │
+2   │
+3   │
+4   │
+5   │
+6   │
+7   │
+8   │
+9   │
+-   │
+1   │
+2   │
+3   │
+4   │
+│   │
+│   │
+│   │
+│   │
+└───┘", output);
+
+			// Postive offset
+			Application.Refresh ();
+			r.Draw (new Point (1, 1));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌───┐
+│-  │
+│1  │
+│2  │
+│3  │
+│4  │
+│5  │
+│6  │
+│7  │
+│8  │
+│9  │
+│-  │
+│1  │
+│2  │
+│3  │
+│4  │
+│   │
+│   │
+│   │
+└───┘", output);
+
+			// Negative offset
+			Application.Refresh ();
+			r.Draw (new Point (1, -1));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌1──┐
+│2  │
+│3  │
+│4  │
+│5  │
+│6  │
+│7  │
+│8  │
+│9  │
+│-  │
+│1  │
+│2  │
+│3  │
+│4  │
+│   │
+│   │
+│   │
+│   │
+│   │
+└───┘", output);
+
+			// Clip
+			Application.Refresh ();
+			r.Draw (new Point (1, 10));
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌───┐
+│   │
+│   │
+│   │
+│   │
+│   │
+│   │
+│   │
+│   │
+│   │
+│-  │
+│1  │
+│2  │
+│3  │
+│4  │
+│5  │
+│6  │
+│7  │
+│8  │
+└9──┘", output);
+		}
+
+		[Fact (), AutoInitShutdown]
+		public void Draw_Vertical_Start ()
+		{
+			var len = 15;
+
+			// Add a frame so we can see the ruler
+			var f = new FrameView () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+			};
+
+
+			Application.Top.Add (f);
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (5, len + 5);
+			Assert.Equal (new Rect (0, 0, 5, len + 5), f.Frame);
+
+			var r = new Ruler ();
+			r.Orientation = Orientation.Vertical;
+			r.Length = len;
+			r.Draw (new Point (0, 0), 1);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+1───┐
+2   │
+3   │
+4   │
+5   │
+6   │
+7   │
+8   │
+9   │
+-   │
+1   │
+2   │
+3   │
+4   │
+5   │
+│   │
+│   │
+│   │
+│   │
+└───┘", output);
+
+			Application.Refresh ();
+			r.Length = len;
+			r.Draw (new Point (0, 1), 1);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌───┐
+1   │
+2   │
+3   │
+4   │
+5   │
+6   │
+7   │
+8   │
+9   │
+-   │
+1   │
+2   │
+3   │
+4   │
+5   │
+│   │
+│   │
+│   │
+└───┘", output);
+
+		}
+	}
+}
+
+

+ 0 - 1
UnitTests/TopLevels/ToplevelTests.cs

@@ -39,7 +39,6 @@ namespace Terminal.Gui.TopLevelTests {
 			Assert.Equal (new Rect (0, 0, Application.Driver.Cols, Application.Driver.Rows), top.Bounds);
 		}
 
-
 		[Fact]
 		[AutoInitShutdown]
 		public void Application_Top_EnsureVisibleBounds_To_Driver_Rows_And_Cols ()

+ 64 - 1
UnitTests/Views/FrameViewTests.cs

@@ -1,12 +1,21 @@
-using System;
+using Microsoft.VisualStudio.TestPlatform.Utilities;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using Xunit;
+using Xunit.Abstractions;
 
 namespace Terminal.Gui.ViewTests {
 	public class FrameViewTests {
+		readonly ITestOutputHelper output;
+
+		public FrameViewTests (ITestOutputHelper output)
+		{
+			this.output = output;
+		}
+
 		[Fact]
 		public void Constuctors_Defaults ()
 		{
@@ -28,5 +37,59 @@ namespace Terminal.Gui.ViewTests {
 			fv.EndInit ();
 			Assert.Equal (new Rect (1, 2, 10, 20), fv.Frame);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_Defaults ()
+		{
+			((FakeDriver)Application.Driver).SetBufferSize (10, 10);
+			var fv = new FrameView ();
+			Assert.Equal (string.Empty, fv.Title);
+			Assert.Equal (string.Empty, fv.Text);
+			Assert.NotNull (fv.Border);
+			Application.Top.Add (fv);
+			Application.Begin (Application.Top);
+			Assert.Equal (new Rect (0, 0, 0, 0), fv.Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"", output);
+
+			fv.Height = 5;
+			fv.Width = 5;
+			Assert.Equal (new Rect (0, 0, 5, 5), fv.Frame);
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌───┐
+│   │
+│   │
+│   │
+└───┘", output);
+
+
+			fv.X = 1;
+			fv.Y = 2;
+			Assert.Equal (new Rect (1, 2, 5, 5), fv.Frame);
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ ┌───┐
+ │   │
+ │   │
+ │   │
+ └───┘", output);
+
+			fv.X = -1;
+			fv.Y = -2;
+			Assert.Equal (new Rect (-1, -2, 5, 5), fv.Frame);
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+   │
+   │
+───┘", output);
+
+			fv.X = 7;
+			fv.Y = 8;
+			Assert.Equal (new Rect (7, 8, 5, 5), fv.Frame);
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+       ┌──
+       │  ", output);
+		}
 	}
 }