Sfoglia il codice sorgente

Fixed a slew of Dim.Auto(Content) issues preventing Pos.Align to work for MessageBox/Dialog.
Messagebox and Dialog now use Pos.Align and Dim.Auto

Tig 1 anno fa
parent
commit
671fdea8a3

+ 66 - 13
Terminal.Gui/View/Layout/DimAuto.cs

@@ -60,7 +60,8 @@ public class DimAuto () : Dim
         var subviewsSize = 0;
 
         int autoMin = MinimumContentDim?.GetAnchor (superviewContentSize) ?? 0;
-        
+        int autoMax = MaximumContentDim?.GetAnchor (superviewContentSize) ?? int.MaxValue;
+
         if (Style.FastHasFlags (DimAutoStyle.Text))
         {
             textSize = int.Max (autoMin, dimension == Dimension.Width ? us.TextFormatter.Size.Width : us.TextFormatter.Size.Height);
@@ -79,13 +80,31 @@ public class DimAuto () : Dim
 
                 List<View> subviews;
 
+                #region Not Anchored and Are Not Dependent
+                // Start with subviews that are not anchored to the end, aligned, or dependent on content size
+                // [x] PosAnchorEnd
+                // [x] PosAlign
+                // [ ] PosCenter
+                // [ ] PosPercent
+                // [ ] PosView
+                // [ ] PosFunc
+                // [x] DimFill
+                // [ ] DimPercent
+                // [ ] DimFunc
+                // [ ] DimView
                 if (dimension == Dimension.Width)
                 {
-                    subviews = us.Subviews.Where (v => v.X is not PosAnchorEnd && v.Width is not DimFill).ToList ();
+                    subviews = us.Subviews.Where (v => v.X is not PosAnchorEnd
+                                                       && v.X is not PosAlign
+                                                       && v.X is not PosCenter
+                                                       && v.Width is not DimFill).ToList ();
                 }
                 else
                 {
-                    subviews = us.Subviews.Where (v => v.Y is not PosAnchorEnd && v.Height is not DimFill).ToList ();
+                    subviews = us.Subviews.Where (v => v.Y is not PosAnchorEnd
+                                                       && v.Y is not PosAlign
+                                                       && v.Y is not PosCenter
+                                                       && v.Height is not DimFill).ToList ();
                 }
 
                 for (var i = 0; i < subviews.Count; i++)
@@ -96,10 +115,15 @@ public class DimAuto () : Dim
 
                     if (size > subviewsSize)
                     {
+                        // BUGBUG: Should we break here? Or choose min/max?
                         subviewsSize = size;
                     }
                 }
+                #endregion Not Anchored and Are Not Dependent
 
+                #region Anchored
+                // Now, handle subviews that are anchored to the end
+                // [x] PosAnchorEnd
                 if (dimension == Dimension.Width)
                 {
                     subviews = us.Subviews.Where (v => v.X is PosAnchorEnd).ToList ();
@@ -117,8 +141,33 @@ public class DimAuto () : Dim
                 }
 
                 subviewsSize += maxAnchorEnd;
+                #endregion Anchored
 
+                #region Center
+                // Now, handle subviews that are Centered
+                if (dimension == Dimension.Width)
+                {
+                    subviews = us.Subviews.Where (v => v.X is PosCenter).ToList ();
+                }
+                else
+                {
+                    subviews = us.Subviews.Where (v => v.Y is PosCenter).ToList ();
+                }
 
+                int maxCenter = 0;
+                for (var i = 0; i < subviews.Count; i++)
+                {
+                    View v = subviews [i];
+                    maxCenter = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
+                }
+
+                subviewsSize += maxCenter;
+                #endregion Center
+
+                #region Are Dependent
+                // Now, go back to those that are dependent on content size
+                // [x] DimFill
+                // [ ] DimPercent
                 if (dimension == Dimension.Width)
                 {
                     subviews = us.Subviews.Where (v => v.Width is DimFill).ToList ();
@@ -128,20 +177,24 @@ public class DimAuto () : Dim
                     subviews = us.Subviews.Where (v => v.Height is DimFill).ToList ();
                 }
 
