Răsfoiți Sursa

Charlie's Mondo Patch (#600)

This PR includes:

#586 - Fixed Clipping
#587 - LayoutComplete
#591 - Sys Console Scenario
#590 - Significantly improves MessageBox, Dialog, Frame drawning and more
See the PRs above for all the details.

Here are the issues this closes:

Closes #299 - MessageBox now auto sizes
Closes #557 - MessageBoxes on small screens
Closes #432 - MessageBox does not deal with long text; width/height params are goofy
Closes #521 - MessageBox should take ustrings (BREAKING CHANGE)
Closes #35 - Dialog should have 1 char padding around edges
Closes #570 - Dialog should use computed layout for buttons
Closes #470 - UI Catalog: Add Dialogs Scenario
Closes #569 - LayoutComplete event
Plus probably more.
Charlie Kindel 5 ani în urmă
părinte
comite
fddfcf8802
36 a modificat fișierele cu 1209 adăugiri și 511 ștergeri
  1. 4 1
      .gitignore
  2. 4 4
      Example/demo.cs
  3. 3 2
      Terminal.Gui/Core/Application.cs
  4. 2 2
      Terminal.Gui/Core/ConsoleDriver.cs
  5. 23 22
      Terminal.Gui/Core/Toplevel.cs
  6. 209 130
      Terminal.Gui/Core/View.cs
  7. 34 34
      Terminal.Gui/Core/Window.cs
  8. 137 75
      Terminal.Gui/Terminal.Gui.csproj
  9. 39 23
      Terminal.Gui/Views/Button.cs
  10. 1 1
      Terminal.Gui/Views/Checkbox.cs
  11. 1 1
      Terminal.Gui/Views/ComboBox.cs
  12. 7 10
      Terminal.Gui/Views/FrameView.cs
  13. 2 2
      Terminal.Gui/Views/HexView.cs
  14. 4 2
      Terminal.Gui/Views/Label.cs
  15. 1 1
      Terminal.Gui/Views/ListView.cs
  16. 3 3
      Terminal.Gui/Views/Menu.cs
  17. 2 2
      Terminal.Gui/Views/RadioGroup.cs
  18. 2 1
      Terminal.Gui/Views/ScrollView.cs
  19. 3 3
      Terminal.Gui/Views/StatusBar.cs
  20. 1 1
      Terminal.Gui/Views/TextField.cs
  21. 9 9
      Terminal.Gui/Views/TextView.cs
  22. 54 23
      Terminal.Gui/Windows/Dialog.cs
  23. 8 5
      Terminal.Gui/Windows/FileDialog.cs
  24. 96 19
      Terminal.Gui/Windows/MessageBox.cs
  25. 69 23
      UICatalog/Scenarios/Buttons.cs
  26. 79 0
      UICatalog/Scenarios/Clipping.cs
  27. 1 1
      UICatalog/Scenarios/ComputedLayout.cs
  28. 171 0
      UICatalog/Scenarios/Dialogs.cs
  29. 4 4
      UICatalog/Scenarios/Editor.cs
  30. 4 4
      UICatalog/Scenarios/HexEditor.cs
  31. 27 11
      UICatalog/Scenarios/MessageBoxes.cs
  32. 122 5
      UICatalog/Scenarios/Scrolling.cs
  33. 1 0
      UICatalog/Scenarios/SystemConsole.cs
  34. 13 14
      UICatalog/Scenarios/WindowsAndFrameViews.cs
  35. 68 73
      UICatalog/UICatalog.cs
  36. 1 0
      UICatalog/UICatalog.csproj

+ 4 - 1
.gitignore

@@ -8,4 +8,7 @@ packages
 # User-specific files
 *.user
 
-docfx/api
+docfx/api
+
+#git merge files
+*.orig

+ 4 - 4
Example/demo.cs

@@ -30,7 +30,7 @@ static class Demo {
 			throw new NotImplementedException ();
 		}
 
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			//Point pos = new Point (region.X, region.Y);
 			Driver.SetAttribute (ColorScheme.Focus);
