Browse Source

Fixing many clipping issues (#580)

* almost got clip rect working

* fixes many bugs related to clipping incl #399

* Merge tweaks.
Charlie Kindel 5 years ago
parent
commit
1024f073b2

+ 67 - 30
Terminal.Gui/Core/ConsoleDriver.cs

@@ -4,10 +4,13 @@
 // Authors:
 //   Miguel de Icaza ([email protected])
 //
+// Define this to enable diagnostics drawing for Window Frames
+//#define DRAW_WINDOW_FRAME_DIAGNOSTICS
 using NStack;
 using System;
 using System.Runtime.CompilerServices;
 
+
 namespace Terminal.Gui {
 
 	/// <summary>
@@ -547,6 +550,17 @@ namespace Terminal.Gui {
 			}
 		}
 
+#if DRAW_WINDOW_FRAME_DIAGNOSTICS
+		const char leftChar = 'L';
+		const char rightChar = 'R';
+		const char topChar = 'T';
+		const char bottomChar = 'B';
+#else
+		const char leftChar = clearChar;
+		const char rightChar = clearChar;
+		const char topChar = clearChar;
+		const char bottomChar = clearChar;
+#endif
 		/// <summary>
 		/// Draws a frame for a window with padding aand n optional visible border inside the padding. 
 		/// </summary>
@@ -565,11 +579,22 @@ namespace Terminal.Gui {
 				AddRune (ch);
 			}
 
+			// fwidth is count of hLine chars
 			int fwidth = (int)(region.Width - (paddingRight + paddingLeft));
+
+			// fheight is count of vLine chars
 			int fheight = (int)(region.Height - (paddingBottom + paddingTop));
-			int fleft = region.X + paddingLeft;
+
+			// fleft is location of left frame line
+			int fleft = region.X + paddingLeft - 1;
+
+			// fright is location of right frame line
 			int fright = fleft + fwidth + 1;
-			int ftop = region.Y + paddingTop;
+
+			// ftop is location of top frame line
+			int ftop = region.Y + paddingTop - 1;
+
+			// fbottom is locaiton of bottom frame line
 			int fbottom = ftop + fheight + 1;
 
 			Rune hLine = border ? HLine : clearChar;
@@ -582,33 +607,33 @@ namespace Terminal.Gui {
 			// Outside top
 			if (paddingTop > 1) {
 				for (int r = region.Y; r < ftop; r++) {
-					for (int c = region.X; c <= fright + paddingRight; c++) {
-						AddRuneAt (c, r, clearChar);
+					for (int c = region.X; c < region.X + region.Width; c++) {
+						AddRuneAt (c, r, topChar);
 					}
 				}
 			}
 
 			// Outside top-left
-			for (int c = region.X; c <= fleft; c++) {
-				AddRuneAt (c, ftop, clearChar);
+			for (int c = region.X; c < fleft; c++) {
+				AddRuneAt (c, ftop, leftChar);
 			}
 
 			// Frame top-left corner
-			AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : clearChar);
+			AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : leftChar);
 
 			// Frame top
-			for (int c = fleft + 1; c <= fright; c++) {
-				AddRuneAt (c, ftop, paddingTop > 0 ? hLine : clearChar);
+			for (int c = fleft + 1; c < fleft + 1 + fwidth; c++) {
+				AddRuneAt (c, ftop, paddingTop > 0 ? hLine : topChar);
 			}
 
 			// Frame top-right corner
 			if (fright > fleft) {
-				AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : clearChar);
+				AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : rightChar);
 			}
 
 			// Outside top-right corner
 			for (int c = fright + 1; c < fright + paddingRight; c++) {
-				AddRuneAt (c, ftop, clearChar);
+				AddRuneAt (c, ftop, rightChar);
 			}
 
 			// Left, Fill, Right
@@ -616,11 +641,11 @@ namespace Terminal.Gui {
 				for (int r = ftop + 1; r < fbottom; r++) {
 					// Outside left
 					for (int c = region.X; c < fleft; c++) {
-						AddRuneAt (c, r, clearChar);
+						AddRuneAt (c, r, leftChar);
 					}
 
 					// Frame left
-					AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : clearChar);
+					AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : leftChar);
 
 					// Fill
 					if (fill) {
@@ -631,44 +656,54 @@ namespace Terminal.Gui {
 
 					// Frame right
 					if (fright > fleft) {
-						AddRuneAt (fright, r, paddingRight > 0 ? vLine : clearChar);
+#if DRAW_WINDOW_FRAME_DIAGNOSTICS
+						var v = (char)(((int)'0') + ((r - ftop) % 10)); // vLine;
+#else
+						var v = vLine;
+#endif
+						AddRuneAt (fright, r, paddingRight > 0 ? v : rightChar);
 					}
 
 					// Outside right
 					for (int c = fright + 1; c < fright + paddingRight; c++) {
-						AddRuneAt (c, r, clearChar);
+						AddRuneAt (c, r, rightChar);
 					}
 				}
 
 				// Outside Bottom
-				for (int c = region.X; c < fleft; c++) {
-					AddRuneAt (c, fbottom, clearChar);
+				for (int c = region.X; c < region.X + region.Width; c++) {
+					AddRuneAt (c, fbottom, leftChar);
 				}
 
 				// Frame bottom-left
-				AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : clearChar);
+				AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : leftChar);
 
 				if (fright > fleft) {
 					// Frame bottom
 					for (int c = fleft + 1; c < fright; c++) {
-						AddRuneAt (c, fbottom, paddingBottom > 0 ? hLine : clearChar);
+#if DRAW_WINDOW_FRAME_DIAGNOSTICS
+						var h = (char)(((int)'0') + ((c - fleft) % 10)); // hLine;
+#else
+						var h = hLine;
+#endif	
+						AddRuneAt (c, fbottom, paddingBottom > 0 ? h : bottomChar);
 					}
 
 					// Frame bottom-right
-					AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : clearChar);
+					AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : rightChar);
 				}
 
 				// Outside right
 				for (int c = fright + 1; c < fright + paddingRight; c++) {
-					AddRuneAt (c, fbottom, clearChar);
+					AddRuneAt (c, fbottom, rightChar);
 				}
 			}
 
 			// Out bottom - ensure top is always drawn if we overlap
 			if (paddingBottom > 0) {
 				for (int r = fbottom + 1; r < fbottom + paddingBottom; r++) {
-					for (int c = region.X; c <= fright + paddingRight; c++) {
-						AddRuneAt (c, r, clearChar);
+					for (int c = region.X; c < region.X + region.Width; c++) {
+						AddRuneAt (c, r, bottomChar);
 					}
 				}
 			}
@@ -677,17 +712,19 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Draws a frame on the specified region with the specified padding around the frame.
 		/// </summary>
-		/// <param name="region">Region where the frame will be drawn..</param>
+		/// <param name="region">Screen relative region where the frame will be drawn.</param>
 		/// <param name="padding">Padding to add on the sides.</param>
 		/// <param name="fill">If set to <c>true</c> it will clear the contents with the current color, otherwise the contents will be left untouched.</param>
-		/// <remarks>This is a legacy/depcrecated API. Use <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/>.</remarks>
-		/// <remarks>A padding value of 0 means there is actually a 1 cell border.</remarks>
+		/// <remarks>This API has been superceded by <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/>.</remarks>
+		/// <remarks>This API is equivlalent to calling <c>DrawWindowFrame(Rect, p - 1, p - 1, p - 1, p - 1)</c>. In other words,
+		/// A padding value of 0 means there is actually a one cell border.
+		/// </remarks>
 		public virtual void DrawFrame (Rect region, int padding, bool fill)
 		{
-			// DrawFrame assumes the frame is always at least one row/col thick
-			// DrawWindowFrame assumes a padding of 0 means NO padding
-			padding++;
-			DrawWindowFrame (new Rect (region.X - 1, region.Y - 1, region.Width, region.Height), padding, padding, padding, padding, fill: fill);
+			// DrawFrame assumes the border is always at least one row/col thick
+			// DrawWindowFrame assumes a padding of 0 means NO padding and no frame
+			DrawWindowFrame (new Rect (region.X, region.Y, region.Width, region.Height), 
+				padding + 1, padding + 1, padding + 1, padding + 1, fill: fill);
 		}
 
 

+ 11 - 4
Terminal.Gui/Core/View.cs

@@ -665,7 +665,7 @@ namespace Terminal.Gui {
 		{
 			var bscreen = RectToScreen (rect);
 			var previous = Driver.Clip;
-			Driver.Clip = ScreenClip (RectToScreen (Bounds));
+			Driver.Clip = ScreenClip (RectToScreen (Bounds)); 
 			return previous;
 		}
 
@@ -678,8 +678,7 @@ namespace Terminal.Gui {
 		public void DrawFrame (Rect rect, int padding = 0, bool fill = false)
 		{
 			var scrRect = RectToScreen (rect);
-			var savedClip = Driver.Clip;
-			Driver.Clip = ScreenClip (RectToScreen (Bounds));
+			var savedClip = ClipToBounds ();
 			Driver.DrawFrame (scrRect, padding, fill);
 			Driver.Clip = savedClip;
 		}
@@ -890,7 +889,15 @@ namespace Terminal.Gui {
 							if (view.layoutNeeded)
 								view.LayoutSubviews ();
 							Application.CurrentView = view;
-							view.Redraw (view.Bounds);
+
+							// Ensure we don't make the Driver's clip rect any bigger
+							if (Driver.Clip.IsEmpty || Driver.Clip.Contains(RectToScreen (view.Frame))) {
+								var savedClip = view.ClipToBounds ();
+								view.Redraw (view.Bounds);
+								Driver.Clip = savedClip;
+							} else {
+								view.Redraw (view.Bounds);
+							}
 						}
 						view.NeedDisplay = Rect.Empty;
 						view.childNeedsDisplay = false;

+ 12 - 5
Terminal.Gui/Core/Window.cs

@@ -40,6 +40,7 @@ namespace Terminal.Gui {
 						Driver.AddRune ('x');
 					}
 				}
+				base.Redraw (region);
 			}
 #endif
 		}
