2
0
Tig 11 сар өмнө
parent
commit
29f3550489
49 өөрчлөгдсөн 2101 нэмэгдсэн , 3874 устгасан
  1. 2 2
      NativeAot/Program.cs
  2. 3 30
      Terminal.Gui/Application/Application.Keyboard.cs
  3. 2 2
      Terminal.Gui/Application/Application.Mouse.cs
  4. 8 4
      Terminal.Gui/Application/Application.cs
  5. 4 4
      Terminal.Gui/Drawing/Thickness.cs
  6. 19 27
      Terminal.Gui/View/Layout/Dim.cs
  7. 3 9
      Terminal.Gui/View/Layout/DimAbsolute.cs
  8. 13 60
      Terminal.Gui/View/Layout/DimAuto.cs
  9. 7 7
      Terminal.Gui/View/Layout/DimCombine.cs
  10. 2 13
      Terminal.Gui/View/Layout/DimFill.cs
  11. 7 13
      Terminal.Gui/View/Layout/DimFunc.cs
  12. 6 17
      Terminal.Gui/View/Layout/DimPercent.cs
  13. 1 7
      Terminal.Gui/View/Layout/DimView.cs
  14. 1 1
      Terminal.Gui/View/Layout/Pos.cs
  15. 3 9
      Terminal.Gui/View/Layout/PosAbsolute.cs
  16. 93 106
      Terminal.Gui/View/Layout/PosAlign.cs
  17. 1 7
      Terminal.Gui/View/Layout/PosAnchorEnd.cs
  18. 1 1
      Terminal.Gui/View/Layout/PosCenter.cs
  19. 7 7
      Terminal.Gui/View/Layout/PosCombine.cs
  20. 4 21
      Terminal.Gui/View/Layout/PosFunc.cs
  21. 3 9
      Terminal.Gui/View/Layout/PosPercent.cs
  22. 5 11
      Terminal.Gui/View/Layout/PosView.cs
  23. 0 1
      Terminal.Gui/View/View.Keyboard.cs
  24. 2 2
      Terminal.Gui/View/View.Layout.cs
  25. 2 2
      Terminal.Gui/Views/Label.cs
  26. 1 2
      Terminal.Gui/Views/Menu/MenuBar.cs
  27. 7 6
      Terminal.Gui/Views/NumericUpDown.cs
  28. 4 59
      Terminal.Gui/Views/Toplevel.cs
  29. 3 1
      UICatalog/Scenarios/ListViewWithSelection.cs
  30. 10 4
      UICatalog/UICatalog.cs
  31. 2 0
      UnitTests/Configuration/ConfigurationMangerTests.cs
  32. 1410 3161
      UnitTests/Text/TextFormatterTests.cs
  33. 215 158
      UnitTests/View/Layout/Dim.AutoTests.cs
  34. 4 1
      UnitTests/View/Layout/Dim.FuncTests.cs
  35. 10 2
      UnitTests/View/Layout/Dim.PercentTests.cs
  36. 52 52
      UnitTests/View/Layout/Pos.AlignTests.cs
  37. 0 9
      UnitTests/View/Layout/Pos.AnchorEndTests.cs
  38. 0 10
      UnitTests/View/Layout/Pos.CenterTests.cs
  39. 1 1
      UnitTests/View/Layout/Pos.FuncTests.cs
  40. 0 15
      UnitTests/View/Layout/Pos.Tests.cs
  41. 1 1
      UnitTests/View/TitleTests.cs
  42. 49 0
      UnitTests/Views/ContextMenuTests.cs
  43. 2 0
      UnitTests/Views/ListViewTests.cs
  44. 1 1
      UnitTests/Views/MenuBarTests.cs
  45. 1 1
      UnitTests/Views/NumericUpDownTests.cs
  46. 1 1
      UnitTests/Views/StatusBarTests.cs
  47. 8 0
      UnitTests/Views/TableViewTests.cs
  48. 76 15
      UnitTests/Views/ToplevelTests.cs
  49. 44 2
      docfx/docs/migratingfromv1.md

+ 2 - 2
NativeAot/Program.cs

@@ -17,14 +17,14 @@ public static class Program
 
         #region The code in this region is not intended for use in a native Aot self-contained. It's just here to make sure there is no functionality break with localization in Terminal.Gui using self-contained
 
-        if (Equals(Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures.Count == 0)
+        if (Equals(Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures!.Count == 0)
         {
             // Only happens if the project has <InvariantGlobalization>true</InvariantGlobalization>
             Debug.Assert (Application.SupportedCultures.Count == 0);
         }
         else
         {
-            Debug.Assert (Application.SupportedCultures.Count > 0);
+            Debug.Assert (Application.SupportedCultures!.Count > 0);
             Debug.Assert (Equals (CultureInfo.CurrentCulture, Thread.CurrentThread.CurrentUICulture));
         }
 

+ 3 - 30
Terminal.Gui/Application/Application.Keyboard.cs

@@ -190,7 +190,7 @@ public static partial class Application // Keyboard handling
 
                 foreach (Command command in appBinding.Commands)
                 {
-                    if (!CommandImplementations.ContainsKey (command))
+                    if (!CommandImplementations!.ContainsKey (command))
                     {
                         throw new NotSupportedException (
                                                          @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application."
@@ -274,7 +274,7 @@ public static partial class Application // Keyboard handling
     /// <summary>
     ///     Commands for Application.
     /// </summary>
-    private static Dictionary<Command, Func<CommandContext, bool?>> CommandImplementations { get; set; }
+    private static Dictionary<Command, Func<CommandContext, bool?>>? CommandImplementations { get; set; }
 
     /// <summary>
     ///     <para>
@@ -292,7 +292,7 @@ public static partial class Application // Keyboard handling
     /// </remarks>
     /// <param name="command">The command.</param>
     /// <param name="f">The function.</param>
-    private static void AddCommand (Command command, Func<bool?> f) { CommandImplementations [command] = ctx => f (); }
+    private static void AddCommand (Command command, Func<bool?> f) { CommandImplementations! [command] = ctx => f (); }
 
     static Application () { AddApplicationKeyBindings (); }
 
@@ -451,31 +451,4 @@ public static partial class Application // Keyboard handling
                           .Distinct ()
                           .ToList ();
     }
-
-    ///// <summary>
-    /////     Gets the list of Views that have <see cref="KeyBindingScope.Application"/> key bindings for the specified key.
-    ///// </summary>
-    ///// <remarks>
-    /////     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
-    ///// </remarks>
-    ///// <param name="key">The key to check.</param>
-    ///// <param name="views">Outputs the list of views bound to <paramref name="key"/></param>
-    ///// <returns><see langword="True"/> if successful.</returns>
-    //internal static bool TryGetKeyBindings (Key key, out List<View> views) { return _keyBindings.TryGetValue (key, out views); }
-
-    /// <summary>
-    ///     Removes all <see cref="KeyBindingScope.Application"/> scoped key bindings for the specified view.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
-    /// </remarks>
-    /// <param name="view">The view that is bound to the key.</param>
-    internal static void RemoveKeyBindings (View view)
-    {
-        List<KeyBinding> list = KeyBindings.Bindings
-                                           .Where (kv => kv.Value.Scope != KeyBindingScope.Application)
-                                           .Select (kv => kv.Value)
-                                           .Distinct ()
-                                           .ToList ();
-    }
 }

+ 2 - 2
Terminal.Gui/Application/Application.Mouse.cs

@@ -160,13 +160,13 @@ public static partial class Application // Mouse handling
                 Position = frameLoc,
                 Flags = mouseEvent.Flags,
                 ScreenPosition = mouseEvent.Position,
-                View = MouseGrabView
+                View = view ?? MouseGrabView
             };
 
             if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.Position) is false)
             {
                 // The mouse has moved outside the bounds of the view that grabbed the mouse
-                MouseEnteredView?.NewMouseLeaveEvent (mouseEvent);
+                MouseGrabView?.NewMouseLeaveEvent (mouseEvent);
             }
 
             //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");

+ 8 - 4
Terminal.Gui/Application/Application.cs

