瀏覽代碼

Merge branch 'v2_develop' into table-cell-draw-fix

Thomas Nind 2 年之前
父節點
當前提交
67c1217e2a

+ 5 - 46
Terminal.Gui/Core/Application.cs

@@ -658,7 +658,7 @@ namespace Terminal.Gui {
 					var rx = x - startFrame.X;
 					var ry = y - startFrame.Y;
 					if (top.Visible && top.Frame.Contains (rx, ry)) {
-						var deep = FindDeepestView (top, rx, ry, out resx, out resy);
+						var deep = View.FindDeepestView (top, rx, ry, out resx, out resy);
 						if (deep == null)
 							return FindDeepestMdiView (top, rx, ry, out resx, out resy);
 						if (deep != MdiTop)
@@ -671,45 +671,6 @@ namespace Terminal.Gui {
 			return start;
 		}
 
-		/// <summary>
-		/// Finds the deepest view at the specified coordinates, specified relative to <paramref name="start"/>'s superview.
-		/// </summary>
-		/// <param name="start"></param>
-		/// <param name="x"></param>
-		/// <param name="y"></param>
-		/// <param name="resx"></param>
-		/// <param name="resy"></param>
-		/// <returns></returns>
-		static View FindDeepestView (View start, int x, int y, out int resx, out int resy)
-		{
-			var startFrame = start.Frame;
-
-			if (!startFrame.Contains (x, y)) {
-				resx = 0;
-				resy = 0;
-				return null;
-			}
-			if (start.InternalSubviews != null) {
-				int count = start.InternalSubviews.Count;
-				if (count > 0) {
-					var rx = x - (startFrame.X + start.GetBoundsOffset ().X);
-					var ry = y - (startFrame.Y + start.GetBoundsOffset ().Y);
-					for (int i = count - 1; i >= 0; i--) {
-						View v = start.InternalSubviews [i];
-						if (v.Visible && v.Frame.Contains (rx, ry)) {
-							var deep = FindDeepestView (v, rx, ry, out resx, out resy);
-							if (deep == null)
-								return v;
-							return deep;
-						}
-					}
-				}
-			}
-			resx = x - startFrame.X;
-			resy = y - startFrame.Y;
-			return start;
-		}
-
 		static View FindTopFromView (View view)
 		{
 			View top = view?.SuperView != null && view?.SuperView != Top
@@ -832,7 +793,7 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			var view = FindDeepestView (Current, me.X, me.Y, out int rx, out int ry);
+			var view = View.FindDeepestView (Current, me.X, me.Y, out int rx, out int ry);
 
 			if (view != null && view.WantContinuousButtonPressed) {
 				WantContinuousButtonPressedView = view;
@@ -849,9 +810,7 @@ namespace Terminal.Gui {
 			}
 
 			if (mouseGrabView != null) {
-				view ??= mouseGrabView;
-
-				var newxy = mouseGrabView.ScreenToBounds (me.X, me.Y);
+				var newxy = mouseGrabView.ScreenToView (me.X, me.Y);
 				var nme = new MouseEvent () {
 					X = newxy.X,
 					Y = newxy.Y,
@@ -873,7 +832,7 @@ namespace Terminal.Gui {
 				&& me.Flags != MouseFlags.ReportMousePosition && me.Flags != 0) {
 
 				var top = FindDeepestTop (Top, me.X, me.Y, out _, out _);
-				view = FindDeepestView (top, me.X, me.Y, out rx, out ry);
+				view = View.FindDeepestView (top, me.X, me.Y, out rx, out ry);
 
 				if (view != null && view != MdiTop && top != Current) {
 					MoveCurrent ((Toplevel)top);
@@ -1272,6 +1231,7 @@ namespace Terminal.Gui {
 
 			if (state.Toplevel != Top
 				&& (!Top._needsDisplay.IsEmpty || Top._childNeedsDisplay || Top.LayoutNeeded)) {
+				state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
 				Top.Redraw (Top.Bounds);
 				foreach (var top in toplevels.Reverse ()) {
 					if (top != Top && top != state.Toplevel) {
@@ -1279,7 +1239,6 @@ namespace Terminal.Gui {
 						top.Redraw (top.Bounds);
 					}
 				}
-				state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
 			}
 			if (toplevels.Count == 1 && state.Toplevel == Top
 				&& (Driver.Cols != state.Toplevel.Frame.Width || Driver.Rows != state.Toplevel.Frame.Height)

+ 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 ();
-		}
-	}
 }

+ 51 - 6
Terminal.Gui/Core/View.cs

@@ -857,7 +857,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Gets or sets the <see cref="Terminal.Gui.TextFormatter"/> which can be handled differently by any derived class.
 		/// </summary>
-		public TextFormatter? TextFormatter { get; set; }
+		public TextFormatter TextFormatter { get; set; }
 
 		/// <summary>
 		/// Returns the container for this view, or null if this view has not been added to a container.
@@ -1038,7 +1038,7 @@ namespace Terminal.Gui {
 				// BUGBUG: v2 - ? - If layoutstyle is absolute, this overwrites the current frame h/w with 0. Hmmm...
 				frame = new Rect (new Point (actX, actY), new Size (w, h)); // Set frame, not Frame!
 
-	
+
 			}
 			//// BUGBUG: I think these calls are redundant or should be moved into just the AutoSize case
 			if (IsInitialized || LayoutStyle == LayoutStyle.Absolute) {
@@ -1047,7 +1047,7 @@ namespace Terminal.Gui {
 				SetMinWidthHeight ();
 				SetNeedsLayout ();
 				SetNeedsDisplay ();
-			}               
+			}
 		}
 
 		void TextFormatter_HotKeyChanged (object sender, KeyChangedEventArgs e)
@@ -1139,8 +1139,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void SetSubViewNeedsDisplay ()
 		{
+			if (_childNeedsDisplay) {
+				return;
+			}
 			_childNeedsDisplay = true;
-			if (_superView != null)
+			if (_superView != null && !_superView._childNeedsDisplay)
 				_superView.SetSubViewNeedsDisplay ();
 		}
 
@@ -1490,7 +1493,7 @@ namespace Terminal.Gui {
 		/// <param name="region">View-relative region for the frame to be drawn.</param>
 		/// <param name="padding">The padding to add around the outside of the drawn frame.</param>
 		/// <param name="fill">If set to <see langword="true"/> it fill will the contents.</param>
-		[ObsoleteAttribute ("This method is obsolete in v2. Use use LineCanvas or Frame instead instead.", false)]
+		[ObsoleteAttribute ("This method is obsolete in v2. Use use LineCanvas or Frame instead.", false)]
 		public void DrawFrame (Rect region, int padding = 0, bool fill = false)
 		{
 			var scrRect = ViewToScreen (region);
@@ -1723,7 +1726,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Removes the <see cref="SetNeedsDisplay()"/> and the <see cref="_childNeedsDisplay"/> setting on this view.
+		/// Removes the <see cref="_needsDisplay"/> and the <see cref="_childNeedsDisplay"/> setting on this view.
 		/// </summary>
 		protected void ClearNeedsDisplay ()
 		{
@@ -3480,5 +3483,47 @@ namespace Terminal.Gui {
 
 			return top;
 		}
+
+		/// <summary>
+		/// Finds which view that belong to the <paramref name="start"/> superview at the provided location.
+		/// </summary>
+		/// <param name="start">The superview where to look for.</param>
+		/// <param name="x">The column location in the superview.</param>
+		/// <param name="y">The row location in the superview.</param>
+		/// <param name="resx">The found view screen relative column location.</param>
+		/// <param name="resy">The found view screen relative row location.</param>
+		/// <returns>
+		///  The view that was found at the <praramref name="x"/> and <praramref name="y"/> coordinates.
+		///  <see langword="null"/> if no view was found.
+		/// </returns>
+		public static View FindDeepestView (View start, int x, int y, out int resx, out int resy)
+		{
+			var startFrame = start.Frame;
+
+			if (!startFrame.Contains (x, y)) {
+				resx = 0;
+				resy = 0;
+				return null;
+			}
+			if (start.InternalSubviews != null) {
+				int count = start.InternalSubviews.Count;
+				if (count > 0) {
+					var rx = x - (startFrame.X + start.GetBoundsOffset ().X);
+					var ry = y - (startFrame.Y + start.GetBoundsOffset ().Y);
+					for (int i = count - 1; i >= 0; i--) {
+						View v = start.InternalSubviews [i];
+						if (v.Visible && v.Frame.Contains (rx, ry)) {
+							var deep = FindDeepestView (v, rx, ry, out resx, out resy);
+							if (deep == null)
+								return v;
+							return deep;
+						}
+					}
+				}
+			}
+			resx = x - startFrame.X;
+			resy = y - startFrame.Y;
+			return start;
+		}
 	}
 }

+ 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 ();
+		}
+	}
+}

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

@@ -91,7 +91,7 @@ namespace Terminal.Gui {
 			if (menuBar != null) {
 				Hide ();
 			}
-			container = Application.Current;
+			container = Application.Top;
 			container.Closing += Container_Closing;
 			container.Resized += Container_Resized;
 			var frame = container.Frame;
@@ -162,7 +162,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void Hide ()
 		{
-			menuBar.CleanUp ();
+			menuBar?.CleanUp ();
 			Dispose ();
 		}
 
@@ -194,7 +194,7 @@ namespace Terminal.Gui {
 			set {
 				var oldKey = key;
 				key = value;
-				KeyChanged?.Invoke (this, new KeyChangedEventArgs(oldKey,key));
+				KeyChanged?.Invoke (this, new KeyChangedEventArgs (oldKey, key));
 			}
 		}
 
@@ -206,7 +206,7 @@ namespace Terminal.Gui {
 			set {
 				var oldFlags = mouseFlags;
 				mouseFlags = value;
-				MouseFlagsChanged?.Invoke (this, new MouseFlagsChangedEventArgs(oldFlags,value));
+				MouseFlagsChanged?.Invoke (this, new MouseFlagsChangedEventArgs (oldFlags, value));
 			}
 		}
 

+ 65 - 36
Terminal.Gui/Views/Menu.cs

@@ -477,6 +477,11 @@ namespace Terminal.Gui {
 				WantMousePositionReports = host.WantMousePositionReports;
 			}
 
+			if (Application.Current != null) {
+				Application.Current.DrawContentComplete += Current_DrawContentComplete;
+			}
+			Application.RootMouseEvent += Application_RootMouseEvent;
+
 			// Things this view knows how to do
 			AddCommand (Command.LineUp, () => MoveUp ());
 			AddCommand (Command.LineDown, () => MoveDown ());
@@ -502,6 +507,22 @@ namespace Terminal.Gui {
 			AddKeyBinding (Key.Enter, Command.Accept);
 		}
 
+		private void Application_RootMouseEvent (MouseEvent me)
+		{
+			var view = View.FindDeepestView (this, me.X, me.Y, out int rx, out int ry);
+			if (view == this) {
+				var nme = new MouseEvent () {
+					X = rx,
+					Y = ry,
+					Flags = me.Flags,
+					View = view
+				};
+				if (MouseEvent (nme) || me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1Released) {
+					me.Handled = true;
+				}
+			}
+		}
+
 		internal Attribute DetermineColorSchemeFor (MenuItem item, int index)
 		{
 			if (item != null) {
@@ -511,9 +532,19 @@ namespace Terminal.Gui {
 			return GetNormalColor ();
 		}
 
-		// Draws the Menu, within the Frame
 		public override void Redraw (Rect bounds)
 		{
+		}
+
+		// Draws the Menu, within the Frame
+		private void Current_DrawContentComplete (object sender, DrawEventArgs e)
+		{
+			if (barItems.Children == null) {
+				return;
+			}
+			var savedClip = Driver.Clip;
+			Application.Driver.Clip = Application.Top.Frame;
+
 			Driver.SetAttribute (GetNormalColor ());
 			DrawFrame (Bounds, padding: 0, fill: true);
 
@@ -612,6 +643,8 @@ namespace Terminal.Gui {
 					}
 				}
 			}
+			Driver.Clip = savedClip;
+
 			PositionCursor ();
 		}
 
@@ -905,6 +938,15 @@ namespace Terminal.Gui {
 
 			return base.OnEnter (view);
 		}
+
+		protected override void Dispose (bool disposing)
+		{
+			if (Application.Current != null) {
+				Application.Current.DrawContentComplete -= Current_DrawContentComplete;
+			}
+			Application.RootMouseEvent -= Application_RootMouseEvent;
+			base.Dispose (disposing);
+		}
 	}
 
 	/// <summary>
@@ -1322,31 +1364,31 @@ namespace Terminal.Gui {
 			switch (subMenu) {
 			case null:
 				// Open a submenu below a MenuBar
-				lastFocused = lastFocused ?? (SuperView == null ? Application.Current.MostFocused : SuperView.MostFocused);
+				lastFocused ??= (SuperView == null ? Application.Current.MostFocused : SuperView.MostFocused);
 				if (openSubMenu != null && !CloseMenu (false, true))
 					return;
 				if (openMenu != null) {
-					if (SuperView == null) {
-						Application.Current.Remove (openMenu);
-					} else {
-						SuperView.Remove (openMenu);
-					}
+					Application.Top.Remove (openMenu);
 					openMenu.Dispose ();
+					openMenu = null;
 				}
 
 				// This positions the submenu horizontally aligned with the first character of the
 				// menu it belongs to's text
 				for (int i = 0; i < index; i++)
 					pos += Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + leftPadding + rightPadding;
-				openMenu = new Menu (this, Frame.X + pos, Frame.Y + 1, Menus [index]);
+				var superView = SuperView == null ? Application.Top : SuperView;
+				Point locationOffset;
+				if (superView.Border != null && superView.Border.BorderStyle != BorderStyle.None) {
+					locationOffset = new Point (superView.Frame.X + 1, superView.Frame.Y + 1);
+				} else {
+					locationOffset = new Point (superView.Frame.X, superView.Frame.Y);
+				}
+				openMenu = new Menu (this, Frame.X + pos + locationOffset.X, Frame.Y + 1 + locationOffset.Y, Menus [index]);
 				openCurrentMenu = openMenu;
 				openCurrentMenu.previousSubFocused = openMenu;
 
-				if (SuperView == null) {
-					Application.Current.Add (openMenu);
-				} else {
-					SuperView.Add (openMenu);
-				}
+				Application.Top.Add (openMenu);
 				openMenu.SetFocus ();
 				break;
 			default:
@@ -1368,17 +1410,14 @@ namespace Terminal.Gui {
 							mbi [j + 2] = subMenu.Children [j];
 						}
 						var newSubMenu = new MenuBarItem (mbi);
-						openCurrentMenu = new Menu (this, first.Frame.Left, first.Frame.Top, newSubMenu);
+						ViewToScreen (first.Frame.Left, first.Frame.Top, out int rx, out int ry);
+						openCurrentMenu = new Menu (this, rx, ry, newSubMenu);
 						last.Visible = false;
 						Application.GrabMouse (openCurrentMenu);
 					}
 					openCurrentMenu.previousSubFocused = last.previousSubFocused;
 					openSubMenu.Add (openCurrentMenu);
-					if (SuperView == null) {
-						Application.Current.Add (openCurrentMenu);
-					} else {
-						SuperView.Add (openCurrentMenu);
-					}
+					Application.Top.Add (openCurrentMenu);
 				}
 				selectedSub = openSubMenu.Count - 1;
 				if (selectedSub > -1 && SelectEnabledItem (openCurrentMenu.barItems.Children, openCurrentMenu.current, out openCurrentMenu.current)) {
@@ -1501,11 +1540,7 @@ namespace Terminal.Gui {
 			switch (isSubMenu) {
 			case false:
 				if (openMenu != null) {
-					if (SuperView == null) {
-						Application.Current.Remove (openMenu);
-					} else {
-						SuperView?.Remove (openMenu);
-					}
+					Application.Top.Remove (openMenu);
 				}
 				SetNeedsDisplay ();
 				if (previousFocused != null && previousFocused is Menu && openMenu != null && previousFocused.ToString () != openCurrentMenu.ToString ())
@@ -1564,11 +1599,7 @@ namespace Terminal.Gui {
 				openCurrentMenu.SetFocus ();
 				if (openSubMenu != null) {
 					menu = openSubMenu [i];
-					if (SuperView == null) {
-						Application.Current.Remove (menu);
-					} else {
-						SuperView.Remove (menu);
-					}
+					Application.Top.Remove (menu);
 					openSubMenu.Remove (menu);
 					menu.Dispose ();
 				}
@@ -1584,11 +1615,7 @@ namespace Terminal.Gui {
 		{
 			if (openSubMenu != null) {
 				foreach (var item in openSubMenu) {
-					if (SuperView == null) {
-						Application.Current.Remove (item);
-					} else {
-						SuperView.Remove (item);
-					}
+					Application.Top.Remove (item);
 					item.Dispose ();
 				}
 			}
@@ -1757,7 +1784,8 @@ namespace Terminal.Gui {
 			}
 
 			if (mi.IsTopLevel) {
-				var menu = new Menu (this, i, 0, mi);
+				ViewToScreen (i, 0, out int rx, out int ry);
+				var menu = new Menu (this, rx, ry, mi);
 				menu.Run (mi.Action);
 				menu.Dispose ();
 			} else {
@@ -1878,7 +1906,8 @@ namespace Terminal.Gui {
 					if (cx >= pos && cx < pos + leftPadding + Menus [i].TitleLength + Menus [i].Help.ConsoleWidth + rightPadding) {
 						if (me.Flags == MouseFlags.Button1Clicked) {
 							if (Menus [i].IsTopLevel) {
-								var menu = new Menu (this, i, 0, Menus [i]);
+								ViewToScreen (i, 0, out int rx, out int ry);
+								var menu = new Menu (this, rx, ry, Menus [i]);
 								menu.Run (Menus [i].Action);
 								menu.Dispose ();
 							} else if (!IsMenuOpen) {

+ 2 - 2
UICatalog/Scenarios/Scrolling.cs

@@ -128,7 +128,7 @@ namespace UICatalog.Scenarios {
 			};
 			label.Text = $"{scrollView}\nContentSize: {scrollView.ContentSize}\nContentOffset: {scrollView.ContentOffset}";
 
-			const string rule = "0123456789";
+			//const string rule = "0123456789";
 
 			var horizontalRuler = new Label () {
 				X = 0,
@@ -140,7 +140,7 @@ namespace UICatalog.Scenarios {
 			};
 			scrollView.Add (horizontalRuler);
 
-			const string vrule = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
+			//const string vrule = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
 
 			var verticalRuler = new Label () {
 				X = 0,

+ 66 - 62
UICatalog/UICatalog.cs

@@ -13,6 +13,7 @@ using System.Threading;
 using Terminal.Gui.Configuration;
 using static Terminal.Gui.Configuration.ConfigurationManager;
 using System.Text.Json.Serialization;
+using System.ComponentModel.DataAnnotations;
 
 #nullable enable
 
@@ -70,7 +71,6 @@ namespace UICatalog {
 
 			_scenarios = Scenario.GetScenarios ();
 			_categories = Scenario.GetAllCategories ();
-			_nameColumnWidth = _scenarios.OrderByDescending (s => s.GetName ().Length).FirstOrDefault ().GetName ().Length;
 
 			if (args.Length > 0 && args.Contains ("-usc")) {
 				_useSystemConsole = true;
@@ -83,7 +83,7 @@ namespace UICatalog {
 			// run it and exit when done.
 			if (args.Length > 0) {
 				var item = _scenarios.FindIndex (s => s.GetName ().Equals (args [0], StringComparison.OrdinalIgnoreCase));
-				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ());
+				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ())!;
 				Application.UseSystemConsole = _useSystemConsole;
 				Application.Init ();
 				_selectedScenario.Theme = _cachedTheme;
@@ -115,7 +115,7 @@ namespace UICatalog {
 			Scenario scenario;
 			while ((scenario = RunUICatalogTopLevel ()) != null) {
 				VerifyObjectsWereDisposed ();
-				ConfigurationManager.Themes.Theme = _cachedTheme;
+				ConfigurationManager.Themes!.Theme = _cachedTheme!;
 				ConfigurationManager.Apply ();
 				scenario.Theme = _cachedTheme;
 				scenario.TopLevelColorScheme = _topLevelColorScheme;
@@ -151,7 +151,7 @@ namespace UICatalog {
 			// Setup a file system watcher for `./.tui/`
 			_currentDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
 			var f = new FileInfo (Assembly.GetExecutingAssembly ().Location);
-			var tuiDir = Path.Combine (f.Directory.FullName, ".tui");
+			var tuiDir = Path.Combine (f.Directory!.FullName, ".tui");
 
 			if (!Directory.Exists (tuiDir)) {
 				Directory.CreateDirectory (tuiDir);
@@ -206,9 +206,9 @@ namespace UICatalog {
 			Application.Init ();
 
 			if (_cachedTheme is null) {
-				_cachedTheme = ConfigurationManager.Themes.Theme;
+				_cachedTheme = ConfigurationManager.Themes?.Theme;
 			} else {
-				ConfigurationManager.Themes.Theme = _cachedTheme;
+				ConfigurationManager.Themes!.Theme = _cachedTheme;
 				ConfigurationManager.Apply ();
 			}
 
@@ -217,40 +217,40 @@ namespace UICatalog {
 			Application.Run<UICatalogTopLevel> ();
 			Application.Shutdown ();
 
-			return _selectedScenario;
+			return _selectedScenario!;
 		}
 
-		static List<Scenario> _scenarios;
-		static List<string> _categories;
-		static int _nameColumnWidth;
+		static List<Scenario>? _scenarios;
+		static List<string>? _categories;
+
 		// When a scenario is run, the main app is killed. These items
 		// are therefore cached so that when the scenario exits the
 		// main app UI can be restored to previous state
 		static int _cachedScenarioIndex = 0;
 		static int _cachedCategoryIndex = 0;
-		static string? _cachedTheme;
-		
-		static StringBuilder _aboutMessage;
+		static string? _cachedTheme = string.Empty;
+
+		static StringBuilder? _aboutMessage = null;
 
 		// If set, holds the scenario the user selected
-		static Scenario _selectedScenario = null;
+		static Scenario? _selectedScenario = null;
 
 		static bool _useSystemConsole = false;
 		static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
 		//static bool _enableConsoleScrolling = false;
 		static bool _isFirstRunning = true;
-		static string _topLevelColorScheme;
-
-		static MenuItem [] _themeMenuItems;
-		static MenuBarItem _themeMenuBarItem;
+		static string _topLevelColorScheme = string.Empty;
 
+		static MenuItem []? _themeMenuItems;
+		static MenuBarItem? _themeMenuBarItem;
+		
 		/// <summary>
 		/// This is the main UI Catalog app view. It is run fresh when the app loads (if a Scenario has not been passed on 
 		/// the command line) and each time a Scenario ends.
 		/// </summary>
 		public class UICatalogTopLevel : Toplevel {
-			public MenuItem miIsMouseDisabled;
-			public MenuItem miEnableConsoleScrolling;
+			public MenuItem? miIsMouseDisabled;
+			public MenuItem? miEnableConsoleScrolling;
 
 			public TileView ContentPane;
 			public ListView CategoryListView;
@@ -276,7 +276,7 @@ namespace UICatalog {
 						new MenuItem ("_gui.cs API Overview", "", () => OpenUrl ("https://gui-cs.github.io/Terminal.Gui/articles/overview.html"), null, null, Key.F1),
 						new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, Key.F2),
 						new MenuItem ("_About...",
-							"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", _aboutMessage.ToString(), 0, false, "_Ok"), null, null, Key.CtrlMask | Key.A),
+							"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", _aboutMessage!.ToString(), 0, false, "_Ok"), null, null, Key.CtrlMask | Key.A),
 					}),
 				});
 
@@ -302,7 +302,7 @@ namespace UICatalog {
 					}),
 					new StatusItem(Key.F10, "~F10~ Status Bar", () => {
 						StatusBar.Visible = !StatusBar.Visible;
-						ContentPane.Height = Dim.Fill(StatusBar.Visible ? 1 : 0);
+						ContentPane!.Height = Dim.Fill(StatusBar.Visible ? 1 : 0);
 						LayoutSubviews();
 						SetSubViewNeedsDisplay();
 					}),
@@ -332,7 +332,7 @@ namespace UICatalog {
 					CanFocus = true,
 				};
 				CategoryListView.OpenSelectedItem += (s,a) => {
-					ScenarioListView.SetFocus ();
+					ScenarioListView!.SetFocus ();
 				};
 				CategoryListView.SelectedItemChanged += CategoryListView_SelectedChanged;
 
@@ -371,12 +371,12 @@ namespace UICatalog {
 				ConfigurationManager.Applied += ConfigAppliedHandler;
 			}
       
-			void LoadedHandler (object sender, EventArgs args)
+			void LoadedHandler (object? sender, EventArgs? args)
 			{
 				ConfigChanged ();
 
-				miIsMouseDisabled.Checked = Application.IsMouseDisabled;
-				miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling;
+				miIsMouseDisabled!.Checked = Application.IsMouseDisabled;
+				miEnableConsoleScrolling!.Checked = Application.EnableConsoleScrolling;
 				DriverName.Title = $"Driver: {Driver.GetType ().Name}";
 				OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}";
 
@@ -391,7 +391,7 @@ namespace UICatalog {
 				StatusBar.VisibleChanged += (s, e) => {
 					UICatalogApp.ShowStatusBar = StatusBar.Visible;
 
-					var height = (StatusBar.Visible ? 1 : 0);// + (MenuBar.Visible ? 1 : 0);
+					var height = (StatusBar.Visible ? 1 : 0);
 					ContentPane.Height = Dim.Fill (height);
 					LayoutSubviews ();
 					SetSubViewNeedsDisplay ();
@@ -400,13 +400,13 @@ namespace UICatalog {
 				Loaded -= LoadedHandler;
 			}
 
-			private void UnloadedHandler (object sender, EventArgs args)
+			private void UnloadedHandler (object? sender, EventArgs? args)
 			{
 				ConfigurationManager.Applied -= ConfigAppliedHandler;
 				Unloaded -= UnloadedHandler;
 			}
       
-			void ConfigAppliedHandler (object sender, ConfigurationManagerEventArgs a)
+			void ConfigAppliedHandler (object? sender, ConfigurationManagerEventArgs? a)
 			{
 				ConfigChanged ();
 			}
@@ -415,14 +415,16 @@ namespace UICatalog {
 			/// Launches the selected scenario, setting the global _selectedScenario
 			/// </summary>
 			/// <param name="e"></param>
-			void ScenarioListView_OpenSelectedItem (object sender, EventArgs e)
+			void ScenarioListView_OpenSelectedItem (object? sender, EventArgs? e)
 			{
 				if (_selectedScenario is null) {
 					// Save selected item state
 					_cachedCategoryIndex = CategoryListView.SelectedItem;
 					_cachedScenarioIndex = ScenarioListView.SelectedItem;
 					// Create new instance of scenario (even though Scenarios contains instances)
-					_selectedScenario = (Scenario)Activator.CreateInstance (ScenarioListView.Source.ToList () [ScenarioListView.SelectedItem].GetType ());
+					var sourceList = ScenarioListView.Source.ToList ();
+
+					_selectedScenario = (Scenario)Activator.CreateInstance (ScenarioListView.Source.ToList () [ScenarioListView.SelectedItem]!.GetType ())!;
 
 					// Tell the main app to stop
 					Application.RequestStop ();
@@ -431,12 +433,13 @@ namespace UICatalog {
 
 			List<MenuItem []> CreateDiagnosticMenuItems ()
 			{
-				List<MenuItem []> menuItems = new List<MenuItem []> ();
-				menuItems.Add (CreateDiagnosticFlagsMenuItems ());
-				menuItems.Add (new MenuItem [] { null });
-				menuItems.Add (CreateEnableConsoleScrollingMenuItems ());
-				menuItems.Add (CreateDisabledEnabledMouseItems ());
-				menuItems.Add (CreateKeybindingsMenuItems ());
+				List<MenuItem []> menuItems = new List<MenuItem []> {
+					CreateDiagnosticFlagsMenuItems (),
+					new MenuItem [] { },
+					CreateEnableConsoleScrollingMenuItems (),
+					CreateDisabledEnabledMouseItems (),
+					CreateKeybindingsMenuItems ()
+				};
 				return menuItems;
 			}
 
@@ -446,10 +449,10 @@ namespace UICatalog {
 				miIsMouseDisabled = new MenuItem {
 					Title = "_Disable Mouse"
 				};
-				miIsMouseDisabled.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miIsMouseDisabled.Title.ToString ().Substring (1, 1) [0];
+				miIsMouseDisabled.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miIsMouseDisabled!.Title!.ToString ()!.Substring (1, 1) [0];
 				miIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked;
 				miIsMouseDisabled.Action += () => {
-					miIsMouseDisabled.Checked = Application.IsMouseDisabled = (bool)!miIsMouseDisabled.Checked;
+					miIsMouseDisabled.Checked = Application.IsMouseDisabled = (bool)!miIsMouseDisabled.Checked!;
 				};
 				menuItems.Add (miIsMouseDisabled);
 
@@ -468,7 +471,7 @@ namespace UICatalog {
 					Application.Run (dlg);
 				};
 
-				menuItems.Add (null);
+				menuItems.Add (null!);
 				menuItems.Add (item);
 
 				return menuItems.ToArray ();
@@ -479,11 +482,11 @@ namespace UICatalog {
 				List<MenuItem> menuItems = new List<MenuItem> ();
 				miEnableConsoleScrolling = new MenuItem ();
 				miEnableConsoleScrolling.Title = "_Enable Console Scrolling";
-				miEnableConsoleScrolling.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miEnableConsoleScrolling.Title.ToString ().Substring (1, 1) [0];
+				miEnableConsoleScrolling.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miEnableConsoleScrolling.Title.ToString ()!.Substring (1, 1) [0];
 				miEnableConsoleScrolling.CheckType |= MenuItemCheckStyle.Checked;
 				miEnableConsoleScrolling.Action += () => {
 					miEnableConsoleScrolling.Checked = !miEnableConsoleScrolling.Checked;
-					Application.EnableConsoleScrolling = (bool)miEnableConsoleScrolling.Checked;
+					Application.EnableConsoleScrolling = (bool)miEnableConsoleScrolling.Checked!;
 				};
 				menuItems.Add (miEnableConsoleScrolling);
 
@@ -554,10 +557,10 @@ namespace UICatalog {
 
 				Enum GetDiagnosticsEnumValue (ustring title)
 				{
-					return title.ToString () switch {
+					return title!.ToString () switch {
 						FRAME_RULER => ConsoleDriver.DiagnosticFlags.FrameRuler,
 						FRAME_PADDING => ConsoleDriver.DiagnosticFlags.FramePadding,
-						_ => null,
+						_ => null!,
 					};
 				}
 
@@ -585,10 +588,10 @@ namespace UICatalog {
 				}
 			}
 
-			public MenuItem [] CreateThemeMenuItems ()
+			public MenuItem []? CreateThemeMenuItems ()
 			{
 				List<MenuItem> menuItems = new List<MenuItem> ();
-				foreach (var theme in ConfigurationManager.Themes) {
+				foreach (var theme in ConfigurationManager.Themes!) {
 					var item = new MenuItem {
 						Title = theme.Key,
 						Shortcut = Key.AltMask + theme.Key [0]
@@ -621,7 +624,7 @@ namespace UICatalog {
 					};
 					schemeMenuItems.Add (item);
 				}
-				menuItems.Add (null);
+				menuItems.Add (null!);
 				var mbi = new MenuBarItem ("_Color Scheme for Application.Top", schemeMenuItems.ToArray ());
 				menuItems.Add (mbi);
 
@@ -635,18 +638,19 @@ namespace UICatalog {
 				}
 
 				_themeMenuItems = ((UICatalogTopLevel)Application.Top).CreateThemeMenuItems ();
-				_themeMenuBarItem.Children = _themeMenuItems;
+				_themeMenuBarItem!.Children = _themeMenuItems;
 
-				var checkedThemeMenu = _themeMenuItems.Where (m => (bool)m.Checked).FirstOrDefault ();
+				var checkedThemeMenu = _themeMenuItems?.Where (m => m?.Checked ?? false).FirstOrDefault ();
 				if (checkedThemeMenu != null) {
 					checkedThemeMenu.Checked = false;
 				}
-				checkedThemeMenu = _themeMenuItems.Where (m => m != null && m.Title == ConfigurationManager.Themes.Theme).FirstOrDefault ();
+				checkedThemeMenu = _themeMenuItems?.Where (m => m != null && m.Title == ConfigurationManager.Themes?.Theme).FirstOrDefault ();
 				if (checkedThemeMenu != null) {
-					ConfigurationManager.Themes.Theme = checkedThemeMenu.Title.ToString ();
+					ConfigurationManager.Themes!.Theme = checkedThemeMenu.Title.ToString ()!;
 					checkedThemeMenu.Checked = true;
 				}
-				var schemeMenuItems = ((MenuBarItem)_themeMenuItems.Where (i => i is MenuBarItem).FirstOrDefault ()).Children;
+
+				var schemeMenuItems = ((MenuBarItem)_themeMenuItems?.Where (i => i is MenuBarItem)!.FirstOrDefault ()!)!.Children;
 				foreach (var schemeMenuItem in schemeMenuItems) {
 					schemeMenuItem.Checked = (string)schemeMenuItem.Data == _topLevelColorScheme;
 				}
@@ -659,8 +663,8 @@ namespace UICatalog {
 				StatusBar.Items [0].Shortcut = Application.QuitKey;
 				StatusBar.Items [0].Title = $"~{Application.QuitKey} to quit";
 
-				miIsMouseDisabled.Checked = Application.IsMouseDisabled;
-				miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling;
+				miIsMouseDisabled!.Checked = Application.IsMouseDisabled;
+				miEnableConsoleScrolling!.Checked = Application.EnableConsoleScrolling;
 
 				var height = (UICatalogApp.ShowStatusBar ? 1 : 0);// + (MenuBar.Visible ? 1 : 0);
 				ContentPane.Height = Dim.Fill (height);
@@ -670,9 +674,9 @@ namespace UICatalog {
 				Application.Top.SetNeedsDisplay ();
 			}
 
-			void KeyDownHandler (object sender, KeyEventEventArgs a)
+			void KeyDownHandler (object? sender, KeyEventEventArgs? a)
 			{
-				if (a.KeyEvent.IsCapslock) {
+				if (a!.KeyEvent.IsCapslock) {
 					Capslock.Title = "Caps: On";
 					StatusBar.SetNeedsDisplay ();
 				} else {
@@ -680,7 +684,7 @@ namespace UICatalog {
 					StatusBar.SetNeedsDisplay ();
 				}
 
-				if (a.KeyEvent.IsNumlock) {
+				if (a!.KeyEvent.IsNumlock) {
 					Numlock.Title = "Num: On";
 					StatusBar.SetNeedsDisplay ();
 				} else {
@@ -688,7 +692,7 @@ namespace UICatalog {
 					StatusBar.SetNeedsDisplay ();
 				}
 
-				if (a.KeyEvent.IsScrolllock) {
+				if (a!.KeyEvent.IsScrolllock) {
 					Scrolllock.Title = "Scroll: On";
 					StatusBar.SetNeedsDisplay ();
 				} else {
@@ -697,16 +701,16 @@ namespace UICatalog {
 				}
 			}
 
-			void CategoryListView_SelectedChanged (object sender, ListViewItemEventArgs e)
+			void CategoryListView_SelectedChanged (object? sender, ListViewItemEventArgs? e)
 			{
-				var item = _categories [e.Item];
+				var item = _categories! [e!.Item];
 				List<Scenario> newlist;
 				if (e.Item == 0) {
 					// First category is "All"
-					newlist = _scenarios;
+					newlist = _scenarios!;
 
 				} else {
-					newlist = _scenarios.Where (s => s.GetCategories ().Contains (item)).ToList ();
+					newlist = _scenarios!.Where (s => s.GetCategories ().Contains (item)).ToList ();
 				}
 				ScenarioListView.SetSource (newlist.ToList ());
 			}

+ 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);
+
+		}
+	}
+}
+
+

+ 82 - 2
UnitTests/Menus/ContextMenuTests.cs

@@ -228,7 +228,7 @@ namespace Terminal.Gui.MenuTests {
 			var oldKey = Key.Null;
 			var cm = new ContextMenu ();
 
-			cm.KeyChanged += (s,e) => oldKey = e.OldKey;
+			cm.KeyChanged += (s, e) => oldKey = e.OldKey;
 
 			cm.Key = Key.Space | Key.CtrlMask;
 			Assert.Equal (Key.Space | Key.CtrlMask, cm.Key);
@@ -241,7 +241,7 @@ namespace Terminal.Gui.MenuTests {
 			var oldMouseFlags = new MouseFlags ();
 			var cm = new ContextMenu ();
 
-			cm.MouseFlagsChanged += (s,e) => oldMouseFlags = e.OldValue;
+			cm.MouseFlagsChanged += (s, e) => oldMouseFlags = e.OldValue;
 
 			cm.MouseFlags = MouseFlags.Button2Clicked;
 			Assert.Equal (MouseFlags.Button2Clicked, cm.MouseFlags);
@@ -902,5 +902,85 @@ namespace Terminal.Gui.MenuTests {
 			Assert.True (top.Subviews [1].ProcessKey (new KeyEvent (Key.F10 | Key.ShiftMask, new KeyModifiers ())));
 			Assert.Null (tf.ContextMenu.MenuBar);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_A_ContextManu_Over_A_Dialog ()
+		{
+			var top = Application.Top;
+			var win = new Window ();
+			top.Add (win);
+			Application.Begin (top);
+			((FakeDriver)Application.Driver).SetBufferSize (20, 15);
+
+			Assert.Equal (new Rect (0, 0, 20, 15), win.Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			var dialog = new Dialog () { X = 2, Y = 2, Width = 15, Height = 4 };
+			dialog.Add (new TextField ("Test") { X = Pos.Center (), Width = 10 });
+			var rs = Application.Begin (dialog);
+
+			Assert.Equal (new Rect (2, 2, 15, 4), dialog.Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│                  │
+│ ┌─────────────┐  │
+│ │ Test        │  │
+│ │             │  │
+│ └─────────────┘  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			ReflectionTools.InvokePrivate (
+				typeof (Application),
+				"ProcessMouseEvent",
+				new MouseEvent () {
+					X = 9,
+					Y = 3,
+					Flags = MouseFlags.Button3Clicked
+				});
+
+			var firstIteration = false;
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│                  │
+│ ┌─────────────┐  │
+│ │ Test        │  │
+┌───────────────────
+│ Select All   Ctrl+
+│ Delete All   Ctrl+
+│ Copy         Ctrl+
+│ Cut          Ctrl+
+│ Paste        Ctrl+
+│ Undo         Ctrl+
+│ Redo         Ctrl+
+└───────────────────
+│                  │
+└──────────────────┘", output);
+
+			Application.End (rs);
+		}
 	}
 }

+ 9 - 9
UnitTests/Menus/MenuTests.cs

@@ -109,7 +109,7 @@ namespace Terminal.Gui.MenuTests {
 					new MenuItem ("_New", "Creates new file.", New)
 				})
 			});
-			menu.MenuOpening += (s,e) => {
+			menu.MenuOpening += (s, e) => {
 				Assert.Equal ("_File", e.CurrentMenu.Title);
 				Assert.Equal ("_New", e.CurrentMenu.Children [0].Title);
 				Assert.Equal ("Creates new file.", e.CurrentMenu.Children [0].Help);
@@ -130,7 +130,7 @@ namespace Terminal.Gui.MenuTests {
 				mi.Action ();
 				Assert.Equal ("Copy", miAction);
 			};
-			menu.MenuClosing += (s,e) => {
+			menu.MenuClosing += (s, e) => {
 				Assert.False (isMenuClosed);
 				if (cancelClosing) {
 					e.Cancel = true;
@@ -196,7 +196,7 @@ Edit
 					new MenuItem ("_Save", "Saves the file.", null, null)
 				})
 			});
-			menu.MenuOpened += (s,e) => {
+			menu.MenuOpened += (s, e) => {
 				miCurrent = e.MenuItem;
 				mCurrent = menu.openMenu;
 			};
@@ -380,12 +380,12 @@ Edit
 				}),
 				new MenuBarItem ("_About", "Top-Level", () => miAction ="About")
 			});
-			menu.MenuOpening += (s,e) => mbiCurrent = e.CurrentMenu;
+			menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu;
 			menu.MenuOpened += (s, e) => {
 				miCurrent = e.MenuItem;
 				mCurrent = menu.openCurrentMenu;
 			};
-			menu.MenuClosing += (s,e) => {
+			menu.MenuClosing += (s, e) => {
 				mbiCurrent = null;
 				miCurrent = null;
 				mCurrent = null;
@@ -1689,7 +1689,7 @@ Edit
 └──────────────────────────────────────┘", output);
 
 			Assert.True (win.ProcessHotKey (new KeyEvent (Key.F9, new KeyModifiers ())));
-			win.Redraw (win.Bounds);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌──────────────────────────────────────┐
 │ File  Edit                           │
@@ -1701,7 +1701,7 @@ Edit
 └──────────────────────────────────────┘", output);
 
 			Assert.True (menu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
-			win.Redraw (win.Bounds);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌──────────────────────────────────────┐
 │ File  Edit                           │
@@ -1713,7 +1713,7 @@ Edit
 └──────────────────────────────────────┘", output);
 
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
-			win.Redraw (win.Bounds);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌──────────────────────────────────────┐
 │ File  Edit                           │
@@ -1725,7 +1725,7 @@ Edit
 └──────────────────────────────────────┘", output);
 
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
-			win.Redraw (win.Bounds);
+			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌──────────────────────────────────────┐
 │ File  Edit                           │

+ 3 - 4
UnitTests/TestHelpers.cs

@@ -28,8 +28,8 @@ public class AutoInitShutdownAttribute : Xunit.Sdk.BeforeAfterTestAttribute {
 	/// </summary>
 	/// <param name="autoInit">If true, Application.Init will be called Before the test runs.</param>
 	/// <param name="autoShutdown">If true, Application.Shutdown will be called After the test runs.</param>
-	/// <param name="consoleDriverType">Determins which ConsoleDriver (FakeDriver, WindowsDriver, 
-	/// CursesDriver, NetDriver) will be used when Appliation.Init is called. If null FakeDriver will be used.
+	/// <param name="consoleDriverType">Determines which ConsoleDriver (FakeDriver, WindowsDriver, 
+	/// CursesDriver, NetDriver) will be used when Application.Init is called. If null FakeDriver will be used.
 	/// Only valid if <paramref name="autoInit"/> is true.</param>
 	/// <param name="useFakeClipboard">If true, will force the use of <see cref="FakeDriver.FakeClipboard"/>. 
 	/// Only valid if <see cref="consoleDriver"/> == <see cref="FakeDriver"/> and <paramref name="autoInit"/> is true.</param>
@@ -126,7 +126,7 @@ class TestHelpers {
 			actualLook = actualLook.Replace ("\r\n", "\n");
 
 			// If test is about to fail show user what things looked like
-			if(!string.Equals(expectedLook,actualLook)) {
+			if (!string.Equals (expectedLook, actualLook)) {
 				output?.WriteLine ("Expected:" + Environment.NewLine + expectedLook);
 				output?.WriteLine ("But Was:" + Environment.NewLine + actualLook);
 			}
@@ -282,4 +282,3 @@ class TestHelpers {
 		return $"{a.Foreground},{a.Background}";
 	}
 }
-

+ 125 - 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 ()
@@ -1380,5 +1379,130 @@ namespace Terminal.Gui.TopLevelTests {
 
 			Application.End (rs);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_A_Top_Subview_On_A_Dialog ()
+		{
+			var top = Application.Top;
+			var win = new Window ();
+			top.Add (win);
+			Application.Begin (top);
+			((FakeDriver)Application.Driver).SetBufferSize (20, 20);
+
+			Assert.Equal (new Rect (0, 0, 20, 20), win.Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			var btnPopup = new Button ("Popup");
+			btnPopup.Clicked += (s, e) => {
+				var viewToScreen = btnPopup.ViewToScreen (top.Frame);
+				var view = new View () {
+					X = 1,
+					Y = viewToScreen.Y + 1,
+					Width = 18,
+					Height = 5,
+					Border = new Border () { BorderStyle = BorderStyle.Single }
+				};
+				Application.Current.DrawContentComplete += Current_DrawContentComplete;
+				top.Add (view);
+
+				void Current_DrawContentComplete (object sender, DrawEventArgs e)
+				{
+					Assert.Equal (new Rect (1, 14, 18, 5), view.Frame);
+
+					var savedClip = Application.Driver.Clip;
+					Application.Driver.Clip = top.Frame;
+					view.Redraw (view.Bounds);
+					top.Move (2, 15);
+					View.Driver.AddStr ("One");
+					top.Move (2, 16);
+					View.Driver.AddStr ("Two");
+					top.Move (2, 17);
+					View.Driver.AddStr ("Three");
+					Application.Driver.Clip = savedClip;
+
+					Application.Current.DrawContentComplete -= Current_DrawContentComplete;
+				}
+			};
+			var dialog = new Dialog ("", 15, 10, btnPopup);
+			var rs = Application.Begin (dialog);
+
+			Assert.Equal (new Rect (2, 5, 15, 10), dialog.Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│ ┌─────────────┐  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │  [ Popup ]  │  │
+│ └─────────────┘  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			ReflectionTools.InvokePrivate (
+				typeof (Application),
+				"ProcessMouseEvent",
+				new MouseEvent () {
+					X = 9,
+					Y = 13,
+					Flags = MouseFlags.Button1Clicked
+				});
+
+			var firstIteration = false;
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│ ┌─────────────┐  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │  [ Popup ]  │  │
+│┌────────────────┐│
+││One             ││
+││Two             ││
+││Three           ││
+│└────────────────┘│
+└──────────────────┘", output);
+
+			Application.End (rs);
+		}
 	}
 }

+ 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);
+		}
 	}
 }

+ 19 - 9
UnitTests/Views/GraphViewTests.cs

@@ -12,7 +12,7 @@ using Xunit.Abstractions;
 using Rune = System.Rune;
 
 namespace Terminal.Gui.ViewTests {
-#if false // BUGBUG: v2 see https://github.com/gui-cs/Terminal.Gui/issues/2463
+// BUGBUG: v2 see https://github.com/gui-cs/Terminal.Gui/issues/2463
 
 	#region Helper Classes
 	class FakeHAxis : HorizontalAxis {
@@ -86,6 +86,8 @@ namespace Terminal.Gui.ViewTests {
 			GraphViewTests.InitFakeDriver ();
 
 			var gv = new GraphView ();
+			gv.BeginInit (); gv.EndInit ();
+
 			gv.ColorScheme = new ColorScheme ();
 			gv.MarginBottom = 1;
 			gv.MarginLeft = 1;
@@ -100,6 +102,8 @@ namespace Terminal.Gui.ViewTests {
 		public void ScreenToGraphSpace_DefaultCellSize ()
 		{
 			var gv = new GraphView ();
+			gv.BeginInit (); gv.EndInit ();
+
 			gv.Bounds = new Rect (0, 0, 20, 10);
 
 			// origin should be bottom left
@@ -119,7 +123,7 @@ namespace Terminal.Gui.ViewTests {
 		public void ScreenToGraphSpace_DefaultCellSize_WithMargin ()
 		{
 			var gv = new GraphView ();
-			gv.LayoutSubviews ();
+			gv.BeginInit (); gv.EndInit ();
 
 			gv.Bounds = new Rect (0, 0, 20, 10);
 
@@ -155,7 +159,7 @@ namespace Terminal.Gui.ViewTests {
 		public void ScreenToGraphSpace_CustomCellSize ()
 		{
 			var gv = new GraphView ();
-			gv.LayoutSubviews ();
+			gv.BeginInit (); gv.EndInit ();
 
 			gv.Bounds = new Rect (0, 0, 20, 10);
 
@@ -186,7 +190,7 @@ namespace Terminal.Gui.ViewTests {
 		public void GraphSpaceToScreen_DefaultCellSize ()
 		{
 			var gv = new GraphView ();
-			gv.LayoutSubviews ();
+			gv.BeginInit (); gv.EndInit ();
 
 			gv.Bounds = new Rect (0, 0, 20, 10);
 
@@ -205,7 +209,7 @@ namespace Terminal.Gui.ViewTests {
 		public void GraphSpaceToScreen_DefaultCellSize_WithMargin ()
 		{
 			var gv = new GraphView ();
-			gv.LayoutSubviews ();
+			gv.BeginInit (); gv.EndInit ();
 
 			gv.Bounds = new Rect (0, 0, 20, 10);
 
@@ -234,7 +238,7 @@ namespace Terminal.Gui.ViewTests {
 		public void GraphSpaceToScreen_ScrollOffset ()
 		{
 			var gv = new GraphView ();
-			gv.LayoutSubviews ();
+			gv.BeginInit (); gv.EndInit ();
 
 			gv.Bounds = new Rect (0, 0, 20, 10);
 
@@ -255,7 +259,7 @@ namespace Terminal.Gui.ViewTests {
 		public void GraphSpaceToScreen_CustomCellSize ()
 		{
 			var gv = new GraphView ();
-			gv.LayoutSubviews ();
+			gv.BeginInit (); gv.EndInit ();
 			
 			gv.Bounds = new Rect (0, 0, 20, 10);
 
@@ -295,7 +299,7 @@ namespace Terminal.Gui.ViewTests {
 		public void GraphSpaceToScreen_CustomCellSize_WithScrollOffset ()
 		{
 			var gv = new GraphView ();
-			gv.LayoutSubviews ();
+			gv.BeginInit (); gv.EndInit ();
 
 			gv.Bounds = new Rect (0, 0, 20, 10);
 
@@ -340,6 +344,8 @@ namespace Terminal.Gui.ViewTests {
 			InitFakeDriver ();
 
 			var gv = new GraphView ();
+			gv.BeginInit (); gv.EndInit ();
+
 			gv.ColorScheme = new ColorScheme ();
 			gv.Bounds = new Rect (0, 0, 50, 30);
 			gv.Series.Add (new ScatterSeries () { Points = new List<PointF> { new PointF (1, 1) } });
@@ -363,6 +369,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestReversing_ScreenToGraphSpace ()
 		{
 			var gv = new GraphView ();
+			gv.BeginInit (); gv.EndInit ();
 			gv.Bounds = new Rect (0, 0, 50, 30);
 
 			// How much graph space each cell of the console depicts
@@ -414,6 +421,7 @@ namespace Terminal.Gui.ViewTests {
 			GraphViewTests.InitFakeDriver ();
 
 			var gv = new GraphView ();
+			gv.BeginInit (); gv.EndInit ();
 			gv.ColorScheme = new ColorScheme ();
 			gv.Bounds = new Rect (0, 0, 50, 30);
 
@@ -460,6 +468,7 @@ namespace Terminal.Gui.ViewTests {
 			GraphViewTests.InitFakeDriver ();
 
 			var gv = new GraphView ();
+			gv.BeginInit (); gv.EndInit ();
 			gv.ColorScheme = new ColorScheme ();
 			gv.Bounds = new Rect (0, 0, 50, 30);
 
@@ -683,6 +692,7 @@ namespace Terminal.Gui.ViewTests {
 			GraphViewTests.InitFakeDriver ();
 
 			var gv = new GraphView ();
+			gv.BeginInit (); gv.EndInit ();
 			gv.ColorScheme = new ColorScheme ();
 
 			// y axis goes from 0.1 to 1 across 10 console rows
@@ -1601,5 +1611,5 @@ namespace Terminal.Gui.ViewTests {
 			Assert.Equal (6.6f, render.Value);
 		}
 	}
-#endif
+
 }

+ 3 - 3
UnitTests/Views/SpinnerViewTests.cs

@@ -3,7 +3,7 @@ using Terminal.Gui;
 using Xunit;
 using Xunit.Abstractions;
 
-namespace UnitTests.Views {
+namespace Terminal.Gui.ViewsTests {
 	public class SpinnerViewTests {
 
 		readonly ITestOutputHelper output;
@@ -23,11 +23,11 @@ namespace UnitTests.Views {
 			Assert.NotEmpty (Application.MainLoop.timeouts);
 
 			//More calls to AutoSpin do not add more timeouts
-			Assert.Equal (1,Application.MainLoop.timeouts.Count);
+			Assert.Single (Application.MainLoop.timeouts);
 			view.AutoSpin ();
 			view.AutoSpin ();
 			view.AutoSpin ();
-			Assert.Equal (1, Application.MainLoop.timeouts.Count);
+			Assert.Single (Application.MainLoop.timeouts);
 
 			// Dispose clears timeout
 			Assert.NotEmpty (Application.MainLoop.timeouts);

+ 20 - 7
UnitTests/Views/TableViewTests.cs

@@ -12,7 +12,6 @@ using System.Reflection;
 namespace Terminal.Gui.ViewTests {
 
 	public class TableViewTests {
-#if false // BUGBUG: v2 - Table scenarios are working fine; Will fix these unit test later
 		readonly ITestOutputHelper output;
 
 		public TableViewTests (ITestOutputHelper output)
@@ -267,6 +266,8 @@ namespace Terminal.Gui.ViewTests {
 
 			// ensure that TableView has the input focus
 			Application.Top.Add (tableView);
+			Application.Begin (Application.Top);
+			
 			Application.Top.FocusFirst ();
 			Assert.True (tableView.HasFocus);
 
@@ -294,6 +295,7 @@ namespace Terminal.Gui.ViewTests {
 				MultiSelect = true,
 				Bounds = new Rect (0, 0, 10, 5)
 			};
+			tableView.BeginInit (); tableView.EndInit ();
 
 			tableView.SelectAll ();
 			Assert.Equal (16, tableView.GetAllSelectedCells ().Count ());
@@ -321,6 +323,7 @@ namespace Terminal.Gui.ViewTests {
 				MultiSelect = true,
 				Bounds = new Rect (0, 0, 10, 5)
 			};
+			tableView.BeginInit (); tableView.EndInit ();
 
 			tableView.ChangeSelectionToEndOfTable (false);
 
@@ -349,6 +352,7 @@ namespace Terminal.Gui.ViewTests {
 				MultiSelect = multiSelect,
 				Bounds = new Rect (0, 0, 10, 5)
 			};
+			tableView.BeginInit (); tableView.EndInit ();
 
 			tableView.SetSelection (1, 1, false);
 
@@ -365,6 +369,7 @@ namespace Terminal.Gui.ViewTests {
 				MultiSelect = true,
 				Bounds = new Rect (0, 0, 10, 5)
 			};
+			tableView.BeginInit (); tableView.EndInit ();
 
 			// move cursor to 1,1
 			tableView.SetSelection (1, 1, false);
@@ -390,6 +395,7 @@ namespace Terminal.Gui.ViewTests {
 				FullRowSelect = true,
 				Bounds = new Rect (0, 0, 10, 5)
 			};
+			tableView.BeginInit (); tableView.EndInit ();
 
 			// move cursor to 1,1
 			tableView.SetSelection (1, 1, false);
@@ -416,6 +422,7 @@ namespace Terminal.Gui.ViewTests {
 				MultiSelect = true,
 				Bounds = new Rect (0, 0, 10, 5)
 			};
+			tableView.BeginInit (); tableView.EndInit ();
 
 			/*  
 				Sets up disconnected selections like:
@@ -1002,7 +1009,7 @@ namespace Terminal.Gui.ViewTests {
 		private TableView SetUpMiniTable ()
 		{
 			var tv = new TableView ();
-			tv.LayoutSubviews ();
+			tv.BeginInit (); tv.EndInit ();
 			tv.Bounds = new Rect (0, 0, 10, 4);
 
 			var dt = new DataTable ();
@@ -1026,10 +1033,10 @@ namespace Terminal.Gui.ViewTests {
 		public void ScrollDown_OneLineAtATime ()
 		{
 			var tableView = new TableView ();
+			tableView.BeginInit (); tableView.EndInit ();
 
 			// Set big table
 			tableView.Table = BuildTable (25, 50);
-			tableView.LayoutSubviews ();
 
 			// 1 header + 4 rows visible
 			tableView.Bounds = new Rect (0, 0, 25, 5);
@@ -1054,6 +1061,8 @@ namespace Terminal.Gui.ViewTests {
 			GraphViewTests.InitFakeDriver ();
 
 			var tableView = new TableView ();
+			tableView.BeginInit (); tableView.EndInit ();
+
 			tableView.ColorScheme = Colors.TopLevel;
 			tableView.LayoutSubviews ();
 
@@ -1120,7 +1129,7 @@ namespace Terminal.Gui.ViewTests {
 			GraphViewTests.InitFakeDriver ();
 
 			var tableView = new TableView ();
-			tableView.LayoutSubviews ();
+			tableView.BeginInit (); tableView.EndInit ();
 			tableView.ColorScheme = Colors.TopLevel;
 
 			// 3 columns are visibile
@@ -1183,7 +1192,8 @@ namespace Terminal.Gui.ViewTests {
 		private TableView GetABCDEFTableView (out DataTable dt)
 		{
 			var tableView = new TableView ();
-			tableView.LayoutSubviews ();
+			tableView.BeginInit (); tableView.EndInit ();
+			
 			tableView.ColorScheme = Colors.TopLevel;
 
 			// 3 columns are visible
@@ -1742,7 +1752,8 @@ namespace Terminal.Gui.ViewTests {
 			GraphViewTests.InitFakeDriver ();
 
 			var tableView = new TableView ();
-			tableView.LayoutSubviews ();
+			tableView.BeginInit (); tableView.EndInit ();
+			
 			tableView.ColorScheme = Colors.TopLevel;
 
 			// 25 characters can be printed into table
@@ -1884,6 +1895,8 @@ namespace Terminal.Gui.ViewTests {
 			GraphViewTests.InitFakeDriver ();
 
 			var tableView = new TableView ();
+			tableView.BeginInit (); tableView.EndInit ();
+
 			tableView.ColorScheme = Colors.TopLevel;
 
 			// 3 columns are visibile
@@ -2055,6 +2068,7 @@ namespace Terminal.Gui.ViewTests {
 		public void Test_ScreenToCell ()
 		{
 			var tableView = GetTwoRowSixColumnTable ();
+			tableView.BeginInit (); tableView.EndInit ();
 			tableView.LayoutSubviews ();
 
 			tableView.Redraw (tableView.Bounds);
@@ -2230,6 +2244,5 @@ namespace Terminal.Gui.ViewTests {
 			tableView.Table = dt;
 			return tableView;
 		}
-#endif 
 	}
 }