@@ -153,16 +154,23 @@ namespace Terminal.Gui {
 		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect bounds)
 		{
+			//var padding = 0;
 			Application.CurrentView = this;
-			var scrRect = RectToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
-			var savedClip = Driver.Clip;
-			Driver.Clip = ScreenClip (RectToScreen (Bounds));
+			var scrRect = RectToScreen (new Rect(0, 0, Frame.Width, Frame.Height));
 
+			// BUGBUG: Why do we draw the frame twice? This call is here to clear the content area, I think. Why not just clear that area?
 			if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
 				Driver.SetAttribute (ColorScheme.Normal);
 				Driver.DrawFrame (scrRect, padding, true);
 			}
-			contentView.Redraw (contentView.Bounds);
+
+			if (Driver.Clip.IsEmpty || Driver.Clip.Contains (contentView.RectToScreen (contentView.Frame))) { 
+				var savedClip = ClipToBounds ();
+				contentView.Redraw (contentView.Bounds);
+				Driver.Clip = savedClip;
+			} else {
+				contentView.Redraw (contentView.Bounds);
+			}
 			ClearNeedsDisplay ();
 			Driver.SetAttribute (ColorScheme.Normal);
 			Driver.DrawFrame (scrRect, padding, false);
@@ -170,7 +178,6 @@ namespace Terminal.Gui {
 			if (HasFocus)
 				Driver.SetAttribute (ColorScheme.HotNormal);
 			Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
-			Driver.Clip = savedClip;
 			Driver.SetAttribute (ColorScheme.Normal);
 		}
 

