2
0
Эх сурвалжийг харах

Backported a bunch of fixes from #3691

Tig 8 сар өмнө
parent
commit
7a15f7b39b

+ 2 - 0
Terminal.Gui/Application/Application.Driver.cs

@@ -8,6 +8,7 @@ public static partial class Application // Driver abstractions
     /// <summary>Gets the <see cref="ConsoleDriver"/> that has been selected. See also <see cref="ForceDriver"/>.</summary>
     public static ConsoleDriver? Driver { get; internal set; }
 
+    // BUGBUG: Force16Colors should be nullable.
     /// <summary>
     ///     Gets or sets whether <see cref="Application.Driver"/> will be forced to output only the 16 colors defined in
     ///     <see cref="ColorName16"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be output
@@ -16,6 +17,7 @@ public static partial class Application // Driver abstractions
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     public static bool Force16Colors { get; set; }
 
+    // BUGBUG: ForceDriver should be nullable.
     /// <summary>
     ///     Forces the use of the specified driver (one of "fake", "ansi", "curses", "net", or "windows"). If not
     ///     specified, the driver is selected based on the platform.

+ 5 - 0
Terminal.Gui/Application/Application.Keyboard.cs

@@ -49,6 +49,11 @@ public static partial class Application // Keyboard handling
         {
             if (binding.Value.BoundView is { })
             {
+                if (!binding.Value.BoundView.Enabled)
+                {
+                    return false;
+                }
+
                 bool? handled = binding.Value.BoundView?.InvokeCommands (binding.Value.Commands, binding.Key, binding.Value);
 
                 if (handled != null && (bool)handled)

+ 5 - 0
Terminal.Gui/Drawing/Glyphs.cs

@@ -20,6 +20,11 @@
 /// </remarks>
 public class GlyphDefinitions
 {
+    // IMPORTANT: If you change these, make sure to update the ./Resources/config.json file as
+    // IMPORTANT: it is the source of truth for the default glyphs at runtime.
+    // IMPORTANT: Configuration Manager test SaveDefaults uses this class to generate the default config file
+    // IMPORTANT: in ./UnitTests/bin/Debug/netX.0/config.json
+
     /// <summary>File icon.  Defaults to ☰ (Trigram For Heaven)</summary>
     public Rune File { get; set; } = (Rune)'☰';
 

+ 43 - 11
Terminal.Gui/Input/KeyBindings.cs

@@ -1,5 +1,7 @@
 #nullable enable
 
+using static System.Formats.Asn1.AsnWriter;
+
 namespace Terminal.Gui;
 
 /// <summary>
@@ -17,7 +19,7 @@ public class KeyBindings
     public KeyBindings () { }
 
     /// <summary>Initializes a new instance bound to <paramref name="boundView"/>.</summary>
-    public KeyBindings (View boundView) { BoundView = boundView; }
+    public KeyBindings (View? boundView) { BoundView = boundView; }
 
     /// <summary>Adds a <see cref="KeyBinding"/> to the collection.</summary>
     /// <param name="key"></param>
@@ -27,7 +29,12 @@ public class KeyBindings
     {
         if (BoundView is { } && binding.Scope.FastHasFlags (KeyBindingScope.Application))
         {
-            throw new ArgumentException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
+            throw new InvalidOperationException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
+        }
+
+        if (BoundView is { } && boundViewForAppScope is null)
+        {
+            boundViewForAppScope = BoundView;
         }
 
         if (TryGet (key, out KeyBinding _))
@@ -78,6 +85,10 @@ public class KeyBindings
         {
             throw new ArgumentException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
         }
+        else
+        {
+            // boundViewForAppScope = BoundView;
+        }
 
         if (key is null || !key.IsValid)
         {
@@ -93,11 +104,9 @@ public class KeyBindings
         if (TryGet (key, out KeyBinding binding))
         {
             throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
-
-            //Bindings [key] = new (commands, scope, BoundView);
         }
 
-        Add (key, new KeyBinding (commands, scope, BoundView), boundViewForAppScope);
+        Add (key, new KeyBinding (commands, scope, boundViewForAppScope), boundViewForAppScope);
     }
 
     /// <summary>
@@ -120,9 +129,14 @@ public class KeyBindings
     /// </param>
     public void Add (Key key, KeyBindingScope scope, params Command [] commands)
     {
+        if (BoundView is null && !scope.FastHasFlags (KeyBindingScope.Application))
+        {
+            throw new InvalidOperationException ("BoundView cannot be null.");
+        }
+
         if (BoundView is { } && scope.FastHasFlags (KeyBindingScope.Application))
         {
-            throw new ArgumentException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
+            throw new InvalidOperationException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
         }
 
         if (key == Key.Empty || !key.IsValid)
@@ -140,7 +154,7 @@ public class KeyBindings
             throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
         }
 
-        Add (key, new KeyBinding (commands, scope, BoundView));
+        Add (key, new KeyBinding (commands, scope, BoundView), BoundView);
     }
 
     /// <summary>
@@ -219,14 +233,22 @@ public class KeyBindings
     /// <summary>The collection of <see cref="KeyBinding"/> objects.</summary>
     public Dictionary<Key, KeyBinding> Bindings { get; } = new (new KeyEqualityComparer ());
 
+    /// <summary>
+    ///     Gets the keys that are bound.
+    /// </summary>
+    /// <returns></returns>
+    public IEnumerable<Key> GetBoundKeys ()
+    {
+        return Bindings.Keys;
+    }
+
     /// <summary>
     ///     The view that the <see cref="KeyBindings"/> are bound to.
     /// </summary>
     /// <remarks>
-    ///     If <see langword="null"/>, the <see cref="KeyBindings"/> are not bound to a <see cref="View"/>. This is used for
-    ///     Application.KeyBindings.
+    ///     If <see langword="null"/> the KeyBindings object is being used for Application.KeyBindings.
     /// </remarks>
-    public View? BoundView { get; }
+    internal View? BoundView { get; }
 
     /// <summary>Removes all <see cref="KeyBinding"/> objects from the collection.</summary>
     public void Clear () { Bindings.Clear (); }
@@ -312,7 +334,7 @@ public class KeyBindings
     /// <param name="boundViewForAppScope">Optional View for <see cref="KeyBindingScope.Application"/> bindings.</param>
     public void Remove (Key key, View? boundViewForAppScope = null)
     {
-        if (!TryGet (key, out KeyBinding binding))
+        if (!TryGet (key, out KeyBinding _))
         {
             return;
         }
@@ -371,6 +393,11 @@ public class KeyBindings
     /// <returns><see langword="true"/> if the Key is bound; otherwise <see langword="false"/>.</returns>
     public bool TryGet (Key key, out KeyBinding binding)
     {
+        //if (BoundView is null)
+        //{
+        //    throw new InvalidOperationException ("KeyBindings must be bound to a View to use this method.");
+        //}
+
         binding = new (Array.Empty<Command> (), KeyBindingScope.Disabled, null);
 
         if (key.IsValid)
@@ -394,6 +421,11 @@ public class KeyBindings
     {
         if (!key.IsValid)
         {
+        //if (BoundView is null)
+        //{
+        //    throw new InvalidOperationException ("KeyBindings must be bound to a View to use this method.");
+        //}
+
             binding = new (Array.Empty<Command> (), KeyBindingScope.Disabled, null);
             return false;
         }

+ 163 - 4
Terminal.Gui/Resources/config.json

@@ -16,14 +16,173 @@
   // to throw exceptions. 
   "ConfigurationManager.ThrowOnJsonErrors": false,
 
-  "Application.NextTabKey": "Tab",
-  "Application.PrevTabKey": "Shift+Tab",
+  // --------------- Application Settings ---------------
+  "Key.Separator": "+",
+
+  "Application.ArrangeKey": "Ctrl+F5",
+  "Application.Force16Colors": false,
+  //"Application.ForceDriver": "", // TODO: ForceDriver should be nullable
+  "Application.IsMouseDisabled": false,
   "Application.NextTabGroupKey": "F6",
+  "Application.NextTabKey": "Tab",
   "Application.PrevTabGroupKey": "Shift+F6",
+  "Application.PrevTabKey": "Shift+Tab",
   "Application.QuitKey": "Esc",
-  "Application.ArrangeKey": "Ctrl+F5",
-  "Key.Separator": "+",
 
+  // --------------- Colors ---------------
+
+
+  // --------------- View Specific Settings ---------------
+  "ContextMenu.DefaultKey": "Shift+F10",
+  "FileDialog.MaxSearchResults": 10000,
+  "FileDialogStyle.DefaultUseColors": false,
+  "FileDialogStyle.DefaultUseUnicodeCharacters": false,
+
+  // --------------- Glyphs ---------------
+  "Glyphs": {
+    "File": "☰",
+    "Folder": "꤉",
+    "HorizontalEllipsis": "…",
+    "VerticalFourDots": "⁞",
+    "CheckStateChecked": "☑",
+    "CheckStateUnChecked": "☐",
+    "CheckStateNone": "☒",
+    "Selected": "◉",
+    "UnSelected": "○",
+    "RightArrow": "►",
+    "LeftArrow": "◄",
+    "DownArrow": "▼",
+    "UpArrow": "▲",
+    "LeftDefaultIndicator": "►",
+    "RightDefaultIndicator": "◄",
+    "LeftBracket": "⟦",
+    "RightBracket": "⟧",
+    "BlocksMeterSegment": "▌",
+    "ContinuousMeterSegment": "█",
+    "Stipple": "░",
+    "Diamond": "◊",
+    "Close": "✘",
+    "Minimize": "❏",
+    "Maximize": "✽",
+    "Dot": "∙",
+    "BlackCircle": "●",
+    "Expand": "+",
+    "Collapse": "-",
+    "IdenticalTo": "≡",
+    "Move": "◊",
+    "SizeHorizontal": "↔",
+    "SizeVertical": "↕",
+    "SizeTopLeft": "↖",
+    "SizeTopRight": "↗",
+    "SizeBottomRight": "↘",
+    "SizeBottomLeft": "↙",
+    "Apple": "\uD83C\uDF4E",
+    "AppleBMP": "❦",
+    "HLine": "─",
+    "VLine": "│",
+    "HLineDbl": "═",
+    "VLineDbl": "║",
+    "HLineHvDa2": "╍",
+    "VLineHvDa3": "┇",
+    "HLineHvDa3": "┅",
+    "HLineHvDa4": "┉",
+    "VLineHvDa2": "╏",
+    "VLineHvDa4": "┋",
+    "HLineDa2": "╌",
+    "VLineDa3": "┆",
+    "HLineDa3": "┄",
+    "HLineDa4": "┈",
+    "VLineDa2": "╎",
+    "VLineDa4": "┊",
+    "HLineHv": "━",
+    "VLineHv": "┃",
+    "HalfLeftLine": "╴",
+    "HalfTopLine": "╵",
+    "HalfRightLine": "╶",
+    "HalfBottomLine": "╷",
+    "HalfLeftLineHv": "╸",
+    "HalfTopLineHv": "╹",
+    "HalfRightLineHv": "╺",
+    "HalfBottomLineLt": "╻",
+    "RightSideLineLtHv": "╼",
+    "BottomSideLineLtHv": "╽",
+    "LeftSideLineHvLt": "╾",
+    "TopSideLineHvLt": "╿",
+    "ULCorner": "┌",
+    "ULCornerDbl": "╔",
+    "ULCornerR": "╭",
+    "ULCornerHv": "┏",
+    "ULCornerHvLt": "┎",
+    "ULCornerLtHv": "┍",
+    "ULCornerDblSingle": "╓",
+    "ULCornerSingleDbl": "╒",
+    "LLCorner": "└",
+    "LLCornerHv": "┗",
+    "LLCornerHvLt": "┖",
+    "LLCornerLtHv": "┕",
+    "LLCornerDbl": "╚",
+    "LLCornerSingleDbl": "╘",
+    "LLCornerDblSingle": "╙",
+    "LLCornerR": "╰",
+    "URCorner": "┐",
+    "URCornerDbl": "╗",
+    "URCornerR": "╮",
+    "URCornerHv": "┓",
+    "URCornerHvLt": "┑",
+    "URCornerLtHv": "┒",
+    "URCornerDblSingle": "╖",
+    "URCornerSingleDbl": "╕",
+    "LRCorner": "┘",
+    "LRCornerDbl": "╝",
+    "LRCornerR": "╯",
+    "LRCornerHv": "┛",
+    "LRCornerDblSingle": "╜",
+    "LRCornerSingleDbl": "╛",
+    "LRCornerLtHv": "┙",
+    "LRCornerHvLt": "┚",
+    "LeftTee": "├",
+    "LeftTeeDblH": "╞",
+    "LeftTeeDblV": "╟",
+    "LeftTeeDbl": "╠",
+    "LeftTeeHvH": "┝",
+    "LeftTeeHvV": "┠",
+    "LeftTeeHvDblH": "┣",
+    "RightTee": "┤",
+    "RightTeeDblH": "╡",
+    "RightTeeDblV": "╢",
+    "RightTeeDbl": "╣",
+    "RightTeeHvH": "┥",
+    "RightTeeHvV": "┨",
+    "RightTeeHvDblH": "┫",
+    "TopTee": "┬",
+    "TopTeeDblH": "╤",
+    "TopTeeDblV": "╥",
+    "TopTeeDbl": "╦",
+    "TopTeeHvH": "┯",
+    "TopTeeHvV": "┰",
+    "TopTeeHvDblH": "┳",
+    "BottomTee": "┴",
+    "BottomTeeDblH": "╧",
+    "BottomTeeDblV": "╨",
+    "BottomTeeDbl": "╩",
+    "BottomTeeHvH": "┷",
+    "BottomTeeHvV": "┸",
+    "BottomTeeHvDblH": "┻",
+    "Cross": "┼",
+    "CrossDblH": "╪",
+    "CrossDblV": "╫",
+    "CrossDbl": "╬",
+    "CrossHvH": "┿",
+    "CrossHvV": "╂",
+    "CrossHv": "╋",
+    "ShadowVerticalStart": "▖",
+    "ShadowVertical": "▌",
+    "ShadowHorizontalStart": "▝",
+    "ShadowHorizontal": "▀",
+    "ShadowHorizontalEnd": "▘"
+  },
+
+  // --------------- Themes -----------------
   "Theme": "Default",
   "Themes": [
     {

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

@@ -35,7 +35,7 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
         int screenX4 = dimension == Dimension.Width ? Application.Screen.Width * 4 : Application.Screen.Height * 4;
         int autoMax = MaximumContentDim?.GetAnchor (superviewContentSize) ?? screenX4;
 
-        Debug.Assert (autoMin <= autoMax, "MinimumContentDim must be less than or equal to MaximumContentDim.");
+        Debug.WriteLineIf (autoMin > autoMax, "MinimumContentDim must be less than or equal to MaximumContentDim.");
 
         if (Style.FastHasFlags (DimAutoStyle.Text))
         {

+ 9 - 1
Terminal.Gui/View/View.Drawing.cs

@@ -55,6 +55,7 @@ public partial class View // Drawing APIs
             // Draw the Border and Padding.
             // We clip to the frame to prevent drawing outside the frame.
             saved = ClipFrame ();
+
             DoDrawBorderAndPadding ();
             SetClip (saved);
 
@@ -71,7 +72,7 @@ public partial class View // Drawing APIs
             DoSetAttribute ();
             DoClearViewport ();
 
-            // Draw the subviews
+            // Draw the subviews only if needed
             if (SubViewNeedsDraw)
             {
                 DoSetAttribute ();
@@ -166,6 +167,13 @@ public partial class View // Drawing APIs
 
     private void DoDrawBorderAndPadding ()
     {
+        if (SubViewNeedsDraw)
+        {
+            // A Subview may add to the LineCanvas. This ensures any Adornment LineCanvas updates happen.
+            Border?.SetNeedsDraw ();
+            Padding?.SetNeedsDraw ();
+        }
+
         if (OnDrawingBorderAndPadding ())
         {
             return;

+ 61 - 0
Terminal.Gui/View/View.Hierarchy.cs

@@ -256,6 +256,67 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
         return top;
     }
 
+    /// <summary>
+    ///     Gets whether <paramref name="view"/> is in the Subview hierarchy of <paramref name="start"/>.
+    /// </summary>
+    /// <param name="start">The View at the start of the hierarchy.</param>
+    /// <param name="view">The View to test.</param>
+    /// <param name="includeAdornments">Will search the subview hierarchy of the adornments if true.</param>
+    /// <returns></returns>
+    public static bool IsInHierarchy (View? start, View? view, bool includeAdornments = false)
+    {
+        if (view is null || start is null)
+        {
+            return false;
+        }
+
+        if (view == start)
+        {
+            return true;
+        }
+
+        foreach (View subView in start.Subviews)
+        {
+            if (view == subView)
+            {
+                return true;
+            }
+
+            bool found = IsInHierarchy (subView, view, includeAdornments);
+
+            if (found)
+            {
+                return found;
+            }
+        }
+
+        if (includeAdornments)
+        {
+            bool found = IsInHierarchy (start.Padding, view, includeAdornments);
+
+            if (found)
+            {
+                return found;
+            }
+
+            found = IsInHierarchy (start.Border, view, includeAdornments);
+
+            if (found)
+            {
+                return found;
+            }
+
+            found = IsInHierarchy (start.Margin, view, includeAdornments);
+
+            if (found)
+            {
+                return found;
+            }
+        }
+
+        return false;
+    }
+
     #region SubViewOrdering
 
     /// <summary>

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

@@ -690,7 +690,7 @@ public partial class View // Keyboard APIs
 
 #if DEBUG
 
-        if (Application.KeyBindings.TryGet (key, KeyBindingScope.Focused | KeyBindingScope.HotKey, out KeyBinding b))
+        if (Application.KeyBindings.TryGet (key, out KeyBinding b))
         {
             Debug.WriteLine (
                              $"WARNING: InvokeKeyBindings ({key}) - An Application scope binding exists for this key. The registered view will not invoke Command.");

+ 2 - 1
Terminal.Gui/View/View.Navigation.cs

@@ -357,6 +357,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// </returns>
     internal bool RestoreFocus ()
     {
+        // Ignore TabStop
         View [] indicies = GetFocusChain (NavigationDirection.Forward, null);
 
         if (Focused is null && _previouslyFocused is { } && indicies.Contains (_previouslyFocused))
@@ -495,7 +496,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// <exception cref="InvalidOperationException"></exception>
     private (bool focusSet, bool cancelled) SetHasFocusTrue (View? currentFocusedView, bool traversingUp = false)
     {
-        Debug.Assert (SuperView is null || ApplicationNavigation.IsInHierarchy (SuperView, this));
+        Debug.Assert (SuperView is null || View.IsInHierarchy (SuperView, this));
 
         // Pre-conditions
         if (_hasFocus)

+ 15 - 13
Terminal.Gui/Views/Bar.cs

@@ -20,7 +20,7 @@ public class Bar : View, IOrientation, IDesignable
     public Bar () : this ([]) { }
 
     /// <inheritdoc/>
-    public Bar (IEnumerable<Shortcut> shortcuts)
+    public Bar (IEnumerable<Shortcut>? shortcuts)
     {
         CanFocus = true;
 
@@ -28,20 +28,16 @@ public class Bar : View, IOrientation, IDesignable
         Height = Dim.Auto ();
 
         _orientationHelper = new (this);
-        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
-        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
 
         // Initialized += Bar_Initialized;
         MouseEvent += OnMouseEvent;
 
-        if (shortcuts is null)
+        if (shortcuts is { })
         {
-            return;
-        }
-
-        foreach (Shortcut shortcut in shortcuts)
-        {
-            Add (shortcut);
+            foreach (Shortcut shortcut in shortcuts)
+            {
+                Add (shortcut);
+            }
         }
     }
 
@@ -243,7 +239,6 @@ public class Bar : View, IOrientation, IDesignable
                     {
                         View barItem = Subviews [index];
 
-                        barItem.X = 0;
 
                         barItem.ColorScheme = ColorScheme;
 
@@ -254,6 +249,7 @@ public class Bar : View, IOrientation, IDesignable
 
                         if (barItem is Shortcut scBarItem)
                         {
+                            barItem.X = 0;
                             scBarItem.MinimumKeyTextSize = minKeyWidth;
                             scBarItem.Width = scBarItem.GetWidthDimAuto ();
                             barItem.Layout (Application.Screen.Size);
@@ -278,14 +274,20 @@ public class Bar : View, IOrientation, IDesignable
 
                     foreach (var subView in Subviews)
                     {
-                        subView.Width = Dim.Auto (DimAutoStyle.Auto, minimumContentDim: _maxBarItemWidth);
+                        if (subView is not Line)
+                        {
+                            subView.Width = Dim.Auto (DimAutoStyle.Auto, minimumContentDim: _maxBarItemWidth);
+                        }
                     }
                 }
                 else
                 {
                     foreach (var subView in Subviews)
                     {
-                        subView.Width = Dim.Fill();
+                        if (subView is not Line)
+                        {
+                            subView.Width = Dim.Fill ();
+                        }
                     }
                 }
 

+ 14 - 37
Terminal.Gui/Views/Line.cs

@@ -1,6 +1,10 @@
 namespace Terminal.Gui;
 
-/// <summary>Draws a single line using the <see cref="LineStyle"/> specified by <see cref="View.BorderStyle"/>.</summary>
+/// <summary>
+///     Draws a single line using the <see cref="LineStyle"/> specified by <see cref="View.BorderStyle"/>.
+/// </summary>
+/// <remarks>
+/// </remarks>
 public class Line : View, IOrientation
 {
     private readonly OrientationHelper _orientationHelper;
@@ -8,14 +12,13 @@ public class Line : View, IOrientation
     /// <summary>Constructs a Line object.</summary>
     public Line ()
     {
-        BorderStyle = LineStyle.Single;
-        Border.Thickness = new Thickness (0);
-        SuperViewRendersLineCanvas = true;
+        CanFocus = false;
+
+        base.SuperViewRendersLineCanvas = true;
 
         _orientationHelper = new (this);
         _orientationHelper.Orientation = Orientation.Horizontal;
-        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
-        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
+        OnOrientationChanged(Orientation);
     }
 
 
@@ -45,10 +48,12 @@ public class Line : View, IOrientation
         {
             case Orientation.Horizontal:
                 Height = 1;
+                Width = Dim.Fill ();
 
                 break;
             case Orientation.Vertical:
                 Width = 1;
+                Height = Dim.Fill ();
 
                 break;
 
@@ -56,48 +61,20 @@ public class Line : View, IOrientation
     }
     #endregion
 
-    /// <inheritdoc/>
-    public override void SetBorderStyle (LineStyle value)
-    {
-        // The default changes the thickness. We don't want that. We just set the style.
-        Border.LineStyle = value;
-    }
-
     /// <inheritdoc/>
     protected override bool OnDrawingContent ()
     {
-        LineCanvas lc = LineCanvas;
-
-        if (SuperViewRendersLineCanvas)
-        {
-            lc = SuperView?.LineCanvas;
-        }
-
-        if (SuperView is Adornment adornment)
-        {
-            lc = adornment.Parent?.LineCanvas;
-        }
-
         Point pos = ViewportToScreen (Viewport).Location;
         int length = Orientation == Orientation.Horizontal ? Frame.Width : Frame.Height;
 
-        if (SuperView is {} && SuperViewRendersLineCanvas && Orientation == Orientation.Horizontal)
-        {
-            pos.Offset (-SuperView.Border.Thickness.Left, 0);
-            length += SuperView.Border.Thickness.Horizontal;
-        }
-
-        if (SuperView is { } && SuperViewRendersLineCanvas && Orientation == Orientation.Vertical)
-        {
-            pos.Offset (0, -SuperView.Border.Thickness.Top);
-            length += SuperView.Border.Thickness.Vertical;
-        }
-        lc?.AddLine (
+        LineCanvas?.AddLine (
                     pos,
                     length,
                     Orientation,
                     BorderStyle
                    );
+
+        //SuperView?.SetNeedsDraw ();
         return true;
     }
 }

+ 21 - 8
Terminal.Gui/Views/Shortcut.cs

@@ -67,7 +67,7 @@ public class Shortcut : View, IOrientation, IDesignable
     /// </param>
     /// <param name="commandText">The text to display for the command.</param>
     /// <param name="helpText">The help text to display.</param>
-    public Shortcut (View targetView, Command command, string commandText, string helpText)
+    public Shortcut (View targetView, Command command, string commandText, string? helpText = null)
         : this (
                 targetView?.KeyBindings.GetKeyFromCommands (command)!,
                 commandText,
@@ -75,7 +75,7 @@ public class Shortcut : View, IOrientation, IDesignable
                 helpText)
     {
         _targetView = targetView;
-        _command = command;
+        Command = command;
     }
 
     /// <summary>
@@ -92,7 +92,7 @@ public class Shortcut : View, IOrientation, IDesignable
     /// <param name="helpText">The help text to display.</param>
     public Shortcut (Key key, string? commandText, Action? action, string? helpText = null)
     {
-        Id = "_shortcut";
+        Id = $"shortcut:{commandText}";
 
         HighlightStyle = HighlightStyle.None;
         CanFocus = true;
@@ -280,7 +280,15 @@ public class Shortcut : View, IOrientation, IDesignable
 
     private readonly View? _targetView; // If set, _command will be invoked
 
-    private readonly Command _command; // Used when _targetView is set
+    /// <summary>
+    ///     Gets the target <see cref="View"/> that the <see cref="Command"/> will be invoked on.
+    /// </summary>
+    public View? TargetView => _targetView;
+
+    /// <summary>
+    ///     Gets the <see cref="Command"/> that will be invoked on <see cref="TargetView"/> when the Shortcut is activated.
+    /// </summary>
+    public Command Command { get; }
 
     private void AddCommands ()
     {
@@ -319,6 +327,11 @@ public class Shortcut : View, IOrientation, IDesignable
             return true;
         }
 
+        if (ctx.Command != Command.Accept)
+        {
+           // return false;
+        }
+
         if (Action is { })
         {
             Action.Invoke ();
@@ -329,7 +342,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
         if (_targetView is { })
         {
-            _targetView.InvokeCommand (_command);
+            _targetView.InvokeCommand (Command);
         }
 
         return cancel;
@@ -616,7 +629,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
             if (_keyBindingScope == KeyBindingScope.Application)
             {
-                Application.KeyBindings.Remove (Key);
+                Application.KeyBindings.Remove (Key, this);
             }
 
             if (_keyBindingScope is KeyBindingScope.HotKey or KeyBindingScope.Focused)
@@ -691,10 +704,10 @@ public class Shortcut : View, IOrientation, IDesignable
             {
                 if (oldKey != Key.Empty)
                 {
-                    Application.KeyBindings.Remove (oldKey);
+                    Application.KeyBindings.Remove (oldKey, this);
                 }
 
-                Application.KeyBindings.Remove (Key);
+                Application.KeyBindings.Remove (Key, this);
                 Application.KeyBindings.Add (Key, this, Command.HotKey);
             }
             else

+ 3 - 3
UICatalog/Scenarios/KeyBindings.cs

@@ -80,10 +80,10 @@ public sealed class KeyBindings : Scenario
         };
         appWindow.Add (appBindingsListView);
 
-        foreach (var appBinding in Application.KeyBindings.Bindings)
+        foreach (var key in Application.KeyBindings.GetBoundKeys())
         {
-            var commands = Application.KeyBindings.GetCommands (appBinding.Key);
-            appBindings.Add ($"{appBinding.Key} -> {appBinding.Value.BoundView?.GetType ().Name} - {commands [0]}");
+            var binding = Application.KeyBindings.Get (key);
+            appBindings.Add ($"{key} -> {binding.BoundView?.GetType ().Name} - {binding.Commands [0]}");
         }
 
         ObservableCollection<string> hotkeyBindings = new ();

+ 49 - 33
UICatalog/Scenarios/Shortcuts.cs

@@ -1,11 +1,9 @@
+#nullable enable
+
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Diagnostics;
 using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using System.Timers;
 using Terminal.Gui;
 
@@ -29,10 +27,10 @@ public class Shortcuts : Scenario
 
     // Setting everything up in Loaded handler because we change the
     // QuitKey and it only sticks if changed after init
-    private void App_Loaded (object sender, EventArgs e)
+    private void App_Loaded (object? sender, EventArgs e)
     {
         Application.QuitKey = Key.F4.WithCtrl;
-        Application.Top.Title = GetQuitKeyAndName ();
+        Application.Top!.Title = GetQuitKeyAndName ();
 
         ObservableCollection<string> eventSource = new ();
 
@@ -47,7 +45,8 @@ public class Shortcuts : Scenario
             BorderStyle = LineStyle.Double,
             Title = "E_vents"
         };
-        eventLog.Width = Dim.Func (() => Math.Min (eventLog.SuperView!.Viewport.Width / 2, eventLog?.MaxLength + eventLog.GetAdornmentsThickness ().Horizontal ?? 0));
+        eventLog.Width = Dim.Func (() => Math.Min (Application.Top.Viewport.Width / 2, eventLog?.MaxLength + eventLog!.GetAdornmentsThickness ().Horizontal ?? 0));
+        eventLog.Width = Dim.Func (() => Math.Min (eventLog.SuperView!.Viewport.Width / 2, eventLog?.MaxLength + eventLog!.GetAdornmentsThickness ().Horizontal ?? 0));
         Application.Top.Add (eventLog);
 
         var alignKeysShortcut = new Shortcut
@@ -55,7 +54,7 @@ public class Shortcuts : Scenario
             Id = "alignKeysShortcut",
             X = 0,
             Y = 0,
-            Width = Dim.Fill () - Dim.Width (eventLog),
+            Width = Dim.Fill ()! - Dim.Width (eventLog),
             HelpText = "Fill to log",
             CommandView = new CheckBox
             {
@@ -67,6 +66,7 @@ public class Shortcuts : Scenario
             KeyBindingScope = KeyBindingScope.HotKey,
         };
 
+        // ((CheckBox)vShortcut3.CommandView).CheckedStateChanging += (_, args) =>
         ((CheckBox)alignKeysShortcut.CommandView).CheckedStateChanging += (s, e) =>
         {
             if (alignKeysShortcut.CommandView is CheckBox cb)
@@ -80,6 +80,7 @@ public class Shortcuts : Scenario
 
                 if (e.NewValue == CheckState.Checked)
                 {
+                    max = (from Shortcut? peer in enumerable select peer.Key.ToString ().GetColumns ()).Prepend (max).Max ();
                     foreach (var view in enumerable)
                     {
                         var peer = (Shortcut)view;
@@ -87,7 +88,7 @@ public class Shortcuts : Scenario
                     }
                 }
 
-                foreach (var view in enumerable)
+                foreach (View view in enumerable)
                 {
                     var peer = (Shortcut)view;
                     peer.MinimumKeyTextSize = max;
@@ -101,7 +102,7 @@ public class Shortcuts : Scenario
             Id = "commandFirstShortcut",
             X = 0,
             Y = Pos.Bottom (alignKeysShortcut),
-            Width = Dim.Fill () - Dim.Width (eventLog),
+            Width = Dim.Fill ()! - Dim.Width (eventLog),
             HelpText = "Show Command first",
             CommandView = new CheckBox
             {
@@ -122,7 +123,6 @@ public class Shortcuts : Scenario
                                                                                  eventSource.Add ($"{commandFirstShortcut.Id}.CommandView.CheckedStateChanging: {cb.Text}");
                                                                                  eventLog.MoveDown ();
 
-                                                                                 var max = 0;
                                                                                  IEnumerable<View> toAlign = Application.Top.Subviews.Where (v => v is Shortcut { Width: not DimAbsolute });
                                                                                  IEnumerable<View> enumerable = toAlign as View [] ?? toAlign.ToArray ();
 
@@ -148,7 +148,7 @@ public class Shortcuts : Scenario
             Id = "canFocusShortcut",
             X = 0,
             Y = Pos.Bottom (commandFirstShortcut),
-            Width = Dim.Fill () - Dim.Width (eventLog),
+            Width = Dim.Fill ()! - Dim.Width (eventLog),
             Key = Key.F4,
             HelpText = "Changes all Command.CanFocus",
             KeyBindingScope = KeyBindingScope.HotKey,
@@ -194,7 +194,7 @@ public class Shortcuts : Scenario
             Id = "buttonShortcut",
             X = 0,
             Y = Pos.Bottom (appShortcut),
-            Width = Dim.Fill () - Dim.Width (eventLog),
+            Width = Dim.Fill ()! - Dim.Width (eventLog),
             HelpText = "Accepting pops MB",
             CommandView = new Button
             {
@@ -217,7 +217,7 @@ public class Shortcuts : Scenario
             X = 0,
             Y = Pos.Bottom (buttonShortcut),
             Key = Key.F2,
-            Width = Dim.Fill () - Dim.Width (eventLog),
+            Width = Dim.Fill ()! - Dim.Width (eventLog),
             KeyBindingScope = KeyBindingScope.HotKey,
             CommandView = new RadioGroup
             {
@@ -228,8 +228,12 @@ public class Shortcuts : Scenario
 
         ((RadioGroup)radioGroupShortcut.CommandView).SelectedItemChanged += (o, args) =>
                                                                             {
-                                                                                eventSource.Add ($"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}");
-                                                                                eventLog.MoveDown ();
+                                                                                if (o is { })
+                                                                                {
+                                                                                    eventSource.Add (
+                                                                                                     $"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}");
+                                                                                    eventLog.MoveDown ();
+                                                                                }
                                                                             };
 
         Application.Top.Add (radioGroupShortcut);
@@ -239,7 +243,7 @@ public class Shortcuts : Scenario
             Id = "sliderShortcut",
             X = 0,
             Y = Pos.Bottom (radioGroupShortcut),
-            Width = Dim.Fill () - Dim.Width (eventLog),
+            Width = Dim.Fill ()! - Dim.Width (eventLog),
             KeyBindingScope = KeyBindingScope.HotKey,
             HelpText = "Sliders work!",
             CommandView = new Slider<string>
@@ -250,12 +254,12 @@ public class Shortcuts : Scenario
             Key = Key.F5,
         };
 
-        ((Slider<string>)sliderShortcut.CommandView).Options = new () { new () { Legend = "A" }, new () { Legend = "B" }, new () { Legend = "C" } };
+        ((Slider<string>)sliderShortcut.CommandView).Options = [new () { Legend = "A" }, new () { Legend = "B" }, new () { Legend = "C" }];
         ((Slider<string>)sliderShortcut.CommandView).SetOption (0);
 
         ((Slider<string>)sliderShortcut.CommandView).OptionsChanged += (o, args) =>
         {
-            eventSource.Add ($"OptionsChanged: {o.GetType ().Name} - {string.Join (",", ((Slider<string>)o).GetSetOptions ())}");
+            eventSource.Add ($"OptionsChanged: {o?.GetType ().Name} - {string.Join (",", ((Slider<string>)o!)!.GetSetOptions ())}");
             eventLog.MoveDown ();
         };
 
@@ -314,11 +318,20 @@ public class Shortcuts : Scenario
             Arrangement = ViewArrangement.RightResizable | ViewArrangement.BottomResizable,
         };
         framedShortcut.Orientation = Orientation.Horizontal;
-        framedShortcut.Padding.Thickness = new (0, 1, 0, 0);
-        framedShortcut.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
-        framedShortcut.CommandView.Margin.ColorScheme = framedShortcut.CommandView.ColorScheme = Colors.ColorSchemes ["Error"];
-        framedShortcut.HelpView.Margin.ColorScheme = framedShortcut.HelpView.ColorScheme = Colors.ColorSchemes ["Dialog"];
-        framedShortcut.KeyView.Margin.ColorScheme = framedShortcut.KeyView.ColorScheme = Colors.ColorSchemes ["Menu"];
+
+        if (framedShortcut.Padding is { })
+        {
+            framedShortcut.Padding.Thickness = new (0, 1, 0, 0);
+            framedShortcut.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
+        }
+
+        if (framedShortcut.CommandView.Margin is { })
+        {
+            framedShortcut.CommandView.Margin.ColorScheme = framedShortcut.CommandView.ColorScheme = Colors.ColorSchemes ["Error"];
+            framedShortcut.HelpView.Margin!.ColorScheme = framedShortcut.HelpView.ColorScheme = Colors.ColorSchemes ["Dialog"];
+            framedShortcut.KeyView.Margin!.ColorScheme = framedShortcut.KeyView.ColorScheme = Colors.ColorSchemes ["Menu"];
+        }
+
         framedShortcut.ColorScheme = Colors.ColorSchemes ["Toplevel"];
         Application.Top.Add (framedShortcut);
 
@@ -423,13 +436,16 @@ public class Shortcuts : Scenario
 
         bgColor.ColorChanged += (o, args) =>
         {
-            eventSource.Add ($"ColorChanged: {o.GetType ().Name} - {args.CurrentValue}");
-            eventLog.MoveDown ();
-
-            Application.Top.ColorScheme = new ColorScheme (Application.Top.ColorScheme)
+            if (o is { })
             {
-                Normal = new Attribute (Application.Top.ColorScheme.Normal.Foreground, args.CurrentValue),
-            };
+                eventSource.Add ($"ColorChanged: {o.GetType ().Name} - {args.CurrentValue}");
+                eventLog.MoveDown ();
+
+                Application.Top.ColorScheme = new ColorScheme (Application.Top.ColorScheme)
+                {
+                    Normal = new (Application.Top!.GetNormalColor ().Foreground, args.CurrentValue),
+                };
+            }
         };
         bgColorShortcut.CommandView = bgColor;
 
@@ -494,10 +510,10 @@ public class Shortcuts : Scenario
         }
     }
 
-    private void Button_Clicked (object sender, CommandEventArgs e)
+    private void Button_Clicked (object? sender, CommandEventArgs e)
     {
         e.Cancel = true;
-        View view = sender as View;
-        MessageBox.Query ("Hi", $"You clicked {view!.Text}", "_Ok");
+        View? view = sender as View;
+        MessageBox.Query ("Hi", $"You clicked {view?.Text}", "_Ok");
     }
 }

+ 4 - 5
UnitTests/Application/KeyboardTests.cs

@@ -159,16 +159,14 @@ public class KeyboardTests
     }
 
     [Fact]
-    [AutoInitShutdown]
     public void KeyBinding_OnKeyDown ()
     {
+        Application.Top = new Toplevel ();
         var view = new ScopedKeyBindingView ();
         var keyWasHandled = false;
         view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
 
-        var top = new Toplevel ();
-        top.Add (view);
-        Application.Begin (top);
+        Application.Top.Add (view);
 
         Application.RaiseKeyDownEvent (Key.A);
         Assert.False (keyWasHandled);
@@ -200,7 +198,8 @@ public class KeyboardTests
         Assert.True (view.ApplicationCommand);
         Assert.True (view.HotKeyCommand);
         Assert.False (view.FocusedCommand);
-        top.Dispose ();
+        Application.Top.Dispose ();
+        Application.ResetState (true);
     }
 
     [Fact]

+ 2 - 2
UnitTests/Application/SynchronizatonContextTests.cs

@@ -33,7 +33,7 @@ public class SyncrhonizationContextTests
         Task.Run (
                   () =>
                   {
-                      Thread.Sleep (1_000);
+                      Thread.Sleep (500);
 
                       // non blocking
                       context.Post (
@@ -68,7 +68,7 @@ public class SyncrhonizationContextTests
         Task.Run (
                   () =>
                   {
-                      Thread.Sleep (1_000);
+                      Thread.Sleep (500);
 
                       // blocking
                       context.Send (

+ 233 - 314
UnitTests/Dialogs/DialogTests.cs

@@ -39,7 +39,7 @@ public class DialogTests
             Width = width,
             Height = 1,
             ButtonAlignment = Alignment.Center,
-            Buttons = [new Button { Text = btn1Text }]
+            Buttons = [new() { Text = btn1Text }]
         };
 
         // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
@@ -67,7 +67,7 @@ public class DialogTests
             Width = width,
             Height = 1,
             ButtonAlignment = Alignment.Fill,
-            Buttons = [new Button { Text = btn1Text }]
+            Buttons = [new() { Text = btn1Text }]
         };
 
         // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
@@ -94,7 +94,7 @@ public class DialogTests
             Width = width,
             Height = 1,
             ButtonAlignment = Alignment.End,
-            Buttons = [new Button { Text = btn1Text }]
+            Buttons = [new() { Text = btn1Text }]
         };
 
         // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
@@ -122,7 +122,7 @@ public class DialogTests
             Width = width,
             Height = 1,
             ButtonAlignment = Alignment.Start,
-            Buttons = [new Button { Text = btn1Text }]
+            Buttons = [new() { Text = btn1Text }]
         };
 
         // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
@@ -169,17 +169,16 @@ public class DialogTests
         int width = buttonRow.Length;
         d.SetBufferSize (buttonRow.Length, 3);
 
-
         // Default - Center
         (runstate, Dialog dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Center,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                                      title,
+                                                      width,
+                                                      Alignment.Center,
+                                                      new Button { Text = btn1Text },
+                                                      new Button { Text = btn2Text },
+                                                      new Button { Text = btn3Text },
+                                                      new Button { Text = btn4Text }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -189,14 +188,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                      title,
-                                                      width,
-                                                      Alignment.Fill,
-                                                      new Button { Text = btn1Text },
-                                                      new Button { Text = btn2Text },
-                                                      new Button { Text = btn3Text },
-                                                      new Button { Text = btn4Text }
-                                                     );
+                                               title,
+                                               width,
+                                               Alignment.Fill,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -206,14 +205,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                      title,
-                                                      width,
-                                                      Alignment.End,
-                                                      new Button { Text = btn1Text },
-                                                      new Button { Text = btn2Text },
-                                                      new Button { Text = btn3Text },
-                                                      new Button { Text = btn4Text }
-                                                     );
+                                               title,
+                                               width,
+                                               Alignment.End,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -223,14 +222,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                      title,
-                                                      width,
-                                                      Alignment.Start,
-                                                      new Button { Text = btn1Text },
-                                                      new Button { Text = btn2Text },
-                                                      new Button { Text = btn3Text },
-                                                      new Button { Text = btn4Text }
-                                                     );
+                                               title,
+                                               width,
+                                               Alignment.Start,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -285,14 +284,14 @@ public class DialogTests
             $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} yes {CM.Glyphs.LeftBracket} no {CM.Glyphs.LeftBracket} maybe {CM.Glyphs.LeftBracket} never {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Fill,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Fill,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -302,14 +301,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.End,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.End,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -318,14 +317,14 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}{btn1}{btn2}{btn3}{CM.Glyphs.LeftBracket} neve{CM.Glyphs.VLine}";
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Start,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Start,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -361,14 +360,14 @@ public class DialogTests
 
         // Default - Center
         (runstate, Dialog dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Center,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                                      title,
+                                                      width,
+                                                      Alignment.Center,
+                                                      new Button { Text = btn1Text },
+                                                      new Button { Text = btn2Text },
+                                                      new Button { Text = btn3Text },
+                                                      new Button { Text = btn4Text }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -378,14 +377,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Fill,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Fill,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -395,14 +394,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.End,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.End,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -412,14 +411,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Start,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Start,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -457,14 +456,14 @@ public class DialogTests
 
         // Default - Center
         (runstate, Dialog dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Center,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                                      title,
+                                                      width,
+                                                      Alignment.Center,
+                                                      new Button { Text = btn1Text },
+                                                      new Button { Text = btn2Text },
+                                                      new Button { Text = btn3Text },
+                                                      new Button { Text = btn4Text }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -474,14 +473,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.GetColumns ());
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Fill,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Fill,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -491,14 +490,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.GetColumns ());
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.End,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.End,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -508,14 +507,14 @@ public class DialogTests
         Assert.Equal (width, buttonRow.GetColumns ());
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Start,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Start,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text },
+                                               new Button { Text = btn4Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -542,11 +541,11 @@ public class DialogTests
         d.SetBufferSize (width, 1);
 
         (runstate, Dialog dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Center,
-                                                    new Button { Text = btnText }
-                                                   );
+                                                      title,
+                                                      width,
+                                                      Alignment.Center,
+                                                      new Button { Text = btnText }
+                                                     );
 
         // Center
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -559,11 +558,11 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Fill,
-                                                    new Button { Text = btnText }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Fill,
+                                               new Button { Text = btnText }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -574,11 +573,11 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.End,
-                                                    new Button { Text = btnText }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.End,
+                                               new Button { Text = btnText }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -589,11 +588,11 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Start,
-                                                    new Button { Text = btnText }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Start,
+                                               new Button { Text = btnText }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -606,11 +605,11 @@ public class DialogTests
         d.SetBufferSize (width, 1);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Center,
-                                                    new Button { Text = btnText }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Center,
+                                               new Button { Text = btnText }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -621,11 +620,11 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Fill,
-                                                    new Button { Text = btnText }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Fill,
+                                               new Button { Text = btnText }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -636,11 +635,11 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.End,
-                                                    new Button { Text = btnText }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.End,
+                                               new Button { Text = btnText }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -651,11 +650,11 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Start,
-                                                    new Button { Text = btnText }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Start,
+                                               new Button { Text = btnText }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -687,13 +686,13 @@ public class DialogTests
         d.SetBufferSize (buttonRow.Length, 3);
 
         (runstate, Dialog dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Center,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text }
-                                                   );
+                                                      title,
+                                                      width,
+                                                      Alignment.Center,
+                                                      new Button { Text = btn1Text },
+                                                      new Button { Text = btn2Text },
+                                                      new Button { Text = btn3Text }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -703,13 +702,13 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Fill,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Fill,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -719,13 +718,13 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.End,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.End,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -735,13 +734,13 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Start,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Start,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text },
+                                               new Button { Text = btn3Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -771,12 +770,12 @@ public class DialogTests
         d.SetBufferSize (buttonRow.Length, 3);
 
         (runstate, Dialog dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Center,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text }
-                                                   );
+                                                      title,
+                                                      width,
+                                                      Alignment.Center,
+                                                      new Button { Text = btn1Text },
+                                                      new Button { Text = btn2Text }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -786,12 +785,12 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Fill,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Fill,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -801,12 +800,12 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.End,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.End,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -816,12 +815,12 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
 
         (runstate, dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Start,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text }
-                                                   );
+                                               title,
+                                               width,
+                                               Alignment.Start,
+                                               new Button { Text = btn1Text },
+                                               new Button { Text = btn2Text }
+                                              );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -855,8 +854,8 @@ public class DialogTests
         Button button1, button2;
 
         // Default (Center)
-        button1 = new Button { Text = btn1Text };
-        button2 = new Button { Text = btn2Text };
+        button1 = new() { Text = btn1Text };
+        button2 = new() { Text = btn2Text };
         (runstate, dlg) = BeginButtonTestDialog (title, width, Alignment.Center, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, firstIteration);
@@ -867,8 +866,8 @@ public class DialogTests
 
         // Justify
         Assert.Equal (width, buttonRow.Length);
-        button1 = new Button { Text = btn1Text };
-        button2 = new Button { Text = btn2Text };
+        button1 = new() { Text = btn1Text };
+        button2 = new() { Text = btn2Text };
         (runstate, dlg) = BeginButtonTestDialog (title, width, Alignment.Fill, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, firstIteration);
@@ -879,8 +878,8 @@ public class DialogTests
 
         // Right
         Assert.Equal (width, buttonRow.Length);
-        button1 = new Button { Text = btn1Text };
-        button2 = new Button { Text = btn2Text };
+        button1 = new() { Text = btn1Text };
+        button2 = new() { Text = btn2Text };
         (runstate, dlg) = BeginButtonTestDialog (title, width, Alignment.End, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, firstIteration);
@@ -890,8 +889,8 @@ public class DialogTests
 
         // Left
         Assert.Equal (width, buttonRow.Length);
-        button1 = new Button { Text = btn1Text };
-        button2 = new Button { Text = btn2Text };
+        button1 = new() { Text = btn1Text };
+        button2 = new() { Text = btn2Text };
         (runstate, dlg) = BeginButtonTestDialog (title, width, Alignment.Start, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, firstIteration);
@@ -1020,7 +1019,7 @@ public class DialogTests
         Dialog.DefaultBorderStyle = LineStyle.Single;
         Dialog.DefaultShadow = ShadowStyle.None;
         Button.DefaultShadow = ShadowStyle.None;
- 
+
         Iteration += (s, a) =>
                      {
                          iterations++;
@@ -1151,7 +1150,7 @@ public class DialogTests
                                                                                _output
                                                                               );
 
-                                 Assert.False (Top!.NewKeyDownEvent (Key.Enter));
+                                 Assert.False (Top.NewKeyDownEvent (Key.Enter));
 
                                  break;
                              case 4:
@@ -1194,7 +1193,7 @@ public class DialogTests
     [AutoInitShutdown]
     public void Location_Default ()
     {
-        var d = new Dialog ()
+        var d = new Dialog
         {
             Width = Dim.Percent (85),
             Height = Dim.Percent (85)
@@ -1253,86 +1252,6 @@ public class DialogTests
         d.Dispose ();
     }
 
-//    [Fact]
-//    [AutoInitShutdown]
-//    public void Location_When_Not_Application_Top_Not_Default ()
-//    {
-//        var top = new Toplevel ();
-//        top.BorderStyle = LineStyle.Double;
-
-//        int iterations = -1;
-
-//        // Override CM
-//        Window.DefaultBorderStyle = LineStyle.Single;
-//        Dialog.DefaultButtonAlignment = Alignment.Center;
-//        Dialog.DefaultBorderStyle = LineStyle.Single;
-//        Dialog.DefaultShadow = ShadowStyle.None;
-
-//        Iteration += (s, a) =>
-//                     {
-//                         iterations++;
-
-//                         if (iterations == 0)
-//                         {
-//                             var d = new Dialog { X = 5, Y = 5, Height = 3, Width = 5 };
-//                             RunState rs = Begin (d);
-
-//                             Assert.Equal (new (5, 5), d.Frame.Location);
-
-//                             TestHelpers.AssertDriverContentsWithFrameAre (
-//                                                                           @"
-//╔══════════════════╗
-//║                  ║
-//║                  ║
-//║                  ║
-//║                  ║
-//║    ┌───┐         ║
-//║    │   │         ║
-//║    └───┘         ║
-//║                  ║
-//╚══════════════════╝",
-//                                                                           _output
-//                                                                          );
-//                             End (rs);
-//                             d.Dispose ();
-
-//                             d = new ()
-//                             {
-//                                 X = 5, Y = 5,
-//                                 Width = Dim.Percent (85),
-//                                 Height = Dim.Percent (85)
-
-//                             };
-//                             rs = Begin (d);
-
-//                             TestHelpers.AssertDriverContentsWithFrameAre (
-//                                                                           @"
-//╔══════════════════╗
-//║                  ║
-//║                  ║
-//║                  ║
-//║                  ║
-//║    ┌──────────────
-//║    │              
-//║    │              
-//║    │              
-//╚════│    ",
-//                                                                           _output
-//                                                                          );
-//                             End (rs);
-//                             d.Dispose ();
-//                         }
-//                         else if (iterations > 0)
-//                         {
-//                             RequestStop ();
-//                         }
-//                     };
-
-//        ((FakeDriver)Driver).SetBufferSize (20, 10);
-//        Run (top);
-//        top.Dispose ();
-//    }
-
     [Fact]
     [AutoInitShutdown]
     public void One_Button_Works ()
@@ -1353,11 +1272,11 @@ public class DialogTests
         d.SetBufferSize (buttonRow.Length, 10);
 
         (runstate, Dialog dlg) = BeginButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Alignment.Center,
-                                                    new Button { Text = btnText }
-                                                   );
+                                                      title,
+                                                      width,
+                                                      Alignment.Center,
+                                                      new Button { Text = btnText }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
         dlg.Dispose ();
@@ -1367,7 +1286,7 @@ public class DialogTests
     [AutoInitShutdown]
     public void Size_Default ()
     {
-        var d = new Dialog ()
+        var d = new Dialog
         {
             Width = Dim.Percent (85),
             Height = Dim.Percent (85)

+ 38 - 20
UnitTests/Input/KeyBindingTests.cs

@@ -1,4 +1,5 @@
-using Xunit.Abstractions;
+using Terminal.Gui.EnumExtensions;
+using Xunit.Abstractions;
 
 namespace Terminal.Gui.InputTests;
 
@@ -10,11 +11,19 @@ public class KeyBindingTests
     [Fact]
     public void Add_Invalid_Key_Throws ()
     {
-        var keyBindings = new KeyBindings ();
+        var keyBindings = new KeyBindings (new View ());
         List<Command> commands = new ();
         Assert.Throws<ArgumentException> (() => keyBindings.Add (Key.Empty, KeyBindingScope.HotKey, Command.Accept));
     }
 
+    [Fact]
+    public void Add_BoundView_Null_Non_AppScope_Throws ()
+    {
+        var keyBindings = new KeyBindings ();
+        List<Command> commands = new ();
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.Empty, KeyBindingScope.HotKey, Command.Accept));
+    }
+
     [Fact]
     public void Add_Multiple_Adds ()
     {
@@ -53,32 +62,41 @@ public class KeyBindingTests
         Assert.Contains (Command.HotKey, resultCommands);
     }
 
+
     // Add should not allow duplicates
     [Fact]
-    public void Add_Throws_If_Exists ()
+    public void Add_With_Bound_View_Throws_If_App_Scope ()
     {
-        var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
+        var keyBindings = new KeyBindings (new View ());
         Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept));
+    }
+
+    // Add should not allow duplicates
+        [Fact]
+    public void Add_With_Throws_If_Exists ()
+    {
+        var keyBindings = new KeyBindings (new View ());
+        keyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey);
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.Accept));
 
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Contains (Command.HotKey, resultCommands);
 
-        keyBindings = new ();
+        keyBindings = new (new View ());
         keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.HotKey);
         Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept));
 
         resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Contains (Command.HotKey, resultCommands);
 
-        keyBindings = new ();
+        keyBindings = new (new View ());
         keyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey);
-        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept));
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.Accept));
 
         resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Contains (Command.HotKey, resultCommands);
 