@@ -53,7 +53,7 @@ static class Demo {
 		{
 		}
 
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			Driver.SetAttribute (ColorScheme.Focus);
 			var f = Frame;
@@ -520,7 +520,7 @@ static class Demo {
 
 		//Application.UseSystemConsole = true;
 
-		Application.Init ();
+		Application.Init();
 
 		var top = Application.Top;
 
@@ -638,7 +638,7 @@ static class Demo {
 		var bottom2 = new Label ("This should go on the bottom of another top-level!");
 		top.Add (bottom2);
 
-		Application.Loaded += (sender, e) => {
+		top.LayoutComplete += (sender, e) => {
 			bottom.X = win.X;
 			bottom.Y = Pos.Bottom (win) - Pos.Top (win) - margin;
 			bottom2.X = Pos.Left (win);

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

@@ -434,7 +434,7 @@ namespace Terminal.Gui {
 			Current = toplevel;
 			Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessKeyDownEvent, ProcessKeyUpEvent, ProcessMouseEvent);
 			if (toplevel.LayoutStyle == LayoutStyle.Computed)
-				toplevel.RelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
+				toplevel.SetRelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
 			toplevel.LayoutSubviews ();
 			Loaded?.Invoke (null, new ResizedEventArgs () { Rows = Driver.Rows, Cols = Driver.Cols });
 			toplevel.WillPresent ();
@@ -481,6 +481,7 @@ namespace Terminal.Gui {
 			if (closeDriver) {
 				MainLoop = null;
 				Driver.End ();
+				Driver = null;
 			}
 
 			_initialized = false;
@@ -670,7 +671,7 @@ namespace Terminal.Gui {
 			Driver.Clip = full;
 			foreach (var t in toplevels) {
 				t.PositionToplevels ();
-				t.RelativeLayout (full);
+				t.SetRelativeLayout (full);
 				t.LayoutSubviews ();
 			}
 			Refresh ();

+ 2 - 2
Terminal.Gui/Core/ConsoleDriver.cs

@@ -562,7 +562,7 @@ namespace Terminal.Gui {
 		const char bottomChar = clearChar;
 #endif
 		/// <summary>
-		/// Draws a frame for a window with padding aand n optional visible border inside the padding. 
+		/// Draws a frame for a window with padding and an optional visible border inside the padding. 
 		/// </summary>
 		/// <param name="region">Screen relative region where the frame will be drawn.</param>
 		/// <param name="paddingLeft">Number of columns to pad on the left (if 0 the border will not appear on the left).</param>
@@ -724,7 +724,7 @@ namespace Terminal.Gui {
 			// 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);
+				padding + 1, padding + 1, padding + 1, padding + 1, border: false, fill: fill);
 		}
 
 

+ 23 - 22
Terminal.Gui/Core/Toplevel.cs

@@ -13,19 +13,18 @@ namespace Terminal.Gui {
 	/// </summary>
 	/// <remarks>
 	///   <para>
-	///     Toplevels can be modally executing views, and they return control
-	///     to the caller when the "Running" property is set to false, or
-	///     by calling <see cref="M:Terminal.Gui.Application.RequestStop()"/>
+	///     Toplevels can be modally executing views, started by calling <see cref="Application.Run(Toplevel, bool)"/>. 
+	///     They return control to the caller when <see cref="Application.RequestStop()"/> has 
+	///     been called (which sets the <see cref="Toplevel.Running"/> property to false). 
 	///   </para>
 	///   <para>
-	///     There will be a toplevel created for you on the first time use
-	///     and can be accessed from the property <see cref="P:Terminal.Gui.Application.Top"/>,
-	///     but new toplevels can be created and ran on top of it.   To run, create the
-	///     toplevel and then invoke <see cref="M:Terminal.Gui.Application.Run"/> with the
-	///     new toplevel.
+	///     A Toplevel is created when an application initialzies Terminal.Gui by callling <see cref="Application.Init()"/>.
+	///     The application Toplevel can be accessed via <see cref="Application.Top"/>. Additional Toplevels can be created 
+	///     and run (e.g. <see cref="Dialog"/>s. To run a Toplevel, create the <see cref="Toplevel"/> and 
+	///     call <see cref="Application.Run(Toplevel, bool)"/>.
 	///   </para>
 	///   <para>
-	///     TopLevels can also opt-in to more sophisticated initialization
+	///     Toplevels can also opt-in to more sophisticated initialization
 	///     by implementing <see cref="ISupportInitialize"/>. When they do
 	///     so, the <see cref="ISupportInitialize.BeginInit"/> and
 	///     <see cref="ISupportInitialize.EndInit"/> methods will be called
@@ -33,27 +32,29 @@ namespace Terminal.Gui {
 	///     If first-run-only initialization is preferred, the <see cref="ISupportInitializeNotification"/>
 	///     can be implemented too, in which case the <see cref="ISupportInitialize"/>
 	///     methods will only be called if <see cref="ISupportInitializeNotification.IsInitialized"/>
-	///     is <see langword="false"/>. This allows proper View inheritance hierarchies
+	///     is <see langword="false"/>. This allows proper <see cref="View"/> inheritance hierarchies
 	///     to override base class layout code optimally by doing so only on first run,
 	///     instead of on every run.
 	///   </para>
 	/// </remarks>
 	public class Toplevel : View {
 		/// <summary>
-		/// Gets or sets whether the Mainloop for this <see cref="Toplevel"/> is running or not. Setting
-		/// this property to false will cause the MainLoop to exit. 
+		/// Gets or sets whether the <see cref="MainLoop"/> for this <see cref="Toplevel"/> is running or not. 
 		/// </summary>
+		/// <remarks>
+		///    Setting this property directly is discouraged. Use <see cref="Application.RequestStop"/> instead. 
+		/// </remarks>
 		public bool Running { get; set; }
 
 		/// <summary>
-		/// Fired once the Toplevel's MainLoop has started it's first iteration. 
+		/// Fired once the Toplevel's <see cref="MainLoop"/> has started it's first iteration. 
 		/// Subscribe to this event to perform tasks when the <see cref="Toplevel"/> has been laid out and focus has been set.
 		/// changes. A Ready event handler is a good place to finalize initialization after calling `<see cref="Application.Run()"/>(topLevel)`. 
 		/// </summary>
 		public event EventHandler Ready;
 
 		/// <summary>
-		/// Called from Application.RunLoop after the <see cref="Toplevel"/> has entered it's first iteration of the loop. 
+		/// Called from <see cref="Application.RunLoop"/> after the <see cref="Toplevel"/> has entered it's first iteration of the loop. 
 		/// </summary>
 		internal virtual void OnReady ()
 		{
@@ -70,7 +71,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="Toplevel"/> class with Computed layout, defaulting to <see langword="async"/> full screen.
+		/// Initializes a new instance of the <see cref="Toplevel"/> class with <see cref="LayoutStyle.Computed"/> layout, defaulting to full screen.
 		/// </summary>
 		public Toplevel () : base ()
 		{
@@ -85,7 +86,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Convenience factory method that creates a new toplevel with the current terminal dimensions.
+		/// Convenience factory method that creates a new Toplevel with the current terminal dimensions.
 		/// </summary>
 		/// <returns>The create.</returns>
 		public static Toplevel Create ()
@@ -109,12 +110,12 @@ namespace Terminal.Gui {
 		public bool Modal { get; set; }
 
 		/// <summary>
-		/// Check id current toplevel has menu bar
+		/// Gets or sets the menu for this Toplevel
 		/// </summary>
 		public MenuBar MenuBar { get; set; }
 
 		/// <summary>
-		/// Check id current toplevel has status bar
+		/// Gets or sets the status bar for this Toplevel
 		/// </summary>
 		public StatusBar StatusBar { get; set; }
 
@@ -256,18 +257,18 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			Application.CurrentView = this;
 
 			if (IsCurrentTop || this == Application.Top) {
 				if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
 					Driver.SetAttribute (Colors.TopLevel.Normal);
-					Clear (region);
+					Clear (Frame);
 					Driver.SetAttribute (Colors.Base.Normal);
 				}
 				foreach (var view in Subviews) {
-					if (view.Frame.IntersectsWith (region)) {
+					if (view.Frame.IntersectsWith (bounds)) {
 						view.SetNeedsLayout ();
 						view.SetNeedsDisplay (view.Bounds);
 					}
@@ -280,7 +281,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// This method is invoked by Application.Begin as part of the Application.Run after
+		/// Invoked by <see cref="Application.Begin"/> as part of the <see cref="Application.Run(Toplevel, bool)"/> after
 		/// the views have been laid out, and before the views are drawn for the first time.
 		/// </summary>
 		public virtual void WillPresent ()

+ 209 - 130
Terminal.Gui/Core/View.cs

@@ -20,7 +20,7 @@ namespace Terminal.Gui {
 
 	/// <summary>
 	/// Determines the LayoutStyle for a view, if Absolute, during LayoutSubviews, the
-	/// value from the Frame will be used, if the value is Computer, then the Frame
+	/// value from the Frame will be used, if the value is Computed, then the Frame
 	/// will be updated from the X, Y Pos objects and the Width and Height Dim objects.
 	/// </summary>
 	public enum LayoutStyle {
@@ -41,68 +41,73 @@ namespace Terminal.Gui {
 	/// </summary>
 	/// <remarks>
 	/// <para>
-	///    The View defines the base functionality for user interface elements in Terminal/gui.cs.  Views
+	///    The View defines the base functionality for user interface elements in Terminal.Gui.  Views
 	///    can contain one or more subviews, can respond to user input and render themselves on the screen.
 	/// </para>
 	/// <para>
-	///    Views can either be created with an absolute position, by calling the constructor that takes a
-	///    Rect parameter to specify the absolute position and size (the Frame of the View) or by setting the
-	///    X, Y, Width and Height properties on the view.    Both approaches use coordinates that are relative
-	///    to the container they are being added to.
+	///    Views supports two layout styles: Absolute or Computed. The choice as to which layout style is used by the View 
+	///    is determined when the View is initizlied. To create a View using Absolute layout, call a constructor that takes a
+	///    Rect parameter to specify the absolute position and size (the <c>View.<see cref="Frame "/></c>)/. To create a View 
+	///    using Computed layout use a constructor that does not take a Rect parametr and set the X, Y, Width and Height 
+	///    properties on the view. Both approaches use coordinates that are relative to the container they are being added to. 
 	/// </para>
 	/// <para>
-	///    When you do not specify a Rect frame you can use the more flexible
-	///    Dim and Pos objects that can dynamically update the position of a view.
+	///    To switch between Absolute and Computed layout, use the <see cref="LayoutStyle"/> property. 
+	/// </para>
+	/// <para>
+	///    Computed layout is more flexible and supports dynamic console apps where controls adjust layout
+	///    as the terminal resizes or other Views change size or position. The X, Y, Width and Height 
+	///    properties are Dim and Pos objects that dynamically update the position of a view.
 	///    The X and Y properties are of type <see cref="Pos"/>
 	///    and you can use either absolute positions, percentages or anchor
 	///    points.   The Width and Height properties are of type
 	///    <see cref="Dim"/> and can use absolute position,
 	///    percentages and anchors.  These are useful as they will take
-	///    care of repositioning your views if your view's frames are resized
-	///    or if the terminal size changes.
+	///    care of repositioning views when view's frames are resized or
+	///    if the terminal size changes.
 	/// </para>
 	/// <para>
-	///    When you specify the Rect parameter to a view, you are setting the LayoutStyle to Absolute, and the
-	///    view will always stay in the position that you placed it.   To change the position change the
-	///    Frame property to the new position.
+	///    Absolute layout requires specifying coordiantes and sizes of Views explicitly, and the
+	///    View will typcialy stay in a fixed position and size. To change the position and size use the
+	///    <see cref="Frame"/> property.
 	/// </para>
 	/// <para>
-	///    Subviews can be added to a View by calling the Add method.   The container of a view is the
-	///    Superview.
+	///    Subviews (child views) can be added to a View by calling the <see cref="Add(View)"/> method.   
+	///    The container of a View can be accessed with the <see cref="SuperView"/> property.
 	/// </para>
 	/// <para>
-	///    Developers can call the SetNeedsDisplay method on the view to flag a region or the entire view
+	///    The <see cref="SetNeedsDisplay(Rect)"/> method flags a region or the entire view
 	///    as requiring to be redrawn.
 	/// </para>
 	/// <para>
-	///    Views have a ColorScheme property that defines the default colors that subviews
+	///    Views have a <see cref="ColorScheme"/> property that defines the default colors that subviews
 	///    should use for rendering.   This ensures that the views fit in the context where
 	///    they are being used, and allows for themes to be plugged in.   For example, the
 	///    default colors for windows and toplevels uses a blue background, while it uses
 	///    a white background for dialog boxes and a red background for errors.
 	/// </para>
 	/// <para>
-	///    If a ColorScheme is not set on a view, the result of the ColorScheme is the
-	///    value of the SuperView and the value might only be valid once a view has been
-	///    added to a SuperView, so your subclasses should not rely on ColorScheme being
-	///    set at construction time.
+	///    Subclasses should not rely on <see cref="ColorScheme"/> being
+	///    set at construction time. If a <see cref="ColorScheme"/> is not set on a view, the view will inherit the
+	///    value from its <see cref="SuperView"/> and the value might only be valid once a view has been
+	///    added to a SuperView. 
 	/// </para>
 	/// <para>
-	///    Using ColorSchemes has the advantage that your application will work both
+	///    By using  <see cref="ColorScheme"/> applications will work both
 	///    in color as well as black and white displays.
 	/// </para>
 	/// <para>
-	///    Views that are focusable should implement the PositionCursor to make sure that
-	///    the cursor is placed in a location that makes sense.   Unix terminals do not have
+	///    Views that are focusable should implement the <see cref="PositionCursor"/> to make sure that
+	///    the cursor is placed in a location that makes sense.  Unix terminals do not have
 	///    a way of hiding the cursor, so it can be distracting to have the cursor left at
 	///    the last focused view.   So views should make sure that they place the cursor
 	///    in a visually sensible place.
 	/// </para>
 	/// <para>
-	///    The metnod LayoutSubviews is invoked when the size or layout of a view has
+	///    The <see cref="LayoutSubviews"/> method is invoked when the size or layout of a view has
 	///    changed.   The default processing system will keep the size and dimensions
-	///    for views that use the LayoutKind.Absolute, and will recompute the
-	///    frames for the vies that use LayoutKind.Computed.
+	///    for views that use the <see cref="LayoutStyle.Absolute"/>, and will recompute the
+	///    frames for the vies that use <see cref="LayoutStyle.Computed"/>.
 	/// </para>
 	/// </remarks>
 	public class View : Responder, IEnumerable {
@@ -111,17 +116,18 @@ namespace Terminal.Gui {
 			Backward
 		}
 
+		// container == SuperView
 		View container = null;
 		View focused = null;
 		Direction focusDirection;
 
 		/// <summary>
-		/// Event fired when the view get focus.
+		/// Event fired when the view gets focus.
 		/// </summary>
 		public event EventHandler<FocusEventArgs> Enter;
 
 		/// <summary>
-		/// Event fired when the view lost focus.
+		/// Event fired when the view looses focus.
 		/// </summary>
 		public event EventHandler<FocusEventArgs> Leave;
 
@@ -131,7 +137,7 @@ namespace Terminal.Gui {
 		public event EventHandler<MouseEventEventArgs> MouseEnter;
 
 		/// <summary>
-		/// Event fired when the view loses mouse event for the last time.
+		/// Event fired when the view receives a mouse event for the last time.
 		/// </summary>
 		public event EventHandler<MouseEventEventArgs> MouseLeave;
 
@@ -173,13 +179,14 @@ namespace Terminal.Gui {
 
 		internal Rect NeedDisplay { get; private set; } = Rect.Empty;
 
-		// The frame for the object
+		// The frame for the object. Superview relative.
 		Rect frame;
 
 		/// <summary>
 		/// Gets or sets an identifier for the view;
 		/// </summary>
 		/// <value>The identifier.</value>
+		/// <remarks>The id should be unique across all Views that share a SuperView.</remarks>
 		public ustring Id { get; set; } = "";
 
 		/// <summary>
@@ -192,7 +199,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Gets or sets a value indicating whether this <see cref="View"/> want mouse position reports.
+		/// Gets or sets a value indicating whether this <see cref="View"/> wants mouse position reports.
 		/// </summary>
 		/// <value><c>true</c> if want mouse position reports; otherwise, <c>false</c>.</value>
 		public virtual bool WantMousePositionReports { get; set; } = false;
@@ -201,13 +208,19 @@ namespace Terminal.Gui {
 		/// Gets or sets a value indicating whether this <see cref="View"/> want continuous button pressed event.
 		/// </summary>
 		public virtual bool WantContinuousButtonPressed { get; set; } = false;
+
 		/// <summary>
-		/// Gets or sets the frame for the view.
+		/// Gets or sets the frame for the view. The frame is relative to the <see cref="SuperView"/>.
 		/// </summary>
 		/// <value>The frame.</value>
 		/// <remarks>
+		/// <para>
+		///    Change the Frame when using the <see cref="LayoutStyle.Absolute"/> layout style to move or resize views. 
+		/// </para>
+		/// <para>
 		///    Altering the Frame of a view will trigger the redrawing of the
-		///    view as well as the redrawing of the affected regions in the superview.
+		///    view as well as the redrawing of the affected regions in the <see cref="SuperView"/>.
+		/// </para>
 		/// </remarks>
 		public virtual Rect Frame {
 			get => frame;
@@ -236,9 +249,9 @@ namespace Terminal.Gui {
 		LayoutStyle layoutStyle;
 
 		/// <summary>
-		/// Controls how the view's Frame is computed during the LayoutSubviews method, if Absolute, then
-		/// LayoutSubviews does not change the Frame properties, otherwise the Frame is updated from the
-		/// values in X, Y, Width and Height properties.
+		/// Controls how the View's <see cref="Frame"/> is computed during the LayoutSubviews method, if the style is set to <see cref="LayoutStyle.Absolute"/>, 
+		/// LayoutSubviews does not change the <see cref="Frame"/>. If the style is <see cref="LayoutStyle.Computed"/> the <see cref="Frame"/> is updated using
+		/// the <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> properties.
 		/// </summary>
 		/// <value>The layout style.</value>
 		public LayoutStyle LayoutStyle {
@@ -250,7 +263,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// The bounds represent the View-relative rectangle used for this view.   Updates to the Bounds update the Frame, and has the same side effects as updating the frame.
+		/// The bounds represent the View-relative rectangle used for this view. Updates to the Bounds update the <see cref="Frame"/>, and has the same side effects as updating the <see cref="Frame"/>.
 		/// </summary>
 		/// <value>The bounds.</value>
 		public Rect Bounds {
@@ -262,10 +275,12 @@ namespace Terminal.Gui {
 
 		Pos x, y;
 		/// <summary>
-		/// Gets or sets the X position for the view (the column).  This is only used when the LayoutStyle is Computed, if the
-		/// LayoutStyle is set to Absolute, this value is ignored.
+		/// Gets or sets the X position for the view (the column). Only used whe <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
 		/// </summary>
 		/// <value>The X Position.</value>
+		/// <remarks>
+		/// If <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
+		/// </remarks>
 		public Pos X {
 			get => x;
 			set {
@@ -275,10 +290,12 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Gets or sets the Y position for the view (line).  This is only used when the LayoutStyle is Computed, if the
-		/// LayoutStyle is set to Absolute, this value is ignored.
+		/// Gets or sets the Y position for the view (the row). Only used whe <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
 		/// </summary>
 		/// <value>The y position (line).</value>
+		/// <remarks>
+		/// If <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
+		/// </remarks>
 		public Pos Y {
 			get => y;
 			set {
@@ -290,10 +307,12 @@ namespace Terminal.Gui {
 		Dim width, height;
 
 		/// <summary>
-		/// Gets or sets the width for the view. This is only used when the LayoutStyle is Computed, if the
-		/// LayoutStyle is set to Absolute, this value is ignored.
+		/// Gets or sets the width of the view. Only used whe <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
 		/// </summary>
 		/// <value>The width.</value>
+		/// <remarks>
+		/// If <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
+		/// </remarks>
 		public Dim Width {
 			get => width;
 			set {
@@ -303,10 +322,10 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Gets or sets the height for the view. This is only used when the LayoutStyle is Computed, if the
-		/// LayoutStyle is set to Absolute, this value is ignored.
+		/// Gets or sets the height of the view. Only used whe <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
 		/// </summary>
 		/// <value>The height.</value>
+		/// If <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
 		public Dim Height {
 			get => height;
 			set {
@@ -322,11 +341,14 @@ namespace Terminal.Gui {
 		public View SuperView => container;
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="View"/> class with the absolute
-		/// dimensions specified in the frame.   If you want to have Views that can be positioned with
-		/// Pos and Dim properties on X, Y, Width and Height, use the empty constructor.
+		/// Initializes a new instance of a <see cref="LayoutStyle.Absolute"/> <see cref="View"/> class with the absolute
+		/// dimensions specified in the <c>frame</c> parameter. 
 		/// </summary>
 		/// <param name="frame">The region covered by this view.</param>
+		/// <remarks>
+		/// This constructor intitalize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use <see cref="View()"/> to 
+		/// initialize a View with  <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/> 
+		/// </remarks>
 		public View (Rect frame)
 		{
 			this.Frame = frame;
@@ -335,10 +357,15 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="View"/> class and sets the
-		/// view up for Computed layout, which will use the values in X, Y, Width and Height to
-		/// compute the View's Frame.
+		/// Initializes a new instance of <see cref="LayoutStyle.Computed"/> <see cref="View"/> class.
 		/// </summary>
+		/// <remarks>
+		///   Use <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> properties to dynamically control the size and location of the view.
+		/// </remarks>
+		/// <remarks>
+		///   This constructor intitalize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
+		///   Use <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> properties to dynamically control the size and location of the view.
+		/// </remarks>
 		public View ()
 		{
 			CanFocus = false;
@@ -346,8 +373,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Invoke to flag that this view needs to be redisplayed, by any code
-		/// that alters the state of the view.
+		/// Sets a flag indicating this view needs to be redisplayed because its state has changed.
 		/// </summary>
 		public void SetNeedsDisplay ()
 		{
@@ -367,9 +393,9 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Flags the specified rectangle region on this view as needing to be repainted.
+		/// Flags the view-relative region on this View as needing to be repainted.
 		/// </summary>
-		/// <param name="region">The region that must be flagged for repaint.</param>
+		/// <param name="region">The view-relative region that must be flagged for repaint.</param>
 		public void SetNeedsDisplay (Rect region)
 		{
 			if (NeedDisplay == null || NeedDisplay.IsEmpty)
@@ -397,7 +423,7 @@ namespace Terminal.Gui {
 		internal bool childNeedsDisplay;
 
 		/// <summary>
-		/// Flags this view for requiring the children views to be repainted.
+		/// Indicates that any child views (in the <see cref="Subviews"/> list) need to be repainted.
 		/// </summary>
 		public void ChildNeedsDisplay ()
 		{
@@ -407,9 +433,10 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Adds a subview to this view.
+		///   Adds a subview (child) to this view.
 		/// </summary>
 		/// <remarks>
+		/// The Views that have been added to this view can be retrieved via the <see cref="Subviews"/> property. See also <seealso cref="Remove(View)"/> <seealso cref="RemoveAll"/> 
 		/// </remarks>
 		public virtual void Add (View view)
 		{
@@ -426,9 +453,12 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Adds the specified views to the view.
+		/// Adds the specified views (children) to the view.
 		/// </summary>
 		/// <param name="views">Array of one or more views (can be optional parameter).</param>
+		/// <remarks>
+		/// The Views that have been added to this view can be retrieved via the <see cref="Subviews"/> property. See also <seealso cref="Remove(View)"/> <seealso cref="RemoveAll"/> 
+		/// </remarks>
 		public void Add (params View [] views)
 		{
 			if (views == null)
@@ -438,10 +468,8 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Removes all the widgets from this container.
+		///   Removes all subviews (children) added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
 		/// </summary>
-		/// <remarks>
-		/// </remarks>
 		public virtual void RemoveAll ()
 		{
 			if (subviews == null)
@@ -453,7 +481,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Removes a widget from this container.
+		///   Removes a subview added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
 		/// </summary>
 		/// <remarks>
 		/// </remarks>
@@ -573,27 +601,30 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Clears the specified rectangular region with the current color
+		///   Clears the specified region with the current color. 
 		/// </summary>
-		public void Clear (Rect r)
+		/// <remarks>
+		/// </remarks>
+		/// <param name="regionScreen">The screen-relative region to clear.</param>
+		public void Clear (Rect regionScreen)
 		{
-			var h = r.Height;
-			var w = r.Width;
-			for (int line = r.Y; line < r.Y + h; line++) {
-				Driver.Move (r.X, line);
+			var h = regionScreen.Height;
+			var w = regionScreen.Width;
+			for (int line = regionScreen.Y; line < regionScreen.Y + h; line++) {
+				Driver.Move (regionScreen.X, line);
 				for (int col = 0; col < w; col++)
 					Driver.AddRune (' ');
 			}
 		}
 
 		/// <summary>
-		/// Converts the (col,row) position from the view into a screen (col,row).  The values are clamped to (0..ScreenDim-1)
+		/// Converts a view-relative (col,row) position to a screen-relative position (col,row). The values are optionally clamped to the screen dimensions.
 		/// </summary>
-		/// <param name="col">View-based column.</param>
-		/// <param name="row">View-based row.</param>
-		/// <param name="rcol">Absolute column, display relative.</param>
-		/// <param name="rrow">Absolute row, display relative.</param>
-		/// <param name="clipped">Whether to clip the result of the ViewToScreen method, if set to true, the rcol, rrow values are clamped to the screen dimensions.</param>
+		/// <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 <c>true</c>, the rcol, rrow values are clamped to the screen (terminal) dimensions (0..TerminalDim-1).</param>
 		internal void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
 		{
 			// Computes the real row, col relative to the screen.
@@ -614,7 +645,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Converts a point from screen coordinates into the view coordinate space.
+		/// 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>
@@ -629,66 +660,72 @@ namespace Terminal.Gui {
 			}
 		}
 
-		// Converts a rectangle in view coordinates to screen coordinates.
-		internal Rect RectToScreen (Rect rect)
+		/// <summary>
+		/// Converts a region in view-relative coordinates to screen-relative coordinates.
+		/// </summary>
+		internal Rect ViewToScreen (Rect region)
 		{
-			ViewToScreen (rect.X, rect.Y, out var x, out var y, clipped: false);
-			return new Rect (x, y, rect.Width, rect.Height);
+			ViewToScreen (region.X, region.Y, out var x, out var y, clipped: false);
+			return new Rect (x, y, region.Width, region.Height);
 		}
 
 		// Clips a rectangle in screen coordinates to the dimensions currently available on the screen
-		internal Rect ScreenClip (Rect rect)
+		internal Rect ScreenClip (Rect regionScreen)
 		{
-			var x = rect.X < 0 ? 0 : rect.X;
-			var y = rect.Y < 0 ? 0 : rect.Y;
-			var w = rect.X + rect.Width >= Driver.Cols ? Driver.Cols - rect.X : rect.Width;
-			var h = rect.Y + rect.Height >= Driver.Rows ? Driver.Rows - rect.Y : rect.Height;
+			var x = regionScreen.X < 0 ? 0 : regionScreen.X;
+			var y = regionScreen.Y < 0 ? 0 : regionScreen.Y;
+			var w = regionScreen.X + regionScreen.Width >= Driver.Cols ? Driver.Cols - regionScreen.X : regionScreen.Width;
+			var h = regionScreen.Y + regionScreen.Height >= Driver.Rows ? Driver.Rows - regionScreen.Y : regionScreen.Height;
 
 			return new Rect (x, y, w, h);
 		}
 
 		/// <summary>
-		/// Sets the Console driver's clip region to the current View's Bounds.
+		/// Sets the <see cref="ConsoleDriver"/>'s clip region to the current View's <see cref="Bounds"/>.
 		/// </summary>
-		/// <returns>The existing driver's Clip region, which can be then set by setting the Driver.Clip property.</returns>
+		/// <returns>The existing driver's clip region, which can be then re-eapplied by setting <c><see cref="Driver"/>.Clip</c> (<see cref="ConsoleDriver.Clip"/>).</returns>
+		/// <remarks>
+		/// <see cref="Bounds"/> is View-relative.
+		/// </remarks>
 		public Rect ClipToBounds ()
 		{
 			return SetClip (Bounds);
 		}
 
 		/// <summary>
-		/// Sets the clipping region to the specified region, the region is view-relative
+		/// Sets the clip region to the specified view-relative region.
 		/// </summary>
-		/// <returns>The previous clip region.</returns>
-		/// <param name="rect">Rectangle region to clip into, the region is view-relative.</param>
-		public Rect SetClip (Rect rect)
+		/// <returns>The previous screen-relative clip region.</returns>
+		/// <param name="region">View-relative clip region.</param>
+		public Rect SetClip (Rect region)
 		{
-			var bscreen = RectToScreen (rect);
 			var previous = Driver.Clip;
-			Driver.Clip = ScreenClip (RectToScreen (Bounds)); 
+			Driver.Clip = Rect.Intersect (previous, ViewToScreen (region));
 			return previous;
 		}
 
 		/// <summary>
 		/// Draws a frame in the current view, clipped by the boundary of this view
 		/// </summary>
-		/// <param name="rect">Rectangular region for the frame to be drawn.</param>
-		/// <param name="padding">The padding to add to the drawn frame.</param>
+		/// <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 <c>true</c> it fill will the contents.</param>
-		public void DrawFrame (Rect rect, int padding = 0, bool fill = false)
+		public void DrawFrame (Rect region, int padding = 0, bool fill = false)
 		{
-			var scrRect = RectToScreen (rect);
+			var scrRect = ViewToScreen (region);
 			var savedClip = ClipToBounds ();
-			Driver.DrawFrame (scrRect, padding, fill);
+			Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: fill);
 			Driver.Clip = savedClip;
 		}
 
 		/// <summary>
-		/// Utility function to draw strings that contain a hotkey
+		/// Utility function to draw strings that contain a hotkey.
 		/// </summary>
 		/// <param name="text">String to display, the underscoore before a letter flags the next letter as the hotkey.</param>
 		/// <param name="hotColor">Hot color.</param>
 		/// <param name="normalColor">Normal color.</param>
+		/// <remarks>
+		/// The hotkey is any character following an underscore ('_') character.</remarks>
 		public void DrawHotString (ustring text, Attribute hotColor, Attribute normalColor)
 		{
 			Driver.SetAttribute (normalColor);
@@ -703,7 +740,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Utility function to draw strings that contains a hotkey using a colorscheme and the "focused" state.
+		/// Utility function to draw strings that contains a hotkey using a <see cref="ColorScheme"/> and the "focused" state.
 		/// </summary>
 		/// <param name="text">String to display, the underscoore before a letter flags the next letter as the hotkey.</param>
 		/// <param name="focused">If set to <c>true</c> this uses the focused colors from the color scheme, otherwise the regular ones.</param>
@@ -720,8 +757,8 @@ 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">Col (view-relative).</param>
+		/// <param name="row">Row (view-relative).</param>
 		public void Move (int col, int row)
 		{
 			ViewToScreen (col, row, out var rcol, out var rrow);
@@ -731,6 +768,11 @@ namespace Terminal.Gui {
 		/// <summary>
 		///   Positions the cursor in the right position based on the currently focused view in the chain.
 		/// </summary>
+		///    Views that are focusable should override <see cref="PositionCursor"/> to ensure
+		///    the cursor is placed in a location that makes sense. Unix terminals do not have
+		///    a way of hiding the cursor, so it can be distracting to have the cursor left at
+		///    the last focused view. Views should make sure that they place the cursor
+		///    in a visually sensible place.
 		public virtual void PositionCursor ()
 		{
 			if (focused != null)
@@ -763,7 +805,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Specifies the event arguments for <see cref="SetFocus(View)"/>
+		/// Defines the event arguments for <see cref="SetFocus(View)"/>
 		/// </summary>
 		public class FocusEventArgs : EventArgs {
 			/// <summary>
@@ -825,7 +867,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// The color scheme for this view, if it is not defined, it returns the parent's
+		/// The color scheme for this view, if it is not defined, it returns the <see cref="SuperView"/>'s
 		/// color scheme.
 		/// </summary>
 		public ColorScheme ColorScheme {
@@ -842,10 +884,10 @@ namespace Terminal.Gui {
 		ColorScheme colorScheme;
 
 		/// <summary>
-		/// Displays the specified character in the specified column and row.
+		/// Displays the specified character in the specified column and row of the View.
 		/// </summary>
-		/// <param name="col">Col.</param>
-		/// <param name="row">Row.</param>
+		/// <param name="col">Column (view-relative).</param>
+		/// <param name="row">Row (view-relative).</param>
 		/// <param name="ch">Ch.</param>
 		public void AddRune (int col, int row, Rune ch)
 		{
@@ -858,7 +900,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Removes the SetNeedsDisplay and the ChildNeedsDisplay setting on this view.
+		/// Removes the <see cref="SetNeedsDisplay()"/> and the <see cref="ChildNeedsDisplay"/> setting on this view.
 		/// </summary>
 		protected void ClearNeedsDisplay ()
 		{
@@ -867,37 +909,40 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Performs a redraw of this view and its subviews, only redraws the views that have been flagged for a re-display.
+		/// Redraws this view and its subviews; only redraws the views that have been flagged for a re-display.
 		/// </summary>
-		/// <param name="region">The region to redraw, this is relative to the view itself.</param>
+		/// <param name="bounds">The bounds (view-relative region) to redraw.</param>
 		/// <remarks>
 		/// <para>
+		///    Always use <see cref="Bounds"/> (view-relative) when calling <see cref="Redraw(Rect)"/>, NOT <see cref="Frame"/> (superview-relative).
+		/// </para>
+		/// <para>
 		///    Views should set the color that they want to use on entry, as otherwise this will inherit
 		///    the last color that was set globaly on the driver.
 		/// </para>
+		/// <para>
+		///    Overrides of <see cref="Redraw"/> must ensure they do not set <c>Driver.Clip</c> to a clip region
+		///    larger than the <c>region</c> parameter.
+		/// </para>
 		/// </remarks>
-		public virtual void Redraw (Rect region)
+		public virtual void Redraw (Rect bounds)
 		{
 			var clipRect = new Rect (Point.Empty, frame.Size);
 
 			if (subviews != null) {
 				foreach (var view in subviews) {
 					if (view.NeedDisplay != null && (!view.NeedDisplay.IsEmpty || view.childNeedsDisplay)) {
-						if (view.Frame.IntersectsWith (clipRect) && view.Frame.IntersectsWith (region)) {
+						if (view.Frame.IntersectsWith (clipRect) && view.Frame.IntersectsWith (bounds)) {
 
 							// FIXED: optimize this by computing the intersection of region and view.Bounds
 							if (view.layoutNeeded)
 								view.LayoutSubviews ();
 							Application.CurrentView = view;
 
-							// 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);
-							}
+							// Clip the sub-view
+							var savedClip = ClipToBounds ();
+							view.Redraw (view.Bounds);
+							Driver.Clip = savedClip;
 						}
 						view.NeedDisplay = Rect.Empty;
 						view.childNeedsDisplay = false;
@@ -908,7 +953,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Focuses the specified sub-view.
+		/// Causes the specified subview to have focus.
 		/// </summary>
 		/// <param name="view">View.</param>
 		public void SetFocus (View view)
@@ -941,7 +986,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Specifies the event arguments for <see cref="KeyEvent"/>
+		/// Defines the event arguments for <see cref="KeyEvent"/>
 		/// </summary>
 		public class KeyEventEventArgs : EventArgs {
 			/// <summary>
@@ -1187,10 +1232,13 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Computes the RelativeLayout for the view, given the frame for its container.
+		/// Sets the View's <see cref="Frame"/> to the relative coordinates if its container, given the <see cref="Frame"/> for its container.
 		/// </summary>
-		/// <param name="hostFrame">The Frame for the host.</param>
-		internal void RelativeLayout (Rect hostFrame)
+		/// <param name="hostFrame">The screen-relative frame for the host.</param>
+		/// <remarks>
+		/// Reminder: <see cref="Frame"/> is superview-relative; <see cref="Bounds"/> is view-relative.
+		/// </remarks>
+		internal void SetRelativeLayout (Rect hostFrame)
 		{
 			int w, h, _x, _y;
 
@@ -1228,7 +1276,6 @@ namespace Terminal.Gui {
 					h = height.Anchor (hostFrame.Height - _y);
 			}
 			Frame = new Rect (_x, _y, w, h);
-			// layoutNeeded = false;
 		}
 
 		// https://en.wikipedia.org/wiki/Topological_sorting
@@ -1274,15 +1321,45 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// This virtual method is invoked when a view starts executing or
-		/// when the dimensions of the view have changed, for example in
+		/// Event arguments for the <see cref="LayoutComplete"/> event.
+		/// </summary>
+		public class LayoutEventArgs : EventArgs {
+			/// <summary>
+			/// The view-relative bounds of the <see cref="View"/> before it was laid out.
+			/// </summary>
+			public Rect OldBounds { get; set; }
+		}
+
+		/// <summary>
+		/// Fired after the Views's <see cref="LayoutSubviews"/> method has completed. 
+		/// </summary>
+		/// <remarks>
+		/// Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has otherwise changed.
+		/// </remarks>
+		public event EventHandler<LayoutEventArgs> LayoutComplete;
+
+		/// <summary>
+		/// Raises the <see cref="LayoutComplete"/> event. Called from  <see cref="LayoutSubviews"/> after all sub-views have been laid out.
+		/// </summary>
+		internal virtual void OnLayoutComplete (LayoutEventArgs args)
+		{
+			LayoutComplete?.Invoke (this, args);
+		}
+
+		/// <summary>
+		/// Invoked when a view starts executing or when the dimensions of the view have changed, for example in
 		/// response to the container view or terminal resizing.
 		/// </summary>
+		/// <remarks>
+		/// Calls <see cref="OnLayoutComplete"/> (which raises the <see cref="LayoutComplete"/> event) before it returns.
+		/// </remarks>
 		public virtual void LayoutSubviews ()
 		{
 			if (!layoutNeeded)
 				return;
 
+			Rect oldBounds = Bounds;
+
 			// Sort out the dependencies of the X, Y, Width, Height properties
 			var nodes = new HashSet<View> ();
 			var edges = new HashSet<(View, View)> ();
@@ -1307,7 +1384,7 @@ namespace Terminal.Gui {
 
 			foreach (var v in ordered) {
 				if (v.LayoutStyle == LayoutStyle.Computed)
-					v.RelativeLayout (Frame);
+					v.SetRelativeLayout (Frame);
 
 				v.LayoutSubviews ();
 				v.layoutNeeded = false;
@@ -1315,10 +1392,12 @@ namespace Terminal.Gui {
 			}
 
 			if (SuperView == Application.Top && layoutNeeded && ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) {
-				RelativeLayout (Frame);
+				SetRelativeLayout (Frame);
 			}
 
 			layoutNeeded = false;
+
+			OnLayoutComplete (new LayoutEventArgs () { OldBounds = oldBounds });
 		}
 
 		/// <inheritdoc cref="ToString"/>

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

@@ -7,7 +7,7 @@ using NStack;
 
 namespace Terminal.Gui {
 	/// <summary>
-	/// A <see cref="Toplevel"/> <see cref="View"/> that draws a frame around its region and has a "ContentView" subview where the contents are added.
+	/// A <see cref="Toplevel"/> <see cref="View"/> that draws a frame around its region and has a "Content" subview where the contents are added.
 	/// </summary>
 	public class Window : Toplevel, IEnumerable {
 		View contentView;
@@ -29,7 +29,7 @@ namespace Terminal.Gui {
 			public ContentView (Rect frame) : base (frame) { }
 			public ContentView () : base () { }
 #if false
-			public override void Redraw (Rect region)
+			public override void Redraw (Rect bounds)
 			{
 				Driver.SetAttribute (ColorScheme.Focus);
 
@@ -50,6 +50,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="frame">Frame.</param>
 		/// <param name="title">Title.</param>
+		/// <remarks>
+		/// This constructor intitalizes a Window with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use constructors
+		/// that do not take <c>Rect</c> parameters to initialize a Window with  <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/> 
+		/// </remarks>
 		public Window (Rect frame, ustring title = null) : this (frame, title, padding: 0)
 		{
 		}
@@ -58,19 +62,26 @@ namespace Terminal.Gui {
 		/// Initializes a new instance of the <see cref="Window"/> class with an optional title.
 		/// </summary>
 		/// <param name="title">Title.</param>
+		/// <remarks>
+		///   This constructor intitalize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
+		///   Use <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and <see cref="View.Height"/> properties to dynamically control the size and location of the view.
+		/// </remarks>
 		public Window (ustring title = null) : this (title, padding: 0)
 		{
 		}
 
 		int padding;
 		/// <summary>
-		/// Initializes a new instance of the <see cref="Window"/> with
-		/// the specified frame for its location, with the specified border
-		/// an optional title.
+		/// Initializes a new instance of the <see cref="Window"/> with the specified frame for its location, with the specified border,
+		/// and an optional title.
 		/// </summary>
 		/// <param name="frame">Frame.</param>
 		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
 		/// <param name="title">Title.</param>
+		/// <remarks>
+		/// This constructor intitalizes a Window with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use constructors
+		/// that do not take <c>Rect</c> parameters to initialize a Window with  <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/> 
+		/// </remarks>
 		public Window (Rect frame, ustring title = null, int padding = 0) : base (frame)
 		{
 			this.Title = title;
@@ -82,12 +93,15 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="Window"/> with
-		/// the specified frame for its location, with the specified border
-		/// an optional title.
+		/// Initializes a new instance of the <see cref="Window"/> with the specified frame for its location, with the specified border,
+		/// and an optional title.
 		/// </summary>
 		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
 		/// <param name="title">Title.</param>
+		/// <remarks>
+		///   This constructor intitalize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
+		///   Use <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and <see cref="View.Height"/> properties to dynamically control the size and location of the view.
+		/// </remarks>
 		public Window (ustring title = null, int padding = 0) : base ()
 		{
 			this.Title = title;
@@ -111,10 +125,7 @@ namespace Terminal.Gui {
 			return contentView.GetEnumerator ();
 		}
 
-		/// <summary>
-		/// Add the specified view to the <see cref="ContentView"/>.
-		/// </summary>
-		/// <param name="view">View to add to the window.</param>
+		/// <inheritdoc cref="Add(View)"/>
 		public override void Add (View view)
 		{
 			contentView.Add (view);
@@ -123,11 +134,7 @@ namespace Terminal.Gui {
 		}
 
 
-		/// <summary>
-		///   Removes a widget from this container.
-		/// </summary>
-		/// <remarks>
-		/// </remarks>
+		/// <inheritdoc cref="Remove(View)"/>
 		public override void Remove (View view)
 		{
 			if (view == null)
@@ -141,11 +148,7 @@ namespace Terminal.Gui {
 				this.CanFocus = false;
 		}
 
-		/// <summary>
-		///   Removes all widgets from this container.
-		/// </summary>
-		/// <remarks>
-		/// </remarks>
+		/// <inheritdoc cref="RemoveAll()"/>
 		public override void RemoveAll ()
 		{
 			contentView.RemoveAll ();
@@ -156,24 +159,21 @@ namespace Terminal.Gui {
 		{
 			//var padding = 0;
 			Application.CurrentView = this;
-			var scrRect = RectToScreen (new Rect(0, 0, Frame.Width, Frame.Height));
+			var scrRect = ViewToScreen (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);
+				Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: true);
 			}
 
-			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);
-			}
+			var savedClip = ClipToBounds ();
+			contentView.Redraw (contentView.Bounds);
+			Driver.Clip = savedClip;
+
 			ClearNeedsDisplay ();
 			Driver.SetAttribute (ColorScheme.Normal);
-			Driver.DrawFrame (scrRect, padding, false);
+			Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false);
 
 			if (HasFocus)
 				Driver.SetAttribute (ColorScheme.HotNormal);
@@ -187,6 +187,7 @@ namespace Terminal.Gui {
 		//
 		internal static Point? dragPosition;
 		Point start;
+
 		///<inheritdoc cref="MouseEvent(Gui.MouseEvent)"/>
 		public override bool MouseEvent (MouseEvent mouseEvent)
 		{
@@ -200,7 +201,7 @@ namespace Terminal.Gui {
 				if (dragPosition.HasValue) {
 					if (SuperView == null) {
 						Application.Top.SetNeedsDisplay (Frame);
-						Application.Top.Redraw (Frame);
+						Application.Top.Redraw (Bounds);
 					} else {
 						SuperView.SetNeedsDisplay (Frame);
 					}
@@ -241,6 +242,5 @@ namespace Terminal.Gui {
 			//Demo.ml.Text = me.ToString ();
 			return false;
 		}
-
 	}
 }

+ 137 - 75
Terminal.Gui/Terminal.Gui.csproj

@@ -5,11 +5,12 @@
     <AssemblyName>Terminal.Gui</AssemblyName>
     <DocumentationFile>bin\Release\Terminal.Gui.xml</DocumentationFile>
     <GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
+    <AssemblyVersion>0.82.0.0</AssemblyVersion>
   </PropertyGroup>
   <PropertyGroup>
     <GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' ">true</GeneratePackageOnBuild>
     <PackageId>Terminal.Gui</PackageId>
-    <PackageVersion>0.81</PackageVersion>
+    <PackageVersion>0.82</PackageVersion>
     <Authors>Miguel de Icaza</Authors>
     <PackageLicenseExpression>MIT</PackageLicenseExpression>
     <PackageProjectUrl>https://github.com/migueldeicaza/gui.cs/</PackageProjectUrl>
@@ -18,80 +19,141 @@
     <Owners>Miguel de Icaza</Owners>
     <Summary>Application framework for creating modern console applications using .NET </Summary>
     <Title>Gui.cs is a framework for creating console user interfaces</Title>
-    <PackageReleaseNotes>0.81: Fix ncurses engine for macOS/Linux, it works again
-
-* Fixes an issue with referencing views that have not been allocated yet causing a stack overflow
-* New OnCloseMenu event on menus
-* Button cursor position looks better
-* Listview in single-selection mode uses a radio-button look
-* Fixes a couple of crashes (356)
-* Default the samples to work on Catalina
-
-
-0.80: Jumbo update from BDisp:
-
-* Fixed key events traversal for modal dialogs
-* Fixes culture info of DataField from pr
-* Fixes the rectangle drawing issue
-* Redraw issue when setting coordinates of label
-* Added sub menus into menu bar with mouse and key navigation
-* Added Colors.Menu.Disabled to CursesDriver.cs
-* Mouse text selection with cut, copy and paste on text fields
-* Change sepChar from char to string in DateField
-* Adding a disabled menu item in the demo file
-* Fixes Button repainting issue when changing the text length to one smaller
-* Fixes Redraw issue when setting coordinates of label
-* Fixes ScrollView does not render some content
-* Fixed bug in Button that caused a loop redraw calling TerminalResized
-* Fixes a repaint issue (282)
-* Mouse features added to FileDialog including wheel support.
-* Switch netcoreapp target to netstandard2.0
-* Added TextView.TextChanged event
-* Fixes issue #306 async/await hang (#312)
-* Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
-* Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
-* Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
-* Minor change to Core.cs::Application.Init(Func) for better initialization status tracking, via backend property instead of relying on the Top field.
-* Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)
-* Resizing the MessageBox width to accommodate all message text (#299)
-* Timefield format with bounds values (#303)
-* Implemented lower and upper bounds to TimeField
-* Passing old text to the Changed event handler
-* Extract methods on ListView to make it controlable from other controls
-
-0.70: Bug fixes (320, 321, 306, 304, 291, 299, 303);  Surface ListView.ListWrapper, surface various internal methods for use in ListView; Allow list item selection; ; 0.65: Added new TimeField from Jörg Preiß; Fixes for Backtab by Martin Björkström;  ListView now supports simple selection;  Bug fixes by giladlevi, Daniel Cazzulino and Marius Ungureanu;  New Application.Run of T entry point by Daniel Cazzulino;   Added various View methods to bring forward, backwards and move views in the hierarchy;  Switch to Portable PDBs by Daniel Cazzulino;  Dims can now be compared by Daniel Cazzulino; OnMenuOpen handler by giladlevi;  Various memory usage optimizations by giladlevi;  FileDialog.FilePath is now a full path by Yanwei Wang;  ISupportInitialize/ISupportInitializeNotification is now supported thanks to the work from Daniel Cazzulino; Support for non-modal TopLevels by Daniel Cazzulino and Adrian Alonso; 0.24: the Windows driver implements WakeUp, allowing some scenarios like bug #207 to be fixed;
-0.23: Better support for disabled menu items;  Raises text changed event after the internals have been updated; Fix Caps-NumLock;  Alt-HotKey now work on menus
-0.22: Correct vertical scrollview behavior, Small curses driver fix for terminals without mouse support, TextView support for scrolling, Surface Used property on TextField, Surface Cursor on RadioGroup.
-
-0.21: Introudce Attribute.Make to more easily create attributes, and fix a bug in the file panel.
-0.20: Expose some of the CursesDriver APIs
-0.19: PageUpDown updates (GaikwadPratik); Fixes in multi-line labels ([email protected]);  Support Delete char in TextView (Greg Amidon); Prevent empty TextViews from crashing;  Allow TextFields to be updated on the Changed event.
-0.18: Fixes hotkeys for menus (Sang Kil); Fixes RemoveAll for all containers;  Allows Toplevels with no views; Fixes FileDialog layout; Prevent crash in TextView
-0.17: Unix, dynamically load ncurses library to support different configurations, and not require -dev on Linux, various bug fixes.
-
-0.16: Support for Shift-Tab on Windows (fix by @mholo65) 
-
-0.15: WindowsDriver fix for Legacy Console keyboard input (issue #105)
-
-0.14: WindowsDriver fix for EventType size.
-
-0.13: Fixes keyboard input for Alt-Gr and numbers.
-
-0.12: Fixes the text editor merge line command.
-
-0.11: Simplify TextField implementation, fixes a couple of editing bugs.
-
-0.10: Fix unicode rendering for TextField, and bring F# example
-
-0.9: File Open/File Save dialogs, HexView, Windows Driver allows resizing, mouse events, faster (thanks to Nick Van Dyck, nickvdyck for the contribution!), smaller bug fixes,
-
-0.8: Completes keyboard support on Windows;  Fixes delete on Windows, some layout fixes.
-0.7: terminal resizing on Linux propagates sizes with new layout system, and new features on the layout system (documented)
-0.6: new layout system, multi-line textview editor, Linux bug fix for .NET Core
-0.5: support Linux with .NET Core, Windows driver fixes.
-0.4: hotkey handling fix for RadioButtons
-0.3: Fix Windows key input to not echo characters on console, proper Fkey mapping
-0.2: Auto-detect the best console</PackageReleaseNotes>
+    <PackageReleaseNotes>
+      0.82: Many fixes
+      * API documentation completely revamped and updated. Readme upated. Contributors guide added (Thanks @tig!)
+      * New sample/demo app - UI Catalog - Replaces demo.cs with an easy to use and extend set of demo scenarios. (Thanks @tig!)
+      * MenuBar can now have MenuItems directly (enables top-level menu items with no submenu). (Thanks @tig!)
+      * Apps can now get KeyUp/KeyDown events.  (Thanks @tig!)
+      * Fixes #396 - Text alignnment issues.  (Thanks @tig!)
+      * Fixes #423 - Fix putting results of ocgv on command line erases cursor. (Thanks @tig!)
+      * Example/Designer csproj files updated to latest Visual Studio model. (Thanks @tig!)
+      * Adjusted the default colors for Windows to make more readable. (Thanks @tig!)
+      * Toplevel.Ready event - Fired once the Toplevel's MainLoop has started (#445). (Thanks @tig!)
+      * Refactored several events to use event vs. Action. (BREAKING CHANGE) (Thanks @tig!)
+      * All compile warnings fixed. (Thanks @tig!)
+      * Fixed a crash in EnsureVisibleBounds. (Thanks @tig!)
+      * Application.Init/Shutdown are more robust. (Thanks @tig!)
+      * New "Draw Window Frame" code; consistent across Window, FrameView, and Menu. Fixes many drawing bugs. (Thanks @tig!)
+      * The project has been refactored an reorganized to reduce risk of bugs and make it easier to contribute #541. (Thanks @tig!)
+      * Fixes #522 - Last view of Frameview not drawn. (Thanks @tig!)
+      * Clipping has been fixed/restored - it now works properly. (#586) (Thanks @tig!)
+      * Added a View.LayoutComplete event (#569). (Thanks @tig!)
+      * Fixes #299 - MessageBox now auto sizes. (Thanks @tig!)
+      * Fixes #557 - MessageBoxes on small screens. (Thanks @tig!)
+      * Fixes #432 - MessageBox does not deal with long text; width/height params are goofy. (Thanks @tig!)
+      * Fixes #35 - Dialog should have 1 char padding. (Thanks @tig!)
+      * `MessageBox.Query` called with `width` and `height` == 0 get auto-size behavior. A new constructor is added making this easy to use. (Thanks @tig!)
+      * Multi-line `MessageBox`es are now supported. Just use `\n` to add lines. The height of the MessageBox will adjust automatically. (Thanks @tig!)
+      * The `MessageBoxes` Scenario in UI Catalog provides a full demo/test-case. (Thanks @tig!)
+      * `Dialog` called with `width` and `height` == 0 are sized to 85% container. A new constructor is added making this easy to use. (Thanks @tig!)
+      * Dialog (and MessageBox `Buttons` are now dynamically laid out using Computed layout. (Thanks @tig!)
+      * A `Dialogs` Scenario has been added to UI Catalog making it easy to test the API. (Thanks @tig!)
+      * `Button` now supports BOTH specifying a hotkey with '_' and the old behavior of using the first uppercase char (if '_' is not found). (Thanks @tig!)
+      * All UI Catalog scenarios that use `Dialog` or `MessageBox` now use the simplified API. (Thanks @tig!)
+      * `Terminal.Gui.dll` now has version metadata and UI Catalog's about box displays it as a test case. (Thanks @tig!)
+      * Button, Dialog, and MessageBox API documentation has been updated/revised. (Thanks @tig!)
+      * `View`, `Window`, `FrameView`, and `Dialog` have been upgraded to use the new `ConsoleDriver.DrawFrameWindow` API directly. (Thanks @tig!)
+      * New ComboBox control (Thanks @fergusonr!)
+      * ConsoleDriver now supports improved KeyModifers (shift keys) with an expanded Keys Sceanrio in UI Catalog. (Thanks @bdisp!)
+      * Tons of mouse handling improvements. (Thanks @bdisp!)
+      * Fsharp sample updated. (Thanks @bdisp!)
+      * Fixes #562 - Background drawing issue. (Thanks @bdisp!)
+      * Fixes #517 - Focus and mouse handlers enahced (BREAKING CHANGE). (Thanks @bdisp!)
+      * Fixed resizing update and correct Toplevel colors without colors. (Thanks @bdisp!)
+      * Fixed #515, #518, #536, #540. (Thanks @bdisp!)
+      * Added Threading Scenario to UI catalog. (Thanks @bdisp!)
+      * Added support for F11 and F12 keys. (Thanks @bdisp!)
+      * Multiple improvements to Date/TimeField. (Thanks @bdisp!)
+      * Fixes #409 - Invoke does not cause Wakeup #501. (Thanks @bdisp!)
+      * Fixed Label text alignemnt. (Thanks @bdisp!)
+      * Added mouse features in the Unix version. Supports xterm-1006. (Thanks @bdisp!)
+      * Several StatusBar fixes. (Thanks @bdisp!)
+      * Tons of mouse improvements including mouse wheel support (e.g. #404, #409). (Thanks @bdisp!)
+      * Added a CloseFile method to the TextView as stated in #452. (Thanks @bdisp)
+      * Added a OpenSelectedItem event to the ListView #429. (Thanks @bdisp!)
+      * Fixes the return value of the position cursor in the TextField. (Thanks @bdisp!)
+      * Updates screen on Unix window resizing. (Thanks @bdisp!)
+      * Fixes the functions of the Edit->Copy-Cut-Paste menu for the TextField that was not working well. (Thanks @bdisp!)
+      * More robust error handing in Pos/Dim. Fixes #355 stack overflow with Pos based on the size of windows at startup. Added a OnResized action to set the Pos after the terminal are resized. (Thanks @bdisp!)
+      * Fixes #389 Window layouting breaks when resizing. (Thanks @bdisp!)
+      * Fixes #557 MessageBox needs to take ustrings (BREAKING CHANGE). (Thanks @tig!)
+
+      0.81:
+      * Fix ncurses engine for macOS/Linux, it works again
+      * Fixes an issue with referencing views that have not been allocated yet causing a stack overflow
+      * New OnCloseMenu event on menus
+      * Button cursor position looks better
+      * Listview in single-selection mode uses a radio-button look
+      * Fixes a couple of crashes (356)
+      * Default the samples to work on Catalina
+
+
+      0.80: Jumbo update from BDisp:
+
+      * Fixed key events traversal for modal dialogs
+      * Fixes culture info of DataField from pr
+      * Fixes the rectangle drawing issue
+      * Redraw issue when setting coordinates of label
+      * Added sub menus into menu bar with mouse and key navigation
+      * Added Colors.Menu.Disabled to CursesDriver.cs
+      * Mouse text selection with cut, copy and paste on text fields
+      * Change sepChar from char to string in DateField
+      * Adding a disabled menu item in the demo file
+      * Fixes Button repainting issue when changing the text length to one smaller
+      * Fixes Redraw issue when setting coordinates of label
+      * Fixes ScrollView does not render some content
+      * Fixed bug in Button that caused a loop redraw calling TerminalResized
+      * Fixes a repaint issue (282)
+      * Mouse features added to FileDialog including wheel support.
+      * Switch netcoreapp target to netstandard2.0
+      * Added TextView.TextChanged event
+      * Fixes issue #306 async/await hang (#312)
+      * Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
+      * Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
+      * Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
+      * Minor change to Core.cs::Application.Init(Func) for better initialization status tracking, via backend property instead of relying on the Top field.
+      * Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)
+      * Resizing the MessageBox width to accommodate all message text (#299)
+      * Timefield format with bounds values (#303)
+      * Implemented lower and upper bounds to TimeField
+      * Passing old text to the Changed event handler
+      * Extract methods on ListView to make it controlable from other controls
+
+      0.70: Bug fixes (320, 321, 306, 304, 291, 299, 303);  Surface ListView.ListWrapper, surface various internal methods for use in ListView; Allow list item selection; ; 0.65: Added new TimeField from Jörg Preiß; Fixes for Backtab by Martin Björkström;  ListView now supports simple selection;  Bug fixes by giladlevi, Daniel Cazzulino and Marius Ungureanu;  New Application.Run of T entry point by Daniel Cazzulino;   Added various View methods to bring forward, backwards and move views in the hierarchy;  Switch to Portable PDBs by Daniel Cazzulino;  Dims can now be compared by Daniel Cazzulino; OnMenuOpen handler by giladlevi;  Various memory usage optimizations by giladlevi;  FileDialog.FilePath is now a full path by Yanwei Wang;  ISupportInitialize/ISupportInitializeNotification is now supported thanks to the work from Daniel Cazzulino; Support for non-modal TopLevels by Daniel Cazzulino and Adrian Alonso; 0.24: the Windows driver implements WakeUp, allowing some scenarios like bug #207 to be fixed;
+      0.23: Better support for disabled menu items;  Raises text changed event after the internals have been updated; Fix Caps-NumLock;  Alt-HotKey now work on menus
+      0.22: Correct vertical scrollview behavior, Small curses driver fix for terminals without mouse support, TextView support for scrolling, Surface Used property on TextField, Surface Cursor on RadioGroup.
+
+      0.21: Introudce Attribute.Make to more easily create attributes, and fix a bug in the file panel.
+      0.20: Expose some of the CursesDriver APIs
+      0.19: PageUpDown updates (GaikwadPratik); Fixes in multi-line labels ([email protected]);  Support Delete char in TextView (Greg Amidon); Prevent empty TextViews from crashing;  Allow TextFields to be updated on the Changed event.
+      0.18: Fixes hotkeys for menus (Sang Kil); Fixes RemoveAll for all containers;  Allows Toplevels with no views; Fixes FileDialog layout; Prevent crash in TextView
+      0.17: Unix, dynamically load ncurses library to support different configurations, and not require -dev on Linux, various bug fixes.
+
+      0.16: Support for Shift-Tab on Windows (fix by @mholo65)
+
+      0.15: WindowsDriver fix for Legacy Console keyboard input (issue #105)
+
+      0.14: WindowsDriver fix for EventType size.
+
+      0.13: Fixes keyboard input for Alt-Gr and numbers.
+
+      0.12: Fixes the text editor merge line command.
+
+      0.11: Simplify TextField implementation, fixes a couple of editing bugs.
+
+      0.10: Fix unicode rendering for TextField, and bring F# example
+
+      0.9: File Open/File Save dialogs, HexView, Windows Driver allows resizing, mouse events, faster (thanks to Nick Van Dyck, nickvdyck for the contribution!), smaller bug fixes,
+
+      0.8: Completes keyboard support on Windows;  Fixes delete on Windows, some layout fixes.
+      0.7: terminal resizing on Linux propagates sizes with new layout system, and new features on the layout system (documented)
+      0.6: new layout system, multi-line textview editor, Linux bug fix for .NET Core
+      0.5: support Linux with .NET Core, Windows driver fixes.
+      0.4: hotkey handling fix for RadioButtons
+      0.3: Fix Windows key input to not echo characters on console, proper Fkey mapping
+      0.2: Auto-detect the best console
+    </PackageReleaseNotes>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

+ 39 - 23
Terminal.Gui/Views/Button.cs

@@ -55,29 +55,32 @@ namespace Terminal.Gui {
 		public Action Clicked;
 
 		/// <summary>
-		///   Initializes a new instance of <see cref="Button"/> based on the given text at position 0,0
+		///   Initializes a new instance of <see cref="Button"/> using <see cref="LayoutStyle.Computed"/> layout.
 		/// </summary>
 		/// <remarks>
-		///   The size of the <see cref="Button"/> is computed based on the
-		///   text length. 
+		///   The width of the <see cref="Button"/> is computed based on the
+		///   text length. The height will always be 1.
 		/// </remarks>
 		/// <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. <seealso cref="IsDefault"/></param>
+		/// <param name="is_default">
+		///   If <c>true</c>, a special decoration is used, and the user pressing the enter key 
+		///   in a <see cref="Dialog"/> will implicitly activate this button.
+		/// </param>
 		public Button (ustring text, bool is_default = false) : base ()
 		{
 			CanFocus = true;
 			Text = text ?? string.Empty;
 			this.IsDefault = is_default;
 			int w = SetWidthHeight (text, is_default);
-			Frame = new Rect (0, 0, w, 1);
+			Frame = new Rect (Frame.Location, new Size (w, 1));
 		}
 
 		/// <summary>
-		///   Initializes a new instance of <see cref="Button"/> at the given coordinates, based on the given text
+		///   Initializes a new instance of <see cref="Button"/> using <see cref="LayoutStyle.Absolute"/> layout, based on the given text
 		/// </summary>
 		/// <remarks>
-		///   The size of the <see cref="Button"/> is computed based on the
-		///   text length. 
+		///   The width of the <see cref="Button"/> is computed based on the
+		///   text length. The height will always be 1.
 		/// </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>
@@ -85,17 +88,19 @@ namespace Terminal.Gui {
 		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
+		///   Initializes a new instance of <see cref="Button"/> using <see cref="LayoutStyle.Absolute"/> layout, based on the given text.
 		/// </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.
+		///   The width of the <see cref="Button"/> is computed based on the
+		///   text length. The height will always be 1.
 		/// </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>
+		/// <param name="is_default">
+		///   If <c>true</c>, a special decoration is used, and the user pressing the enter key 
+		///   in a <see cref="Dialog"/> will implicitly activate this 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))
 		{
@@ -110,6 +115,7 @@ namespace Terminal.Gui {
 			int w = text.Length + 4 + (is_default ? 2 : 0);
 			Width = w;
 			Height = 1;
+			Frame = new Rect (Frame.Location, new Size (w, 1));
 			return w;
 		}
 
@@ -137,22 +143,32 @@ namespace Terminal.Gui {
 			else
 				shown_text = "[ " + text + " ]";
 
-			hot_pos = -1;
 			hot_key = (Rune)0;
-			int i = 0;
-			foreach (Rune c in shown_text) {
-				if (Rune.IsUpper (c)) {
-					hot_key = c;
-					hot_pos = i;
-					break;
+			hot_pos = shown_text.IndexOf ('_');
+
+			if (hot_pos == -1) {
+				// Use first upper-case char
+				int i = 0;
+				foreach (Rune c in shown_text) {
+					if (Rune.IsUpper (c)) {
+						hot_key = c;
+						hot_pos = i;
+						break;
+					}
+					i++;
 				}
-				i++;
+			} else {
+				// Use char after '_'
+				var start = shown_text [0, hot_pos];
+				shown_text = start + shown_text [hot_pos + 1, shown_text.Length];
+				hot_key = Char.ToUpper((char)shown_text [hot_pos]);
 			}
+
 			SetNeedsDisplay ();
 		}
 
 		///<inheritdoc cref="Redraw(Rect)"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
 			Move (0, 0);
@@ -214,7 +230,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="MouseEvent"/>
-		public override bool MouseEvent(MouseEvent me)
+		public override bool MouseEvent (MouseEvent me)
 		{
 			if (me.Flags == MouseFlags.Button1Clicked) {
 				SuperView.SetFocus (this);

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

@@ -98,7 +98,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
 			Move (0, 0);

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

@@ -58,7 +58,7 @@ namespace Terminal.Gui {
 					SetValue (searchset [listview.SelectedItem]);
 			};
 
-			Application.Loaded += (object sender, Application.ResizedEventArgs e) => {
+			LayoutComplete += (sender, a) => {
 				// Determine if this view is hosted inside a dialog
 				for (View view = this.SuperView; view != null; view = view.SuperView) {
 					if (view is Dialog) {

+ 7 - 10
Terminal.Gui/Views/FrameView.cs

@@ -136,23 +136,20 @@ namespace Terminal.Gui {
 		{
 			var padding = 0;
 			Application.CurrentView = this;
-			var scrRect = RectToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
+			var scrRect = ViewToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
 
 			if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
 				Driver.SetAttribute (ColorScheme.Normal);
-				Driver.DrawFrame (scrRect, padding, true);
+				Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: true);
 			}
 
-			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);
-			}
+			var savedClip = ClipToBounds ();
+			contentView.Redraw (contentView.Bounds);
+			Driver.Clip = savedClip;
+
 			ClearNeedsDisplay ();
 			Driver.SetAttribute (ColorScheme.Normal);
-			Driver.DrawFrame (scrRect, padding, false);
+			Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false);
 
 			if (HasFocus)
 				Driver.SetAttribute (ColorScheme.HotNormal);

+ 2 - 2
Terminal.Gui/Views/HexView.cs

@@ -130,7 +130,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			Attribute currentAttribute;
 			var current = ColorScheme.Focus;
@@ -149,7 +149,7 @@ namespace Terminal.Gui {
 
 			for (int line = 0; line < frame.Height; line++) {
 				var lineRect = new Rect (0, line, frame.Width, 1);
-				if (!region.Contains (lineRect))
+				if (!bounds.Contains (lineRect))
 					continue;
 				
 				Move (0, line);

+ 4 - 2
Terminal.Gui/Views/Label.cs

@@ -99,6 +99,8 @@ namespace Terminal.Gui {
 
 		static ustring ClipAndJustify (ustring str, int width, TextAlignment talign)
 		{
+			// Get rid of any '\r' added by Windows
+			str = str.Replace ("\r", ustring.Empty);
 			int slen = str.RuneCount;
 			if (slen > width){
 				var uints = str.ToRunes (width);
@@ -161,7 +163,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			if (recalcPending)
 				Recalc ();
@@ -173,7 +175,7 @@ namespace Terminal.Gui {
 
 			Clear ();
 			for (int line = 0; line < lines.Count; line++) {
-				if (line < region.Top || line > region.Bottom)
+				if (line < bounds.Top || line > bounds.Bottom)
 					continue;
 				var str = lines [line];
 				int x;

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

@@ -272,7 +272,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw(Rect)"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			var current = ColorScheme.Focus;
 			Driver.SetAttribute (current);

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

@@ -276,10 +276,10 @@ namespace Terminal.Gui {
 			return ColorScheme.Normal;
 		}
 
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			Driver.SetAttribute (ColorScheme.Normal);
-			DrawFrame (region, padding: 0, fill: true);
+			DrawFrame (bounds, padding: 0, fill: true);
 
 			for (int i = 0; i < barItems.Children.Length; i++) {
 				var item = barItems.Children [i];
@@ -624,7 +624,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			Move (0, 0);
 			Driver.SetAttribute (Colors.Menu.Normal);

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

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

+ 2 - 1
Terminal.Gui/Views/ScrollView.cs

@@ -400,7 +400,6 @@ namespace Terminal.Gui {
 		/// This event is raised when the contents have scrolled
 		/// </summary>
 		//public event Action<ScrollView> Scrolled;
-
 		public override void Redraw(Rect region)
 		{
 			SetViewsNeedsDisplay ();
@@ -409,6 +408,8 @@ namespace Terminal.Gui {
 
 			var savedClip = ClipToBounds ();
 			contentView.Redraw (contentView.Bounds);
+			Driver.Clip = savedClip;
+
 			vertical.Redraw (vertical.Bounds);
 			horizontal.Redraw (horizontal.Bounds);
 			Driver.Clip = savedClip;

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

@@ -120,7 +120,7 @@ namespace Terminal.Gui {
 			Width = Dim.Fill ();
 			Height = 1;
 
-			Application.Loaded += (sender, e) => {
+			LayoutComplete += (sender, e) => {
 				X = 0;
 				Height = 1;
 #if SNAP_TO_TOP
@@ -132,7 +132,7 @@ namespace Terminal.Gui {
 				case StatusBarStyle.SnapToBottom:
 #endif
 					if (Parent == null) {
-						Y = e.Rows - 1; 
+						Y = Driver.Rows - 1; 
 					} else {
 						Y = Pos.Bottom (Parent);
 					}
@@ -151,7 +151,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			//if (Frame.Y != Driver.Rows - 1) {
 			//	Frame = new Rect (Frame.X, Driver.Rows - 1, Frame.Width, Frame.Height);

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

@@ -184,7 +184,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw(Rect)"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			ColorScheme color = Colors.Menu;
 			SetSelectedStartSelectedLength ();

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

@@ -525,31 +525,31 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc cref="Redraw(Rect)"/>
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			ColorNormal ();
 
-			int bottom = region.Bottom;
-			int right = region.Right;
-			for (int row = region.Top; row < bottom; row++) 
+			int bottom = bounds.Bottom;
+			int right = bounds.Right;
+			for (int row = bounds.Top; row < bottom; row++) 
 			{
 				int textLine = topRow + row;
 				if (textLine >= model.Count) 
 				{
 					ColorNormal ();
-					ClearRegion (region.Left, row, region.Right, row + 1);
+					ClearRegion (bounds.Left, row, bounds.Right, row + 1);
 					continue;
 				}
 				var line = model.GetLine (textLine);
 				int lineRuneCount = line.Count;
-				if (line.Count < region.Left)
+				if (line.Count < bounds.Left)
 				{
-					ClearRegion (region.Left, row, region.Right, row + 1);
+					ClearRegion (bounds.Left, row, bounds.Right, row + 1);
 					continue;
 				}
 
-				Move (region.Left, row);
-				for (int col = region.Left; col < right; col++) 
+				Move (bounds.Left, row);
+				for (int col = bounds.Left; col < right; col++) 
 				{
 					var lineCol = leftColumn + col;
 					var rune = lineCol >= lineRuneCount ? ' ' : line [lineCol];

+ 54 - 23
Terminal.Gui/Windows/Dialog.cs

@@ -4,15 +4,15 @@
 // Authors:
 //   Miguel de Icaza ([email protected])
 //
-
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using NStack;
 
 namespace Terminal.Gui {
 	/// <summary>
 	/// The <see cref="Dialog"/> <see cref="View"/> is a <see cref="Window"/> that by default is centered and contains one 
-	/// or more <see cref="Button"/>. It defaults to the <see cref="Colors.Dialog"/> color scheme and has a 1 cell padding around the edges.
+	/// or more <see cref="Button"/>s. It defaults to the <see cref="Colors.Dialog"/> color scheme and has a 1 cell padding around the edges.
 	/// </summary>
 	/// <remarks>
 	///  To run the <see cref="Dialog"/> modally, create the <see cref="Dialog"/>, and pass it to <see cref="Application.Run()"/>. 
@@ -21,21 +21,37 @@ namespace Terminal.Gui {
 	/// </remarks>
 	public class Dialog : Window {
 		List<Button> buttons = new List<Button> ();
-		const int padding = 1;
+		const int padding = 0;
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="Dialog"/> class with an optional set of <see cref="Button"/>s to display
+		/// Initializes a new instance of the <see cref="Dialog"/> class using <see cref="LayoutStyle.Absolute"/> positioning 
+		/// and an optional set of <see cref="Button"/>s to display
 		/// </summary>
 		/// <param name="title">Title for the dialog.</param>
 		/// <param name="width">Width for the dialog.</param>
 		/// <param name="height">Height for the dialog.</param>
 		/// <param name="buttons">Optional buttons to lay out at the bottom of the dialog.</param>
+		/// <remarks>
+		/// if <c>width</c> and <c>height</c> are both 0, the Dialog will be vertically and horizontally centered in the
+		/// container and the size will be 85% of the container. 
+		/// After initialzation use <c>X</c>, <c>Y</c>, <c>Width</c>, and <c>Height</c> to override this with a location or size.
+		/// </remarks>
+		/// <remarks>
+		/// Use the constructor that does not take a <c>width</c> and <c>height</c> instead.
+		/// </remarks>
 		public Dialog (ustring title, int width, int height, params Button [] buttons) : base (title, padding: padding)
 		{
 			X = Pos.Center ();
 			Y = Pos.Center ();
-			Width = width;
-			Height = height;
+
+			if (width == 0 & height == 0) {
+				Width = Dim.Percent (85);
+				Height = Dim.Percent (85);
+			} else {
+				Width = width;
+				Height = height;
+			}
+
 			ColorScheme = Colors.Dialog;
 			Modal = true;
 
@@ -45,6 +61,22 @@ namespace Terminal.Gui {
 					Add (b);
 				}
 			}
+
+			//LayoutComplete += (sender, a) => AdjustButtonLayout ();
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Dialog"/> class using <see cref="LayoutStyle.Computed"/> positioning 
+		/// and with an optional set of <see cref="Button"/>s to display
+		/// </summary>
+		/// <param name="title">Title for the dialog.</param>
+		/// <param name="buttons">Optional buttons to lay out at the bottom of the dialog.</param>
+		/// <remarks>
+		/// if <c>width</c> and <c>height</c> are both 0, the Dialog will be vertically and horizontally centered in the
+		/// container and the size will be 85% of the container. 
+		/// After initialzation use <c>X</c>, <c>Y</c>, <c>Width</c>, and <c>Height</c> to override this with a location or size.
+		/// </remarks>
+		public Dialog (ustring title, params Button [] buttons) : this (title: title, width: 0, height: 0, buttons: buttons) { 
 		}
 
 		/// <summary>
@@ -58,31 +90,30 @@ namespace Terminal.Gui {
 
 			buttons.Add (button);
 			Add (button);
+			LayoutSubviews ();
 		}
 
+		internal int GetButtonsWidth ()
+		{
+			if (buttons.Count == 0) {
+				return 0;
+			}
+			return buttons.Select (b => b.Bounds.Width).Sum () + buttons.Count() - 1;
+		}
 		///<inheritdoc cref="LayoutSubviews"/>
 		public override void LayoutSubviews ()
 		{
-			base.LayoutSubviews ();
+			int buttonsWidth = GetButtonsWidth ();
 
-			int buttonSpace = 0;
-			int maxHeight = 0;
-
-			foreach (var b in buttons) {
-				buttonSpace += b.Frame.Width + 1;
-				maxHeight = Math.Max (maxHeight, b.Frame.Height);
+			int shiftLeft = Math.Max ((Bounds.Width - buttonsWidth) / 2 - 2, 0);
+			for (int i = buttons.Count - 1; i >= 0; i--) {
+				Button button = buttons [i];
+				shiftLeft += button.Frame.Width + 1;
+				button.X = Pos.AnchorEnd (shiftLeft);
+				button.Y = Pos.AnchorEnd (1);
 			}
-			const int borderWidth = 2;
-			var start = (Frame.Width-borderWidth - buttonSpace) / 2;
-
-			var y = Frame.Height - borderWidth  - maxHeight-1-padding;
-			foreach (var b in buttons) {
-				var bf = b.Frame;
-
-				b.Frame = new Rect (start, y, bf.Width, bf.Height);
 
-				start += bf.Width + 1;
-			}
+			base.LayoutSubviews ();
 		}
 
 		///<inheritdoc cref="ProcessKey"/>

+ 8 - 5
Terminal.Gui/Windows/FileDialog.cs

@@ -159,7 +159,7 @@ namespace Terminal.Gui {
 
 			Move (allowsMultipleSelection ? 3 : 2, line);
 			int byteLen = ustr.Length;
-			int used = 0;
+			int used = allowsMultipleSelection ? 2 : 1;
 			for (int i = 0; i < byteLen;) {
 				(var rune, var size) = Utf8.DecodeRune (ustr, i, i - byteLen);
 				var count = Rune.ColumnWidth (rune);
@@ -169,12 +169,12 @@ namespace Terminal.Gui {
 				used += count;
 				i += size;
 			}
-			for (; used < width; used++) {
+			for (; used < width - 1; used++) {
 				Driver.AddRune (' ');
 			}
 		}
 
-		public override void Redraw (Rect region)
+		public override void Redraw (Rect bounds)
 		{
 			var current = ColorScheme.Focus;
 			Driver.SetAttribute (current);
@@ -182,7 +182,7 @@ namespace Terminal.Gui {
 			var f = Frame;
 			var item = top;
 			bool focused = HasFocus;
-			var width = region.Width;
+			var width = bounds.Width;
 
 			for (int row = 0; row < f.Height; row++, item++) {
 				bool isSelected = item == selected;
@@ -463,7 +463,7 @@ namespace Terminal.Gui {
 			dirListView = new DirListView (this) {
 				X = 1,
 				Y = 3 + msgLines + 2,
-				Width = Dim.Fill () - 3,
+				Width = Dim.Fill () - 1,
 				Height = Dim.Fill () - 2,
 			};
 			DirectoryPath = Path.GetFullPath (Environment.CurrentDirectory);
@@ -488,6 +488,9 @@ namespace Terminal.Gui {
 			};
 			AddButton (this.prompt);
 
+			Width = Dim.Percent (80);
+			Height = Dim.Percent (80);
+
 			// On success, we will set this to false.
 			canceled = true;
 		}

+ 96 - 19
Terminal.Gui/Windows/MessageBox.cs

@@ -1,18 +1,23 @@
-using System;
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
 namespace Terminal.Gui {
 	/// <summary>
 	/// MessageBox displays a modal message to the user, with a title, a message and a series of options that the user can choose from.
 	/// </summary>
 	/// <para>
-	///   The difference between the <see cref="Query"/> and <see cref="ErrorQuery"/> method is the default set of colors used for the message box.
+	///   The difference between the <see cref="Query(ustring, ustring, ustring[])"/> and <see cref="ErrorQuery(ustring, ustring, ustring[])"/> 
+	///   method is the default set of colors used for the message box.
 	/// </para>
 	/// <para>
-	/// The following example pops up a <see cref="MessageBox"/> with 50 columns, and 7 lines, with the specified title and text, plus two <see cref="Button"/>s.
+	/// The following example pops up a <see cref="MessageBox"/> with the specified title and text, plus two <see cref="Button"/>s.
 	/// The value -1 is returned when the user cancels the <see cref="MessageBox"/> by pressing the ESC key.
 	/// </para>
 	/// <example>
 	/// <code lang="c#">
-	/// var n = MessageBox.Query (50, 7, "Quit Demo", "Are you sure you want to quit this demo?", "Yes", "No");
+	/// var n = MessageBox.Query ("Quit Demo", "Are you sure you want to quit this demo?", "Yes", "No");
 	/// if (n == 0)
 	///    quit = true;
 	/// else
@@ -29,11 +34,30 @@ namespace Terminal.Gui {
 		/// <param name="title">Title for the query.</param>
 		/// <param name="message">Message to display, might contain multiple lines..</param>
 		/// <param name="buttons">Array of buttons to add.</param>
-		public static int Query (int width, int height, string title, string message, params string [] buttons)
+		/// <remarks>
+		/// Use <see cref="Query(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
+		/// </remarks>
+		public static int Query (int width, int height, ustring title, ustring message, params ustring [] buttons)
 		{
 			return QueryFull (false, width, height, title, message, buttons);
 		}
 
+		/// <summary>
+		/// Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show to the user.
+		/// </summary>
+		/// <returns>The index of the selected button, or -1 if the user pressed ESC to close the dialog.</returns>
+		/// <param name="title">Title for the query.</param>
+		/// <param name="message">Message to display, might contain multiple lines.</param>
+		/// <param name="buttons">Array of buttons to add.</param>
+		/// <remarks>
+		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
+		/// from the size of the message and buttons.
+		/// </remarks>
+		public static int Query (ustring title, ustring message, params ustring [] buttons)
+		{
+			return QueryFull (false, 0, 0, title, message, buttons);
+		}
+
 		/// <summary>
 		/// Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show to the user.
 		/// </summary>
@@ -43,34 +67,87 @@ namespace Terminal.Gui {
 		/// <param name="title">Title for the query.</param>
 		/// <param name="message">Message to display, might contain multiple lines.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
-		public static int ErrorQuery (int width, int height, string title, string message, params string [] buttons)
+		/// <remarks>
+		/// Use <see cref="ErrorQuery(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
+		/// </remarks>
+		public static int ErrorQuery (int width, int height, ustring title, ustring message, params ustring [] buttons)
 		{
 			return QueryFull (true, width, height, title, message, buttons);
 		}
 
-		static int QueryFull (bool useErrorColors, int width, int height, string title, string message, params string [] buttons)
+		/// <summary>
+		/// Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show to the user.
+		/// </summary>
+		/// <returns>The index of the selected button, or -1 if the user pressed ESC to close the dialog.</returns>
+		/// <param name="title">Title for the query.</param>
+		/// <param name="message">Message to display, might contain multiple lines.</param>
+		/// <param name="buttons">Array of buttons to add.</param>
+		/// <remarks>
+		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
+		/// from the size of the title, message. and buttons.
+		/// </remarks>
+		public static int ErrorQuery (ustring title, ustring message, params ustring [] buttons)
+		{
+			return QueryFull (true, 0, 0, title, message, buttons);
+		}
+
+		static int QueryFull (bool useErrorColors, int width, int height, ustring title, ustring message, params ustring [] buttons)
 		{
+			const int defaultWidth = 30;
 			int textWidth = Label.MaxWidth (message, width);
-			int clicked = -1, count = 0;
-
-			var d = new Dialog (title, Math.Max(width, textWidth) + 4, height);
-			if (useErrorColors)
-				d.ColorScheme = Colors.Error;
+			int textHeight = message.Count (ustring.Make ('\n')) + 1;
+			int msgboxHeight = Math.Max (1, textHeight) + 4; // textHeight + (top + top padding + buttons + bottom)
 
+			// Create button array for Dialog
+			int count = 0;
+			List<Button> buttonList = new List<Button> ();
 			foreach (var s in buttons) {
-				int n = count++;
 				var b = new Button (s);
-				b.Clicked += delegate {
-					clicked = n;
-					d.Running = false;
-				};
-				d.AddButton (b);
+				if (count == 0) {
+					b.IsDefault = true;
+				}
+				buttonList.Add (b);
+				count++;
+			}
+
+			// Create Dialog (retain backwards compat by supporting specifying height/width)
+			Dialog d;
+			if (width == 0 & height == 0) {
+				d = new Dialog (title, buttonList.ToArray ());
+				d.Height = msgboxHeight;
+			} else {
+				d = new Dialog (title, Math.Max (width, textWidth) + 4, height, buttonList.ToArray ());
 			}
+
+			if (useErrorColors) {
+				d.ColorScheme = Colors.Error;
+			}
+
 			if (message != null) {
-				var l = new Label (textWidth > width ? 0 : (width - 4 - textWidth) / 2, 0, message);
+				var l = new Label (textWidth > width ? 0 : (width - 4 - textWidth) / 2, 1, message);
+				l.LayoutStyle = LayoutStyle.Computed;
+				l.TextAlignment = TextAlignment.Centered;
+				l.X = Pos.Center ();
+				l.Y = Pos.Center ();
+				l.Width = Dim.Fill (2);
+				l.Height = Dim.Fill (2);
 				d.Add (l);
 			}
 
+			// Dynamically size Width
+			int msgboxWidth = Math.Max (defaultWidth, Math.Max (title.Length + 8, Math.Max (textWidth + 4, d.GetButtonsWidth ()) + 8)); // textWidth + (left + padding + padding + right)
+			d.Width = msgboxWidth;
+
+			// Setup actions
+			int clicked = -1;
+			for (int n = 0; n < buttonList.Count; n++) {
+				int buttonId = n;
+				buttonList [n].Clicked += () => {
+					clicked = buttonId;
+					Application.RequestStop ();
+				};
+			}
+
 			Application.Run (d);
 			return clicked;
 		}

+ 69 - 23
UICatalog/Scenarios/Buttons.cs

@@ -1,4 +1,6 @@
-using Terminal.Gui;
+using NStack;
+using System;
+using Terminal.Gui;
 
 namespace UICatalog {
 	[ScenarioMetadata (Name: "Buttons", Description: "Demonstrates all sorts of Buttons")]
@@ -13,59 +15,64 @@ namespace UICatalog {
 				Y = 0,
 			};
 			Win.Add (editLabel);
-			var edit = new TextField ("") {
-				X = Pos.Right (editLabel) + 1,
-				Y = Pos.Top (editLabel),
-				Width = Dim.Fill (2),
-			};
+			// Add a TextField using Absolute layout. Use buttons to move/grow.
+			var edit = new TextField (31, 0, 25, "");
 			Win.Add (edit);
 
 			// This is the default button (IsDefault = true); if user presses ENTER in the TextField
 			// the scenario will quit
-			var defaultButton = new Button ("Quit") {
+			var defaultButton = new Button ("_Quit") {
 				X = Pos.Center (),
 				//TODO: Change to use Pos.AnchorEnd()
-				Y= Pos.Bottom(Win) - 3,
+				Y = Pos.Bottom (Win) - 3,
 				IsDefault = true,
 				Clicked = () => Application.RequestStop (),
 			};
 			Win.Add (defaultButton);
 
+			static void DoMessage (Button button, ustring txt)
+			{
+				button.Clicked = () => {
+					var btnText = button.Text.ToString ();
+					MessageBox.Query (30, 7, "Message", $"Did you click {txt.ToString ()}?", "Yes", "No");
+				};
+			}
+
 			var y = 2;
-			var button = new Button (10, y, "Base Color") {
+			var button = new Button (10, y, "Ba_se Color") {
 				ColorScheme = Colors.Base,
-				Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No") 
 			};
+			DoMessage (button, button.Text);
 			Win.Add (button);
 
 			y += 2;
-			Win.Add (new Button (10, y, "Error Color") { 
+			Win.Add (button = new Button (10, y, "Error Color") {
 				ColorScheme = Colors.Error,
-				Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No") 
 			});
+			DoMessage (button, button.Text);
 
 			y += 2;
-			Win.Add (new Button (10, y, "Dialog Color") {
+			Win.Add (button = new Button (10, y, "Dialog Color") {
 				ColorScheme = Colors.Dialog,
-				Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
 			});
+			DoMessage (button, button.Text);
 
 			y += 2;
-			Win.Add (new Button (10, y, "Menu Color") {
+			Win.Add (button = new Button (10, y, "Menu Color") {
 				ColorScheme = Colors.Menu,
-				Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
 			});
+			DoMessage (button, button.Text);
 
 			y += 2;
-			Win.Add (new Button (10, y, "TopLevel Color") {
+			Win.Add (button = new Button (10, y, "TopLevel Color") {
 				ColorScheme = Colors.TopLevel,
-				Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
 			});
+			DoMessage (button, button.Text);
 
 			y += 2;
-			Win.Add (new Button (10, y, "A super long button that will probably expose a bug in clipping or wrapping of text. Will it?") {
-				Clicked = () => MessageBox.Query (30, 7, "Message", "Question?", "Yes", "No")
+			Win.Add (button = new Button (10, y, "A super long _Button that will probably expose a bug in clipping or wrapping of text. Will it?") {
 			});
+			DoMessage (button, button.Text);
 
 			y += 2;
 			// Note the 'N' in 'Newline' will be the hotkey
@@ -76,13 +83,14 @@ namespace UICatalog {
 			y += 2;
 			// BUGBUG: Buttons don't support specifying hotkeys with _?!?
 			Win.Add (button = new Button ("Te_xt Changer") {
-				X = 10, 
+				X = 10,
 				Y = y
 			});
-			button.Clicked = () => button.Text += $"{y++}";
+
+			button.Clicked = () => button.Text += "!"; 
 
 			Win.Add (new Button ("Lets see if this will move as \"Text Changer\" grows") {
-				X = Pos.Right(button) + 10,
+				X = Pos.Right (button) + 10,
 				Y = y,
 			});
 
@@ -100,7 +108,45 @@ namespace UICatalog {
 				},
 			});
 
+			// Demonstrates how changing the View.Frame property can move Views
+			y += 2;
+			var moveBtn = new Button (10, y, "Move This Button via Frame") {
+				ColorScheme = Colors.Error,
+			};
+			moveBtn.Clicked = () => {
+				moveBtn.Frame = new Rect (moveBtn.Frame.X + 5, moveBtn.Frame.Y, moveBtn.Frame.Width, moveBtn.Frame.Height);
+			};
+			Win.Add (moveBtn);
+
+			// Demo changing hotkey
+			ustring MoveHotkey (ustring txt)
+			{
+				// Remove the '_'
+				var i = txt.IndexOf ('_');
+				var start = txt [0, i];
+				txt = start + txt [i + 1, txt.Length];
+
+				// Move over one or go to start
+				i++;
+				if (i >= txt.Length) {
+					i = 0;
+				}
+
+				// Slip in the '_'
+				start = txt [0, i];
+				txt = start + ustring.Make ('_') + txt [i, txt.Length];
+
+				return txt;
+			}
 
+			y += 2;
+			var moveHotKeyBtn = new Button (10, y, "Click to Change th_is Button's Hotkey") {
+				ColorScheme = Colors.TopLevel,
+			};
+			moveHotKeyBtn.Clicked = () => {
+				moveHotKeyBtn.Text = MoveHotkey (moveHotKeyBtn.Text);
+			};
+			Win.Add (moveHotKeyBtn);
 		}
 	}
 }

+ 79 - 0
UICatalog/Scenarios/Clipping.cs

@@ -0,0 +1,79 @@
+using System;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Clipping", Description: "Used to test that things clip correctly")]
+	[ScenarioCategory ("Bug Repro")]
+
+	class Clipping : Scenario {
+
+		public override void Init (Toplevel top)
+		{
+			Application.Init ();
+
+			Top = top;
+			if (Top == null) {
+				Top = Application.Top;
+			}
+
+			Top.ColorScheme = Colors.Base;
+			//Win = new TopLevel($"CTRL-Q to Close - Scenario: {GetName ()}") {
+			//	X = 0,
+			//	Y = 0,
+			//	Width = Dim.Fill (),
+			//	Height = Dim.Fill ()
+			//};
+			//Top.Add (Win);
+		}
+
+		public override void Setup ()
+		{
+			//Win.X = 1;
+			//Win.Y = 2;
+			//Win.Width = Dim.Fill () - 4;
+			//Win.Height = Dim.Fill () - 2;
+			var label = new Label ("ScrollView (new Rect (5, 5, 100, 60)) with a 200, 100 ContentSize...") {
+				X = 0, Y = 0,
+				ColorScheme = Colors.Dialog
+			};
+			Top.Add (label);
+
+			var scrollView = new ScrollView (new Rect (3, 3, 50, 20));
+			scrollView.ColorScheme = Colors.Menu;
+			scrollView.ContentSize = new Size (100, 60);
+			//ContentOffset = new Point (0, 0),
+			scrollView.ShowVerticalScrollIndicator = true;
+			scrollView.ShowHorizontalScrollIndicator = true;
+
+			var embedded1 = new Window ("1") {
+				X = 3,
+				Y = 3,
+				Width = Dim.Fill (3),
+				Height = Dim.Fill (3),
+				ColorScheme = Colors.Dialog
+			};
+
+			var embedded2 = new Window ("2") {
+				X = 3,
+				Y = 3,
+				Width = Dim.Fill (3),
+				Height = Dim.Fill (3),
+				ColorScheme = Colors.Error
+			};
+			embedded1.Add (embedded2);
+
+			var embedded3 = new Window ("3") {
+				X = 3,
+				Y = 3,
+				Width = Dim.Fill (3),
+				Height = Dim.Fill (3),
+				ColorScheme = Colors.TopLevel
+			};
+			embedded2.Add (embedded3);
+
+			scrollView.Add (embedded1);
+					
+			Top.Add (scrollView);
+		}
+	}
+}

+ 1 - 1
UICatalog/Scenarios/ComputedLayout.cs

@@ -42,7 +42,7 @@ namespace UICatalog {
 				ColorScheme = Colors.Error
 			};
 
-			Application.Resized += (sender, a) => {
+			Win.LayoutComplete += (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)];
 			};

+ 171 - 0
UICatalog/Scenarios/Dialogs.cs

@@ -0,0 +1,171 @@
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Dialogs", Description: "Demonstrates how to the Dialog class")]
+	[ScenarioCategory ("Controls")]
+	[ScenarioCategory ("Dialogs")]
+	class Dialogs : Scenario {
+		public override void Setup ()
+		{
+			var frame = new FrameView ("Dialog Options") {
+				X = Pos.Center(),
+				Y = 1,
+				Width = Dim.Percent(75),
+				Height = 10
+			};
+			Win.Add (frame);
+
+			var label = new Label ("width:") {
+				X = 0,
+				Y = 0,
+				Width = 15,
+				Height = 1,
+				TextAlignment = Terminal.Gui.TextAlignment.Right,
+			};
+			frame.Add (label);
+			var widthEdit = new TextField ("0") {
+				X = Pos.Right (label) + 1,
+				Y = Pos.Top (label),
+				Width = 5,
+				Height = 1
+			};
+			frame.Add (widthEdit);
+
+			label = new Label ("height:") {
+				X = 0,
+				Y = Pos.Bottom (label),
+				Width = Dim.Width(label),
+				Height = 1,
+				TextAlignment = Terminal.Gui.TextAlignment.Right,
+			};
+			frame.Add (label);
+			var heightEdit = new TextField ("0") {
+				X = Pos.Right (label) + 1,
+				Y = Pos.Top (label),
+				Width = 5,
+				Height = 1
+			};
+			frame.Add (heightEdit);
+
+			frame.Add (new Label ("If height & width are both 0,") {
+				X = Pos.Right (widthEdit) + 2,
+				Y = Pos.Top (widthEdit),
+			});
+			frame.Add (new Label ("the Dialog will size to 80% of container.") {
+				X = Pos.Right (heightEdit) + 2,
+				Y = Pos.Top (heightEdit),
+			});
+
+			label = new Label ("Title:") {
+				X = 0,
+				Y = Pos.Bottom (label),
+				Width = Dim.Width (label),
+				Height = 1,
+				TextAlignment = Terminal.Gui.TextAlignment.Right,
+			};
+			frame.Add (label);
+			var titleEdit = new TextField ("Title") {
+				X = Pos.Right (label) + 1,
+				Y = Pos.Top (label),
+				Width = Dim.Fill(),
+				Height = 1
+			};
+			frame.Add (titleEdit);
+
+			label = new Label ("Num Buttons:") {
+				X = 0,
+				Y = Pos.Bottom (titleEdit),
+				Width = Dim.Width (label),
+				Height = 1,
+				TextAlignment = Terminal.Gui.TextAlignment.Right,
+			};
+			frame.Add (label);
+			var numButtonsEdit = new TextField ("3") {
+				X = Pos.Right (label) + 1,
+				Y = Pos.Top (label),
+				Width = 5,
+				Height = 1
+			};
+			frame.Add (numButtonsEdit);
+
+			frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit) 
+				+ Dim.Height(numButtonsEdit) + 2;
+
+			label = new Label ("Button Pressed:") {
+				X = Pos.Center (),
+				Y = Pos.Bottom (frame) + 2,
+				Height = 1,
+				TextAlignment = Terminal.Gui.TextAlignment.Right,
+			};
+			Win.Add (label);
+			var buttonPressedLabel = new Label ("") {
+				X = Pos.Center (),
+				Y = Pos.Bottom (frame) + 4,
+				Width = 25,
+				Height = 1,
+				ColorScheme = Colors.Error,
+			};
+
+			var btnText = new [] { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" };
+			var showDialogButton = new Button ("Show Dialog") {
+				X = Pos.Center(),
+				Y = Pos.Bottom (frame) + 2			,
+				IsDefault = true,
+				Clicked = () => {
+					try {
+						int width = int.Parse (widthEdit.Text.ToString ());
+						int height = int.Parse (heightEdit.Text.ToString ());
+						int numButtons = int.Parse (numButtonsEdit.Text.ToString ());
+
+						var buttons = new List<Button> ();
+						var clicked = -1;
+						for (int i = 0; i < numButtons; i++) {
+							var buttonId = i;
+							var button = new Button (btnText [buttonId % 10], is_default: buttonId == 0) {
+								Clicked = () => {
+									clicked = buttonId;
+									Application.RequestStop ();
+								},
+							};
+							buttons.Add(button);
+						}
+
+						// This tests dynamically adding buttons; ensuring the dialog resizes if needed and 
+						// the buttons are laid out correctly
+						var dialog = new Dialog (titleEdit.Text, width, height, buttons.ToArray ());
+						var add = new Button ("Add a button") {
+							X = Pos.Center (),
+							Y = Pos.Center (),
+							Clicked = () => {
+								var buttonId = buttons.Count;
+								var button = new Button (btnText [buttonId % 10], is_default: buttonId == 0) {
+									Clicked = () => {
+										clicked = buttonId;
+										Application.RequestStop ();
+									},
+								};
+								buttons.Add (button);
+								dialog.AddButton (button);
+							},
+						};
+						dialog.Add (add);
+
+						Application.Run (dialog);
+						buttonPressedLabel.Text = $"{clicked}";
+
+					} catch (FormatException) {
+						buttonPressedLabel.Text = "Invalid Options";
+					}
+				},
+			};
+			Win.Add (showDialogButton);
+
+			Win.Add (buttonPressedLabel);
+		}
+	}
+}

+ 4 - 4
UICatalog/Scenarios/Editor.cs

@@ -76,7 +76,7 @@ namespace UICatalog {
 		private void LoadFile ()
 		{
 			if (!_saved) {
-				MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+				MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok");
 			}
 
 			if (_fileName != null) {
@@ -90,17 +90,17 @@ namespace UICatalog {
 
 		private void Paste ()
 		{
-			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok");
 		}
 
 		private void Cut ()
 		{
-			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok");
 		}
 
 		private void Copy ()
 		{
-			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok");
 			//if (_textView != null && _textView.SelectedLength != 0) {
 			//	_textView.Copy ();
 			//}

+ 4 - 4
UICatalog/Scenarios/HexEditor.cs

@@ -69,7 +69,7 @@ namespace UICatalog {
 		{
 			MemoryStream stream = null;
 			if (!_saved) {
-				MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+				MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok");
 			}
 
 			if (_fileName != null) {
@@ -83,17 +83,17 @@ namespace UICatalog {
 
 		private void Paste ()
 		{
-			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok");
 		}
 
 		private void Cut ()
 		{
-			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok");
 		}
 
 		private void Copy ()
 		{
-			MessageBox.ErrorQuery (0, 10, "Not Implemented", "Functionality not yet implemented.", "Ok");
+			MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok");
 			//if (_textView != null && _textView.SelectedLength != 0) {
 			//	_textView.Copy ();
 			//}

+ 27 - 11
UICatalog/Scenarios/MessageBoxes.cs

@@ -1,4 +1,5 @@
-using System;
+using NStack;
+using System;
 using System.Collections.Generic;
 using System.Text;
 using Terminal.Gui;
@@ -18,7 +19,7 @@ namespace UICatalog {
 			};
 			Win.Add (frame);
 
-			var label = new Label ("Width:") {
+			var label = new Label ("width:") {
 				X = 0,
 				Y = 0,
 				Width = 15,
@@ -26,7 +27,7 @@ namespace UICatalog {
 				TextAlignment = Terminal.Gui.TextAlignment.Right,
 			};
 			frame.Add (label);
-			var widthEdit = new TextField ("50") {
+			var widthEdit = new TextField ("0") {
 				X = Pos.Right (label) + 1,
 				Y = Pos.Top (label),
 				Width = 5,
@@ -34,7 +35,7 @@ namespace UICatalog {
 			};
 			frame.Add (widthEdit);
 
-			label = new Label ("Height:") {
+			label = new Label ("height:") {
 				X = 0,
 				Y = Pos.Bottom (label),
 				Width = Dim.Width(label),
@@ -42,7 +43,7 @@ namespace UICatalog {
 				TextAlignment = Terminal.Gui.TextAlignment.Right,
 			};
 			frame.Add (label);
-			var heightEdit = new TextField ("6") {
+			var heightEdit = new TextField ("0") {
 				X = Pos.Right (label) + 1,
 				Y = Pos.Top (label),
 				Width = 5,
@@ -50,6 +51,15 @@ namespace UICatalog {
 			};
 			frame.Add (heightEdit);
 
+			frame.Add (new Label ("If height & width are both 0,") {
+				X = Pos.Right (widthEdit) + 2,
+				Y = Pos.Top (widthEdit),
+			});
+			frame.Add (new Label ("the MessageBox will be sized automatically.") {
+				X = Pos.Right (heightEdit) + 2,
+				Y = Pos.Top (heightEdit),
+			});
+
 			label = new Label ("Title:") {
 				X = 0,
 				Y = Pos.Bottom (label),
@@ -74,17 +84,19 @@ namespace UICatalog {
 				TextAlignment = Terminal.Gui.TextAlignment.Right,
 			};
 			frame.Add (label);
-			var messageEdit = new TextField ("Message") {
+			var messageEdit = new TextView () {
+				Text = "Message",
 				X = Pos.Right (label) + 1,
 				Y = Pos.Top (label),
 				Width = Dim.Fill (),
-				Height = 1
+				Height = 5,
+				ColorScheme = Colors.Dialog,
 			};
 			frame.Add (messageEdit);
 
 			label = new Label ("Num Buttons:") {
 				X = 0,
-				Y = Pos.Bottom (label),
+				Y = Pos.Bottom (messageEdit),
 				Width = Dim.Width (label),
 				Height = 1,
 				TextAlignment = Terminal.Gui.TextAlignment.Right,
@@ -130,6 +142,8 @@ namespace UICatalog {
 				ColorScheme = Colors.Error,
 			};
 
+			var btnText = new [] { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" };
+
 			var showMessageBoxButton = new Button ("Show MessageBox") {
 				X = Pos.Center(),
 				Y = Pos.Bottom (frame) + 2			,
@@ -139,21 +153,23 @@ namespace UICatalog {
 						int width = int.Parse (widthEdit.Text.ToString ());
 						int height = int.Parse (heightEdit.Text.ToString ());
 						int numButtons = int.Parse (numButtonsEdit.Text.ToString ());
-						var btns = new List<string> ();
+
+						var btns = new List<ustring> ();
 						for (int i = 0; i < numButtons; i++) {
-							btns.Add($"Btn {i}");
+							btns.Add(btnText[i % 10]);
 						}
 						if (styleRadioGroup.Selected == 0) {
 							buttonPressedLabel.Text = $"{MessageBox.Query (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), btns.ToArray ())}";
 						} else {
 							buttonPressedLabel.Text = $"{MessageBox.ErrorQuery (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), btns.ToArray ())}";
 						}
-					} catch {
+					} catch (FormatException) {
 						buttonPressedLabel.Text = "Invalid Options";
 					}
 				},
 			};
 			Win.Add (showMessageBoxButton);
+
 			Win.Add (buttonPressedLabel);
 		}
 	}

+ 122 - 5
UICatalog/Scenarios/Scrolling.cs

@@ -7,10 +7,89 @@ namespace UICatalog {
 	[ScenarioCategory ("Bug Repro")]
 
 	class Scrolling : Scenario {
+
+		//class Box10x : View, IScrollView {
+		class Box10x : View {
+			int w = 40;
+			int h = 50;
+
+			public bool WantCursorPosition { get; set; } = false;
+
+			public Box10x (int x, int y) : base (new Rect (x, y, 20, 10))
+			{
+			}
+
+			public Size GetContentSize ()
+			{
+				return new Size (w, h);
+			}
+
+			public void SetCursorPosition (Point pos)
+			{
+				throw new NotImplementedException ();
+			}
+
+			public override void Redraw (Rect bounds)
+			{
+				//Point pos = new Point (region.X, region.Y);
+				Driver.SetAttribute (ColorScheme.Focus);
+
+				for (int y = 0; y < h; y++) {
+					Move (0, y);
+					Driver.AddStr (y.ToString ());
+					for (int x = 0; x < w - y.ToString ().Length; x++) {
+						//Driver.AddRune ((Rune)('0' + (x + y) % 10));
+						if (y.ToString ().Length < w)
+							Driver.AddStr (" ");
+					}
+				}
+				//Move (pos.X, pos.Y);
+			}
+		}
+
+		class Filler : View {
+			public Filler (Rect rect) : base (rect)
+			{
+			}
+
+			public override void Redraw (Rect bounds)
+			{
+				Driver.SetAttribute (ColorScheme.Focus);
+				var f = Frame;
+
+				for (int y = 0; y < f.Width; y++) {
+					Move (0, y);
+					for (int x = 0; x < f.Height; x++) {
+						Rune r;
+						switch (x % 3) {
+						case 0:
+							Driver.AddRune (y.ToString ().ToCharArray (0, 1) [0]);
+							if (y > 9)
+								Driver.AddRune (y.ToString ().ToCharArray (1, 1) [0]);
+							r = '.';
+							break;
+						case 1:
+							r = 'o';
+							break;
+						default:
+							r = 'O';
+							break;
+						}
+						Driver.AddRune (r);
+					}
+				}
+			}
+		}
+
 		public override void Setup ()
 		{
+			Win.X = 3;
+			Win.Y = 3;
+			Win.Width = Dim.Fill () - 3;
+			Win.Height = Dim.Fill () - 3;
 			var label = new Label ("ScrollView (new Rect (2, 2, 50, 20)) with a 200, 100 ContentSize...") {
-				X = 0, Y = 0,
+				X = 0,
+				Y = 0,
 				ColorScheme = Colors.Dialog
 			};
 			Win.Add (label);
@@ -42,8 +121,9 @@ namespace UICatalog {
 			};
 			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)];
+			Win.LayoutComplete += (sender, a) => {
+				horizontalRuler.Text = rule.Repeat ((int)Math.Ceiling ((double)(horizontalRuler.Bounds.Width) / (double)rule.Length)) [0..(horizontalRuler.Bounds.Width)] +
+				"\n" + "|         ".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)];
 			};
 
@@ -56,7 +136,7 @@ namespace UICatalog {
 			scrollView.Add (new Button ("A very long button. Should be wide enough to demo clipping!") {
 				X = 3,
 				Y = 4,
-				Width = 50,
+				Width = Dim.Fill (6),
 				Clicked = () => MessageBox.Query (20, 7, "MessageBox", "Neat?", "Yes", "No")
 			});
 
@@ -97,7 +177,44 @@ namespace UICatalog {
 			};
 			scrollView.Add (anchorButton);
 
-			Win.Add (scrollView);
+			var scrollView2 = new ScrollView (new Rect (55, 2, 20, 8)) {
+				ContentSize = new Size (20, 50),
+				//ContentOffset = new Point (0, 0),
+				ShowVerticalScrollIndicator = true,
+				ShowHorizontalScrollIndicator = true
+			};
+			scrollView2.Add (new Filler(new Rect (0, 0, 60, 40)));
+
+			// This is just to debug the visuals of the scrollview when small
+			var scrollView3 = new ScrollView (new Rect (55, 15, 3, 3)) {
+				ContentSize = new Size (100, 100),
+				ShowVerticalScrollIndicator = true,
+				ShowHorizontalScrollIndicator = true
+			};
+			scrollView3.Add (new Box10x (0, 0));
+
+			int count = 0;
+			var mousePos = new Label ("Mouse: ");
+			mousePos.X = Pos.Center ();
+			mousePos.Y = Pos.AnchorEnd (1);
+			mousePos.Width = 50;
+			Application.RootMouseEvent += delegate (MouseEvent me) {
+				mousePos.TextColor = Colors.TopLevel.Normal;
+				mousePos.Text = $"Mouse: ({me.X},{me.Y}) - {me.Flags} {count++}";
+			};
+
+			var progress = new ProgressBar ();
+			progress.X = 5;
+			progress.Y = Pos.AnchorEnd (3);
+			progress.Width = 50;
+			bool timer (MainLoop caller)
+			{
+				progress.Pulse ();
+				return true;
+			}
+			Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
+
+			Win.Add (scrollView, scrollView2, scrollView3, mousePos, progress);
 		}
 	}
 }

+ 1 - 0
UICatalog/Scenarios/SystemConsole.cs

@@ -14,6 +14,7 @@ namespace UICatalog {
 		public override void RequestStop ()
 		{
 			base.RequestStop ();
+			Application.UseSystemConsole = false;
 		}
 
 		public override void Run ()

+ 13 - 14
UICatalog/Scenarios/WindowsAndFrameViews.cs

@@ -32,21 +32,20 @@ namespace UICatalog {
 		{
 			static int About ()
 			{
-				//return MessageBox.Query (50, 10, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")
+				return MessageBox.Query ("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,
+				//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;
+				//Application.Run (about);
+				//return 0;
 
 			}
 
@@ -88,7 +87,7 @@ namespace UICatalog {
 					X = Pos.Center (),
 					Y = 0,
 					ColorScheme = Colors.Error,
-					Clicked = () => MessageBox.ErrorQuery (30, 10, win.Title.ToString (), "Neat?", "Yes", "No")
+					Clicked = () => MessageBox.ErrorQuery (win.Title.ToString (), "Neat?", "Yes", "No")
 				});
 				var subWin = new Window ("Sub Window") {
 					X = Pos.Percent (0),
@@ -128,7 +127,7 @@ namespace UICatalog {
 				X = Pos.Center (),
 				Y = 0,
 				ColorScheme = Colors.Error,
-				//Clicked = () => MessageBox.ErrorQuery (30, 10, frame.Title.ToString (), "Neat?", "Yes", "No")
+				//Clicked = () => MessageBox.ErrorQuery (frame.Title.ToString (), "Neat?", "Yes", "No")
 			});
 			var subWinofFV = new Window ("this is a Sub-Window") {
 				X = Pos.Percent (0),

+ 68 - 73
UICatalog/UICatalog.cs

@@ -5,7 +5,10 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.Globalization;
 using System.Linq;
+using System.Reflection;
+using System.Text;
 using Terminal.Gui;
+using Rune = System.Rune;
 
 /// <remarks>
 /// <para>
@@ -55,6 +58,8 @@ namespace UICatalog {
 		private static StatusItem _scrolllock;
 
 		private static Scenario _runningScenario = null;
+		private static bool _useSystemConsole = false;
+		private static MenuItem _sysConsoleMenu;
 
 		static void Main (string [] args)
 		{
@@ -76,6 +81,7 @@ namespace UICatalog {
 
 			Scenario scenario = GetScenarioToRun ();
 			while (scenario != null) {
+				Application.UseSystemConsole = _useSystemConsole;
 				Application.Init ();
 				scenario.Init (Application.Top);
 				scenario.Setup ();
@@ -86,16 +92,66 @@ namespace UICatalog {
 			Application.Shutdown ();
 		}
 
+		/// <summary>
+		/// This shows the selection UI. Each time it is run, it calls Application.Init to reset everything.
+		/// </summary>
+		/// <returns></returns>
+		private static Scenario GetScenarioToRun ()
+		{
+			Application.UseSystemConsole = false;
+			Application.Init ();
+
+			if (_menu == null) {
+				Setup ();
+			}
+
+			_top = Application.Top;
+
+			_top.KeyDown += KeyDownHandler;
+
+			_top.Add (_menu);
+			_top.Add (_leftPane);
+			_top.Add (_rightPane);
+			_top.Add (_statusBar);
+
+			_top.Ready += (o, a) => {
+				if (_runningScenario != null) {
+					_top.SetFocus (_rightPane);
+					_runningScenario = null;
+				}
+			};
+
+			Application.Run (_top, false);
+			Application.Shutdown ();
+			return _runningScenario;
+		}
+
+
 		/// <summary>
 		/// Create all controls. This gets called once and the controls remain with their state between Sceanrio runs.
 		/// </summary>
 		private static void Setup ()
 		{
+			StringBuilder aboutMessage = new StringBuilder ();
+			aboutMessage.AppendLine ("UI Catalog is a comprehensive sample library for Terminal.Gui");
+			aboutMessage.AppendLine ("");
+			aboutMessage.AppendLine ($"Version: {typeof(UICatalogApp).Assembly.GetName ().Version}");
+			aboutMessage.AppendLine ($"Using Terminal.Gui Version: {typeof (Terminal.Gui.Application).Assembly.GetName ().Version}");
+			aboutMessage.AppendLine ("");
+
+			void HandleSysConsoleMenuChange ()
+			{
+				_useSystemConsole = !_useSystemConsole;
+				_sysConsoleMenu.Title = $"[{(_useSystemConsole ? 'x' : ' ')}] _Use System Console";
+			}
+			_sysConsoleMenu = new MenuItem ($"[{(_useSystemConsole ? 'x' : ' ')}] _Use System Console", "", () => HandleSysConsoleMenuChange ());
+
 			_menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("_Quit", "", () => Application.RequestStop() )
 				}),
-				new MenuBarItem ("_About...", "About this app", () =>  MessageBox.Query (50, 10, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")),
+				new MenuBarItem ("_Settings", new MenuItem [] { _sysConsoleMenu }),
+				new MenuBarItem ("_About...", "About this app", () =>  MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "Ok")),
 			});
 
 			_leftPane = new Window ("Categories") {
@@ -142,24 +198,17 @@ namespace UICatalog {
 				CanFocus = true,
 			};
 
-			//_scenarioListView.OnKeyPress += (KeyEvent ke) => {
-			//	if (_top.MostFocused == _scenarioListView && ke.Key == Key.Enter) {
-			//		_scenarioListView_OpenSelectedItem (null, null);
-			//	}
-			//};
-
 			_scenarioListView.OpenSelectedItem += _scenarioListView_OpenSelectedItem;
 			_rightPane.Add (_scenarioListView);
 
 			_categoryListView.SelectedItem = 0;
 			_categoryListView.OnSelectedChanged ();
 
-			_capslock = new StatusItem (Key.CharMask, "CapslockOff", null);
-			_numlock = new StatusItem (Key.CharMask, "NumlockOff", null);
-			_scrolllock = new StatusItem (Key.CharMask, "ScrolllockOff", null);
+			_capslock = new StatusItem (Key.CharMask, "Capslock", null);
+			_numlock = new StatusItem (Key.CharMask, "Numlock", null);
+			_scrolllock = new StatusItem (Key.CharMask, "Scrolllock", null);
 
 			_statusBar = new StatusBar (new StatusItem [] {
-				//new StatusItem(Key.F1, "~F1~ Help", () => Help()),
 				new StatusItem(Key.ControlQ, "~CTRL-Q~ Quit", () => {
 					if (_runningScenario is null){
 						// This causes GetScenarioToRun to return null
@@ -175,51 +224,6 @@ namespace UICatalog {
 			});
 		}
 
-		/// <summary>
-		/// This shows the selection UI. Each time it is run, it calls Application.Init to reset everything.
-		/// </summary>
-		/// <returns></returns>
-		private static Scenario GetScenarioToRun ()
-		{
-			Application.Init ();
-
-			if (_menu == null) {
-				Setup ();
-			}
-
-			_top = Application.Top;
-
-			_top.KeyDown += KeyDownHandler;
-
-			_top.Add (_menu);
-			_top.Add (_leftPane);
-			_top.Add (_rightPane);
-			_top.Add (_statusBar);
-
-			// HACK: There is no other way to SetFocus before Application.Run. See Issue #445
-#if false
-			if (_runningScenario != null)
-				Application.Iteration += Application_Iteration;
-#else
-			_top.Ready += (o, a) => {
-				if (_runningScenario != null) {
-					_top.SetFocus (_rightPane);
-					_runningScenario = null;
-				}
-			};
-#endif
-			
-			Application.Run (_top, false);
-			return _runningScenario;
-		}
-
-#if false
-		private static void Application_Iteration (object sender, EventArgs e)
-		{
-			Application.Iteration -= Application_Iteration;
-			_top.SetFocus (_rightPane);
-		}
-#endif
 		private static void _scenarioListView_OpenSelectedItem (object sender, EventArgs e)
 		{
 			if (_runningScenario is null) {
@@ -232,7 +236,7 @@ namespace UICatalog {
 		internal class ScenarioListDataSource : IListDataSource {
 			public List<Type> Scenarios { get; set; }
 
-			public bool IsMarked (int item) => false;//  Scenarios [item].IsMarked;
+			public bool IsMarked (int item) => false;
 
 			public int Count => Scenarios.Count;
 
@@ -274,7 +278,6 @@ namespace UICatalog {
 			{
 				return Scenarios;
 			}
-
 		}
 
 		/// <summary>
@@ -285,15 +288,7 @@ namespace UICatalog {
 		/// <param name="ke"></param>
 		private static void KeyDownHandler (object sender, View.KeyEventEventArgs a)
 		{
-			if (_runningScenario != null) {
-				//switch (ke.Key) {
-				//case Key.Esc:
-				//	//_runningScenario.RequestStop ();
-				//	break;
-				//case Key.Enter:
-				//	break;
-				//}<
-			} else if (a.KeyEvent.Key == Key.Tab || a.KeyEvent.Key == Key.BackTab) {
+			if (a.KeyEvent.Key == Key.Tab || a.KeyEvent.Key == Key.BackTab) {
 				// BUGBUG: Work around Issue #434 by implementing our own TAB navigation
 				if (_top.MostFocused == _categoryListView)
 					_top.SetFocus (_rightPane);
@@ -302,26 +297,26 @@ namespace UICatalog {
 			}
 
 			if (a.KeyEvent.IsCapslock) {
-				_capslock.Title = "CapslockOn";
+				_capslock.Title = "Capslock: On";
 				_statusBar.SetNeedsDisplay ();
 			} else {
-				_capslock.Title = "CapslockOff";
+				_capslock.Title = "Capslock: Off";
 				_statusBar.SetNeedsDisplay ();
 			}
 
 			if (a.KeyEvent.IsNumlock) {
-				_numlock.Title = "NumlockOn";
+				_numlock.Title = "Numlock: On";
 				_statusBar.SetNeedsDisplay ();
 			} else {
-				_numlock.Title = "NumlockOff";
+				_numlock.Title = "Numlock: Off";
 				_statusBar.SetNeedsDisplay ();
 			}
 
 			if (a.KeyEvent.IsScrolllock) {
-				_scrolllock.Title = "ScrolllockOn";
+				_scrolllock.Title = "Scrolllock: On";
 				_statusBar.SetNeedsDisplay ();
 			} else {
-				_scrolllock.Title = "ScrolllockOff";
+				_scrolllock.Title = "Scrolllock: Off";
 				_statusBar.SetNeedsDisplay ();
 			}
 		}

+ 1 - 0
UICatalog/UICatalog.csproj

@@ -4,6 +4,7 @@
     <OutputType>Exe</OutputType>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <StartupObject>UICatalog.UICatalogApp</StartupObject>
+    <AssemblyVersion>1.0.0.1</AssemblyVersion>
   </PropertyGroup>
 
   <ItemGroup>