+ 31 - 30
Terminal.Gui/Views/Button.cs

@@ -39,6 +39,7 @@ namespace Terminal.Gui {
 			get => is_default;
 			set {
 				is_default = value;
+				SetWidthHeight (Text, is_default);
 				Update ();
 			}
 		}
@@ -65,20 +66,12 @@ namespace Terminal.Gui {
 		public Button (ustring text, bool is_default = false) : base ()
 		{
 			CanFocus = true;
+			Text = text ?? string.Empty;
 			this.IsDefault = is_default;
-			Text = text;
 			int w = SetWidthHeight (text, is_default);
 			Frame = new Rect (0, 0, w, 1);
 		}
 
-		int SetWidthHeight (ustring text, bool is_default)
-		{
-			int w = text.Length + 4 + (is_default ? 2 : 0);
-			Width = w;
-			Height = 1;
-			return w;
-		}
-
 		/// <summary>
 		///   Initializes a new instance of <see cref="Button"/> at the given coordinates, based on the given text
 		/// </summary>
@@ -91,6 +84,35 @@ namespace Terminal.Gui {
 		/// <param name="text">The button's text</param>
 		public Button (int x, int y, ustring text) : this (x, y, text, false) { }
 
+		/// <summary>
+		///   Initializes a new instance of <see cref="Button"/> at the given coordinates, based on the given text, and with the specified <see cref="IsDefault"/> value
+		/// </summary>
+		/// <remarks>
+		///   If the value for is_default is true, a special
+		///   decoration is used, and the enter key on a
+		///   dialog would implicitly activate this button.
+		/// </remarks>
+		/// <param name="x">X position where the button will be shown.</param>
+		/// <param name="y">Y position where the button will be shown.</param>
+		/// <param name="text">The button's text</param>
+		/// <param name="is_default">If set, this makes the button the default button in the current view, which means that if the user presses return on a view that does not handle return, it will be treated as if he had clicked on the button</param>
+		public Button (int x, int y, ustring text, bool is_default)
+		    : base (new Rect (x, y, text.Length + 4 + (is_default ? 2 : 0), 1))
+		{
+			CanFocus = true;
+			Text = text ?? string.Empty;
+			this.IsDefault = is_default;
+		}
+
+
+		int SetWidthHeight (ustring text, bool is_default)
+		{
+			int w = text.Length + 4 + (is_default ? 2 : 0);
+			Width = w;
+			Height = 1;
+			return w;
+		}
+
 		/// <summary>
 		///   The text displayed by this <see cref="Button"/>.
 		/// </summary>
@@ -129,27 +151,6 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 		}
 
-		/// <summary>
-		///   Initializes a new instance of <see cref="Button"/> at the given coordinates, based on the given text, and with the specified <see cref="IsDefault"/> value
-		/// </summary>
-		/// <remarks>
-		///   If the value for is_default is true, a special
-		///   decoration is used, and the enter key on a
-		///   dialog would implicitly activate this button.
-		/// </remarks>
-		/// <param name="x">X position where the button will be shown.</param>
-		/// <param name="y">Y position where the button will be shown.</param>
-		/// <param name="text">The button's text</param>
-		/// <param name="is_default">If set, this makes the button the default button in the current view, which means that if the user presses return on a view that does not handle return, it will be treated as if he had clicked on the button</param>
-		public Button (int x, int y, ustring text, bool is_default)
-		    : base (new Rect (x, y, text.Length + 4 + (is_default ? 2 : 0), 1))
-		{
-			CanFocus = true;
-
-			this.IsDefault = is_default;
-			Text = text;
-		}
-
 		///<inheritdoc cref="Redraw(Rect)"/>
 		public override void Redraw (Rect region)
 		{

+ 8 - 4
Terminal.Gui/Views/FrameView.cs

@@ -137,14 +137,19 @@ namespace Terminal.Gui {
 			var padding = 0;
 			Application.CurrentView = this;
 			var scrRect = RectToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
-			var savedClip = Driver.Clip;
-			Driver.Clip = ScreenClip (RectToScreen (Bounds));
 
 			if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
 				Driver.SetAttribute (ColorScheme.Normal);
 				Driver.DrawFrame (scrRect, padding, true);
 			}
-			contentView.Redraw (contentView.Bounds);
+
+			if (Driver.Clip.IsEmpty || Driver.Clip.Contains (contentView.RectToScreen (contentView.Frame))) {
+				var savedClip = ClipToBounds (); 
+				contentView.Redraw (contentView.Bounds);
+				Driver.Clip = savedClip;
+			} else {
+				contentView.Redraw (contentView.Bounds);
+			}
 			ClearNeedsDisplay ();
 			Driver.SetAttribute (ColorScheme.Normal);
 			Driver.DrawFrame (scrRect, padding, false);
@@ -152,7 +157,6 @@ namespace Terminal.Gui {
 			if (HasFocus)
 				Driver.SetAttribute (ColorScheme.HotNormal);
 			Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
-			Driver.Clip = savedClip;
 			Driver.SetAttribute (ColorScheme.Normal);
 		}
 	}

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

@@ -110,13 +110,13 @@ namespace Terminal.Gui {
 		///<inheritdoc cref="Redraw(Rect)"/>
 		public override void Redraw (Rect region)
 		{
-			base.Redraw (region);
 			for (int i = 0; i < radioLabels.Length; i++) {
 				Move (0, i);
 				Driver.SetAttribute (ColorScheme.Normal);
 				Driver.AddStr (i == selected ? "(o) " : "( ) ");
 				DrawHotString (radioLabels [i], HasFocus && i == cursor, ColorScheme);
 			}
+			base.Redraw (region);
 		}
 
 		///<inheritdoc cref="PositionCursor"/>

+ 6 - 3
Terminal.Gui/Views/ScrollView.cs

@@ -401,11 +401,14 @@ namespace Terminal.Gui {
 		public override void Redraw(Rect region)
 		{
 			SetViewsNeedsDisplay ();
-			var oldClip = ClipToBounds ();
 			Driver.SetAttribute (ColorScheme.Normal);
 			Clear ();
-			base.Redraw(region);
-			Driver.Clip = oldClip;
+
+			var savedClip = ClipToBounds ();
+			contentView.Redraw (contentView.Bounds);
+			vertical.Redraw (vertical.Bounds);
+			horizontal.Redraw (vertical.Bounds);
+			Driver.Clip = savedClip;
 			Driver.SetAttribute (ColorScheme.Normal);
 		}
 

+ 3 - 1
UICatalog/Scenarios/Buttons.cs

@@ -75,7 +75,9 @@ namespace UICatalog {
 
 			y += 2;
 			// BUGBUG: Buttons don't support specifying hotkeys with _?!?
-			Win.Add (button = new Button (10, y, "Te_xt Changer") {
+			Win.Add (button = new Button ("Te_xt Changer") {
+				X = 10, 
+				Y = y
 			});
 			button.Clicked = () => button.Text += $"{y++}";
 

+ 0 - 3
UICatalog/Scenarios/MessageBoxes.cs

@@ -7,7 +7,6 @@ namespace UICatalog {
 	[ScenarioMetadata (Name: "MessageBoxes", Description: "Demonstrates how to use MessageBoxes")]
 	[ScenarioCategory ("Controls")]
 	[ScenarioCategory ("Dialogs")]
-	[ScenarioCategory ("Bug Repro")]
 	class MessageBoxes : Scenario {
 		public override void Setup ()
 		{
@@ -110,7 +109,6 @@ namespace UICatalog {
 			var styleRadioGroup = new RadioGroup (new [] { "_Query", "_Error" } ) {
 				X = Pos.Right (label) + 1,
 				Y = Pos.Top (label),
-				Width = 5, // BUGBUG: This should cause clipping!
 			};
 			frame.Add (styleRadioGroup);
 
@@ -132,7 +130,6 @@ namespace UICatalog {
 				ColorScheme = Colors.Error,
 			};
 
-			// BUGBUG: Why is this button not centered???
 			var showMessageBoxButton = new Button ("Show MessageBox") {
 				X = Pos.Center(),
 				Y = Pos.Bottom (frame) + 2			,

+ 2 - 3
UICatalog/Scenarios/Progress.cs

@@ -41,7 +41,7 @@ namespace UICatalog {
 				LeftFrame = new FrameView ("Settings") {
 					X = 0,
 					Y = 0,
-					Height = Dim.Percent (100) + 1, // BUGBUG: This +1 should not be needed
+					Height = Dim.Percent (100), 
 					Width = Dim.Percent (25)
 				};
 				var lbl = new Label (1, 1, "Tick every (ms):");
@@ -223,14 +223,13 @@ namespace UICatalog {
 
 			var startBoth = new Button ("Start Both") {
 				X = Pos.Center (),
-				Y = Pos.AnchorEnd () - 1,
+				Y = Pos.Bottom(mainLoopTimeoutDemo) + 1,
 			};
 			startBoth.Clicked = () => {
 				systemTimerDemo.Start ();
 				mainLoopTimeoutDemo.Start ();
 			};
 			Win.Add (startBoth);
-
 		}
 
 		protected override void Dispose (bool disposing)

+ 103 - 0
UICatalog/Scenarios/Scrolling.cs

@@ -0,0 +1,103 @@
+using System;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Scrolling", Description: "Demonstrates ScrollView etc...")]
+	[ScenarioCategory ("Controls")]
+	[ScenarioCategory ("Bug Repro")]
+
+	class Scrolling : Scenario {
+		public override void Setup ()
+		{
+			var label = new Label ("ScrollView (new Rect (2, 2, 50, 20)) with a 200, 100 ContentSize...") {
+				X = 0, Y = 0,
+				ColorScheme = Colors.Dialog
+			};
+			Win.Add (label);
+
+			// BUGBUG: ScrollView only supports Absolute Positioning (#72)
+			var scrollView = new ScrollView (new Rect (2, 2, 50, 20));
+			scrollView.ColorScheme = Colors.TopLevel;
+			scrollView.ContentSize = new Size (200, 100);
+			//ContentOffset = new Point (0, 0),
+			scrollView.ShowVerticalScrollIndicator = true;
+			scrollView.ShowHorizontalScrollIndicator = true;
+
+			const string rule = "|123456789";
+			var horizontalRuler = new Label ("") {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (1),  // BUGBUG: I don't think this should be needed; DimFill() should respect container's frame. X does.
+				ColorScheme = Colors.Error
+			};
+			scrollView.Add (horizontalRuler);
+			const string vrule = "|\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
+
+			var verticalRuler = new Label ("") {
+				X = 0,
+				Y = 0,
+				Width = 1,
+				Height = Dim.Fill (),
+				ColorScheme = Colors.Error
+			};
+			scrollView.Add (verticalRuler);
+
+			Application.Resized += (sender, a) => {
+				horizontalRuler.Text = rule.Repeat ((int)Math.Ceiling ((double)(horizontalRuler.Bounds.Width) / (double)rule.Length)) [0..(horizontalRuler.Bounds.Width)];
+				verticalRuler.Text = vrule.Repeat ((int)Math.Ceiling ((double)(verticalRuler.Bounds.Height * 2) / (double)rule.Length)) [0..(verticalRuler.Bounds.Height * 2)];
+			};
+
+			scrollView.Add (new Button ("Press me!") {
+				X = 3,
+				Y = 3,
+				Clicked = () => MessageBox.Query (20, 7, "MessageBox", "Neat?", "Yes", "No")
+			});
+
+			scrollView.Add (new Button ("A very long button. Should be wide enough to demo clipping!") {
+				X = 3,
+				Y = 4,
+				Width = 50,
+				Clicked = () => MessageBox.Query (20, 7, "MessageBox", "Neat?", "Yes", "No")
+			});
+
+			scrollView.Add (new TextField ("This is a test of...") {
+				X = 3,
+				Y = 5,
+				Width = 50,
+				ColorScheme = Colors.Dialog
+			});
+
+			scrollView.Add (new TextField ("... the emergency broadcast sytem.") {
+				X = 3,
+				Y = 10,
+				Width = 50,
+				ColorScheme = Colors.Dialog
+			});
+
+			scrollView.Add (new TextField ("Last line") {
+				X = 3,
+				Y = 99,
+				Width = 50,
+				ColorScheme = Colors.Dialog
+			});
+
+			// Demonstrate AnchorEnd - Button is anchored to bottom/right
+			var anchorButton = new Button ("Bottom Right") {
+				Y = Pos.AnchorEnd () - 1,
+			};
+			// TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
+			anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
+			anchorButton.Clicked = () => {
+				// Ths demonstrates how to have a dynamically sized button
+				// Each time the button is clicked the button's text gets longer
+				// The call to Win.LayoutSubviews causes the Computed layout to
+				// get updated. 
+				anchorButton.Text += "!";
+				Win.LayoutSubviews ();
+			};
+			scrollView.Add (anchorButton);
+
+			Win.Add (scrollView);
+		}
+	}
+}

+ 31 - 11
UICatalog/Scenarios/WindowsAndFrameViews.cs

@@ -30,22 +30,42 @@ namespace UICatalog {
 
 		public override void Setup ()
 		{
+			static int About ()
+			{
+				//return MessageBox.Query (50, 10, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")
+
+				var about = new Window (new Rect (0, 0, 50, 10), "About UI catalog", 0) {
+					X = Pos.Center (),
+					Y = Pos.Center (),
+					Width = 50,
+					Height = 10,
+					LayoutStyle = LayoutStyle.Computed,
+					ColorScheme = Colors.Error,
+
+				};
+				//about.Add (new Label ("UI Catalog is a comprehensive sample library for Terminal.Gui"));
+
+				Application.Run (about);
+				return 0;
+
+			}
+
 			int margin = 2;
-			int padding = 0;
-			int height = 10;
+			int padding = 1;
+			int contentHeight = 7;
 			var listWin = new List<View> ();
 			Win = new Window ($"{listWin.Count} - Scenario: {GetName ()}", padding) {
 				X = Pos.Center (),
 				Y = 1,
 				Width = Dim.Fill (10),
-				Height = Dim.Percent (15),
+				Height = Dim.Percent (15)
 			};
 			Win.ColorScheme = Colors.Dialog;
-			Win.Add (new Button ("Press me! (Y = 0)") {
+			Win.Add (new Button ($"Padding of container is {padding}") {
 				X = Pos.Center (),
 				Y = 0,
 				ColorScheme = Colors.Error,
-				Clicked = () => MessageBox.ErrorQuery (30, 10, Win.Title.ToString (), "Neat?", "Yes", "No")
+				Clicked = () => About()
 			});
 			Win.Add (new Button ("Press ME! (Y = Pos.AnchorEnd(1))") {
 				X = Pos.Center (),
@@ -55,13 +75,13 @@ namespace UICatalog {
 			Top.Add (Win);
 			listWin.Add (Win);
 
-			for (var i = 0; i < 2; i++) {
+			for (var i = 0; i < 3; i++) {
 				Window win = null;
-				win = new Window ($"{listWin.Count} - Loop {i}", padding) {
+				win = new Window ($"{listWin.Count} - Window Loop - padding = {i}", i) {
 					X = margin,
 					Y = Pos.Bottom (listWin.Last ()) + (margin),
 					Width = Dim.Fill (margin),
-					Height = height,
+					Height = contentHeight + (i*2) + 2,
 				};
 				win.ColorScheme = Colors.Dialog;
 				win.Add (new Button ("Press me! (Y = 0)") {
@@ -101,14 +121,14 @@ namespace UICatalog {
 				X = margin,
 				Y = Pos.Bottom (listWin.Last ()) + (margin / 2),
 				Width = Dim.Fill (margin),
-				Height = height,
+				Height = contentHeight + 2,  // 2 for default padding
 			};
 			frame.ColorScheme = Colors.Dialog;
-			frame.Add (new Button ("Press me! (Y = 0)") {
+			frame.Add (new Label ("This is a Label! (Y = 0)") {
 				X = Pos.Center (),
 				Y = 0,
 				ColorScheme = Colors.Error,
-				Clicked = () => MessageBox.ErrorQuery (30, 10, frame.Title.ToString (), "Neat?", "Yes", "No")
+				//Clicked = () => MessageBox.ErrorQuery (30, 10, frame.Title.ToString (), "Neat?", "Yes", "No")
 			});
 			var subWinofFV = new Window ("this is a Sub-Window") {
 				X = Pos.Percent (0),

+ 3 - 1
UICatalog/UICatalog.cs

@@ -92,7 +92,7 @@ namespace UICatalog {
 				new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("_Quit", "", () => Application.RequestStop() )
 				}),
-				new MenuBarItem ("_About...", "About this app", () =>  MessageBox.Query (0, 10, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")),
+				new MenuBarItem ("_About...", "About this app", () =>  MessageBox.Query (50, 10, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")),
 			});
 
 			_leftPane = new Window ("Categories") {
@@ -163,6 +163,8 @@ namespace UICatalog {
 					}
 				}),
 			});
+
+
 		}
 
 		/// <summary>