-        keyBindings = new ();
+        keyBindings = new (new View ());
         keyBindings.Add (Key.A, new KeyBinding (new [] { Command.HotKey }, KeyBindingScope.HotKey));
         Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, new KeyBinding (new [] { Command.Accept }, KeyBindingScope.HotKey)));
 
@@ -207,11 +225,11 @@ public class KeyBindingTests
     [Fact]
     public void ReplaceKey_Replaces ()
     {
-        var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
-        keyBindings.Add (Key.B, KeyBindingScope.Application, Command.HotKey);
-        keyBindings.Add (Key.C, KeyBindingScope.Application, Command.HotKey);
-        keyBindings.Add (Key.D, KeyBindingScope.Application, Command.HotKey);
+        var keyBindings = new KeyBindings (new ());
+        keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.HotKey);
+        keyBindings.Add (Key.B, KeyBindingScope.Focused, Command.HotKey);
+        keyBindings.Add (Key.C, KeyBindingScope.Focused, Command.HotKey);
+        keyBindings.Add (Key.D, KeyBindingScope.Focused, Command.HotKey);
 
         keyBindings.ReplaceKey (Key.A, Key.E);
         Assert.Empty (keyBindings.GetCommands (Key.A));
@@ -233,9 +251,9 @@ public class KeyBindingTests
     [Fact]
     public void ReplaceKey_Replaces_Leaves_Old_Binding ()
     {
-        var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
-        keyBindings.Add (Key.B, KeyBindingScope.Application, Command.HotKey);
+        var keyBindings = new KeyBindings (new ());
+        keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept);
+        keyBindings.Add (Key.B, KeyBindingScope.Focused, Command.HotKey);
 
         keyBindings.ReplaceKey (keyBindings.GetKeyFromCommands (Command.Accept), Key.C);
         Assert.Empty (keyBindings.GetCommands (Key.A));