@@ -32,7 +32,7 @@ public static partial class Application
     /// <returns>A string representation of the Application </returns>
     public new static string ToString ()
     {
-        ConsoleDriver driver = Driver;
+        ConsoleDriver? driver = Driver;
 
         if (driver is null)
         {
@@ -47,13 +47,17 @@ public static partial class Application
     /// </summary>
     /// <param name="driver">The driver to use to render the contents.</param>
     /// <returns>A string representation of the Application </returns>
-    public static string ToString (ConsoleDriver driver)
+    public static string ToString (ConsoleDriver? driver)
     {
+        if (driver is null)
+        {
+            return string.Empty;
+        }
         var sb = new StringBuilder ();
 
-        Cell [,] contents = driver.Contents;
+        Cell [,] contents = driver?.Contents!;
 
-        for (var r = 0; r < driver.Rows; r++)
+        for (var r = 0; r < driver!.Rows; r++)
         {
             for (var c = 0; c < driver.Cols; c++)
             {

+ 4 - 4
Terminal.Gui/Drawing/Thickness.cs

@@ -61,7 +61,7 @@ public record struct Thickness
     [JsonInclude]
     public int Bottom
     {
-        get => (int)_sides.W;
+        readonly get => (int)_sides.W;
         set => _sides.W = value;
     }
 
@@ -249,7 +249,7 @@ public record struct Thickness
     [JsonInclude]
     public int Left
     {
-        get => (int)_sides.X;
+        readonly get => (int)_sides.X;
         set => _sides.X = value;
     }
 
@@ -265,7 +265,7 @@ public record struct Thickness
     [JsonInclude]
     public int Right
     {
-        get => (int)_sides.Z;
+        readonly get => (int)_sides.Z;
         set => _sides.Z = value;
     }
 
@@ -273,7 +273,7 @@ public record struct Thickness
     [JsonInclude]
     public int Top
     {
-        get => (int)_sides.Y;
+        readonly get => (int)_sides.Y;
         set => _sides.Y = value;
     }
 

+ 19 - 27
Terminal.Gui/View/Layout/Dim.cs

@@ -1,8 +1,8 @@
 #nullable enable
-using System.Diagnostics;
-
 namespace Terminal.Gui;
 
+using System.Numerics;
+
 /// <summary>
 ///     <para>
 ///         A Dim object describes the dimensions of a <see cref="View"/>. Dim is the type of the
@@ -78,7 +78,7 @@ namespace Terminal.Gui;
 ///     </para>
 ///     <para></para>
 /// </remarks>
-public abstract class Dim
+public abstract record Dim : IEqualityOperators<Dim, Dim, bool>
 {
     #region static Dim creation methods
 
@@ -113,12 +113,10 @@ public abstract class Dim
     /// <param name="maximumContentDim">The maximum dimension the View's ContentSize will be fit to.</param>
     public static Dim? Auto (DimAutoStyle style = DimAutoStyle.Auto, Dim? minimumContentDim = null, Dim? maximumContentDim = null)
     {
-        return new DimAuto ()
-        {
-            MinimumContentDim = minimumContentDim,
-            MaximumContentDim = maximumContentDim,
-            Style = style
-        };
+        return new DimAuto (
+                            MinimumContentDim: minimumContentDim,
+                            MaximumContentDim: maximumContentDim,
+                            Style: style);
     }
 
     /// <summary>
@@ -172,28 +170,22 @@ public abstract class Dim
 
     #endregion static Dim creation methods
 
+
     /// <summary>
-    ///     Indicates whether the specified type is in the hierarchy of this Dim object.
+    ///     Indicates whether the specified type <typeparamref name="T"/> is in the hierarchy of this Dim object.
     /// </summary>
-    /// <param name="type"></param>
-    /// <param name="dim"></param>
+    /// <param name="dim">A reference to this <see cref="Dim"/> instance.</param>
     /// <returns></returns>
-    public bool Has (Type type, out Dim dim)
+    public bool Has<T> (out Dim dim) where T : Dim
     {
         dim = this;
-        if (type == GetType ())
-        {
-            return true;
-        }
-
-        // If we are a PosCombine, we have to check the left and right
-        // to see if they are of the type we are looking for.
-        if (this is DimCombine { } combine && (combine.Left.Has (type, out dim) || combine.Right.Has (type, out dim)))
-        {
-            return true;
-        }
 
-        return false;
+        return this switch
+               {
+                   DimCombine combine => combine.Left.Has<T> (out dim) || combine.Right.Has<T> (out dim),
+                   T => true,
+                   _ => false
+               };
     }
 
     #region virtual methods
@@ -208,7 +200,7 @@ public abstract class Dim
     ///     subclass of Dim that is used. For example, DimAbsolute returns a fixed dimension, DimFactor returns a
     ///     dimension that is a certain percentage of the super view's size, and so on.
     /// </returns>
-    internal virtual int GetAnchor (int size) { return 0; }
+    internal abstract int GetAnchor (int size);
 
     /// <summary>
     ///     Calculates and returns the dimension of a <see cref="View"/> object. It takes into account the location of the
@@ -228,7 +220,7 @@ public abstract class Dim
     /// </returns>
     internal virtual int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
     {
-        return Math.Max (GetAnchor (superviewContentSize - location), 0);
+        return Math.Clamp (GetAnchor (superviewContentSize - location), 0, short.MaxValue);
     }
 
     /// <summary>

+ 3 - 9
Terminal.Gui/View/Layout/DimAbsolute.cs

@@ -10,19 +10,13 @@ namespace Terminal.Gui;
 ///         methods on the <see cref="Dim"/> class to create <see cref="Dim"/> objects instead.
 ///     </para>
 /// </remarks>
-/// <param name="size"></param>
-public class DimAbsolute (int size) : Dim
+/// <param name="Size"></param>
+public record DimAbsolute (int Size) : Dim
 {
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is DimAbsolute abs && abs.Size == Size; }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Size.GetHashCode (); }
-
     /// <summary>
     ///     Gets the size of the dimension.
     /// </summary>
-    public int Size { get; } = size;
+    public int Size { get; } = Size;
 
     /// <inheritdoc/>
     public override string ToString () { return $"Absolute({Size})"; }

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

@@ -15,64 +15,17 @@ namespace Terminal.Gui;
 ///         methods on the <see cref="Dim"/> class to create <see cref="Dim"/> objects instead.
 ///     </para>
 /// </remarks>
-public class DimAuto : Dim
+/// <param name="MaximumContentDim">The maximum dimension the View's ContentSize will be fit to.</param>
+/// <param name="MinimumContentDim">The minimum dimension the View's ContentSize will be constrained to.</param>
+/// <param name="Style">The <see cref="DimAutoStyle"/> of the <see cref="DimAuto"/>.</param>
+public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoStyle Style) : Dim
 {
-    private readonly Dim? _maximumContentDim;
-
-    private readonly Dim? _minimumContentDim;
-
-    private readonly DimAutoStyle _style;
-
-    /// <inheritdoc/>
-    public override bool Equals (object? other)
-    {
-        if (other is not DimAuto auto)
-        {
-            return false;
-        }
-
-        return auto.MinimumContentDim == MinimumContentDim && auto.MaximumContentDim == MaximumContentDim && auto.Style == Style;
-    }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return HashCode.Combine (MinimumContentDim, MaximumContentDim, Style); }
-
-    /// <summary>
-    ///     Gets the maximum dimension the View's ContentSize will be fit to. NOT CURRENTLY SUPPORTED.
-    /// </summary>
-
-    // ReSharper disable once ConvertToAutoProperty
-    public required Dim? MaximumContentDim
-    {
-        get => _maximumContentDim;
-        init => _maximumContentDim = value;
-    }
-
-    /// <summary>
-    ///     Gets the minimum dimension the View's ContentSize will be constrained to.
-    /// </summary>
-
-    // ReSharper disable once ConvertToAutoProperty
-    public required Dim? MinimumContentDim
-    {
-        get => _minimumContentDim;
-        init => _minimumContentDim = value;
-    }
-
-    /// <summary>
-    ///     Gets the style of the DimAuto.
-    /// </summary>
-
-    // ReSharper disable once ConvertToAutoProperty
-    public required DimAutoStyle Style
-    {
-        get => _style;
-        init => _style = value;
-    }
-
     /// <inheritdoc/>
     public override string ToString () { return $"Auto({Style},{MinimumContentDim},{MaximumContentDim})"; }
 
+    /// <inheritdoc />
+    internal override int GetAnchor (int size) => 0;
+
     internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
     {
         var textSize = 0;
@@ -181,8 +134,8 @@ public class DimAuto : Dim
                                                                         && !v.X.Has (typeof (PosAnchorEnd), out _)
                                                                         && !v.X.Has (typeof (PosAlign), out _)
                                                                         && !v.X.Has (typeof (PosCenter), out _)
-                                                                        && !v.Width.Has (typeof (DimFill), out _)
-                                                                        && !v.Width.Has (typeof (DimPercent), out _)
+                                                                        && !v.Width.Has<DimFill> (out _)
+                                                                        && !v.Width.Has<DimPercent> (out _)
                                                                   )
                                                            .ToList ();
                 }
@@ -194,8 +147,8 @@ public class DimAuto : Dim
                                                                         && !v.Y.Has (typeof (PosAnchorEnd), out _)
                                                                         && !v.Y.Has (typeof (PosAlign), out _)
                                                                         && !v.Y.Has (typeof (PosCenter), out _)
-                                                                        && !v.Height.Has (typeof (DimFill), out _)
-                                                                        && !v.Height.Has (typeof (DimPercent), out _)
+                                                                        && !v.Height.Has<DimFill> (out _)
+                                                                        && !v.Height.Has<DimPercent> (out _)
                                                                   )
                                                            .ToList ();
                 }
@@ -419,11 +372,11 @@ public class DimAuto : Dim
 
                 if (dimension == Dimension.Width)
                 {
-                    dimViewSubViews = includedSubviews.Where (v => v.Width is { } && v.Width.Has (typeof (DimView), out _)).ToList ();
+                    dimViewSubViews = includedSubviews.Where (v => v.Width is { } && v.Width.Has<DimView> (out _)).ToList ();
                 }
                 else
                 {
-                    dimViewSubViews = includedSubviews.Where (v => v.Height is { } && v.Height.Has (typeof (DimView), out _)).ToList ();
+                    dimViewSubViews = includedSubviews.Where (v => v.Height is { } && v.Height.Has<DimView> (out _)).ToList ();
                 }
 
                 for (var i = 0; i < dimViewSubViews.Count; i++)

+ 7 - 7
Terminal.Gui/View/Layout/DimCombine.cs

@@ -4,31 +4,31 @@ namespace Terminal.Gui;
 /// <summary>
 ///     Represents a dimension that is a combination of two other dimensions.
 /// </summary>
-/// <param name="add">
+/// <param name="Add">
 ///     Indicates whether the two dimensions are added or subtracted.
 /// </param>
 /// <remarks>
 ///     This is a low-level API that is typically used internally by the layout system. Use the various static
 ///     methods on the <see cref="Dim"/> class to create <see cref="Dim"/> objects instead.
 /// </remarks>
-/// <param name="left">The left dimension.</param>
-/// <param name="right">The right dimension.</param>
-public class DimCombine (AddOrSubtract add, Dim left, Dim right) : Dim
+/// <param name="Left">The left dimension.</param>
+/// <param name="Right">The right dimension.</param>
+public record DimCombine (AddOrSubtract Add, Dim Left, Dim Right) : Dim
 {
     /// <summary>
     ///     Gets whether the two dimensions are added or subtracted.
     /// </summary>
-    public AddOrSubtract Add { get; } = add;
+    public AddOrSubtract Add { get; } = Add;
 
     /// <summary>
     ///     Gets the left dimension.
     /// </summary>
-    public Dim Left { get; } = left;
+    public Dim Left { get; } = Left;
 
     /// <summary>
     ///     Gets the right dimension.
     /// </summary>
-    public Dim Right { get; } = right;
+    public Dim Right { get; } = Right;
 
     /// <inheritdoc/>
     public override string ToString () { return $"Combine({Left}{(Add == AddOrSubtract.Add ? '+' : '-')}{Right})"; }

+ 2 - 13
Terminal.Gui/View/Layout/DimFill.cs

@@ -8,20 +8,9 @@ namespace Terminal.Gui;
 ///     This is a low-level API that is typically used internally by the layout system. Use the various static
 ///     methods on the <see cref="Dim"/> class to create <see cref="Dim"/> objects instead.
 /// </remarks>
-/// <param name="margin">The margin to not fill.</param>
-public class DimFill (int margin) : Dim
+/// <param name="Margin">The margin to not fill.</param>
+public record DimFill (int Margin) : Dim
 {
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is DimFill fill && fill.Margin == Margin; }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Margin.GetHashCode (); }
-
-    /// <summary>
-    ///     Gets the margin to not fill.
-    /// </summary>
-    public int Margin { get; } = margin;
-
     /// <inheritdoc/>
     public override string ToString () { return $"Fill({Margin})"; }
 

+ 7 - 13
Terminal.Gui/View/Layout/DimFunc.cs

@@ -2,28 +2,22 @@
 namespace Terminal.Gui;
 
 /// <summary>
-///     Represents a function <see cref="Dim"/> object that computes the dimension by executing the provided function.
+///     Represents a function <see cref="Gui.Dim"/> object that computes the dimension by executing the provided function.
 /// </summary>
 /// <remarks>
 ///     This is a low-level API that is typically used internally by the layout system. Use the various static
-///     methods on the <see cref="Dim"/> class to create <see cref="Dim"/> objects instead.
+///     methods on the <see cref="Gui.Dim"/> class to create <see cref="Gui.Dim"/> objects instead.
 /// </remarks>
-/// <param name="dim"></param>
-public class DimFunc (Func<int> dim) : Dim
+/// <param name="Fn">The function that computes the dimension.</param>
+public record DimFunc (Func<int> Fn) : Dim
 {
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is DimFunc f && f.Func () == Func (); }
-
     /// <summary>
     ///     Gets the function that computes the dimension.
     /// </summary>
-    public new Func<int> Func { get; } = dim;
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Func.GetHashCode (); }
+    public Func<int> Fn { get; } = Fn;
 
     /// <inheritdoc/>
-    public override string ToString () { return $"DimFunc({Func ()})"; }
+    public override string ToString () { return $"DimFunc({Fn ()})"; }
 
-    internal override int GetAnchor (int size) { return Func (); }
+    internal override int GetAnchor (int size) { return Fn (); }
 }

+ 6 - 17
Terminal.Gui/View/Layout/DimPercent.cs

@@ -8,35 +8,24 @@ namespace Terminal.Gui;
 ///     This is a low-level API that is typically used internally by the layout system. Use the various static
 ///     methods on the <see cref="Dim"/> class to create <see cref="Dim"/> objects instead.
 /// </remarks>
-/// <param name="percent">The percentage.</param>
-/// <param name="mode">
+/// <param name="Percentage">The percentage.</param>
+/// <param name="Mode">
 ///     If <see cref="DimPercentMode.Position"/> the dimension is computed using the View's position (<see cref="View.X"/> or
 ///     <see cref="View.Y"/>); otherwise, the dimension is computed using the View's <see cref="View.GetContentSize ()"/>.
 /// </param>