+                int maxFill = 0;
                 for (var i = 0; i < subviews.Count; i++)
                 {
                     View v = subviews [i];
 
                     if (dimension == Dimension.Width)
                     {
-                        v.SetRelativeLayout (new Size (autoMin - subviewsSize, 0));
+                        v.SetRelativeLayout (new Size (autoMax - subviewsSize, 0));
                     }
                     else
                     {
-                        v.SetRelativeLayout (new Size (0, autoMin - subviewsSize));
+                        v.SetRelativeLayout (new Size (0, autoMax - subviewsSize));
                     }
+                    maxFill = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
                 }
 
+                subviewsSize += maxFill;
+                #endregion Are Dependent
             }
         }
 
@@ -156,14 +209,14 @@ public class DimAuto () : Dim
         Thickness thickness = us.GetAdornmentsThickness ();
 
         max += dimension switch
-               {
-                   Dimension.Width => thickness.Horizontal,
-                   Dimension.Height => thickness.Vertical,
-                   Dimension.None => 0,
-                   _ => throw new ArgumentOutOfRangeException (nameof (dimension), dimension, null)
-               };
-
-        return int.Min (max, MaximumContentDim?.GetAnchor (superviewContentSize) ?? max);
+        {
+            Dimension.Width => thickness.Horizontal,
+            Dimension.Height => thickness.Vertical,
+            Dimension.None => 0,
+            _ => throw new ArgumentOutOfRangeException (nameof (dimension), dimension, null)
+        };
+
+        return int.Min (max, autoMax);
     }
 
     internal override bool ReferencesOtherViews ()

+ 2 - 2
Terminal.Gui/View/Layout/DimAutoStyle.cs

@@ -24,7 +24,7 @@ public enum DimAutoStyle
     ///         The corresponding dimension of the view's <see cref="View.Text"/> will be ignored.
     ///     </para>
     /// </summary>
-    Content = 0,
+    Content = 1,
 
     /// <summary>
     ///     <para>
@@ -36,7 +36,7 @@ public enum DimAutoStyle
     ///         The corresponding dimensions of the <see cref="View.Subviews"/> will be ignored.
     ///     </para>
     /// </summary>
-    Text = 1,
+    Text = 2,
 
     /// <summary>
     ///     The dimension will be computed using both the view's <see cref="View.Text"/> and

+ 28 - 44
Terminal.Gui/Views/Dialog.cs

@@ -15,6 +15,32 @@ namespace Terminal.Gui;
 /// </remarks>
 public class Dialog : Window
 {
+    /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
+    /// <remarks>This property can be set in a Theme.</remarks>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    [JsonConverter (typeof (JsonStringEnumConverter))]
+    public static Alignment DefaultButtonAlignment { get; set; } = Alignment.End;
+
+    /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
+    /// <remarks>This property can be set in a Theme.</remarks>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    [JsonConverter (typeof (JsonStringEnumConverter))]
+    public static AlignmentModes DefaultButtonAlignmentModes { get; set; } = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems;
+
+    /// <summary>
+    ///     Defines the default minimum Dialog width, as a percentage of the container width. Can be configured via
+    ///     <see cref="ConfigurationManager"/>.
+    /// </summary>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    public static int DefaultMinimumWidth { get; set; } = 25;
+
+    /// <summary>
+    ///     Defines the default minimum Dialog height, as a percentage of the container width. Can be configured via
+    ///     <see cref="ConfigurationManager"/>.
+    /// </summary>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    public static int DefaultMinimumHeight { get; set; } = 25;
+
 
     // TODO: Reenable once border/borderframe design is settled
     /// <summary>
@@ -42,10 +68,9 @@ public class Dialog : Window
         Arrangement = ViewArrangement.Movable;
         X = Pos.Center ();
         Y = Pos.Center ();
-        //ValidatePosDim = true;
 
-        Width = Dim.Percent (85);
-        Height = Dim.Percent (85);
+        Width = Dim.Auto (DimAutoStyle.Content, minimumContentDim: Dim.Percent (DefaultMinimumWidth), Dim.Percent (90));
+        Height = Dim.Auto (DimAutoStyle.Content, minimumContentDim: Dim.Percent (DefaultMinimumHeight), Dim.Percent (90));
         ColorScheme = Colors.ColorSchemes ["Dialog"];
 
         Modal = true;
@@ -122,18 +147,6 @@ public class Dialog : Window
         }
     }
 