@@ -264,7 +282,7 @@ public class KeyBindingTests
     [InlineData (KeyBindingScope.Application)]
     public void Scope_Add_Adds (KeyBindingScope scope)
     {
-        var keyBindings = new KeyBindings ();
+        var keyBindings = new KeyBindings (scope.FastHasFlags(KeyBindingScope.Application) ? null : new ());
         Command [] commands = { Command.Right, Command.Left };
 
         var key = new Key (Key.A);
@@ -288,7 +306,7 @@ public class KeyBindingTests
     [InlineData (KeyBindingScope.Application)]
     public void Scope_Get_Filters (KeyBindingScope scope)
     {
-        var keyBindings = new KeyBindings ();
+        var keyBindings = new KeyBindings (scope.FastHasFlags (KeyBindingScope.Application) ? null : new ());
         Command [] commands = { Command.Right, Command.Left };
 
         var key = new Key (Key.A);
@@ -308,7 +326,7 @@ public class KeyBindingTests
     [InlineData (KeyBindingScope.Application)]
     public void Scope_TryGet_Filters (KeyBindingScope scope)
     {
-        var keyBindings = new KeyBindings ();
+        var keyBindings = new KeyBindings (scope.FastHasFlags (KeyBindingScope.Application) ? null : new ());
         Command [] commands = { Command.Right, Command.Left };
 
         var key = new Key (Key.A);

+ 8 - 5
UnitTests/View/Draw/AllViewsDrawTests.cs

@@ -42,11 +42,14 @@ public class AllViewsDrawTests (ITestOutputHelper _output) : TestsAllViews
         Assert.Equal (1, layoutStartedCount);
         Assert.Equal (1, layoutCompleteCount);
 
-        view.SetNeedsDraw ();
-        view.Draw ();
+        if (view.Visible)
+        {
+            view.SetNeedsDraw ();
+            view.Draw ();
 
-        Assert.Equal (1, drawCompleteCount);
-        Assert.Equal (1, layoutStartedCount);
-        Assert.Equal (1, layoutCompleteCount);
+            Assert.Equal (1, drawCompleteCount);
+            Assert.Equal (1, layoutStartedCount);
+            Assert.Equal (1, layoutCompleteCount);
+        }
     }
 }

+ 108 - 0
UnitTests/View/SubviewTests.cs

@@ -499,4 +499,112 @@ public class SubviewTests
         superView.MoveSubviewTowardsEnd (subview2);
         Assert.Equal (subview2, superView.Subviews [^1]);
     }