-public class DimPercent (int percent, DimPercentMode mode = DimPercentMode.ContentSize) : Dim
+public record DimPercent (int Percentage, DimPercentMode Mode = DimPercentMode.ContentSize) : Dim
 {
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is DimPercent f && f.Percent == Percent && f.Mode == Mode; }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Percent.GetHashCode (); }
-
-    /// <summary>
-    ///     Gets the percentage.
-    /// </summary>
-    public new int Percent { get; } = percent;
-
     /// <summary>
     /// </summary>
     /// <returns></returns>
-    public override string ToString () { return $"Percent({Percent},{Mode})"; }
+    public override string ToString () { return $"Percent({Percentage},{Mode})"; }
 
     /// <summary>
     ///     Gets whether the dimension is computed using the View's position or GetContentSize ().
     /// </summary>
-    public DimPercentMode Mode { get; } = mode;
+    public DimPercentMode Mode { get; } = Mode;
 
-    internal override int GetAnchor (int size) { return (int)(size * (Percent / 100f)); }
+    internal override int GetAnchor (int size) { return (int)(size * (Percentage / 100f)); }
 
     internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
     {

+ 1 - 7
Terminal.Gui/View/Layout/DimView.cs

@@ -8,7 +8,7 @@ namespace Terminal.Gui;
 ///     This is a low-level API that is typically used internally by the layout system. Use the various static
 ///     methods on the <see cref="Dim"/> class to create <see cref="Dim"/> objects instead.
 /// </remarks>
-public class DimView : Dim
+public record DimView : Dim
 {
     /// <summary>
     ///     Initializes a new instance of the <see cref="DimView"/> class.
@@ -26,12 +26,6 @@ public class DimView : Dim
     /// </summary>
     public Dimension Dimension { get; }
 
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is DimView abs && abs.Target == Target && abs.Dimension == Dimension; }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Target!.GetHashCode (); }
-
     /// <summary>
     ///     Gets the View the dimension is anchored to.
     /// </summary>

+ 1 - 1
Terminal.Gui/View/Layout/Pos.cs

@@ -131,7 +131,7 @@ namespace Terminal.Gui;
 ///         </list>
 ///     </para>
 /// </remarks>
-public abstract class Pos
+public abstract record Pos
 {
     #region static Pos creation methods
 

+ 3 - 9
Terminal.Gui/View/Layout/PosAbsolute.cs

@@ -10,19 +10,13 @@ namespace Terminal.Gui;
 ///         methods on the <see cref="Pos"/> class to create <see cref="Pos"/> objects instead.
 ///     </para>
 /// </remarks>
-/// <param name="position"></param>
-public class PosAbsolute (int position) : Pos
+/// <param name="Position"></param>
+public record PosAbsolute (int Position) : Pos
 {
     /// <summary>
     ///     The position of the <see cref="View"/> in the layout.
     /// </summary>
-    public int Position { get; } = position;
-
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is PosAbsolute abs && abs.Position == Position; }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Position.GetHashCode (); }
+    public int Position { get; } = Position;
 
     /// <inheritdoc/>
     public override string ToString () { return $"Absolute({Position})"; }

+ 93 - 106
Terminal.Gui/View/Layout/PosAlign.cs

@@ -1,7 +1,6 @@
 #nullable enable
 
 using System.ComponentModel;
-using System.Drawing;
 
 namespace Terminal.Gui;
 
@@ -24,19 +23,13 @@ namespace Terminal.Gui;
 ///         The alignment is applied to all views with the same <see cref="GroupId"/>.
 ///     </para>
 /// </remarks>
-public class PosAlign : Pos
+public record PosAlign : Pos
 {
     /// <summary>
     ///     The cached location. Used to store the calculated location to minimize recalculating it.
     /// </summary>
     public int? _cachedLocation;
 
-    /// <summary>
-    ///     Gets the identifier of a set of views that should be aligned together. When only a single
-    ///     set of views in a SuperView is aligned, setting <see cref="GroupId"/> is not needed because it defaults to 0.
-    /// </summary>
-    public int GroupId { get; init; }
-
     private readonly Aligner? _aligner;
 
     /// <summary>
@@ -57,103 +50,64 @@ public class PosAlign : Pos
         }
     }
 
+    // TODO: PosAlign.CalculateMinDimension is a hack. Need to figure out a better way of doing this.
     /// <summary>
-    ///     Aligns the views in <paramref name="views"/> that have the same group ID as <paramref name="groupId"/>.
-    ///     Updates each view's cached _location.
+    ///     Returns the minimum size a group of views with the same <paramref name="groupId"/> can be.
     /// </summary>
     /// <param name="groupId"></param>
     /// <param name="views"></param>
     /// <param name="dimension"></param>
-    /// <param name="size"></param>
-    private static void AlignAndUpdateGroup (int groupId, IList<View> views, Dimension dimension, int size)
+    /// <returns></returns>
+    public static int CalculateMinDimension (int groupId, IList<View> views, Dimension dimension)
     {
         List<int> dimensionsList = new ();
 
         // PERF: If this proves a perf issue, consider caching a ref to this list in each item
-        List<PosAlign?> posAligns = views.Select (
-                                                v =>
-                                                {
-                                                    switch (dimension)
-                                                    {
-                                                        case Dimension.Width when v.X.Has (typeof (PosAlign), out var pos):
-
-                                                            if (pos is PosAlign posAlignX && posAlignX.GroupId == groupId)
-                                                            {
-                                                                return posAlignX;
-                                                            }
-
-                                                            break;
-                                                        case Dimension.Height when v.Y.Has (typeof (PosAlign), out var pos):
-                                                            if (pos is PosAlign posAlignY && posAlignY.GroupId == groupId)
-                                                            {
-                                                                return posAlignY;
-                                                            }
-
-                                                            break;
-                                                    }
-
-                                                    return null;
-                                                })
+        List<View> viewsInGroup = views.Where (
+                                               v =>
+                                               {
+                                                   return dimension switch
+                                                          {
+                                                              Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
+                                                              Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
+                                                              _ => false
+                                                          };
+                                               })
                                        .ToList ();
 
-        // PERF: We iterate over viewsInGroup multiple times here.
-
-        Aligner? firstInGroup = null;
-
-        // Update the dimensionList with the sizes of the views
-        for (var index = 0; index < posAligns.Count; index++)
+        if (viewsInGroup.Count == 0)
         {
-            if (posAligns [index] is { })
-            {
-                if (firstInGroup is null)
-                {
-                    firstInGroup = posAligns [index]!.Aligner;
-                }
-
-                dimensionsList.Add (dimension == Dimension.Width ? views [index].Frame.Width : views [index].Frame.Height);
-            }
+            return 0;
         }
 
-        if (firstInGroup is null)
-        {
-            return;
-        }
+        // PERF: We iterate over viewsInGroup multiple times here.
 
-        // Update the first item in the group with the new container size.
-        firstInGroup.ContainerSize = size;
+        // Update the dimensionList with the sizes of the views
+        for (var index = 0; index < viewsInGroup.Count; index++)
+        {
+            View view = viewsInGroup [index];
 
-        // Align
-        int [] locations = firstInGroup.Align (dimensionsList.ToArray ());
+            PosAlign? posAlign = dimension == Dimension.Width ? view.X as PosAlign : view.Y as PosAlign;
 
-        // Update the cached location for each item
-        for (int posIndex = 0, locIndex = 0; posIndex < posAligns.Count; posIndex++)
-        {
-            if (posAligns [posIndex] is { })
+            if (posAlign is { })
             {
-                posAligns [posIndex]!._cachedLocation = locations [locIndex++];
+                dimensionsList.Add (dimension == Dimension.Width ? view.Frame.Width : view.Frame.Height);
             }
         }
-    }
 
-    private void Aligner_PropertyChanged (object? sender, PropertyChangedEventArgs e) { _cachedLocation = null; }
-
-    /// <inheritdoc/>
-    public override bool Equals (object? other)
-    {
-        return other is PosAlign align
-               && GroupId == align.GroupId
-               && align.Aligner.Alignment == Aligner.Alignment
-               && align.Aligner.AlignmentModes == Aligner.AlignmentModes;
+        // Align
+        return dimensionsList.Sum ();
     }
 
-    /// <inheritdoc/>
-    public override int GetHashCode () { return HashCode.Combine (Aligner, GroupId); }
+    /// <summary>
+    ///     Gets the identifier of a set of views that should be aligned together. When only a single
+    ///     set of views in a SuperView is aligned, setting <see cref="GroupId"/> is not needed because it defaults to 0.
+    /// </summary>
+    public int GroupId { get; init; }
 
     /// <inheritdoc/>
     public override string ToString () { return $"Align(alignment={Aligner.Alignment},modes={Aligner.AlignmentModes},groupId={GroupId})"; }
 
-    internal override int GetAnchor (int width) { return _cachedLocation ?? 0 - width; }
-
     internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
     {
         if (_cachedLocation.HasValue && Aligner.ContainerSize == superviewDimension)
@@ -176,52 +130,85 @@ public class PosAlign : Pos
         return 0;
     }
 
-    // TODO: PosAlign.CalculateMinDimension is a hack. Need to figure out a better way of doing this.
+    internal override int GetAnchor (int width) { return _cachedLocation ?? 0 - width; }
+
     /// <summary>
-    /// Returns the minimum size a group of views with the same <paramref name="groupId"/> can be.
+    ///     Aligns the views in <paramref name="views"/> that have the same group ID as <paramref name="groupId"/>.
+    ///     Updates each view's cached _location.
     /// </summary>
     /// <param name="groupId"></param>
     /// <param name="views"></param>
     /// <param name="dimension"></param>
-    /// <returns></returns>
-    public static int CalculateMinDimension (int groupId, IList<View> views, Dimension dimension)
+    /// <param name="size"></param>
+    private static void AlignAndUpdateGroup (int groupId, IList<View> views, Dimension dimension, int size)
     {
         List<int> dimensionsList = new ();
 
         // PERF: If this proves a perf issue, consider caching a ref to this list in each item
-        List<View> viewsInGroup = views.Where (
-                                               v =>
-                                               {
-                                                   return dimension switch
-                                                   {
-                                                       Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
-                                                       Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
-                                                       _ => false
-                                                   };
-                                               })
-                                       .ToList ();
-
-        if (viewsInGroup.Count == 0)
-        {
-            return 0;
-        }
+        List<PosAlign?> posAligns = views.Select (
+                                                  v =>
+                                                  {
+                                                      switch (dimension)
+                                                      {
+                                                          case Dimension.Width when v.X.Has (typeof (PosAlign), out Pos pos):
+
+                                                              if (pos is PosAlign posAlignX && posAlignX.GroupId == groupId)
+                                                              {
+                                                                  return posAlignX;
+                                                              }
+
+                                                              break;
+                                                          case Dimension.Height when v.Y.Has (typeof (PosAlign), out Pos pos):
+                                                              if (pos is PosAlign posAlignY && posAlignY.GroupId == groupId)
+                                                              {
+                                                                  return posAlignY;
+                                                              }
+
+                                                              break;
+                                                      }
+
+                                                      return null;
+                                                  })
+                                         .ToList ();
 
         // PERF: We iterate over viewsInGroup multiple times here.
 
+        Aligner? firstInGroup = null;
+
         // Update the dimensionList with the sizes of the views
-        for (var index = 0; index < viewsInGroup.Count; index++)
+        for (var index = 0; index < posAligns.Count; index++)
         {
-            View view = viewsInGroup [index];
-
-            PosAlign? posAlign = dimension == Dimension.Width ? view.X as PosAlign : view.Y as PosAlign;
-
-            if (posAlign is { })
+            if (posAligns [index] is { })
             {
-                dimensionsList.Add (dimension == Dimension.Width ? view.Frame.Width : view.Frame.Height);
+                if (firstInGroup is null)
+                {
+                    firstInGroup = posAligns [index]!.Aligner;
+                }
+
+                dimensionsList.Add (dimension == Dimension.Width ? views [index].Frame.Width : views [index].Frame.Height);
             }
         }
 
+        if (firstInGroup is null)
+        {
+            return;
+        }
+
+        // Update the first item in the group with the new container size.
+        firstInGroup.ContainerSize = size;
+
         // Align
-        return dimensionsList.Sum ();
+        int [] locations = firstInGroup.Align (dimensionsList.ToArray ());
+
+        // Update the cached location for each item
+        for (int posIndex = 0, locIndex = 0; posIndex < posAligns.Count; posIndex++)
+        {
+            if (posAligns [posIndex] is { })
+            {
+                posAligns [posIndex]!._cachedLocation = locations [locIndex++];
+            }
+        }
     }
+
+    private void Aligner_PropertyChanged (object? sender, PropertyChangedEventArgs e) { _cachedLocation = null; }
 }