-    /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
-    /// <remarks>This property can be set in a Theme.</remarks>
-    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter))]
-    public static Alignment DefaultButtonAlignment { get; set; } = Alignment.End;
-
-    /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
-    /// <remarks>This property can be set in a Theme.</remarks>
-    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter))]
-    public static AlignmentModes DefaultButtonAlignmentModes { get; set; } = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems;
-
     /// <summary>
     ///     Adds a <see cref="Button"/> to the <see cref="Dialog"/>, its layout will be controlled by the
     ///     <see cref="Dialog"/>
@@ -160,33 +173,4 @@ public class Dialog : Window
             LayoutSubviews ();
         }
     }
-
-    /// <inheritdoc/>
-    //public override void LayoutSubviews ()
-    //{
-    //    if (_inLayout)
-    //    {
-    //        return;
-    //    }
-
-    //    _inLayout = true;
-    //    SetRelativeLayout(SuperView?.ContentSize ?? Driver.Screen.Size);
-    //    LayoutButtons ();
-    //    base.LayoutSubviews ();
-    //    _inLayout = false;
-    //}
-
-    // Get the width of all buttons, not including any Margin.
-    internal int GetButtonsWidth ()
-    {
-        if (_buttons.Count == 0)
-        {
-            return 0;
-        }
-
-        //var widths = buttons.Select (b => b.TextFormatter.GetFormattedSize ().Width + b.BorderFrame.Thickness.Horizontal + b.Padding.Thickness.Horizontal);
-        IEnumerable<int> widths = _buttons.Select (b => b.Frame.Width);
-
-        return widths.Sum ();
-    }
 }

+ 105 - 90
Terminal.Gui/Views/MessageBox.cs

@@ -25,27 +25,42 @@
 public static class MessageBox
 {
     /// <summary>
-    ///     The index of the selected button, or -1 if the user pressed ESC to close the dialog. This is useful for web
-    ///     based console where by default there is no SynchronizationContext or TaskScheduler.
+    ///     Defines the default border styling for <see cref="MessageBox"/>. Can be configured via
+    ///     <see cref="ConfigurationManager"/>.
     /// </summary>
-    public static int Clicked { get; private set; } = -1;
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
 
     /// <summary>
-    ///     Defines the default border styling for <see cref="Dialog"/>. Can be configured via
+    ///     Defines the default minimum MessageBox width, as a percentage of the container width. Can be configured via
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
+    public static int DefaultMinimumWidth { get; set; } = 60;
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Defines the default minimum Dialog height, as a percentage of the container width. Can be configured via
+    ///     <see cref="ConfigurationManager"/>.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed ESC to close the dialog.</returns>
-    /// <param name="width">Width for the window.</param>
-    /// <param name="height">Height for the window.</param>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    public static int DefaultMinimumHeight { get; set; } = 5;
+    /// <summary>
+    ///     The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox. This is useful for web
+    ///     based console where there is no SynchronizationContext or TaskScheduler.
+    /// </summary>
+    /// <remarks>
+    ///     Warning: This is a global variable and should be used with caution. It is not thread safe.
+    /// </remarks>
+    public static int Clicked { get; private set; } = -1;
+
+    /// <summary>
+    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons.
+    /// </summary>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="width">Width for the MessageBox.</param>
+    /// <param name="height">Height for the MessageBox.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
     ///     Use <see cref="ErrorQuery(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
@@ -60,9 +75,9 @@ public static class MessageBox
     ///     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>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
     /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</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
@@ -71,14 +86,13 @@ public static class MessageBox
     public static int ErrorQuery (string title, string message, params string [] buttons) { return QueryFull (true, 0, 0, title, message, 0, true, buttons); }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed ESC to close the dialog.</returns>
-    /// <param name="width">Width for the window.</param>
-    /// <param name="height">Height for the window.</param>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="width">Width for the MessageBox.</param>
+    /// <param name="height">Height for the MessageBox.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="defaultButton">Index of the default button.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
@@ -101,9 +115,9 @@ public static class MessageBox
     ///     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>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="defaultButton">Index of the default button.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
@@ -119,13 +133,13 @@ public static class MessageBox
     ///     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>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
     /// <param name="width">Width for the window.</param>
     /// <param name="height">Height for the window.</param>
     /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="wrapMessagge">If wrap the message or not.</param>
+    /// <param name="wrapMessage">If wrap the message or not.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
     ///     Use <see cref="ErrorQuery(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
@@ -137,22 +151,22 @@ public static class MessageBox
         string title,
         string message,
         int defaultButton = 0,
-        bool wrapMessagge = true,
+        bool wrapMessage = true,
         params string [] buttons
     )
     {
-        return QueryFull (true, width, height, title, message, defaultButton, wrapMessagge, buttons);
+        return QueryFull (true, width, height, title, message, defaultButton, wrapMessage, 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>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
     /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="wrapMessagge">If wrap the message or not.</param>
+    /// <param name="wrapMessage">If wrap the message or not. The default is <see langword="true"/></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
@@ -162,26 +176,25 @@ public static class MessageBox
         string title,
         string message,
         int defaultButton = 0,
-        bool wrapMessagge = true,
+        bool wrapMessage = true,
         params string [] buttons
     )
     {
-        return QueryFull (true, 0, 0, title, message, defaultButton, wrapMessagge, buttons);
+        return QueryFull (true, 0, 0, title, message, defaultButton, wrapMessage, buttons);
     }
 
     /// <summary>
-    ///     Presents a normal <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed ESC to close the dialog.</returns>
-    /// <param name="width">Width for the window.</param>
-    /// <param name="height">Height for the window.</param>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="width">Width for the MessageBox.</param>
+    /// <param name="height">Height for the MessageBox.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
-    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the
-    ///     contents.
+    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
+    ///     the contents.
     /// </remarks>
     public static int Query (int width, int height, string title, string message, params string [] buttons)
     {
@@ -189,33 +202,43 @@ public static class MessageBox
     }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons.
     /// </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>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
+    /// <para>
     ///     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.
+    ///     automatically determined from the size of the title, message. and buttons.
+    /// </para>
+    /// <para>
+    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
+    ///     the contents.
+    /// </para>
     /// </remarks>
     public static int Query (string title, string message, params string [] buttons) { return QueryFull (false, 0, 0, title, message, 0, true, buttons); }
 
     /// <summary>
-    ///     Presents a normal <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed ESC to close the dialog.</returns>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
     /// <param name="width">Width for the window.</param>
     /// <param name="height">Height for the window.</param>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="defaultButton">Index of the default button.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
-    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the
-    ///     contents.
+    /// <para>
+    ///     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.
+    /// </para>
+    /// <para>
+    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
+    ///     the contents.
+    /// </para>
     /// </remarks>
     public static int Query (
         int width,
@@ -230,12 +253,11 @@ public static class MessageBox
     }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons.
     /// </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>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
     /// <param name="defaultButton">Index of the default button.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
@@ -248,16 +270,16 @@ public static class MessageBox
     }
 
     /// <summary>