+
+    [Fact]
+    public void IsInHierarchy_ViewIsNull_ReturnsFalse ()
+    {
+        // Arrange
+        var start = new View ();
+
+        // Act
+        var result = View.IsInHierarchy (start, null);
+
+        // Assert
+        Assert.False (result);
+    }
+
+    [Fact]
+    public void IsInHierarchy_StartIsNull_ReturnsFalse ()
+    {
+        // Arrange
+        var view = new View ();
+
+        // Act
+        var result = View.IsInHierarchy (null, view);
+
+        // Assert
+        Assert.False (result);
+    }
+
+    [Fact]
+    public void IsInHierarchy_ViewIsStart_ReturnsTrue ()
+    {
+        // Arrange
+        var start = new View ();
+
+        // Act
+        var result = View.IsInHierarchy (start, start);
+
+        // Assert
+        Assert.True (result);
+    }
+
+    [Fact]
+    public void IsInHierarchy_ViewIsDirectSubview_ReturnsTrue ()
+    {
+        // Arrange
+        var start = new View ();
+        var subview = new View ();
+        start.Add (subview);
+
+        // Act
+        var result = View.IsInHierarchy (start, subview);
+
+        // Assert
+        Assert.True (result);
+    }
+
+    [Fact]
+    public void IsInHierarchy_ViewIsNestedSubview_ReturnsTrue ()
+    {
+        // Arrange
+        var start = new View ();
+        var subview = new View ();
+        var nestedSubview = new View ();
+        start.Add (subview);
+        subview.Add (nestedSubview);
+
+        // Act
+        var result = View.IsInHierarchy (start, nestedSubview);
+
+        // Assert
+        Assert.True (result);
+    }
+
+    [Fact]
+    public void IsInHierarchy_ViewIsNotInHierarchy_ReturnsFalse ()
+    {
+        // Arrange
+        var start = new View ();
+        var subview = new View ();
+
+        // Act
+        var result = View.IsInHierarchy (start, subview);
+
+        // Assert
+        Assert.False (result);
+    }
+
+    [Theory]
+    [CombinatorialData]
+    public void IsInHierarchy_ViewIsInAdornments_ReturnsTrue (bool includeAdornments)
+    {
+        // Arrange
+        var start = new View ()
+        {
+            Id = "start"
+        };
+        var inPadding = new View ()
+        {
+            Id = "inPadding"
+        };
+
+        start.Padding.Add (inPadding);
+
+        // Act
+        var result = View.IsInHierarchy (start, inPadding, includeAdornments: includeAdornments);
+
+        // Assert
+        Assert.Equal(includeAdornments, result);
+    }
 }