+ 1 - 7
Terminal.Gui/View/Layout/PosAnchorEnd.cs

@@ -10,7 +10,7 @@ namespace Terminal.Gui;
 ///         methods on the <see cref="Pos"/> class to create <see cref="Pos"/> objects instead.
 ///     </para>
 /// </remarks>
-public class PosAnchorEnd : Pos
+public record PosAnchorEnd : Pos
 {
     /// <summary>
     ///     Gets the offset of the position from the right/bottom.
@@ -30,12 +30,6 @@ public class PosAnchorEnd : Pos
     /// <param name="offset"></param>
     public PosAnchorEnd (int offset) { Offset = offset; }
 
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is PosAnchorEnd anchorEnd && anchorEnd.Offset == Offset; }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Offset.GetHashCode (); }
-
     /// <summary>
     ///     If true, the offset is the width of the view, if false, the offset is the offset value.
     /// </summary>

+ 1 - 1
Terminal.Gui/View/Layout/PosCenter.cs

@@ -4,7 +4,7 @@ namespace Terminal.Gui;
 /// <summary>
 ///     Represents a position that is centered.
 /// </summary>
-public class PosCenter : Pos
+public record PosCenter : Pos
 {
     /// <inheritdoc/>
     public override string ToString () { return "Center"; }

+ 7 - 7
Terminal.Gui/View/Layout/PosCombine.cs

@@ -10,27 +10,27 @@ namespace Terminal.Gui;
 ///         methods on the <see cref="Pos"/> class to create <see cref="Pos"/> objects instead.
 ///     </para>
 /// </remarks>
-/// <param name="add">
+/// <param name="Add">
 ///     Indicates whether the two positions are added or subtracted.
 /// </param>
-/// <param name="left">The left position.</param>
-/// <param name="right">The right position.</param>
-public class PosCombine (AddOrSubtract add, Pos left, Pos right) : Pos
+/// <param name="Left">The left position.</param>
+/// <param name="Right">The right position.</param>
+public record PosCombine (AddOrSubtract Add, Pos Left, Pos Right) : Pos
 {
     /// <summary>
     ///     Gets whether the two positions are added or subtracted.
     /// </summary>
-    public AddOrSubtract Add { get; } = add;
+    public AddOrSubtract Add { get; } = Add;
 
     /// <summary>
     ///     Gets the left position.
     /// </summary>
-    public new Pos Left { get; } = left;
+    public new Pos Left { get; } = Left;
 
     /// <summary>
     ///     Gets the right position.
     /// </summary>
-    public new Pos Right { get; } = right;
+    public new Pos Right { get; } = Right;
 
     /// <inheritdoc/>
     public override string ToString () { return $"Combine({Left}{(Add == AddOrSubtract.Add ? '+' : '-')}{Right})"; }

+ 4 - 21
Terminal.Gui/View/Layout/PosFunc.cs

@@ -4,28 +4,11 @@ namespace Terminal.Gui;
 /// <summary>
 ///     Represents a position that is computed by executing a function that returns an integer position.
 /// </summary>
-/// <remarks>
-///     <para>
-///         This is a low-level API that is typically used internally by the layout system. Use the various static
-///         methods on the <see cref="Pos"/> class to create <see cref="Pos"/> objects instead.
-///     </para>
-/// </remarks>
-/// <param name="pos">The position.</param>
-public class PosFunc (Func<int> pos) : Pos
+/// <param name="Fn">The function that computes the position.</param>
+public record PosFunc (Func<int> Fn) : Pos
 {
-    /// <summary>
-    ///     Gets the function that computes the position.
-    /// </summary>
-    public new Func<int> Func { get; } = pos;
-
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is PosFunc f && f.Func () == Func (); }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Func.GetHashCode (); }
-
     /// <inheritdoc/>
-    public override string ToString () { return $"PosFunc({Func ()})"; }
+    public override string ToString () { return $"PosFunc({Fn ()})"; }
 
-    internal override int GetAnchor (int size) { return Func (); }
+    internal override int GetAnchor (int size) { return Fn (); }
 }

+ 3 - 9
Terminal.Gui/View/Layout/PosPercent.cs

@@ -10,19 +10,13 @@ namespace Terminal.Gui;
 ///         methods on the <see cref="Pos"/> class to create <see cref="Pos"/> objects instead.
 ///     </para>
 /// </remarks>
-/// <param name="percent"></param>
-public class PosPercent (int percent) : Pos
+/// <param name="Percent"></param>
+public record PosPercent (int Percent) : Pos
 {
     /// <summary>
     ///     Gets the percentage of the width or height of the SuperView.
     /// </summary>
-    public new int Percent { get; } = percent;
-
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is PosPercent i && i.Percent == Percent; }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Percent.GetHashCode (); }
+    public new int Percent { get; } = Percent;
 
     /// <inheritdoc/>
     public override string ToString () { return $"Percent({Percent})"; }

+ 5 - 11
Terminal.Gui/View/Layout/PosView.cs

@@ -10,25 +10,19 @@ namespace Terminal.Gui;
 ///         methods on the <see cref="Pos"/> class to create <see cref="Pos"/> objects instead.
 ///     </para>
 /// </remarks>
-/// <param name="view">The View the position is anchored to.</param>
-/// <param name="side">The side of the View the position is anchored to.</param>
-public class PosView (View? view, Side side) : Pos
+/// <param name="View">The View the position is anchored to.</param>
+/// <param name="Side">The side of the View the position is anchored to.</param>
+public record PosView (View? View, Side Side) : Pos
 {
     /// <summary>
     ///     Gets the View the position is anchored to.
     /// </summary>
-    public View? Target { get; } = view;
+    public View? Target { get; } = View;
 
     /// <summary>
     ///     Gets the side of the View the position is anchored to.
     /// </summary>
-    public Side Side { get; } = side;
-
-    /// <inheritdoc/>
-    public override bool Equals (object? other) { return other is PosView abs && abs.Target == Target && abs.Side == Side; }
-
-    /// <inheritdoc/>
-    public override int GetHashCode () { return Target!.GetHashCode (); }
+    public Side Side { get; } = Side;
 
     /// <inheritdoc/>
     public override string ToString ()

+ 0 - 1
Terminal.Gui/View/View.Keyboard.cs

@@ -27,7 +27,6 @@ public partial class View  // Keyboard APIs
     private void DisposeKeyboard ()
     {
         TitleTextFormatter.HotKeyChanged -= TitleTextFormatter_HotKeyChanged;
-        Application.RemoveKeyBindings (this);
     }
 
     #region HotKey Support

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

@@ -474,7 +474,7 @@ public partial class View // Layout APIs
                 return;
             }
 
-            if (_height is { } && _height.Has (typeof (DimAuto), out _))
+            if (_height is { } && _height.Has<DimAuto> (out _))
             {
                 // Reset ContentSize to Viewport
                 _contentSize = null;
@@ -523,7 +523,7 @@ public partial class View // Layout APIs
                 return;
             }
 
-            if (_width is { } && _width.Has (typeof (DimAuto), out _))
+            if (_width is { } && _width.Has<DimAuto> (out _))
             {
                 // Reset ContentSize to Viewport
                 _contentSize = null;

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

@@ -51,9 +51,9 @@ public class Label : View
         set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
     }
 
-    private new bool? FocusNext ()
+    private bool? FocusNext ()
     {
-        var me = SuperView?.Subviews.IndexOf (this) ?? -1;
+        int me = SuperView?.Subviews.IndexOf (this) ?? -1;
         if (me != -1 && me < SuperView?.Subviews.Count - 1)
         {
             SuperView?.Subviews [me + 1].SetFocus ();

+ 1 - 2
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -1504,8 +1504,7 @@ public class MenuBar : View, IDesignable
                     return false;
                 }
             }
-            else if (!_isContextMenuLoading
-                     && !(me.View is MenuBar || me.View is Menu)
+            else if (!(me.View is MenuBar || me.View is Menu)
                      && me.Flags != MouseFlags.ReportMousePosition
                      && me.Flags != 0)
             {

+ 7 - 6
Terminal.Gui/Views/NumericUpDown.cs

@@ -100,7 +100,7 @@ public class NumericUpDown<T> : View where T : notnull
                             return false;
                         }
 
-                        if (Value is { })
+                        if (Value is { } && Increment is { })
                         {
                             Value = (dynamic)Value + (dynamic)Increment;
                         }
@@ -117,11 +117,12 @@ public class NumericUpDown<T> : View where T : notnull
                             return false;
                         }
 
-                        if (Value is { })
+                        if (Value is { } && Increment is { })
                         {
                             Value = (dynamic)Value - (dynamic)Increment;
                         }
 
+
                         return true;
                     });
 
@@ -218,23 +219,23 @@ public class NumericUpDown<T> : View where T : notnull
         Text = _number.Text;
     }
 
-    private T _increment;
+    private T? _increment;
 
     /// <summary>
     /// </summary>
-    public T Increment
+    public T? Increment
     {
         get => _increment;
         set
         {
-            if ((dynamic)_increment == (dynamic)value)
+            if (_increment is { } && value is { } && (dynamic)_increment == (dynamic)value)
             {
                 return;
             }
 
             _increment = value;
 
-            IncrementChanged?.Invoke (this, new (value));
+            IncrementChanged?.Invoke (this, new (value!));
         }
     }
 

+ 4 - 59
Terminal.Gui/Views/Toplevel.cs

@@ -69,74 +69,19 @@ public partial class Toplevel : View
     #region Subviews
 
     // TODO: Deprecate - Any view can host a menubar in v2
-    /// <summary>Gets or sets the menu for this Toplevel.</summary>
-    public MenuBar? MenuBar { get; set; }
+    /// <summary>Gets the latest <see cref="MenuBar"/> added into this Toplevel.</summary>
+    public MenuBar? MenuBar => (MenuBar?)Subviews?.LastOrDefault (s => s is MenuBar);
 
     // TODO: Deprecate - Any view can host a statusbar in v2
-    /// <summary>Gets or sets the status bar for this Toplevel.</summary>
-    public StatusBar? StatusBar { get; set; }
+    /// <summary>Gets the latest <see cref="StatusBar"/> added into this Toplevel.</summary>
+    public StatusBar? StatusBar => (StatusBar?)Subviews?.LastOrDefault (s => s is StatusBar);
 
     /// <inheritdoc/>
     public override View Add (View view)
     {
-        AddMenuStatusBar (view);
-
         return base.Add (view);
     }
 