-    ///     Presents a normal <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
+    ///     Presents a <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>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
     /// <param name="width">Width for the window.</param>
     /// <param name="height">Height for the window.</param>
     /// <param name="title">Title for the query.</param>
     /// <param name="message">Message to display, might contain multiple lines.</param>
     /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="wrapMessagge">If wrap the message or not.</param>
+    /// <param name="wrapMessage">If wrap the message or not.</param>
     /// <param name="buttons">Array of buttons to add.</param>
     /// <remarks>
     ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the
@@ -269,27 +291,23 @@ public static class MessageBox
         string title,
         string message,
         int defaultButton = 0,
-        bool wrapMessagge = true,
+        bool wrapMessage = true,
         params string [] buttons
     )
     {
-        return QueryFull (false, width, height, title, message, defaultButton, wrapMessagge, buttons);
+        return QueryFull (false, width, height, title, message, defaultButton, wrapMessage, buttons);
     }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
+    ///     Presents a <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>
+    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
     /// <param name="title">Title for the query.</param>
     /// <param name="message">Message to display, might contain multiple lines.</param>
     /// <param name="defaultButton">Index of the default button.</param>
     /// <param name="wrapMessage">If wrap the message or not.</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 (
         string title,
         string message,
@@ -342,13 +360,13 @@ public static class MessageBox
 
         var d = new Dialog
         {
+            Title = title,
+            Buttons = buttonList.ToArray (),
             ButtonAlignment = Alignment.Center,
             ButtonAlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems,
-            Buttons = buttonList.ToArray (),
-            Title = title,
             BorderStyle = DefaultBorderStyle,
-            Width = Dim.Auto (DimAutoStyle.Content, minimumContentDim: Dim.Percent(60)),
-            Height = Dim.Auto (DimAutoStyle.Content),
+            Width = Dim.Auto (DimAutoStyle.Content, minimumContentDim: 1, Dim.Percent (90)),
+            Height = Dim.Auto (DimAutoStyle.Content, minimumContentDim: 1, Dim.Percent (90)),
         };
 
         if (width != 0)