+ 1 - 1
UnitTests/Views/DateFieldTests.cs

@@ -188,7 +188,7 @@ public class DateFieldTests
 
         DateTime date = DateTime.Parse ("1/1/1971");
 
-        foreach (CultureInfo culture in CultureInfo.GetCultures (CultureTypes.AllCultures))
+        foreach (CultureInfo culture in CultureInfo.GetCultures (CultureTypes.NeutralCultures))
         {
             CultureInfo.CurrentCulture = culture;
             string separator = culture.DateTimeFormat.DateSeparator.Trim ();

+ 2 - 4
UnitTests/Views/ToplevelTests.cs

@@ -1,8 +1,6 @@
-using Xunit.Abstractions;
+namespace Terminal.Gui.ViewsTests;
 
-namespace Terminal.Gui.ViewsTests;
-
-public partial class ToplevelTests (ITestOutputHelper output)
+public partial class ToplevelTests ()
 {
     [Fact]
     public void Constructor_Default ()

+ 3 - 2
UnitTests/Views/ViewDisposalTest.cs

@@ -32,8 +32,8 @@ public class ViewDisposalTest (ITestOutputHelper output)
     private WeakReference DoTest ()
     {
         GetSpecialParams ();
-        var container = new View ();
-        Toplevel top = new ();
+        var container = new View () { Id = "container" };
+        Toplevel top = new () { Id = "top" };
         List<Type> views = GetViews ();
 
         foreach (Type view in views)
@@ -51,6 +51,7 @@ public class ViewDisposalTest (ITestOutputHelper output)
             }
 
             Assert.NotNull (instance);
+            instance.Id = $"{view.Name}";
             container.Add (instance);
             output.WriteLine ($"Added instance of {view}!");
         }