-    /// <inheritdoc/>
-    public override View Remove (View view)
-    {
-        if (this is Toplevel { MenuBar: { } })
-        {
-            RemoveMenuStatusBar (view);
-        }
-
-        return base.Remove (view);
-    }
-
-    /// <inheritdoc/>
-    public override void RemoveAll ()
-    {
-        if (this == Application.Top)
-        {
-            MenuBar?.Dispose ();
-            MenuBar = null;
-            StatusBar?.Dispose ();
-            StatusBar = null;
-        }
-
-        base.RemoveAll ();
-    }
-
-    internal void AddMenuStatusBar (View view)
-    {
-        if (view is MenuBar)
-        {
-            MenuBar = view as MenuBar;
-        }
-
-        if (view is StatusBar)
-        {
-            StatusBar = view as StatusBar;
-        }
-    }
-
-    internal void RemoveMenuStatusBar (View view)
-    {
-        if (view is MenuBar)
-        {
-            MenuBar?.Dispose ();
-            MenuBar = null;
-        }
-
-        if (view is StatusBar)
-        {
-            StatusBar?.Dispose ();
-            StatusBar = null;
-        }
-    }
-
     // TODO: Overlapped - Rename to AllSubviewsClosed - Move to View?
     /// <summary>
     ///     Invoked when the last child of the Toplevel <see cref="RunState"/> is closed from by

+ 3 - 1
UICatalog/Scenarios/ListViewWithSelection.cs

@@ -202,9 +202,11 @@ public class ListViewWithSelection : Scenario
 
             return false;
         }
-
+#pragma warning disable CS0067
         /// <inheritdoc />
         public event NotifyCollectionChangedEventHandler CollectionChanged;
+#pragma warning restore CS0067
+
         public int Count => Scenarios?.Count ?? 0;
         public int Length { get; private set; }
         public bool SuspendCollectionChangedEvent { get => throw new System.NotImplementedException (); set => throw new System.NotImplementedException (); }

+ 10 - 4
UICatalog/UICatalog.cs

@@ -408,7 +408,7 @@ public class UICatalogApp
             _themeMenuItems = CreateThemeMenuItems ();
             _themeMenuBarItem = new ("_Themes", _themeMenuItems);
 
-            MenuBar = new ()
+            MenuBar menuBar = new ()
             {
                 Menus =
                 [
@@ -462,13 +462,15 @@ public class UICatalogApp
                         )
                 ]
             };
+            Add (menuBar);
 
-            StatusBar = new ()
+            StatusBar statusBar = new ()
             {
                 Visible = ShowStatusBar,
                 AlignmentModes = AlignmentModes.IgnoreFirstOrLast,
                 CanFocus = false
             };
+            Add (statusBar);
 
             if (StatusBar is { })
             {
@@ -484,7 +486,11 @@ public class UICatalogApp
                     Title = "Show/Hide Status Bar",
                     CanFocus = false,
                 };
-                statusBarShortcut.Accept += (sender, args) => { StatusBar.Visible = !StatusBar.Visible; };
+                statusBarShortcut.Accept += (sender, args) =>
+                                            {
+                                                StatusBar.Visible = !StatusBar.Visible;
+                                                args.Handled = true;
+                                            };
 
                 ShForce16Colors = new ()
                 {
@@ -637,7 +643,7 @@ public class UICatalogApp
             Add (CategoryList);
             Add (ScenarioList);
 
-            Add (MenuBar);
+            Add (MenuBar!);
 
             if (StatusBar is { })
             {

+ 2 - 0
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -389,6 +389,7 @@ public class ConfigurationManagerTests
                                             )
                    );
 
+#pragma warning disable xUnit2029
         Assert.Empty (
                       Settings.Where (
                                       cp => cp.Value.PropertyInfo!.GetCustomAttribute (
@@ -397,6 +398,7 @@ public class ConfigurationManagerTests
                                             == null
                                      )
                      );
+#pragma warning restore xUnit2029
 
         // Application is a static class
         PropertyInfo pi = typeof (Application).GetProperty ("QuitKey");

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 1410 - 3161
UnitTests/Text/TextFormatterTests.cs


+ 215 - 158
UnitTests/View/Layout/Dim.AutoTests.cs

@@ -4,33 +4,36 @@ using static Terminal.Gui.Dim;
 
 namespace Terminal.Gui.LayoutTests;
 
+[Trait("Category", "Layout")]
 public partial class DimAutoTests (ITestOutputHelper output)
 {
     private readonly ITestOutputHelper _output = output;
 
-    private class DimAutoTestView : View
+    [SetupFakeDriver]
+    [Fact]
+    public void Change_To_Non_Auto_Resets_ContentSize ()
     {
-        public DimAutoTestView ()
+        View view = new ()
         {
-            ValidatePosDim = true;
-            Width = Auto ();
-            Height = Auto ();
-        }
+            Width = Auto (),
+            Height = Auto (),
+            Text = "01234"
+        };
+        view.SetRelativeLayout (new (100, 100));
+        Assert.Equal (new (0, 0, 5, 1), view.Frame);
+        Assert.Equal (new (5, 1), view.GetContentSize ());
 
-        public DimAutoTestView (Dim width, Dim height)
-        {
-            ValidatePosDim = true;
-            Width = width;
-            Height = height;
-        }
+        // Change text to a longer string
+        view.Text = "0123456789";
 
-        public DimAutoTestView (string text, Dim width, Dim height)
-        {
-            ValidatePosDim = true;
-            Text = text;
-            Width = width;
-            Height = height;
-        }
+        Assert.Equal (new (0, 0, 10, 1), view.Frame);
+        Assert.Equal (new (10, 1), view.GetContentSize ());
+
+        // If ContentSize was reset, these should cause it to update
+        view.Width = 5;
+        view.Height = 1;
+
+        Assert.Equal (new (5, 1), view.GetContentSize ());
     }
 
     [Theory]
@@ -74,6 +77,46 @@ public partial class DimAutoTests (ITestOutputHelper output)
         Assert.Equal (new (0, 0, 10, expectedHeight), superView.Frame);
     }
 
+    [Theory]
+    [CombinatorialData]
+    public void HotKey_TextFormatter_Height_Correct ([CombinatorialValues ("1234", "_1234", "1_234", "____")] string text)
+    {
+        View view = new ()
+        {
+            HotKeySpecifier = (Rune)'_',
+            Text = text,
+            Width = Auto (),
+            Height = 1
+        };
+        Assert.Equal (4, view.TextFormatter.ConstrainToWidth);
+        Assert.Equal (1, view.TextFormatter.ConstrainToHeight);
+
+        view = new ()
+        {
+            HotKeySpecifier = (Rune)'_',
+            TextDirection = TextDirection.TopBottom_LeftRight,
+            Text = text,
+            Width = 1,
+            Height = Auto ()
+        };
+        Assert.Equal (1, view.TextFormatter.ConstrainToWidth);
+        Assert.Equal (4, view.TextFormatter.ConstrainToHeight);
+    }
+
+    [Theory]
+    [CombinatorialData]
+    public void HotKey_TextFormatter_Width_Correct ([CombinatorialValues ("1234", "_1234", "1_234", "____")] string text)
+    {
+        View view = new ()
+        {
+            Text = text,
+            Height = 1,
+            Width = Auto ()
+        };
+        Assert.Equal (4, view.TextFormatter.ConstrainToWidth);
+        Assert.Equal (1, view.TextFormatter.ConstrainToHeight);
+    }
+
     [Fact]
     public void NoSubViews_Does_Nothing ()
     {
@@ -158,6 +201,122 @@ public partial class DimAutoTests (ITestOutputHelper output)
         Assert.Equal (new (0, 0, expectedWidth, expectedHeight), superView.Frame);
     }
 
+    [Fact]
+    public void TestEquality ()
+    {
+        var a = new DimAuto (
+                             MaximumContentDim: null,
+                             MinimumContentDim: 1,
+                             Style: DimAutoStyle.Auto
+                            );
+
+        var b = new DimAuto (
+                             MaximumContentDim: null,
+                             MinimumContentDim: 1,
+                             Style: DimAutoStyle.Auto
+                            );
+
+        var c = new DimAuto(
+                             MaximumContentDim: 2,
+                             MinimumContentDim: 1,
+                             Style: DimAutoStyle.Auto
+                            );
+
+        var d = new DimAuto (
+                             MaximumContentDim: null,
+                             MinimumContentDim: 1,
+                             Style: DimAutoStyle.Content
+                            );
+
+        var e = new DimAuto (
+                             MaximumContentDim: null,
+                             MinimumContentDim: 2,
+                             Style: DimAutoStyle.Auto
+                            );
+
+        // Test equality with same values
+        Assert.True (a.Equals (b));
+        Assert.True (a.GetHashCode () == b.GetHashCode ());
+
+        // Test inequality with different MaximumContentDim
+        Assert.False (a.Equals (c));
+        Assert.False (a.GetHashCode () == c.GetHashCode ());
+
+        // Test inequality with different Style
+        Assert.False (a.Equals (d));
+        Assert.False (a.GetHashCode () == d.GetHashCode ());
+
+        // Test inequality with different MinimumContentDim
+        Assert.False (a.Equals (e));
+        Assert.False (a.GetHashCode () == e.GetHashCode ());
+
+        // Test inequality with null
+        Assert.False (a.Equals (null));
+    }
+
+    [Fact]
+    public void TestEquality_Simple ()
+    {
+        Dim a = Auto ();
+        Dim b = Auto ();
+        Assert.True (a.Equals (b));
+        Assert.True (a.GetHashCode () == b.GetHashCode ());
+    }
+
+    [Fact]
+    public void TextFormatter_Settings_Change_View_Size ()
+    {
+        View view = new ()
+        {
+            Text = "_1234",
+            Width = Auto ()
+        };
+        Assert.Equal (new (4, 0), view.Frame.Size);
+
+        view.Height = 1;
+        view.SetRelativeLayout (Application.Screen.Size);
+        Assert.Equal (new (4, 1), view.Frame.Size);
+        Size lastSize = view.Frame.Size;
+
+        view.TextAlignment = Alignment.Fill;
+        Assert.Equal (lastSize, view.Frame.Size);
+
+        view = new ()
+        {
+            Text = "_1234",
+            Width = Auto (),
+            Height = 1
+        };
+        view.SetRelativeLayout (Application.Screen.Size);
+
+        lastSize = view.Frame.Size;
+        view.VerticalTextAlignment = Alignment.Center;
+        Assert.Equal (lastSize, view.Frame.Size);
+
+        view = new ()
+        {
+            Text = "_1234",
+            Width = Auto (),
+            Height = 1
+        };
+        view.SetRelativeLayout (Application.Screen.Size);
+        lastSize = view.Frame.Size;
+        view.HotKeySpecifier = (Rune)'*';
+        view.SetRelativeLayout (Application.Screen.Size);
+        Assert.NotEqual (lastSize, view.Frame.Size);
+
+        view = new ()
+        {
+            Text = "_1234",
+            Width = Auto (),
+            Height = 1
+        };
+        view.SetRelativeLayout (Application.Screen.Size);
+        lastSize = view.Frame.Size;
+        view.Text = "*ABCD";
+        Assert.NotEqual (lastSize, view.Frame.Size);
+    }
+
     // Test validation
     [Fact]
     public void ValidatePosDim_True_Throws_When_SubView_Uses_SuperView_Dims ()
@@ -419,7 +578,7 @@ public partial class DimAutoTests (ITestOutputHelper output)
     [InlineData (1, 10, 10)]
     [InlineData (9, 10, 10)]
     [InlineData (10, 10, 10)]