@@ -361,22 +379,18 @@ public static class MessageBox
             d.Height = height;
         }
 
-        if (useErrorColors)
-        {
-            d.ColorScheme = Colors.ColorSchemes ["Error"];
-        }
-        else
-        {
-            d.ColorScheme = Colors.ColorSchemes ["Dialog"];
-        }
+        d.ColorScheme = useErrorColors ? Colors.ColorSchemes ["Error"] : Colors.ColorSchemes ["Dialog"];
 
         var messageLabel = new Label
         {
+            HotKeySpecifier = new Rune ('\xFFFF'),
+            Width = Dim.Auto (DimAutoStyle.Text),
+            Height = Dim.Auto (DimAutoStyle.Text),
             Text = message,
             TextAlignment = Alignment.Center,
             X = Pos.Center (),
             Y = 0,
-           // ColorScheme = Colors.ColorSchemes ["Error"]
+            //ColorScheme = Colors.ColorSchemes ["Error"],
         };
 
         messageLabel.TextFormatter.WordWrap = wrapMessage;
@@ -384,15 +398,16 @@ public static class MessageBox
 
         if (wrapMessage)
         {
+            int buttonHeight = buttonList.Count > 0 ? buttonList [0].Frame.Height : 0;
+
             messageLabel.Width = Dim.Fill ();
-            messageLabel.Height = Dim.Fill (1);
-            int GetWrapSize ()
+            messageLabel.Height = Dim.Func (() => GetWrapSize ().Height);
+            Size GetWrapSize ()
             {
                 // A bit of a hack to get the height of the wrapped text.
-                messageLabel.TextFormatter.Size = new (d.ContentSize.Width, 1000);
-                return messageLabel.TextFormatter.FormatAndGetSize ().Height;
+                messageLabel.TextFormatter.Size = d.ContentSize with { Height = 1000 };
+                return messageLabel.TextFormatter.FormatAndGetSize ();
             }
-            d.Height = Dim.Auto (DimAutoStyle.Content, minimumContentDim: Dim.Func (GetWrapSize) + 1);
         }
 
         d.Add (messageLabel);

+ 1 - 0
UICatalog/Resources/config.json

