Răsfoiți Sursa

still broken - documenting weird behavior

Tigger Kindel 2 ani în urmă
părinte
comite
1de683c13a

+ 6 - 6
Terminal.Gui/Core/Application.cs

@@ -1219,7 +1219,7 @@ namespace Terminal.Gui {
 					MdiTop?.OnDeactivate (state.Toplevel);
 					state.Toplevel = Current;
 					MdiTop?.OnActivate (state.Toplevel);
-					Top.SetChildNeedsDisplay ();
+					Top.SetSubViewNeedsDisplay ();
 					Refresh ();
 				}
 				if (Driver.EnsureCursorVisibility ()) {
@@ -1231,7 +1231,7 @@ namespace Terminal.Gui {
 			firstIteration = false;
 
 			if (state.Toplevel != Top
-				&& (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
+				&& (!Top._needsDisplay.IsEmpty || Top._childNeedsDisplay || Top.LayoutNeeded)) {
 				Top.Redraw (Top.Bounds);
 				foreach (var top in toplevels.Reverse ()) {
 					if (top != Top && top != state.Toplevel) {
@@ -1241,7 +1241,7 @@ namespace Terminal.Gui {
 				}
 				state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
 			}
-			if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.ChildNeedsDisplay || state.Toplevel.LayoutNeeded
+			if (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._childNeedsDisplay || state.Toplevel.LayoutNeeded
 				|| MdiChildNeedsDisplay ()) {
 				state.Toplevel.Redraw (state.Toplevel.Bounds);
 				if (DebugDrawBounds) {
@@ -1253,7 +1253,7 @@ namespace Terminal.Gui {
 				Driver.UpdateCursor ();
 			}
 			if (state.Toplevel != Top && !state.Toplevel.Modal
-				&& (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
+				&& (!Top._needsDisplay.IsEmpty || Top._childNeedsDisplay || Top.LayoutNeeded)) {
 				Top.Redraw (Top.Bounds);
 			}
 		}
@@ -1282,8 +1282,8 @@ namespace Terminal.Gui {
 			}
 
 			foreach (var top in toplevels) {
-				if (top != Current && top.Visible && (!top.NeedDisplay.IsEmpty || top.ChildNeedsDisplay || top.LayoutNeeded)) {
-					MdiTop.SetChildNeedsDisplay ();
+				if (top != Current && top.Visible && (!top._needsDisplay.IsEmpty || top._childNeedsDisplay || top.LayoutNeeded)) {
+					MdiTop.SetSubViewNeedsDisplay ();
 					return true;
 				}
 			}

+ 11 - 9
Terminal.Gui/Core/Frame.cs

@@ -13,6 +13,9 @@ namespace Terminal.Gui {
 	/// </summary>
 	public class Frame : View {
 
+		// TODO: v2 - If a Frame has focus, navigation keys (e.g Command.NextView) should cycle through SubViews of the Frame
+		// QUESTION: How does a user navigate out of a Frame to another Frame, or back into the Parent's SubViews?
+
 		/// <summary>
 		/// Frames are a special form of <see cref="View"/> that act as adornments; they appear outside of the <see cref="View.Bounds"/>
 		/// enabling borders, menus, etc... 
@@ -42,17 +45,16 @@ namespace Terminal.Gui {
 		/// <inheritdoc/>
 		public override void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
 		{
-			// Frames are children of a View, not SubViews. Thus ViewToScreen will not work.
+			// Frames are *Children* of a View, not SubViews. Thus View.ViewToScreen will not work.
 			// To get the screen-relative coordinates of a Frame, we need to know who
 			// the Parent is
+			var parentFrame = Parent?.Frame ?? Frame;
+			rrow = row + parentFrame.Y;
+			rcol = col + parentFrame.X;
 
-			// Computes the real row, col relative to the screen.
-			var inner = Parent?.Bounds ?? Bounds;
-			rrow = row - inner.Y;
-			rcol = col - inner.X;
-
-			Parent?.ViewToScreen (rcol, rrow, out rcol, out rrow, clipped);
-
+			// We now have rcol/rrow in coordinates relative to our SuperView. If our SuperView has
+			// a SuperView, keep going...
+			Parent?.SuperView?.SuperView?.ViewToScreen (rcol, rrow, out rcol, out rrow, clipped);
 		}
 
 		/// <summary>
@@ -87,7 +89,7 @@ namespace Terminal.Gui {
 		{
 			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
 				Clear (viewport);
-				SetChildNeedsDisplay ();
+				SetSubViewNeedsDisplay ();
 				// Draw any Text
 				if (TextFormatter != null) {
 					TextFormatter.NeedsFormat = true;

+ 54 - 31
Terminal.Gui/Core/Toplevel.cs

@@ -147,7 +147,7 @@ namespace Terminal.Gui {
 		internal virtual void OnChildClosed (Toplevel top)
 		{
 			if (IsMdiContainer) {
-				SetChildNeedsDisplay ();
+				SetSubViewNeedsDisplay ();
 			}
 			ChildClosed?.Invoke (top);
 		}
@@ -221,6 +221,10 @@ namespace Terminal.Gui {
 		{
 			ColorScheme = Colors.TopLevel;
 
+			// TODO: v2 - ALL Views (Responders??!?!) should support the commands related to 
+			//    - Focus
+			//  Move the appropriate AddCommand calls to `Responder`
+
 			// Things this view knows how to do
 			AddCommand (Command.QuitToplevel, () => { QuitToplevel (); return true; });
 			AddCommand (Command.Suspend, () => { Driver.Suspend (); ; return true; });
@@ -370,7 +374,7 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// <see langword="true"/> if was already loaded by the <see cref="Application.Begin(Toplevel)"/>
-		/// <see langword="false"/>, otherwise. This is used to avoid the <see cref="View.NeedDisplay"/>
+		/// <see langword="false"/>, otherwise. This is used to avoid the <see cref="View._needsDisplay"/>
 		/// having wrong values while this was not yet loaded.
 		/// </summary>
 		public bool IsLoaded { get; private set; }
@@ -607,61 +611,75 @@ namespace Terminal.Gui {
 			}
 		}
 
+		/// <summary>
+		///  Ensures the new position of the <see cref="Toplevel"/> is within the bounds of the screen (e.g. for dragging a Window).
+		///  The `out` parameters are the new X and Y coordinates.
+		/// </summary>
+		/// <param name="top">The Toplevel that is to be moved.</param>
+		/// <param name="x">The target x location.</param>
+		/// <param name="y">The target y location.</param>
+		/// <param name="nx">The x location after ensuring <paramref name="top"/> will remain visible.</param>
+		/// <param name="ny">The y location after ensuring <paramref name="top"/> will remain visible.</param>
+		/// <param name="menuBar">The new top most menuBar</param>
+		/// <param name="statusBar">The new top most statusBar</param>
+		/// <returns>The <see cref="Toplevel"/> that is Application.Top</returns>
 		internal View EnsureVisibleBounds (Toplevel top, int x, int y,
-			out int nx, out int ny, out View mb, out View sb)
+			out int nx, out int ny, out MenuBar menuBar, out StatusBar statusBar)
 		{
-			int l;
+			int maxWidth;
 			View superView;
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				l = Driver.Cols;
+			var isTopTop = top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top;
+			if (isTopTop) {
+				maxWidth = Driver.Cols;
 				superView = Application.Top;
 			} else {
-				l = top.SuperView.Frame.Width;
+				maxWidth = top.SuperView.Frame.Width;
+				// BUGBUG: v2 - No code ever uses the return of this function if `top` is not Application.Top
 				superView = top.SuperView;
 			}
 			nx = Math.Max (x, 0);
-			nx = nx + top.Frame.Width > l ? Math.Max (l - top.Frame.Width, 0) : nx;
+			nx = nx + top.Frame.Width > maxWidth ? Math.Max (maxWidth - top.Frame.Width, 0) : nx;
 			var mfLength = top.Border?.DrawMarginFrame == true ? 2 : 1;
 			if (nx + mfLength > top.Frame.X + top.Frame.Width) {
 				nx = Math.Max (top.Frame.Right - mfLength, 0);
 			}
 			//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
-			bool m, s;
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				m = Application.Top.MenuBar?.Visible == true;
-				mb = Application.Top.MenuBar;
+			bool isMenuBarVisible, isStatusBarVisible;
+			if (isTopTop) {
+				isMenuBarVisible = Application.Top.MenuBar?.Visible == true;
+				menuBar = Application.Top.MenuBar;
 			} else {
 				var t = top.SuperView;
 				while (!(t is Toplevel)) {
 					t = t.SuperView;
 				}
-				m = ((Toplevel)t).MenuBar?.Visible == true;
-				mb = ((Toplevel)t).MenuBar;
+				isMenuBarVisible = ((Toplevel)t).MenuBar?.Visible == true;
+				menuBar = ((Toplevel)t).MenuBar;
 			}
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				l = m ? 1 : 0;
+			if (isTopTop) {
+				maxWidth = isMenuBarVisible ? 1 : 0;
 			} else {
-				l = 0;
+				maxWidth = 0;
 			}
-			ny = Math.Max (y, l);
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				s = Application.Top.StatusBar?.Visible == true;
-				sb = Application.Top.StatusBar;
+			ny = Math.Max (y, maxWidth);
+			if (isTopTop) {
+				isStatusBarVisible = Application.Top.StatusBar?.Visible == true;
+				statusBar = Application.Top.StatusBar;
 			} else {
 				var t = top.SuperView;
 				while (!(t is Toplevel)) {
 					t = t.SuperView;
 				}
-				s = ((Toplevel)t).StatusBar?.Visible == true;
-				sb = ((Toplevel)t).StatusBar;
+				isStatusBarVisible = ((Toplevel)t).StatusBar?.Visible == true;
+				statusBar = ((Toplevel)t).StatusBar;
 			}
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
-				l = s ? Driver.Rows - 1 : Driver.Rows;
+			if (isTopTop) {
+				maxWidth = isStatusBarVisible ? Driver.Rows - 1 : Driver.Rows;
 			} else {
-				l = s ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
+				maxWidth = isStatusBarVisible ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
 			}
-			ny = Math.Min (ny, l);
-			ny = ny + top.Frame.Height >= l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
+			ny = Math.Min (ny, maxWidth);
+			ny = ny + top.Frame.Height >= maxWidth ? Math.Max (maxWidth - top.Frame.Height, isMenuBarVisible ? 1 : 0) : ny;
 			if (ny + mfLength > top.Frame.Y + top.Frame.Height) {
 				ny = Math.Max (top.Frame.Bottom - mfLength, 0);
 			}
@@ -670,6 +688,7 @@ namespace Terminal.Gui {
 			return superView;
 		}
 
+		// TODO: v2 - Not sure this is needed anymore.
 		internal void PositionToplevels ()
 		{
 			PositionToplevel (this);
@@ -681,13 +700,14 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
+		/// Adjusts the location and size of <paramref name="top"/> within this Toplevel.
 		/// Virtual method enabling implementation of specific positions for inherited <see cref="Toplevel"/> views.
 		/// </summary>
-		/// <param name="top">The toplevel.</param>
+		/// <param name="top">The Toplevel to adjust.</param>
 		public virtual void PositionToplevel (Toplevel top)
 		{
 			var superView = EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y,
-				out int nx, out int ny, out _, out View sb);
+				out int nx, out int ny, out _, out StatusBar sb);
 			bool layoutSubviews = false;
 			if ((top?.SuperView != null || (top != Application.Top && top.Modal)
 				|| (top?.SuperView == null && top.IsMdiChild))
@@ -703,6 +723,7 @@ namespace Terminal.Gui {
 				}
 			}
 
+			// TODO: v2 - This is a hack to get the StatusBar to be positioned correctly.
 			if (sb != null && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0)
 				&& top.Height is Dim.DimFill && -top.Height.Anchor (0) < 1) {
 
@@ -722,7 +743,7 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			if (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded) {
+			if (!_needsDisplay.IsEmpty || _childNeedsDisplay || LayoutNeeded) {
 				Driver.SetAttribute (GetNormalColor ());
 
 				// This is the Application.Top. Clear just the region we're being asked to redraw 
@@ -751,6 +772,8 @@ namespace Terminal.Gui {
 						view.SetNeedsDisplay (view.Bounds);
 					}
 				}
+
+				// BUGBUG: shouldn't we just return here? the call to base.Redraw below is redundant
 			}
 
 			base.Redraw (Bounds);

+ 159 - 101
Terminal.Gui/Core/View.cs

@@ -403,8 +403,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-		internal Rect NeedDisplay { get; private set; } = Rect.Empty;
-
 		// The frame for the object. Superview relative.
 		Rect frame;
 
@@ -601,7 +599,7 @@ namespace Terminal.Gui {
 				if (Padding == null || BorderFrame == null || Margin == null) {
 					return new Rect (default, Frame.Size);
 				}
-				var frameRelativeBounds = new Rect (default, Padding.Bounds.Size);
+				var frameRelativeBounds = Padding.Thickness.GetInnerRect (Padding.Frame);
 				return frameRelativeBounds;
 			}
 			set {
@@ -944,14 +942,6 @@ namespace Terminal.Gui {
 			HotKeyChanged?.Invoke (obj);
 		}
 
-		/// <summary>
-		/// Sets a flag indicating this view needs to be redisplayed because its state has changed.
-		/// </summary>
-		public void SetNeedsDisplay ()
-		{
-			SetNeedsDisplay (Bounds);
-		}
-
 		internal bool LayoutNeeded { get; private set; } = true;
 
 		internal void SetNeedsLayout ()
@@ -976,22 +966,33 @@ namespace Terminal.Gui {
 			LayoutNeeded = false;
 		}
 
+		// The view-relative region that needs to be redrawn
+		internal Rect _needsDisplay { get; private set; } = Rect.Empty;
+
+		/// <summary>
+		/// Sets a flag indicating this view needs to be redisplayed because its state has changed.
+		/// </summary>
+		public void SetNeedsDisplay ()
+		{
+			SetNeedsDisplay (Bounds);
+		}
+
 		/// <summary>
-		/// Flags the view-relative region on this View as needing to be repainted.
+		/// Flags the view-relative region on this View as needing to be redrawn.
 		/// </summary>
-		/// <param name="region">The view-relative region that must be flagged for repaint.</param>
+		/// <param name="region">The view-relative region that needs to be redrawn.</param>
 		public void SetNeedsDisplay (Rect region)
 		{
-			if (NeedDisplay.IsEmpty)
-				NeedDisplay = region;
+			if (_needsDisplay.IsEmpty)
+				_needsDisplay = region;
 			else {
-				var x = Math.Min (NeedDisplay.X, region.X);
-				var y = Math.Min (NeedDisplay.Y, region.Y);
-				var w = Math.Max (NeedDisplay.Width, region.Width);
-				var h = Math.Max (NeedDisplay.Height, region.Height);
-				NeedDisplay = new Rect (x, y, w, h);
+				var x = Math.Min (_needsDisplay.X, region.X);
+				var y = Math.Min (_needsDisplay.Y, region.Y);
+				var w = Math.Max (_needsDisplay.Width, region.Width);
+				var h = Math.Max (_needsDisplay.Height, region.Height);
+				_needsDisplay = new Rect (x, y, w, h);
 			}
-			_superView?.SetChildNeedsDisplay ();
+			_superView?.SetSubViewNeedsDisplay ();
 
 			if (subviews == null)
 				return;
@@ -1005,16 +1006,28 @@ namespace Terminal.Gui {
 				}
 		}
 
-		internal bool ChildNeedsDisplay { get; private set; }
+		private Rect GetNeedsDisplayRectScreen (Rect containerBounds)
+		{
+			Rect rect = ViewToScreen (_needsDisplay);
+			if (!containerBounds.IsEmpty) {
+				rect.Width = Math.Min (_needsDisplay.Width, containerBounds.Width);
+				rect.Height = Math.Min (_needsDisplay.Height, containerBounds.Height);
+			}
+
+			return rect;
+		}
+
+
+		internal bool _childNeedsDisplay { get; private set; }
 
 		/// <summary>
-		/// Indicates that any child views (in the <see cref="Subviews"/> list) need to be repainted.
+		/// Indicates that any Subviews (in the <see cref="Subviews"/> list) need to be repainted.
 		/// </summary>
-		public void SetChildNeedsDisplay ()
+		public void SetSubViewNeedsDisplay ()
 		{
-			ChildNeedsDisplay = true;
+			_childNeedsDisplay = true;
 			if (_superView != null)
-				_superView.SetChildNeedsDisplay ();
+				_superView.SetSubViewNeedsDisplay ();
 		}
 
 		internal bool addingView;
@@ -1217,6 +1230,9 @@ namespace Terminal.Gui {
 			}
 		}
 
+
+		// BUGBUG: Stupid that this takes screen-relative. We should have a tenet that says 
+		// "View APIs only deal with View-relative coords". 
 		/// <summary>
 		///   Clears the specified region with the current color. 
 		/// </summary>
@@ -1235,14 +1251,32 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Converts a view-relative (col,row) position to a screen-relative position (col,row). The values are optionally clamped to the screen dimensions.
+		/// Converts a point from screen-relative coordinates to view-relative coordinates.
+		/// </summary>
+		/// <returns>The mapped point.</returns>
+		/// <param name="x">X screen-coordinate point.</param>
+		/// <param name="y">Y screen-coordinate point.</param>
+		public Point ScreenToView (int x, int y)
+		{
+			if (SuperView == null) {
+				return new Point (x - Frame.X, y - frame.Y);
+			} else {
+				var parent = SuperView.ScreenToView (x, y);
+				return new Point (parent.X - frame.X, parent.Y - frame.Y);
+			}
+		}
+
+		/// <summary>
+		/// Converts a view-relative location to a screen-relative location (col,row). The output is optionally clamped to the screen dimensions.
 		/// </summary>
 		/// <param name="col">View-relative column.</param>
 		/// <param name="row">View-relative row.</param>
 		/// <param name="rcol">Absolute column; screen-relative.</param>
 		/// <param name="rrow">Absolute row; screen-relative.</param>
-		/// <param name="clipped">Whether to clip the result of the ViewToScreen method, if set to <see langword="true"/>, the rcol, rrow values are clamped to the screen (terminal) dimensions (0..TerminalDim-1).</param>
-		public virtual void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
+		/// <param name="clamped">If <see langword="true"/>, <paramref name="rcol"/> and <paramref name="rrow"/> will be clamped to the 
+		/// screen dimensions (they never be negative and will always be less than to <see cref="ConsoleDriver.Cols"/> and
+		/// <see cref="ConsoleDriver.Rows"/>, respectively.</param>
+		public virtual void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clamped = true)
 		{
 			// Computes the real row, col relative to the screen.
 			if (Padding == null || BorderFrame == null || Margin == null) {
@@ -1258,47 +1292,28 @@ namespace Terminal.Gui {
 			while (super != null) {
 				if (!(super.Padding == null || super.BorderFrame == null || super.Margin == null)) {
 					var inner = super.Padding.Thickness.GetInnerRect (super.BorderFrame.Thickness.GetInnerRect (super.Margin.Thickness.GetInnerRect (super.Frame)));
-
-					//rrow += inner.Y - curContainer.frame.Y;
-					//rcol += inner.X - curContainer.frame.X;
-					rrow += super.frame.Y;
-					rcol += super.frame.X;
+					rrow += super.Frame.Y;
+					rcol += super.Frame.X;
 				} else {
-					rrow += super.frame.Y;
-					rcol += super.frame.X;
+					rrow += super.Frame.Y;
+					rcol += super.Frame.X;
 				}
 				super = super.SuperView;
 			}
 
 			// The following ensures that the cursor is always in the screen boundaries.
-			if (clipped) {
+			if (clamped) {
 				rrow = Math.Min (rrow, Driver.Rows - 1);
 				rcol = Math.Min (rcol, Driver.Cols - 1);
 			}
 		}
 
-		/// <summary>
-		/// Converts a point from screen-relative coordinates to view-relative coordinates.
-		/// </summary>
-		/// <returns>The mapped point.</returns>
-		/// <param name="x">X screen-coordinate point.</param>
-		/// <param name="y">Y screen-coordinate point.</param>
-		public Point ScreenToView (int x, int y)
-		{
-			if (SuperView == null) {
-				return new Point (x - Frame.X, y - frame.Y);
-			} else {
-				var parent = SuperView.ScreenToView (x, y);
-				return new Point (parent.X - frame.X, parent.Y - frame.Y);
-			}
-		}
-
 		/// <summary>
 		/// Converts a region in view-relative coordinates to screen-relative coordinates.
 		/// </summary>
 		internal Rect ViewToScreen (Rect region)
 		{
-			ViewToScreen (region.X, region.Y, out var x, out var y, clipped: false);
+			ViewToScreen (region.X, region.Y, out var x, out var y, clamped: false);
 			return new Rect (x, y, region.Width, region.Height);
 		}
 
@@ -1314,11 +1329,16 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Sets the <see cref="ConsoleDriver"/>'s clip region to the current View's <see cref="Bounds"/>.
+		/// Sets the <see cref="ConsoleDriver"/>'s clip region to <see cref="Bounds"/>.
 		/// </summary>
-		/// <returns>The existing driver's clip region, which can be then re-applied by setting <c><see cref="Driver"/>.Clip</c> (<see cref="ConsoleDriver.Clip"/>).</returns>
+		/// <returns>The current screen-relative clip region, which can be then re-applied by setting <see cref="ConsoleDriver.Clip"/>.</returns>
 		/// <remarks>
+		/// <para>
 		/// <see cref="Bounds"/> is View-relative.
+		/// </para>
+		/// <para>
+		/// If <see cref="ConsoleDriver.Clip"/> and <see cref="Bounds"/> do not intersect, the clip region will be set to <see cref="Rect.Empty"/>.
+		/// </para>
 		/// </remarks>
 		public Rect ClipToBounds ()
 		{
@@ -1328,8 +1348,11 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Sets the clip region to the specified view-relative region.
 		/// </summary>
-		/// <returns>The previous screen-relative clip region.</returns>
+		/// <returns>The current screen-relative clip region, which can be then re-applied by setting <see cref="ConsoleDriver.Clip"/>.</returns>
 		/// <param name="region">View-relative clip region.</param>
+		/// <remarks>
+		/// If <see cref="ConsoleDriver.Clip"/> and <paramref name="region"/> do not intersect, the clip region will be set to <see cref="Rect.Empty"/>.
+		/// </remarks>
 		public Rect SetClip (Rect region)
 		{
 			var previous = Driver.Clip;
@@ -1337,6 +1360,7 @@ namespace Terminal.Gui {
 			return previous;
 		}
 
+		// TODO: v2 - Deprecate this API - callers should use LineCanvas instead
 		/// <summary>
 		/// Draws a frame in the current view, clipped by the boundary of this view
 		/// </summary>
@@ -1393,10 +1417,10 @@ namespace Terminal.Gui {
 		/// This moves the cursor to the specified column and row in the view.
 		/// </summary>
 		/// <returns>The move.</returns>
-		/// <param name="col">Col.</param>
-		/// <param name="row">Row.</param>
+		/// <param name="col">The column to move to, in view-relative coordinates.</param>
+		/// <param name="row">the row to move to, in view-relative coordinates.</param>
 		/// <param name="clipped">Whether to clip the result of the ViewToScreen method,
-		///  if set to <see langword="true"/>, the col, row values are clamped to the screen (terminal) dimensions (0..TerminalDim-1).</param>
+		///  If  <see langword="true"/>, the <paramref name="col"/> and <paramref name="row"/> values are clamped to the screen (terminal) dimensions (0..TerminalDim-1).</param>
 		public void Move (int col, int row, bool clipped = true)
 		{
 			if (Driver.Rows == 0) {
@@ -1421,6 +1445,8 @@ namespace Terminal.Gui {
 				return;
 			}
 
+			// BUGBUG: v2 - This needs to support children of Frames too
+
 			if (focused == null && SuperView != null) {
 				SuperView.EnsureFocus ();
 			} else if (focused?.Visible == true && focused?.Enabled == true && focused?.Frame.Width > 0 && focused.Frame.Height > 0) {
@@ -1434,15 +1460,16 @@ namespace Terminal.Gui {
 			}
 		}
 
-		bool hasFocus;
+		// BUGBUG: v2 - Seems weird that this is in View and not Responder.
+		bool _hasFocus;
 
 		/// <inheritdoc/>
-		public override bool HasFocus => hasFocus;
+		public override bool HasFocus => _hasFocus;
 
 		void SetHasFocus (bool value, View view, bool force = false)
 		{
-			if (hasFocus != value || force) {
-				hasFocus = value;
+			if (_hasFocus != value || force) {
+				_hasFocus = value;
 				if (value) {
 					OnEnter (view);
 				} else {
@@ -1590,12 +1617,12 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Removes the <see cref="SetNeedsDisplay()"/> and the <see cref="ChildNeedsDisplay"/> setting on this view.
+		/// Removes the <see cref="SetNeedsDisplay()"/> and the <see cref="_childNeedsDisplay"/> setting on this view.
 		/// </summary>
 		protected void ClearNeedsDisplay ()
 		{
-			NeedDisplay = Rect.Empty;
-			ChildNeedsDisplay = false;
+			_needsDisplay = Rect.Empty;
+			_childNeedsDisplay = false;
 		}
 
 		// TODO: Make this cancelable
@@ -1605,9 +1632,9 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public virtual bool OnDrawFrames (Rect bounds)
 		{
-			Margin?.Redraw (Frame);
-			BorderFrame?.Redraw (Frame);
-			Padding?.Redraw (Frame);
+			Margin?.Redraw (Margin.Frame);
+			BorderFrame?.Redraw (BorderFrame.Frame);
+			Padding?.Redraw (Padding.Frame);
 
 			//var margin = Margin.Thickness.GetInnerRect (frame);
 			//var padding = BorderFrame.Thickness.GetInnerRect (margin);
@@ -1648,7 +1675,8 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			var clipRect = new Rect (Point.Empty, frame.Size);
+			var prevClip = Driver.Clip;
+			Driver.Clip = ViewToScreen (Bounds);
 
 			if (ColorScheme != null) {
 				Driver.SetAttribute (HasFocus ? GetFocusColor () : GetNormalColor ());
@@ -1656,28 +1684,33 @@ namespace Terminal.Gui {
 
 			OnDrawFrames (Frame);
 
-			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
-				Rect containerBounds = GetContainerBounds ();
-				if (!containerBounds.IsEmpty) {
-					Clear (GetNeedDisplay (containerBounds));
-					SetChildNeedsDisplay ();
-					// Draw any Text
-					if (TextFormatter != null) {
-						TextFormatter.NeedsFormat = true;
-					}
-					TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
-					    HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
-					    containerBounds);
-				}
-			}
+			// TODO: Implement complete event
+			// OnDrawFramesComplete (Frame)
+			
+			//if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
+			//	Rect containerBounds = GetContainerBounds ();
+			//	if (!containerBounds.IsEmpty) {
+			//		Clear (GetNeedDisplay (containerBounds));
+			//		SetChildNeedsDisplay ();
+			//		// Draw any Text
+			//		if (TextFormatter != null) {
+			//			TextFormatter.NeedsFormat = true;
+			//		}
+			//		TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
+			//		    HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+			//		    containerBounds);
+			//	}
+			//}
 
 			// Invoke DrawContentEvent
 			OnDrawContent (bounds);
 
+			// Draw subviews
+			// TODO: Implement OnDrawSubviews (cancelable);		
 			if (subviews != null) {
 				foreach (var view in subviews) {
-					if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay || view.LayoutNeeded) {
-						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
+					if (!view._needsDisplay.IsEmpty || view._childNeedsDisplay || view.LayoutNeeded) {
+						if (view.Frame.IntersectsWith (bounds)) { // && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
 							if (view.LayoutNeeded) {
 								view.LayoutSubviews ();
 							}
@@ -1699,24 +1732,18 @@ namespace Terminal.Gui {
 			// Invoke DrawContentCompleteEvent
 			OnDrawContentComplete (bounds);
 
+			Driver.Clip = prevClip;
 			ClearLayoutNeeded ();
 			ClearNeedsDisplay ();
 		}
 
-		Rect GetNeedDisplay (Rect containerBounds)
-		{
-			Rect rect = ViewToScreen (NeedDisplay);
-			if (!containerBounds.IsEmpty) {
-				rect.Width = Math.Min (NeedDisplay.Width, containerBounds.Width);
-				rect.Height = Math.Min (NeedDisplay.Height, containerBounds.Height);
-			}
-
-			return rect;
-		}
-
+		// Gets the screen relative rectangle describing the larger of our Superview's bounds or the Driver.Cliprect.
 		internal Rect GetContainerBounds ()
 		{
+			// Get the screen-relative rectangle describing our superview's Bounds
 			var containerBounds = SuperView == null ? default : SuperView.ViewToScreen (SuperView.Bounds);
+
+			// Ensure if clip is larger, we grow
 			var driverClip = Driver == null ? Rect.Empty : Driver.Clip;
 			containerBounds.X = Math.Max (containerBounds.X, driverClip.X);
 			containerBounds.Y = Math.Max (containerBounds.Y, driverClip.Y);
@@ -1755,9 +1782,40 @@ namespace Terminal.Gui {
 		/// <remarks>
 		/// This method will be called before any subviews added with <see cref="Add(View)"/> have been drawn. 
 		/// </remarks>
-		public virtual void OnDrawContent (Rect viewport)
+		public virtual void OnDrawContent (Rect contentArea)
 		{
-			DrawContent?.Invoke (viewport);
+			// TODO: Make DrawContent a cancelable event
+			// if (!DrawContent?.Invoke(viewport)) {
+			DrawContent?.Invoke (contentArea);
+
+			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
+				Rect containerBounds = GetContainerBounds ();
+				if (!containerBounds.IsEmpty) {
+					Clear (GetNeedsDisplayRectScreen (containerBounds));
+					SetSubViewNeedsDisplay ();
+					// Draw any Text
+					if (TextFormatter != null) {
+						TextFormatter.NeedsFormat = true;
+					}
+					TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
+					    HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+					    containerBounds);
+				}
+			}
+
+			
+			//if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
+			//	//Rect containerBounds = GetContainerBounds ();
+			//	//Clear (ViewToScreen (GetNeedDisplay (containerBounds)));
+			//	SetChildNeedsDisplay ();
+			//	// Draw any Text
+			//	if (TextFormatter != null) {
+			//		TextFormatter.NeedsFormat = true;
+			//	}
+			//	TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? ColorScheme.Focus : GetNormalColor (),
+			//	    HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled,
+			//	    contentArea, false);
+			//}
 		}
 
 		/// <summary>
@@ -1796,7 +1854,7 @@ namespace Terminal.Gui {
 			//Console.WriteLine ($"Request to focus {view}");
 			if (!view.CanFocus || !view.Visible || !view.Enabled)
 				return;
-			if (focused?.hasFocus == true && focused == view)
+			if (focused?._hasFocus == true && focused == view)
 				return;
 
 			// Make sure that this view is a subview

+ 1 - 1
Terminal.Gui/Types/Rect.cs

@@ -130,7 +130,7 @@ namespace Terminal.Gui
 		///
 		/// <remarks>
 		///	Produces a new Rectangle by intersecting 2 existing 
-		///	Rectangles. Returns null if there is no	intersection.
+		///	Rectangles. Returns Empty if there is no intersection.
 		/// </remarks>
 
 		public static Rect Intersect (Rect a, Rect b)

+ 3 - 3
Terminal.Gui/Views/TextView.cs

@@ -2513,7 +2513,7 @@ namespace Terminal.Gui {
 				InsertText (new KeyEvent () { Key = key });
 			}
 
-			if (NeedDisplay.IsEmpty) {
+			if (_needsDisplay.IsEmpty) {
 				PositionCursor ();
 			} else {
 				Adjust ();
@@ -2668,7 +2668,7 @@ namespace Terminal.Gui {
 		{
 			var offB = OffSetBackground ();
 			var line = GetCurrentLine ();
-			bool need = !NeedDisplay.IsEmpty || wrapNeeded;
+			bool need = !_needsDisplay.IsEmpty || wrapNeeded;
 			var tSize = TextModel.DisplaySize (line, -1, -1, false, TabWidth);
 			var dSize = TextModel.DisplaySize (line, leftColumn, currentColumn, true, TabWidth);
 			if (!wordWrap && currentColumn < leftColumn) {
@@ -3775,7 +3775,7 @@ namespace Terminal.Gui {
 
 		void DoNeededAction ()
 		{
-			if (NeedDisplay.IsEmpty) {
+			if (_needsDisplay.IsEmpty) {
 				PositionCursor ();
 			} else {
 				Adjust ();

+ 2 - 2
UICatalog/UICatalog.cs

@@ -286,7 +286,7 @@ namespace UICatalog {
 						StatusBar.Visible = !StatusBar.Visible;
 						ContentPane.Height = Dim.Fill(StatusBar.Visible ? 1 : 0);
 						LayoutSubviews();
-						SetChildNeedsDisplay();
+						SetSubViewNeedsDisplay();
 					}),
 					DriverName,
 					OS
@@ -375,7 +375,7 @@ namespace UICatalog {
 					var height = (StatusBar.Visible ? 1 : 0);// + (MenuBar.Visible ? 1 : 0);
 					ContentPane.Height = Dim.Fill (height);
 					LayoutSubviews ();
-					SetChildNeedsDisplay ();
+					SetSubViewNeedsDisplay ();
 				};
 
 				Loaded -= LoadedHandler;

+ 2 - 2
UnitTests/Core/LayoutTests.cs

@@ -472,8 +472,8 @@ Y
 			// that was set on the OnAdded method with the text length of 3
 			// and height 1 because wasn't set and the text has 1 line
 			Assert.Equal (new Rect (0, 0, 3, 1), lbl.Frame);
-			Assert.Equal (new Rect (0, 0, 3, 1), lbl.NeedDisplay);
-			Assert.Equal (new Rect (0, 0, 0, 0), lbl.SuperView.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 3, 1), lbl._needsDisplay);
+			Assert.Equal (new Rect (0, 0, 0, 0), lbl.SuperView._needsDisplay);
 			Assert.True (lbl.SuperView.LayoutNeeded);
 			lbl.SuperView.Redraw (lbl.SuperView.Bounds);
 			Assert.Equal ("12  ", GetContents ());

+ 23 - 23
UnitTests/Core/ViewTests.cs

@@ -1204,9 +1204,9 @@ namespace Terminal.Gui.CoreTests {
 			view.FocusDirection = View.Direction.Backward;
 			Assert.Equal (View.Direction.Backward, view.FocusDirection);
 			Assert.Empty (view.InternalSubviews);
-			Assert.Equal (new Rect (new Point (0, 0), rect.Size), view.NeedDisplay);
+			Assert.Equal (new Rect (new Point (0, 0), rect.Size), view._needsDisplay);
 			Assert.True (view.LayoutNeeded);
-			Assert.False (view.ChildNeedsDisplay);
+			Assert.False (view._childNeedsDisplay);
 			Assert.False (view.addingView);
 			view.addingView = true;
 			Assert.True (view.addingView);
@@ -2572,7 +2572,7 @@ At 0,0
 			Assert.Equal (LayoutStyle.Computed, view.LayoutStyle);
 			view.LayoutStyle = LayoutStyle.Absolute;
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplay);
 			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0     
@@ -2607,7 +2607,7 @@ At 0,0
 			view.Height = 1;
 			Assert.Equal (new Rect (1, 1, 10, 1), view.Frame);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplay);
 			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0     
@@ -2641,7 +2641,7 @@ At 0,0
 			Assert.Equal (LayoutStyle.Computed, view.LayoutStyle);
 			view.LayoutStyle = LayoutStyle.Absolute;
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplay);
 			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
@@ -2678,7 +2678,7 @@ At 0,0
 			view.Height = 1;
 			Assert.Equal (new Rect (1, 1, 10, 1), view.Frame);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplay);
 			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
@@ -2713,7 +2713,7 @@ At 0,0
 			Assert.Equal (LayoutStyle.Computed, view.LayoutStyle);
 			view.LayoutStyle = LayoutStyle.Absolute;
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplay);
 			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0       
@@ -2750,7 +2750,7 @@ At 0,0
 			view.Height = 1;
 			Assert.Equal (new Rect (3, 3, 10, 1), view.Frame);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplay);
 			top.Redraw (top.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0       
@@ -2783,7 +2783,7 @@ At 0,0
 
 			view.Frame = new Rect (3, 3, 10, 1);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (0, 0, 10, 1), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplay);
 			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
@@ -2820,7 +2820,7 @@ At 0,0
 			view.Height = 1;
 			Assert.Equal (new Rect (3, 3, 10, 1), view.Frame);
 			Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds);
-			Assert.Equal (new Rect (0, 0, 30, 2), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplay);
 			view.Redraw (view.Bounds);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 At 0,0                       
@@ -2895,37 +2895,37 @@ At 0,0
 			Assert.Equal (new Rect (0, 0, 30, 1), label.Frame);
 			Assert.Equal (new Rect (0, 0, 13, 1), button.Frame);
 
-			Assert.Equal (new Rect (0, 0, 80, 25), top.NeedDisplay);
-			Assert.Equal (new Rect (0, 0, 40, 8), frame.NeedDisplay);
-			Assert.Equal (Rect.Empty, frame.Subviews [0].NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 80, 25), top._needsDisplay);
+			Assert.Equal (new Rect (0, 0, 40, 8), frame._needsDisplay);
+			Assert.Equal (Rect.Empty, frame.Subviews [0]._needsDisplay);
 			Assert.Equal (new Rect (0, 0, 40, 8), new Rect (
-				frame.NeedDisplay.Left, frame.NeedDisplay.Top,
-				frame.NeedDisplay.Right, frame.NeedDisplay.Bottom));
-			Assert.Equal (new Rect (0, 0, 30, 1), label.NeedDisplay);
-			Assert.Equal (new Rect (0, 0, 13, 1), button.NeedDisplay);
+				frame._needsDisplay.Left, frame._needsDisplay.Top,
+				frame._needsDisplay.Right, frame._needsDisplay.Bottom));
+			Assert.Equal (new Rect (0, 0, 30, 1), label._needsDisplay);
+			Assert.Equal (new Rect (0, 0, 13, 1), button._needsDisplay);
 
 			top.LayoutComplete += e => {
-				Assert.Equal (new Rect (0, 0, 80, 25), top.NeedDisplay);
+				Assert.Equal (new Rect (0, 0, 80, 25), top._needsDisplay);
 			};
 
 			frame.LayoutComplete += e => {
-				Assert.Equal (new Rect (0, 0, 40, 8), frame.NeedDisplay);
+				Assert.Equal (new Rect (0, 0, 40, 8), frame._needsDisplay);
 			};
 
 			frame.Subviews [0].LayoutComplete += e => {
 				if (top.IsLoaded) {
-					Assert.Equal (new Rect (0, 0, 38, 6), frame.Subviews [0].NeedDisplay);
+					Assert.Equal (new Rect (0, 0, 38, 6), frame.Subviews [0]._needsDisplay);
 				} else {
-					Assert.Equal (new Rect (0, 0, 38, 6), frame.Subviews [0].NeedDisplay);
+					Assert.Equal (new Rect (0, 0, 38, 6), frame.Subviews [0]._needsDisplay);
 				}
 			};
 
 			label.LayoutComplete += e => {
-				Assert.Equal (new Rect (0, 0, 38, 1), label.NeedDisplay);
+				Assert.Equal (new Rect (0, 0, 38, 1), label._needsDisplay);
 			};
 
 			button.LayoutComplete += e => {
-				Assert.Equal (new Rect (0, 0, 13, 1), button.NeedDisplay);
+				Assert.Equal (new Rect (0, 0, 13, 1), button._needsDisplay);
 			};
 
 			Application.Begin (top);

+ 5 - 5
UnitTests/TopLevels/ToplevelTests.cs

@@ -201,7 +201,7 @@ namespace Terminal.Gui.TopLevelTests {
 			Assert.Equal (top, Application.Top);
 
 			// top is Application.Top without menu and status bar.
-			var supView = top.EnsureVisibleBounds (top, 2, 2, out int nx, out int ny, out View mb, out View sb);
+			var supView = top.EnsureVisibleBounds (top, 2, 2, out int nx, out int ny, out MenuBar mb, out StatusBar sb);
 			Assert.Equal (Application.Top, supView);
 			Assert.Equal (0, nx);
 			Assert.Equal (0, ny);
@@ -1056,13 +1056,13 @@ namespace Terminal.Gui.TopLevelTests {
 			Assert.False (top.IsLoaded);
 			Assert.False (subTop.IsLoaded);
 			Assert.Equal (new Rect (0, 0, 20, 10), view.Frame);
-			Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 20, 10), view._needsDisplay);
 
 			view.LayoutStarted += view_LayoutStarted;
 
 			void view_LayoutStarted (View.LayoutEventArgs e)
 			{
-				Assert.Equal (new Rect (0, 0, 20, 10), view.NeedDisplay);
+				Assert.Equal (new Rect (0, 0, 20, 10), view._needsDisplay);
 				view.LayoutStarted -= view_LayoutStarted;
 			}
 
@@ -1074,12 +1074,12 @@ namespace Terminal.Gui.TopLevelTests {
 
 			view.Frame = new Rect (1, 3, 10, 5);
 			Assert.Equal (new Rect (1, 3, 10, 5), view.Frame);
-			Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 10, 5), view._needsDisplay);
 
 			view.Redraw (view.Bounds);
 			view.Frame = new Rect (1, 3, 10, 5);
 			Assert.Equal (new Rect (1, 3, 10, 5), view.Frame);
-			Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 10, 5), view._needsDisplay);
 		}
 	}
 }

+ 7 - 0
docfx/v2specs/View.md

@@ -51,6 +51,13 @@ This covers my thinking on how we will refactor `View` and the classes in the `V
   * *Window* - A View that, by default, has a `Border` and a `Title`. 
     * QUESTION: Why can't this just be a property on `View` (e.g. `View.Border = true`)? Why do we need a `Window` class at all in v2?
 
+
+## Focus
+
+* Focus is a concept that is used to describe which Responder is currently receiving user input.
+* QUESTION: Since `Frame`s are `Views` in v2, the `Frame` is a `Responder` that receives user input. This raises the quesiton of how a user can use the keyboard to navigate between `Frame`s and `View`s within a `Frame` (and the `Frame`'s `Parent`'s subviews).
+
+
 ### View classes to be nuked
 * PanelView (done)
 * FrameView (almost done)