-    public void Width_Auto_Text_Does_Not_Constrain_To_SuperView (int subX, int textLen, int expectedSubWidth)
+    public void Width_Auto_Subviews_Does_Not_Constrain_To_SuperView (int subX, int subSubViewWidth, int expectedSubWidth)
     {
         var superView = new View
         {
@@ -432,14 +591,23 @@ public partial class DimAutoTests (ITestOutputHelper output)
 
         var subView = new View
         {
-            Text = new ('*', textLen),
             X = subX,
             Y = 0,
-            Width = Auto (DimAutoStyle.Text),
+            Width = Auto (DimAutoStyle.Content),
             Height = 1,
             ValidatePosDim = true
         };
 
+        var subSubView = new View
+        {
+            X = 0,
+            Y = 0,
+            Width = subSubViewWidth,
+            Height = 1,
+            ValidatePosDim = true
+        };
+        subView.Add (subSubView);
+
         superView.Add (subView);
 
         superView.BeginInit ();
@@ -459,7 +627,7 @@ public partial class DimAutoTests (ITestOutputHelper output)
     [InlineData (1, 10, 10)]
     [InlineData (9, 10, 10)]
     [InlineData (10, 10, 10)]
-    public void Width_Auto_Subviews_Does_Not_Constrain_To_SuperView (int subX, int subSubViewWidth, int expectedSubWidth)
+    public void Width_Auto_Text_Does_Not_Constrain_To_SuperView (int subX, int textLen, int expectedSubWidth)
     {
         var superView = new View
         {
@@ -472,22 +640,13 @@ public partial class DimAutoTests (ITestOutputHelper output)
 
         var subView = new View
         {
+            Text = new ('*', textLen),
             X = subX,
             Y = 0,
-            Width = Auto (DimAutoStyle.Content),
-            Height = 1,
-            ValidatePosDim = true
-        };
-
-        var subSubView = new View
-        {
-            X = 0,
-            Y = 0,
-            Width = subSubViewWidth,
+            Width = Auto (DimAutoStyle.Text),
             Height = 1,
             ValidatePosDim = true
         };
-        subView.Add (subSubView);
 
         superView.Add (subView);
 
@@ -499,31 +658,29 @@ public partial class DimAutoTests (ITestOutputHelper output)
         Assert.Equal (expectedSubWidth, subView.Frame.Width);
     }
 
-    [SetupFakeDriver]
-    [Fact]
-    public void Change_To_Non_Auto_Resets_ContentSize ()
+    private class DimAutoTestView : View
     {
-        View view = new ()
+        public DimAutoTestView ()
         {
-            Width = Auto (),
-            Height = Auto (),
-            Text = "01234"
-        };
-        view.SetRelativeLayout (new (100, 100));
-        Assert.Equal (new (0, 0, 5, 1), view.Frame);
-        Assert.Equal (new (5, 1), view.GetContentSize ());
-
-        // Change text to a longer string
-        view.Text = "0123456789";
-
-        Assert.Equal (new (0, 0, 10, 1), view.Frame);
-        Assert.Equal (new (10, 1), view.GetContentSize ());
+            ValidatePosDim = true;
+            Width = Auto ();
+            Height = Auto ();
+        }
 
-        // If ContentSize was reset, these should cause it to update
-        view.Width = 5;
-        view.Height = 1;
+        public DimAutoTestView (Dim width, Dim height)
+        {
+            ValidatePosDim = true;
+            Width = width;
+            Height = height;
+        }
 
-        Assert.Equal (new (5, 1), view.GetContentSize ());
+        public DimAutoTestView (string text, Dim width, Dim height)
+        {
+            ValidatePosDim = true;
+            Text = text;
+            Width = width;
+            Height = height;
+        }
     }
 
     #region DimAutoStyle.Auto tests
@@ -544,7 +701,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
         view.SetRelativeLayout (Application.Screen.Size);
 
         Assert.Equal (new (expectedW, expectedH), view.Frame.Size);
-
     }
 
     [Fact]
@@ -653,7 +809,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
         view.SetRelativeLayout (Application.Screen.Size);
 
         Assert.Equal (new (expectedW, expectedH), view.Frame.Size);
-
     }
 
     [Theory]
@@ -665,15 +820,14 @@ public partial class DimAutoTests (ITestOutputHelper output)
     public void DimAutoStyle_Text_Sizes_Correctly_With_Min (string text, int minWidth, int minHeight, int expectedW, int expectedH)
     {
         var view = new View ();
-        view.Width = Auto (DimAutoStyle.Text, minimumContentDim: minWidth);
-        view.Height = Auto (DimAutoStyle.Text, minimumContentDim: minHeight);
+        view.Width = Auto (DimAutoStyle.Text, minWidth);
+        view.Height = Auto (DimAutoStyle.Text, minHeight);
 
         view.Text = text;
 
         view.SetRelativeLayout (Application.Screen.Size);
 
         Assert.Equal (new (expectedW, expectedH), view.Frame.Size);
-
     }
 
     [Theory]
@@ -699,7 +853,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
     [InlineData ("01234", 5, 1)]
     [InlineData ("01234ABCDE", 10, 1)]
     [InlineData ("01234\nABCDE", 5, 2)]
-
     public void DimAutoStyle_Text_NoMin_Not_Constrained_By_ContentSize (string text, int expectedW, int expectedH)
     {
         var view = new View ();
@@ -711,7 +864,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
         Assert.Equal (new (expectedW, expectedH), view.Frame.Size);
     }
 
-
     [Theory]
     [InlineData ("", 0, 0)]
     [InlineData (" ", 1, 1)]
@@ -720,7 +872,7 @@ public partial class DimAutoTests (ITestOutputHelper output)
     [InlineData ("01234\nABCDE", 5, 2)]
     public void DimAutoStyle_Text_NoMin_Not_Constrained_By_SuperView (string text, int expectedW, int expectedH)
     {
-        var superView = new View ()
+        var superView = new View
         {
             Width = 1, Height = 1
         };
@@ -870,100 +1022,5 @@ public partial class DimAutoTests (ITestOutputHelper output)
 
     #endregion DimAutoStyle.Content tests
 
-    [Fact]
-    public void TextFormatter_Settings_Change_View_Size ()
-    {
-        View view = new ()
-        {
-            Text = "_1234",
-            Width = Auto ()
-        };
-        Assert.Equal (new (4, 0), view.Frame.Size);
-
-        view.Height = 1;
-        view.SetRelativeLayout (Application.Screen.Size);
-        Assert.Equal (new (4, 1), view.Frame.Size);
-        Size lastSize = view.Frame.Size;
-
-        view.TextAlignment = Alignment.Fill;
-        Assert.Equal (lastSize, view.Frame.Size);
-
-        view = new ()
-        {
-            Text = "_1234",
-            Width = Auto (),
-            Height = 1
-        };
-        view.SetRelativeLayout (Application.Screen.Size);
-
-        lastSize = view.Frame.Size;
-        view.VerticalTextAlignment = Alignment.Center;
-        Assert.Equal (lastSize, view.Frame.Size);
-
-        view = new ()
-        {
-            Text = "_1234",
-            Width = Auto (),
-            Height = 1
-        };
-        view.SetRelativeLayout (Application.Screen.Size);
-        lastSize = view.Frame.Size;
-        view.HotKeySpecifier = (Rune)'*';
-        view.SetRelativeLayout (Application.Screen.Size);
-        Assert.NotEqual (lastSize, view.Frame.Size);
-
-        view = new ()
-        {
-            Text = "_1234",
-            Width = Auto (),
-            Height = 1
-        };
-        view.SetRelativeLayout (Application.Screen.Size);
-        lastSize = view.Frame.Size;
-        view.Text = "*ABCD";
-        Assert.NotEqual (lastSize, view.Frame.Size);
-    }
-
-
-    [Theory]
-    [CombinatorialData]
-    public void HotKey_TextFormatter_Width_Correct ([CombinatorialValues ("1234", "_1234", "1_234", "____")] string text)
-    {
-        View view = new ()
-        {
-            Text = text,
-            Height = 1,
-            Width = Auto ()
-        };
-        Assert.Equal (4, view.TextFormatter.ConstrainToWidth);
-        Assert.Equal (1, view.TextFormatter.ConstrainToHeight);
-    }
-
-    [Theory]
-    [CombinatorialData]
-    public void HotKey_TextFormatter_Height_Correct ([CombinatorialValues ("1234", "_1234", "1_234", "____")] string text)
-    {
-        View view = new ()
-        {
-            HotKeySpecifier = (Rune)'_',
-            Text = text,
-            Width = Auto (),
-            Height = 1
-        };
-        Assert.Equal (4, view.TextFormatter.ConstrainToWidth);
-        Assert.Equal (1, view.TextFormatter.ConstrainToHeight);
-
-        view = new ()
-        {
-            HotKeySpecifier = (Rune)'_',
-            TextDirection = TextDirection.TopBottom_LeftRight,
-            Text = text,
-            Width = 1,
-            Height = Auto ()
-        };
-        Assert.Equal (1, view.TextFormatter.ConstrainToWidth);
-        Assert.Equal (4, view.TextFormatter.ConstrainToHeight);
-    }
-
     // Test variations of Frame
 }

+ 4 - 1
UnitTests/View/Layout/Dim.FuncTests.cs

@@ -14,9 +14,12 @@ public class DimFuncTests (ITestOutputHelper output)
         Func<int> f2 = () => 0;
 
         Dim dim1 = Func (f1);
-        Dim dim2 = Func (f2);
+        Dim dim2 = Func (f1);
         Assert.Equal (dim1, dim2);
 
+        dim2 = Func (f2);
+        Assert.NotEqual (dim1, dim2);
+
         f2 = () => 1;
         dim2 = Func (f2);
         Assert.NotEqual (dim1, dim2);

+ 10 - 2
UnitTests/View/Layout/Dim.PercentTests.cs

@@ -15,7 +15,6 @@ public class DimPercentTests
         Assert.Equal (50, result);
     }
 
-
     [Fact]
     public void DimPercent_Equals ()
     {
@@ -63,6 +62,15 @@ public class DimPercentTests
         Assert.NotEqual (dim1, dim2);
     }
 
+    [Fact]
+    public void TestEquality ()
+    {
+        var a = Dim.Percent (32);
+        var b = Dim.Percent (32);
+        Assert.True (a.Equals (b));
+        Assert.True (a.GetHashCode () == b.GetHashCode ());
+    }
+
     [Fact]
     public void DimPercent_Invalid_Throws ()
     {
@@ -157,7 +165,7 @@ public class DimPercentTests
     }
 
     [Theory]
-    [InlineData(0)]
+    [InlineData (0)]
     [InlineData (1)]
     [InlineData (50)]
     [InlineData (100)]

+ 52 - 52
UnitTests/View/Layout/Pos.AlignTests.cs

@@ -27,58 +27,58 @@ public class PosAlignTests
         Assert.Equal (0, posAlign.Aligner.ContainerSize);
     }
 