@@ -32,6 +32,7 @@
   "Themes": [
     {
       "UI Catalog Theme": {
+        "Dialog.DefaultButtonAlignment": "Fill",
         "ColorSchemes": [
           {
             "UI Catalog Scheme": {

+ 2 - 1
UICatalog/Scenarios/CharacterMap.cs

@@ -1081,7 +1081,7 @@ internal class CharMap : View
             };
             dlg.Add (label);
 
-            var json = new TextView
+            var json = new TextView ()
             {
                 X = 0,
                 Y = Pos.Bottom (label),
@@ -1090,6 +1090,7 @@ internal class CharMap : View
                 ReadOnly = true,
                 Text = decResponse
             };
+
             dlg.Add (json);
 
             Application.Run (dlg);

+ 7 - 15
UICatalog/Scenarios/Dialogs.cs

@@ -171,11 +171,6 @@ public class Dialogs : Scenario
             X = Pos.Center (), Y = Pos.Bottom (frame) + 5, ColorScheme = Colors.ColorSchemes ["Error"], Text = " "
         };
 
-        // glyphsNotWords
-        // false:var btnText = new [] { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" };
-        // true: var btnText = new [] { "0", "\u2780", "➁", "\u2783", "\u2784", "\u2785", "\u2786", "\u2787", "\u2788", "\u2789" };
-        // \u2781 is ➁ dingbats \ufb70 is	
-
         var showDialogButton = new Button
         {
             X = Pos.Center (), Y = Pos.Bottom (frame) + 2, IsDefault = true, Text = "_Show Dialog"
@@ -258,14 +253,6 @@ public class Dialogs : Scenario
                 buttons.Add (button);
             }
 
-            //if (buttons.Count > 1) {
-            //	buttons [1].Text = "Accept";
-            //	buttons [1].IsDefault = true;
-            //	buttons [0].Visible = false;
-            //	buttons [0].Text = "_Back";
-            //	buttons [0].IsDefault = false;
-            //}
-
             // This tests dynamically adding buttons; ensuring the dialog resizes if needed and 
             // the buttons are laid out correctly
             dialog = new ()
@@ -282,7 +269,12 @@ public class Dialogs : Scenario
                 dialog.Width = width;
             }
 
-            var add = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "_Add a button" };
+            var add = new Button
+            {
+                X = Pos.Center (),
+                Y = Pos.Center (),
+                Text = "_Add a button"
+            };
 
             add.Accept += (s, e) =>
                           {
@@ -321,7 +313,7 @@ public class Dialogs : Scenario
             {
                 X = Pos.Center (),
                 Y = Pos.Center () + 1,
-                Text = $"A_dd a {char.ConvertFromUtf32 (CODE_POINT)} to each button"
+                Text = $"A_dd a {char.ConvertFromUtf32 (CODE_POINT)} to each button. This text is really long for a reason."
             };
 
             addChar.Accept += (s, e) =>

+ 1 - 4
UICatalog/Scenarios/MessageBoxes.cs

@@ -186,7 +186,7 @@ public class MessageBoxes : Scenario
 
         var ckbWrapMessage = new CheckBox
         {
-            X = Pos.Right (label) + 1, Y = Pos.Bottom (styleRadioGroup), Text = "_Wrap Message", Checked = false
+            X = Pos.Right (label) + 1, Y = Pos.Bottom (styleRadioGroup), Text = "_Wrap Message", Checked = true
         };
         frame.Add (ckbWrapMessage);
 
@@ -207,8 +207,6 @@ public class MessageBoxes : Scenario
             Text = " "
         };
 
-        //var btnText = new [] { "_Zero", "_One", "T_wo", "_Three", "_Four", "Fi_ve", "Si_x", "_Seven", "_Eight", "_Nine" };
-
         var showMessageBoxButton = new Button
         {
             X = Pos.Center (), Y = Pos.Bottom (frame) + 2, IsDefault = true, Text = "_Show MessageBox"
@@ -227,7 +225,6 @@ public class MessageBoxes : Scenario
 
                                                for (var i = 0; i < numButtons; i++)
                                                {
-                                                   //btns.Add(btnText[i % 10]);
                                                    btns.Add (NumberToWords.Convert (i));
                                                }
 

+ 14 - 15
UICatalog/UICatalog.cs

@@ -303,7 +303,7 @@ internal class UICatalogApp
         _aboutMessage = new ();
         _aboutMessage.AppendLine (@"A comprehensive sample library for");
         _aboutMessage.AppendLine (@"");
-        _aboutMessage.AppendLine (@"  _______                  _             _   _____       _  ");
+        _aboutMessage.AppendLine (@" _______                  _             _   _____       _   ");
         _aboutMessage.AppendLine (@" |__   __|                (_)           | | / ____|     (_) ");
         _aboutMessage.AppendLine (@"    | | ___ _ __ _ __ ___  _ _ __   __ _| || |  __ _   _ _  ");
         _aboutMessage.AppendLine (@"    | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | | ");
@@ -345,7 +345,6 @@ internal class UICatalogApp
         // 'app' closed cleanly.
         foreach (Responder? inst in Responder.Instances)
         {
-            
             Debug.Assert (inst.WasDisposed);
         }
 
@@ -810,24 +809,24 @@ internal class UICatalogApp
             string GetDiagnosticsTitle (Enum diag)
             {
                 return Enum.GetName (_diagnosticFlags.GetType (), diag) switch
-                       {
-                           "Off" => OFF,
-                           "Ruler" => RULER,
-                           "Padding" => PADDING,
-                           "MouseEnter" => MOUSEENTER,
-                           _ => ""
-                       };
+                {
+                    "Off" => OFF,
+                    "Ruler" => RULER,
+                    "Padding" => PADDING,
+                    "MouseEnter" => MOUSEENTER,
+                    _ => ""
+                };
             }
 
             Enum GetDiagnosticsEnumValue (string title)
             {
                 return title switch
-                       {
-                           RULER => ViewDiagnosticFlags.Ruler,
-                           PADDING => ViewDiagnosticFlags.Padding,
-                           MOUSEENTER => ViewDiagnosticFlags.MouseEnter,
-                           _ => null!
-                       };
+                {
+                    RULER => ViewDiagnosticFlags.Ruler,
+                    PADDING => ViewDiagnosticFlags.Padding,
+                    MOUSEENTER => ViewDiagnosticFlags.MouseEnter,
+                    _ => null!
+                };
             }
 
             void SetDiagnosticsFlag (Enum diag, bool add)