-    [Theory]
-    [InlineData (Alignment.Start, Alignment.Start, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, true)]
-    [InlineData (Alignment.Center, Alignment.Center, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, true)]
-    [InlineData (Alignment.Start, Alignment.Center, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, false)]
-    [InlineData (Alignment.Center, Alignment.Start, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, false)]
-    [InlineData (Alignment.Start, Alignment.Start, AlignmentModes.StartToEnd, AlignmentModes.AddSpaceBetweenItems, false)]
-    public void PosAlign_Equals (Alignment align1, Alignment align2, AlignmentModes mode1, AlignmentModes mode2, bool expectedEquals)
-    {
-        var posAlign1 = new PosAlign
-        {
-            Aligner = new()
-            {
-                Alignment = align1,
-                AlignmentModes = mode1
-            }
-        };
-
-        var posAlign2 = new PosAlign
-        {
-            Aligner = new()
-            {
-                Alignment = align2,
-                AlignmentModes = mode2
-            }
-        };
-
-        Assert.Equal (expectedEquals, posAlign1.Equals (posAlign2));
-        Assert.Equal (expectedEquals, posAlign2.Equals (posAlign1));
-    }
-
-    [Fact]
-    public void PosAlign_Equals_CachedLocation_Not_Used ()
-    {
-        View superView = new ()
-        {
-            Width = 10,
-            Height = 25
-        };
-        View view = new ();
-        superView.Add (view);
-
-        Pos posAlign1 = Pos.Align (Alignment.Center);
-        view.X = posAlign1;
-        int pos1 = posAlign1.Calculate (10, Dim.Absolute (0)!, view, Dimension.Width);
-
-        Pos posAlign2 = Pos.Align (Alignment.Center);
-        view.Y = posAlign2;
-        int pos2 = posAlign2.Calculate (25, Dim.Absolute (0)!, view, Dimension.Height);
-
-        Assert.NotEqual (pos1, pos2);
-        Assert.Equal (posAlign1, posAlign2);
-    }
+    //[Theory]
+    //[InlineData (Alignment.Start, Alignment.Start, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, true)]
+    //[InlineData (Alignment.Center, Alignment.Center, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, true)]
+    //[InlineData (Alignment.Start, Alignment.Center, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, false)]
+    //[InlineData (Alignment.Center, Alignment.Start, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, false)]
+    //[InlineData (Alignment.Start, Alignment.Start, AlignmentModes.StartToEnd, AlignmentModes.AddSpaceBetweenItems, false)]
+    //public void PosAlign_Equals (Alignment align1, Alignment align2, AlignmentModes mode1, AlignmentModes mode2, bool expectedEquals)
+    //{
+    //    var posAlign1 = new PosAlign
+    //    {
+    //        Aligner = new ()
+    //        {
+    //            Alignment = align1,
+    //            AlignmentModes = mode1
+    //        }
+    //    };
+
+    //    var posAlign2 = new PosAlign
+    //    {
+    //        Aligner = new ()
+    //        {
+    //            Alignment = align2,
+    //            AlignmentModes = mode2
+    //        }
+    //    };
+
+    //    Assert.Equal (expectedEquals, posAlign1.Equals (posAlign2));
+    //    Assert.Equal (expectedEquals, posAlign2.Equals (posAlign1));
+    //}
+
+    //[Fact]
+    //public void PosAlign_Equals_CachedLocation_Not_Used ()
+    //{
+    //    View superView = new ()
+    //    {
+    //        Width = 10,
+    //        Height = 25
+    //    };
+    //    View view = new ();
+    //    superView.Add (view);
+
+    //    Pos posAlign1 = Pos.Align (Alignment.Center);
+    //    view.X = posAlign1;
+    //    int pos1 = posAlign1.Calculate (10, Dim.Absolute (0)!, view, Dimension.Width);
+
+    //    Pos posAlign2 = Pos.Align (Alignment.Center);
+    //    view.Y = posAlign2;
+    //    int pos2 = posAlign2.Calculate (25, Dim.Absolute (0)!, view, Dimension.Height);
+
+    //    Assert.NotEqual (pos1, pos2);
+    //    Assert.Equal (posAlign1, posAlign2);
+    //}
 
     [Fact]
     public void PosAlign_ToString ()

+ 0 - 9
UnitTests/View/Layout/Pos.AnchorEndTests.cs

@@ -27,15 +27,6 @@ public class PosAnchorEndTests (ITestOutputHelper output)
         Assert.Equal (expectedEquals, posAnchorEnd2.Equals (posAnchorEnd1));
     }
 
-    [Fact]
-    public void PosAnchorEnd_GetHashCode ()
-    {
-        var posAnchorEnd = new PosAnchorEnd (10);
-        var expectedHashCode = 10.GetHashCode ();
-
-        Assert.Equal (expectedHashCode, posAnchorEnd.GetHashCode ());
-    }
-
     [Fact]
     public void PosAnchorEnd_ToString ()
     {

+ 0 - 10
UnitTests/View/Layout/Pos.CenterTests.cs

@@ -15,16 +15,6 @@ public class PosCenterTests (ITestOutputHelper output)
         Assert.NotNull (posCenter);
     }
 
-    [Fact]
-    public void PosCenter_Equals ()
-    {
-        var posCenter1 = new PosCenter ();
-        var posCenter2 = new PosCenter ();
-
-        Assert.False (posCenter1.Equals (posCenter2));
-        Assert.False (posCenter2.Equals (posCenter1));
-    }
-
     [Fact]
     public void PosCenter_ToString ()
     {

+ 1 - 1
UnitTests/View/Layout/Pos.FuncTests.cs

@@ -13,7 +13,7 @@ public class PosFuncTests (ITestOutputHelper output)
         Func<int> f2 = () => 0;
 
         Pos pos1 = Pos.Func (f1);
-        Pos pos2 = Pos.Func (f2);
+        Pos pos2 = Pos.Func (f1);
         Assert.Equal (pos1, pos2);
 
         f2 = () => 1;

+ 0 - 15
UnitTests/View/Layout/Pos.Tests.cs

@@ -145,21 +145,6 @@ public class PosTests ()
                      );
     }
 
-    [Fact]
-    public void PosFunction_Equal ()
-    {
-        Func<int> f1 = () => 0;
-        Func<int> f2 = () => 0;
-
-        Pos pos1 = Pos.Func (f1);
-        Pos pos2 = Pos.Func (f2);
-        Assert.Equal (pos1, pos2);
-
-        f2 = () => 1;
-        pos2 = Pos.Func (f2);
-        Assert.NotEqual (pos1, pos2);
-    }
-
     [Fact]
     public void PosFunction_SetsValue ()
     {

+ 1 - 1
UnitTests/View/TitleTests.cs

@@ -3,7 +3,7 @@ using Xunit.Abstractions;
 
 namespace Terminal.Gui.ViewTests;
 
-public class TitleTests (ITestOutputHelper output)
+public class TitleTests
 {
     // Unit tests that verify look & feel of title are in BorderTests.cs
 

+ 49 - 0
UnitTests/Views/ContextMenuTests.cs

@@ -1359,4 +1359,53 @@ public class ContextMenuTests (ITestOutputHelper output)
                                         )
         };
     }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Handling_TextField_With_Opened_ContextMenu_By_Mouse_HasFocus ()
+    {
+        var tf1 = new TextField { Width = 10, Text = "TextField 1" };
+        var tf2 = new TextField { Y = 2, Width = 10, Text = "TextField 2" };
+        var win = new Window ();
+        win.Add (tf1, tf2);
+        var rs = Application.Begin (win);
+
+        Assert.True (tf1.HasFocus);
+        Assert.False (tf2.HasFocus);
+        Assert.Equal (2, win.Subviews.Count);
+        Assert.Null (Application.MouseEnteredView);
+
+        // Right click on tf2 to open context menu
+        Application.OnMouseEvent (new () { Position = new (1, 3), Flags = MouseFlags.Button3Clicked });
+        Assert.False (tf1.HasFocus);
+        Assert.False (tf2.HasFocus);
+        Assert.Equal (3, win.Subviews.Count);
+        Assert.True (tf2.ContextMenu.MenuBar.IsMenuOpen);
+        Assert.True (win.Focused is Menu);
+        Assert.True (Application.MouseGrabView is MenuBar);
+        Assert.Equal (tf2, Application.MouseEnteredView);
+
+        // Click on tf1 to focus it, which cause context menu being closed
+        Application.OnMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Clicked });
+        Assert.True (tf1.HasFocus);
+        Assert.False (tf2.HasFocus);
+        Assert.Equal (2, win.Subviews.Count);
+        Assert.Null (tf2.ContextMenu.MenuBar);
+        Assert.Equal (win.Focused, tf1);
+        Assert.Null (Application.MouseGrabView);
+        Assert.Equal (tf1, Application.MouseEnteredView);
+
+        // Click on tf2 to focus it
+        Application.OnMouseEvent (new () { Position = new (1, 3), Flags = MouseFlags.Button1Clicked });
+        Assert.False (tf1.HasFocus);
+        Assert.True (tf2.HasFocus);
+        Assert.Equal (2, win.Subviews.Count);
+        Assert.Null (tf2.ContextMenu.MenuBar);
+        Assert.Equal (win.Focused, tf2);
+        Assert.Null (Application.MouseGrabView);
+        Assert.Equal (tf2, Application.MouseEnteredView);
+
+        Application.End (rs);
+        win.Dispose ();
+    }
 }

+ 2 - 0
UnitTests/Views/ListViewTests.cs

@@ -674,8 +674,10 @@ Item 6",
 
     private class NewListDataSource : IListDataSource
     {
+#pragma warning disable CS0067
         /// <inheritdoc />
         public event NotifyCollectionChangedEventHandler CollectionChanged;
+#pragma warning restore CS0067
 
         public int Count => 0;
         public int Length => 0;

+ 1 - 1
UnitTests/Views/MenuBarTests.cs

@@ -3800,7 +3800,7 @@ Edit
 
         menuItem.RemoveMenuItem ();
         Assert.Single (menuBar.Menus);
-        Assert.Equal (null, menuBar.Menus [0].Children);
+        Assert.Null (menuBar.Menus [0].Children);
         Assert.Contains (Key.N.WithAlt, menuBar.KeyBindings.Bindings);
         Assert.DoesNotContain (Key.I, menuBar.KeyBindings.Bindings);
 

+ 1 - 1
UnitTests/Views/NumericUpDownTests.cs

@@ -3,7 +3,7 @@ using Xunit.Abstractions;
 
 namespace Terminal.Gui.ViewsTests;
 
-public class NumericUpDownTests (ITestOutputHelper _output)
+public class NumericUpDownTests
 {
     [Fact]
     public void WhenCreated_ShouldHaveDefaultValues_int ()

+ 1 - 1
UnitTests/Views/StatusBarTests.cs

@@ -1,7 +1,7 @@
 using Xunit.Abstractions;
 
 namespace Terminal.Gui.ViewsTests;
-public class StatusBarTests (ITestOutputHelper output)
+public class StatusBarTests
 {
     [Fact]
     public void AddItemAt_RemoveItem_Replacing ()

+ 8 - 0
UnitTests/Views/TableViewTests.cs

@@ -2582,7 +2582,9 @@ A B C
 
         TestHelpers.AssertDriverContentsAre (expected, output);
 
+#pragma warning disable xUnit2029
         Assert.Empty (pets.Where (p => p.IsPicked));
+#pragma warning restore xUnit2029
 
         tv.NewKeyDownEvent (Key.Space);
 
@@ -2795,7 +2797,9 @@ A B C
 
         tv.NewKeyDownEvent (Key.Space);
 
+#pragma warning disable xUnit2029
         Assert.Empty (pets.Where (p => p.IsPicked));
+#pragma warning restore xUnit2029
 
         tv.Draw ();
 
@@ -2924,7 +2928,9 @@ A B C
 
         TestHelpers.AssertDriverContentsAre (expected, output);
 
+#pragma warning disable xUnit2029
         Assert.Empty (pets.Where (p => p.IsPicked));
+#pragma warning restore xUnit2029
 
         tv.NewKeyDownEvent (Key.Space);
 
@@ -3089,7 +3095,9 @@ A B C
         // Can untoggle at 1,0 even though 0,0 was initial toggle because FullRowSelect is on
         tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.Space });
 
+#pragma warning disable xUnit2029
         Assert.Empty (tableView.MultiSelectedRegions.Where (r => r.IsToggled));
+#pragma warning restore xUnit2029
     }
 
     [Fact]

+ 76 - 15
UnitTests/Views/ToplevelTests.cs

@@ -246,14 +246,26 @@ public partial class ToplevelTests (ITestOutputHelper output)
         top.OnUnloaded ();
         Assert.Equal ("Unloaded", eventInvoked);
 
-        top.AddMenuStatusBar (new MenuBar ());
+        top.Add (new MenuBar ());
         Assert.NotNull (top.MenuBar);
-        top.AddMenuStatusBar (new StatusBar ());
+        top.Add (new StatusBar ());
         Assert.NotNull (top.StatusBar);
-        top.RemoveMenuStatusBar (top.MenuBar);
+        var menuBar = top.MenuBar;
+        top.Remove (top.MenuBar);
         Assert.Null (top.MenuBar);
-        top.RemoveMenuStatusBar (top.StatusBar);
+        Assert.NotNull (menuBar);
+        var statusBar = top.StatusBar;
+        top.Remove (top.StatusBar);
         Assert.Null (top.StatusBar);
+        Assert.NotNull (statusBar);
+#if true
+        Assert.False (menuBar.WasDisposed);
+        Assert.False (statusBar.WasDisposed);
+        menuBar.Dispose ();
+        statusBar.Dispose ();
+        Assert.True (menuBar.WasDisposed);
+        Assert.True (statusBar.WasDisposed);
+#endif
 
         Application.Begin (top);
         Assert.Equal (top, Application.Top);
@@ -265,7 +277,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (0, ny);
         Assert.Null (sb);
 
-        top.AddMenuStatusBar (new MenuBar ());
+        top.Add (new MenuBar ());
         Assert.NotNull (top.MenuBar);
 
         // Application.Top with a menu and without status bar.
@@ -274,7 +286,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (1, ny);
         Assert.Null (sb);
 
-        top.AddMenuStatusBar (new StatusBar ());
+        top.Add (new StatusBar ());
         Assert.NotNull (top.StatusBar);
 
         // Application.Top with a menu and status bar.
@@ -286,8 +298,10 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (2, ny);
         Assert.NotNull (sb);
 
-        top.RemoveMenuStatusBar (top.MenuBar);
+        menuBar = top.MenuBar;
+        top.Remove (top.MenuBar);
         Assert.Null (top.MenuBar);
+        Assert.NotNull (menuBar);
 
         // Application.Top without a menu and with a status bar.
         View.GetLocationEnsuringFullVisibility (top, 2, 2, out nx, out ny, out sb);
@@ -298,8 +312,10 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (2, ny);
         Assert.NotNull (sb);
 
-        top.RemoveMenuStatusBar (top.StatusBar);
+        statusBar = top.StatusBar;
+        top.Remove (top.StatusBar);
         Assert.Null (top.StatusBar);
+        Assert.NotNull (statusBar);
         Assert.Null (top.MenuBar);
 
         var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
@@ -318,7 +334,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (0, ny);
         Assert.Null (sb);
 
-        top.AddMenuStatusBar (new MenuBar ());
+        top.Add (new MenuBar ());
         Assert.NotNull (top.MenuBar);
 
         // Application.Top with a menu and without status bar.
@@ -327,7 +343,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (1, ny);
         Assert.Null (sb);
 
-        top.AddMenuStatusBar (new StatusBar ());
+        top.Add (new StatusBar ());
         Assert.NotNull (top.StatusBar);
 
         // Application.Top with a menu and status bar.
@@ -339,10 +355,14 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (20, ny);
         Assert.NotNull (sb);
 
-        top.RemoveMenuStatusBar (top.MenuBar);
-        top.RemoveMenuStatusBar (top.StatusBar);
-        Assert.Null (top.StatusBar);
+        menuBar = top.MenuBar;
+        statusBar = top.StatusBar;
+        top.Remove (top.MenuBar);
         Assert.Null (top.MenuBar);
+        Assert.NotNull (menuBar);
+        top.Remove (top.StatusBar);
+        Assert.Null (top.StatusBar);
+        Assert.NotNull (statusBar);
 
         top.Remove (win);
 
@@ -355,7 +375,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (0, ny);
         Assert.Null (sb);
 
-        top.AddMenuStatusBar (new MenuBar ());
+        top.Add (new MenuBar ());
         Assert.NotNull (top.MenuBar);
 
         // Application.Top with a menu and without status bar.
@@ -364,7 +384,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (2, ny);
         Assert.Null (sb);
 
-        top.AddMenuStatusBar (new StatusBar ());
+        top.Add (new StatusBar ());
         Assert.NotNull (top.StatusBar);
 
         // Application.Top with a menu and status bar.
@@ -387,7 +407,21 @@ public partial class ToplevelTests (ITestOutputHelper output)
         win.NewMouseEvent (new () { Position = new (6, 0), Flags = MouseFlags.Button1Pressed });
 
         //Assert.Null (Toplevel._dragPosition);
+#if true
+        Assert.False (top.MenuBar.WasDisposed);
+        Assert.False (top.StatusBar.WasDisposed);
+#endif
+        menuBar = top.MenuBar;
+        statusBar = top.StatusBar;
         top.Dispose ();
+        Assert.Null (top.MenuBar);
+        Assert.Null (top.StatusBar);
+        Assert.NotNull (menuBar);
+        Assert.NotNull (statusBar);
+#if true
+        Assert.True (menuBar.WasDisposed);
+        Assert.True (statusBar.WasDisposed);
+#endif
     }
 
     [Fact (Skip = "#2491 - Test is broken until #2491 is more mature.")]
@@ -1569,4 +1603,31 @@ public partial class ToplevelTests (ITestOutputHelper output)
         t.Dispose ();
         Application.Shutdown ();
     }
+
+    [Fact]
+    public void Remove_Do_Not_Dispose_MenuBar_Or_StatusBar ()
+    {
+        var mb = new MenuBar ();
+        var sb = new StatusBar ();
+        var tl = new Toplevel ();
+
+#if DEBUG
+        Assert.False (mb.WasDisposed);
+        Assert.False (sb.WasDisposed);
+#endif
+        tl.Add (mb, sb);
+        Assert.NotNull (tl.MenuBar);
+        Assert.NotNull (tl.StatusBar);
+#if DEBUG
+        Assert.False (mb.WasDisposed);
+        Assert.False (sb.WasDisposed);
+#endif
+        tl.RemoveAll ();
+        Assert.Null (tl.MenuBar);
+        Assert.Null (tl.StatusBar);
+#if DEBUG
+        Assert.False (mb.WasDisposed);
+        Assert.False (sb.WasDisposed);
+#endif
+    }
 }

+ 44 - 2
docfx/docs/migratingfromv1.md

@@ -167,6 +167,7 @@ The API for handling keyboard input is significantly improved. See [Keyboard API
 * The preferred way to enable Application-wide or View-heirarchy-dependent keystrokes is to use the [Shortcut](~/api/Terminal.Gui.Shortcut.yml) View or the built-in View's that utilize it, such as the [Bar](~/api/Terminal.Gui.Bar.yml)-based views.
 * The preferred way to handle single keystrokes is to use **Key Bindings**. Key Bindings map a key press to a [Command](~/api/Terminal.Gui.Command.yml). A view can declare which commands it supports, and provide a lambda that implements the functionality of the command, using `View.AddCommand()`. Use the [View.Keybindings](~/api/Terminal.Gui.View.Keybindings.yml) to configure the key bindings.
 * For better consistency and user experience, the default key for closing an app or `Toplevel` is now `Esc` (it was previously `Ctrl+Q`).
+* The `Application.RootKeyEvent` method has been replaced with `Application.KeyDown`
 
 ### How to Fix
 
@@ -176,6 +177,12 @@ The API for handling keyboard input is significantly improved. See [Keyboard API
 * It should be very uncommon for v2 code to override `OnKeyPressed` etc... 
 * Anywhere `Ctrl+Q` was hard-coded as the "quit key", replace with `Application.QuitKey`.
 * See *Navigation* below for more information on v2's navigation keys.
+* Replace `Application.RootKeyEvent` with `Application.KeyDown`.  If the reason for subscribing to RootKeyEvent was to enable an application-wide action based on a key-press, consider using Application.KeyBindings instead.
+
+```diff
+- Application.RootKeyEvent(KeyEvent arg)
++ Application.KeyDown(object? sender, Key e)
+```
 
 ## Updated Mouse API
 
@@ -186,6 +193,7 @@ The API for mouse input is now internally consistent and easier to use.
 * Views can use the [View.Highlight](~/api/Terminal.Gui.View.Highlight.yml) event to have the view be visibly highlighted on various mouse events.
 * Views can set `View.WantContinousButtonPresses = true` to have their [Command.Accept](~/api/Terminal.Gui.Command.Accept.yml) command be invoked repeatedly as the user holds a mouse button down on the view.
 * Mouse and draw events now provide coordinates relative to the `Viewport` not the `Screen`.
+* The `Application.RootMouseEvent` method has been replaced with `Application.MouseEvent`
 
 ### How to Fix
 
@@ -193,6 +201,12 @@ The API for mouse input is now internally consistent and easier to use.
 * Use the [View.Highlight](~/api/Terminal.Gui.View.Highlight.yml) event to have the view be visibly highlighted on various mouse events.
 * Set `View.WantContinousButtonPresses = true` to have the [Command.Accept](~/api/Terminal.Gui.Command.Accept.yml) command be invoked repeatedly as the user holds a mouse button down on the view.
 * Update any code that assumed mouse events provided coordinates relative to the `Screen`.
+* Replace `Application.RootMouseEvent` with `Application.MouseEvent`.  
+
+```diff
+- Application.RootMouseEvent(KeyEvent arg)
++ Application.MouseEvent(object? sender, MouseEvent mouseEvent)
+```
 
 ## Navigation - `Cursor`, `Focus`, `TabStop` etc... 
 
@@ -270,6 +284,21 @@ These keys are all registered as `KeyBindingScope.Application` key bindings by `
 
 ...
 
+## Button.Clicked Event Renamed
+
+The `Button.Clicked` event has been renamed `Button.Accept`
+
+## How to Fix
+
+Rename all instances of `Button.Clicked` to `Button.Accept`.  Note the signature change to mouse events below.
+
+```diff
+- btnLogin.Clicked 
++ btnLogin.Accept
+```
+
+Alternatively, if you want to have key events as well as mouse events to fire an event, use `Button.Accept`.
+
 ## Events now use `object sender, EventArgs args` signature
 
 Previously events in Terminal.Gui used a mixture of `Action` (no arguments), `Action<string>` (or other raw datatype) and `Action<EventArgs>`. Now all events use the `EventHandler<EventArgs>` [standard .net design pattern](https://learn.microsoft.com/en-us/dotnet/csharp/event-pattern#event-delegate-signatures).
@@ -302,8 +331,9 @@ If you previously had a lambda expression, you can simply add the extra argument
 
 ```diff
 - btnLogin.Clicked += () => { /*do something*/ };
-+ btnLogin.Clicked += (s,e) => { /*do something*/ };
++ btnLogin.Accept += (s,e) => { /*do something*/ };
 ```
+Note that the event name has also changed as noted above.
 
 If you have used a named method instead of a lamda you will need to update the signature e.g.
 
@@ -391,4 +421,16 @@ Additionally, the `Toggle` event was renamed `CheckStateChanging` and made cance
 +}
 +preventChange = false;
 +cb.AdvanceCheckState ();
-```
+```
+
+## `MainLoop` is no longer accessible from `Application`
+
+In v1, you could add timeouts via `Application.MainLoop.AddTimeout` among other things.  In v2, the `MainLoop` object is internal to `Application` and methods previously accessed via `MainLoop` can now be accessed directly via `Application`
+
+### How to Fix
+
+```diff
+- Application.MainLoop.AddTimeout (TimeSpan time, Func<MainLoop, bool> callback)
++ Application.AddTimeout (TimeSpan time, Func<bool> callback)
+```
+

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно