Browse Source

Partially Fixes #2975 - Upgrades UICatalog to `Menuv2` (#4011)

Tig 5 months ago
parent
commit
47833bfc80
31 changed files with 6388 additions and 5137 deletions
  1. 1 1
      Terminal.Gui/Application/ApplicationPopover.cs
  2. 6 2
      Terminal.Gui/View/View.Command.cs
  3. 10 0
      Terminal.Gui/View/View.Keyboard.cs
  4. 367 0
      Terminal.Gui/Views/FlagSelector.cs
  5. 31 0
      Terminal.Gui/Views/FlagSelectorStyles.cs
  6. 29 1
      Terminal.Gui/Views/Menu/MenuBarItemv2.cs
  7. 238 9
      Terminal.Gui/Views/Menu/MenuBarv2.cs
  8. 9 2
      Terminal.Gui/Views/Menu/MenuItemv2.cs
  9. 25 11
      Terminal.Gui/Views/Menu/Menuv2.cs
  10. 35 24
      Terminal.Gui/Views/Menu/PopoverMenu.cs
  11. 56 18
      Terminal.Gui/Views/RadioGroup.cs
  12. 9 3
      Terminal.Gui/Views/Shortcut.cs
  13. 1 0
      Terminal.sln.DotSettings
  14. 1 1
      Tests/UnitTests/Dialogs/MessageBoxTests.cs
  15. 1 1
      Tests/UnitTests/Text/TextFormatterTests.cs
  16. 22 0
      Tests/UnitTests/View/Keyboard/KeyBindingsTests.cs
  17. 513 3706
      Tests/UnitTests/Views/MenuBarTests.cs
  18. 3886 0
      Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs
  19. 2 2
      Tests/UnitTests/Views/Menuv1/Menuv1Tests.cs
  20. 124 0
      Tests/UnitTestsParallelizable/Views/FlagSelectorTests.cs
  21. 22 0
      Tests/UnitTestsParallelizable/Views/MenuBarItemTests.cs
  22. 14 0
      Tests/UnitTestsParallelizable/Views/MenuItemTests.cs
  23. 17 0
      Tests/UnitTestsParallelizable/Views/MenuTests.cs
  24. 0 211
      UICatalog/KeyBindingsDialog.cs
  25. 11 8
      UICatalog/Scenarios/CharacterMap/CharacterMap.cs
  26. 24 11
      UICatalog/Scenarios/HexEditor.cs
  27. 1 1
      UICatalog/Scenarios/MenusV2.cs
  28. 193 1124
      UICatalog/UICatalog.cs
  29. 1 1
      UICatalog/UICatalog.csproj
  30. 18 0
      UICatalog/UICatalogCommandLineOptions.cs
  31. 721 0
      UICatalog/UICatalogTopLevel.cs

+ 1 - 1
Terminal.Gui/Application/ApplicationPopover.cs

@@ -126,8 +126,8 @@ public sealed class ApplicationPopover : IDisposable
         // If there's an existing popover, hide it.
         // If there's an existing popover, hide it.
         if (_activePopover is View popoverView && popoverView == popover)
         if (_activePopover is View popoverView && popoverView == popover)
         {
         {
-            popoverView.Visible = false;
             _activePopover = null;
             _activePopover = null;
+            popoverView.Visible = false;
             Application.Top?.SetNeedsDraw ();
             Application.Top?.SetNeedsDraw ();
         }
         }
     }
     }

+ 6 - 2
Terminal.Gui/View/View.Command.cs

@@ -115,15 +115,18 @@ public partial class View // Command APIs
     /// </returns>
     /// </returns>
     protected bool? RaiseAccepting (ICommandContext? ctx)
     protected bool? RaiseAccepting (ICommandContext? ctx)
     {
     {
+        Logging.Trace($"{ctx?.Source?.Title}");
         CommandEventArgs args = new () { Context = ctx };
         CommandEventArgs args = new () { Context = ctx };
 
 
         // Best practice is to invoke the virtual method first.
         // Best practice is to invoke the virtual method first.
         // This allows derived classes to handle the event and potentially cancel it.
         // This allows derived classes to handle the event and potentially cancel it.
+        Logging.Trace ($"Calling OnAccepting...");
         args.Cancel = OnAccepting (args) || args.Cancel;
         args.Cancel = OnAccepting (args) || args.Cancel;
 
 
         if (!args.Cancel)
         if (!args.Cancel)
         {
         {
             // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
             // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
+            Logging.Trace ($"Raising Accepting...");
             Accepting?.Invoke (this, args);
             Accepting?.Invoke (this, args);
         }
         }
 
 
@@ -148,11 +151,12 @@ public partial class View // Command APIs
 
 
             if (SuperView is { })
             if (SuperView is { })
             {
             {
-                return SuperView?.InvokeCommand (Command.Accept, ctx) is true;
+                Logging.Trace ($"Invoking Accept on SuperView: {SuperView.Title}...");
+                return SuperView?.InvokeCommand (Command.Accept, ctx);
             }
             }
         }
         }
 
 
-        return Accepting is null ? null : args.Cancel;
+        return args.Cancel;
     }
     }
 
 
     /// <summary>
     /// <summary>

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

@@ -555,6 +555,11 @@ public partial class View // Keyboard APIs
 
 
     private static bool InvokeCommandsBoundToKeyOnAdornment (Adornment adornment, Key key, ref bool? handled)
     private static bool InvokeCommandsBoundToKeyOnAdornment (Adornment adornment, Key key, ref bool? handled)
     {
     {
+        if (!adornment.Enabled)
+        {
+            return false;
+        }
+
         bool? adornmentHandled = adornment.InvokeCommands (key);
         bool? adornmentHandled = adornment.InvokeCommands (key);
 
 
         if (adornmentHandled is true)
         if (adornmentHandled is true)
@@ -599,6 +604,11 @@ public partial class View // Keyboard APIs
     /// </returns>
     /// </returns>
     internal bool? InvokeCommandsBoundToHotKey (Key hotKey)
     internal bool? InvokeCommandsBoundToHotKey (Key hotKey)
     {
     {
+        if (!Enabled)
+        {
+            return false;
+        }
+
         bool? handled = null;
         bool? handled = null;
         // Process this View
         // Process this View
         if (HotKeyBindings.TryGet (hotKey, out KeyBinding binding))
         if (HotKeyBindings.TryGet (hotKey, out KeyBinding binding))

+ 367 - 0
Terminal.Gui/Views/FlagSelector.cs

@@ -0,0 +1,367 @@
+#nullable enable
+namespace Terminal.Gui;
+
+
+/// <summary>
+///     Provides a user interface for displaying and selecting flags.
+///     Flags can be set from a dictionary or directly from an enum type.
+/// </summary>
+public class FlagSelector : View, IDesignable, IOrientation
+{
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="FlagSelector"/> class.
+    /// </summary>
+    public FlagSelector ()
+    {
+        CanFocus = true;
+
+        Width = Dim.Auto (DimAutoStyle.Content);
+        Height = Dim.Auto (DimAutoStyle.Content);
+
+        // ReSharper disable once UseObjectOrCollectionInitializer
+        _orientationHelper = new (this);
+        _orientationHelper.Orientation = Orientation.Vertical;
+
+        // Accept (Enter key or DoubleClick) - Raise Accept event - DO NOT advance state
+        AddCommand (Command.Accept, HandleAcceptCommand);
+
+        CreateSubViews ();
+    }
+
+    private bool? HandleAcceptCommand (ICommandContext? ctx) { return RaiseAccepting (ctx); }
+
+    private uint _value;
+
+    /// <summary>
+    /// Gets or sets the value of the selected flags.
+    /// </summary>
+    public uint Value
+    {
+        get => _value;
+        set
+        {
+            if (_value == value)
+            {
+                return;
+            }
+
+            _value = value;
+
+            if (_value == 0)
+            {
+                UncheckAll ();
+            }
+            else
+            {
+                UncheckNone ();
+                UpdateChecked ();
+            }
+
+            if (ValueEdit is { })
+            {
+                ValueEdit.Text = value.ToString ();
+            }
+
+            RaiseValueChanged ();
+        }
+    }
+
+    private void RaiseValueChanged ()
+    {
+        OnValueChanged ();
+        ValueChanged?.Invoke (this, new (Value));
+    }
+
+    /// <summary>
+    ///     Called when <see cref="Value"/> has changed.
+    /// </summary>
+    protected virtual void OnValueChanged () { }
+
+    /// <summary>
+    ///     Raised when <see cref="Value"/> has changed.
+    /// </summary>
+    public event EventHandler<EventArgs<uint>>? ValueChanged;
+
+    private FlagSelectorStyles _styles;
+
+    /// <summary>
+    /// Gets or sets the styles for the flag selector.
+    /// </summary>
+    public FlagSelectorStyles Styles
+    {
+        get => _styles;
+        set
+        {
+            if (_styles == value)
+            {
+                return;
+            }
+
+            _styles = value;
+
+            CreateSubViews ();
+        }
+    }
+
+    /// <summary>
+    ///     Set the flags and flag names.
+    /// </summary>
+    /// <param name="flags"></param>
+    public void SetFlags (IReadOnlyDictionary<uint, string> flags)
+    {
+        Flags = flags;
+        CreateSubViews ();
+    }
+
+    /// <summary>
+    ///     Set the flags and flag names from an enum type.
+    /// </summary>
+    /// <typeparam name="TEnum">The enum type to extract flags from</typeparam>
+    /// <remarks>
+    ///     This is a convenience method that converts an enum to a dictionary of flag values and names.
+    ///     The enum values are converted to uint values and the enum names become the display text.
+    /// </remarks>
+    public void SetFlags<TEnum> () where TEnum : struct, Enum
+    {
+        // Convert enum names and values to a dictionary
+        Dictionary<uint, string> flagsDictionary = Enum.GetValues<TEnum> ()
+                                                       .ToDictionary (
+                                                                      f => Convert.ToUInt32 (f),
+                                                                      f => f.ToString ()
+                                                                     );
+
+        SetFlags (flagsDictionary);
+    }
+
+    /// <summary>
+    ///     Set the flags and flag names from an enum type with custom display names.
+    /// </summary>
+    /// <typeparam name="TEnum">The enum type to extract flags from</typeparam>
+    /// <param name="nameSelector">A function that converts enum values to display names</param>
+    /// <remarks>
+    ///     This is a convenience method that converts an enum to a dictionary of flag values and custom names.
+    ///     The enum values are converted to uint values and the display names are determined by the nameSelector function.
+    /// </remarks>
+    /// <example>
+    ///     <code>
+    ///        // Use enum values with custom display names
+    ///        var flagSelector = new FlagSelector ();
+    ///        flagSelector.SetFlags&lt;FlagSelectorStyles&gt;
+    ///             (f => f switch {
+    ///             FlagSelectorStyles.ShowNone => "Show None Value",
+    ///             FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
+    ///             FlagSelectorStyles.All => "Everything",
+    ///             _ => f.ToString()
+    ///             });
+    ///     </code>
+    /// </example>
+    public void SetFlags<TEnum> (Func<TEnum, string> nameSelector) where TEnum : struct, Enum
+    {
+        // Convert enum values and custom names to a dictionary
+        Dictionary<uint, string> flagsDictionary = Enum.GetValues<TEnum> ()
+                                                       .ToDictionary (
+                                                                      f => Convert.ToUInt32 (f),
+                                                                      nameSelector
+                                                                     );
+
+        SetFlags (flagsDictionary);
+    }
+
+    /// <summary>
+    ///     Gets the flags.
+    /// </summary>
+    public IReadOnlyDictionary<uint, string>? Flags { get; internal set; }
+
+    private TextField? ValueEdit { get; set; }
+
+    private void CreateSubViews ()
+    {
+        if (Flags is null)
+        {
+            return;
+        }
+
+        View [] subviews = SubViews.ToArray ();
+
+        RemoveAll ();
+
+        foreach (View v in subviews)
+        {
+            v.Dispose ();
+        }
+
+        if (Styles.HasFlag (FlagSelectorStyles.ShowNone) && !Flags.ContainsKey (0))
+        {
+            Add (CreateCheckBox ("None", 0));
+        }
+
+        for (var index = 0; index < Flags.Count; index++)
+        {
+            if (!Styles.HasFlag (FlagSelectorStyles.ShowNone) && Flags.ElementAt (index).Key == 0)
+            {
+                continue;
+            }
+
+            Add (CreateCheckBox (Flags.ElementAt (index).Value, Flags.ElementAt (index).Key));
+        }
+
+        if (Styles.HasFlag (FlagSelectorStyles.ShowValueEdit))
+        {
+            ValueEdit = new ()
+            {
+                Id = "valueEdit",
+                CanFocus = false,
+                Text = Value.ToString (),
+                Width = 5,
+                ReadOnly = true
+            };
+
+            Add (ValueEdit);
+        }
+
+        SetLayout ();
+
+        return;
+
+        CheckBox CreateCheckBox (string name, uint flag)
+        {
+            var checkbox = new CheckBox
+            {
+                CanFocus = false,
+                Title = name,
+                Id = name,
+                Data = flag,
+                HighlightStyle = HighlightStyle
+            };
+
+            checkbox.Selecting += (sender, args) => { RaiseSelecting (args.Context); };
+
+            checkbox.CheckedStateChanged += (sender, args) =>
+                                            {
+                                                uint newValue = Value;
+
+                                                if (checkbox.CheckedState == CheckState.Checked)
+                                                {
+                                                    if ((uint)checkbox.Data == 0)
+                                                    {
+                                                        newValue = 0;
+                                                    }
+                                                    else
+                                                    {
+                                                        newValue |= flag;
+                                                    }
+                                                }
+                                                else
+                                                {
+                                                    newValue &= ~flag;
+                                                }
+
+                                                Value = newValue;
+
+                                                //UpdateChecked();
+                                            };
+
+            return checkbox;
+        }
+    }
+
+    private void SetLayout ()
+    {
+        foreach (View sv in SubViews)
+        {
+            if (Orientation == Orientation.Vertical)
+            {
+                sv.X = 0;
+                sv.Y = Pos.Align (Alignment.Start);
+            }
+            else
+            {
+                sv.X = Pos.Align (Alignment.Start);
+                sv.Y = 0;
+                sv.Margin!.Thickness = new (0, 0, 1, 0);
+            }
+        }
+    }
+
+    private void UncheckAll ()
+    {
+        foreach (CheckBox cb in SubViews.Where (sv => sv is CheckBox cb && cb.Title != "None").Cast<CheckBox> ())
+        {
+            cb.CheckedState = CheckState.UnChecked;
+        }
+    }
+
+    private void UncheckNone ()
+    {
+        foreach (CheckBox cb in SubViews.Where (sv => sv is CheckBox { Title: "None" }).Cast<CheckBox> ())
+        {
+            cb.CheckedState = CheckState.UnChecked;
+        }
+    }
+
+    private void UpdateChecked ()
+    {
+        foreach (CheckBox cb in SubViews.Where (sv => sv is CheckBox { }).Cast<CheckBox> ())
+        {
+            var flag = (uint)(cb.Data ?? throw new InvalidOperationException ("ComboBox.Data must be set"));
+
+            // If this flag is set in Value, check the checkbox. Otherwise, uncheck it.
+            if (flag == 0 && Value != 0)
+            {
+                cb.CheckedState = CheckState.UnChecked;
+            }
+            else
+            {
+                cb.CheckedState = (Value & flag) == flag ? CheckState.Checked : CheckState.UnChecked;
+            }
+        }
+    }
+
+    /// <inheritdoc/>
+    protected override void OnSubViewAdded (View view) { }
+
+    #region IOrientation
+
+    /// <summary>
+    ///     Gets or sets the <see cref="Orientation"/> for this <see cref="RadioGroup"/>. The default is
+    ///     <see cref="Orientation.Vertical"/>.
+    /// </summary>
+    public Orientation Orientation
+    {
+        get => _orientationHelper.Orientation;
+        set => _orientationHelper.Orientation = value;
+    }
+
+    private readonly OrientationHelper _orientationHelper;
+
+#pragma warning disable CS0067 // The event is never used
+    /// <inheritdoc/>
+    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
+
+    /// <inheritdoc/>
+    public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
+#pragma warning restore CS0067 // The event is never used
+
+#pragma warning restore CS0067
+
+    /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
+    /// <param name="newOrientation"></param>
+    public void OnOrientationChanged (Orientation newOrientation) { SetLayout (); }
+
+    #endregion IOrientation
+
+    /// <inheritdoc/>
+    public bool EnableForDesign ()
+    {
+        SetFlags<FlagSelectorStyles> (
+                                      f => f switch
+                                           {
+                                               FlagSelectorStyles.ShowNone => "Show _None Value",
+                                               FlagSelectorStyles.ShowValueEdit => "Show _Value Editor",
+                                               FlagSelectorStyles.All => "Show _All Flags Selector",
+                                               _ => f.ToString ()
+                                           });
+
+        return true;
+    }
+}

+ 31 - 0
Terminal.Gui/Views/FlagSelectorStyles.cs

@@ -0,0 +1,31 @@
+#nullable enable
+namespace Terminal.Gui;
+
+/// <summary>
+///     Styles for <see cref="FlagSelector"/>.
+/// </summary>
+[Flags]
+public enum FlagSelectorStyles
+{
+    /// <summary>
+    ///     No styles.
+    /// </summary>
+    None = 0b_0000_0000,
+
+    /// <summary>
+    ///     Show the `None` checkbox. This will add a checkbox with the title "None" and a value of 0
+    ///     even if the flags do not contain a value of 0.
+    /// </summary>
+    ShowNone = 0b_0000_0001,
+
+    /// <summary>
+    ///     Show the value edit. This will add a read-only <see cref="TextField"/> to the <see cref="FlagSelector"/> to allow
+    ///     the user to see the value.
+    /// </summary>
+    ShowValueEdit = 0b_0000_0010,
+
+    /// <summary>
+    ///     All styles.
+    /// </summary>
+    All = ShowNone | ShowValueEdit
+}

+ 29 - 1
Terminal.Gui/Views/Menu/MenuBarItemv2.cs

@@ -77,13 +77,41 @@ public class MenuBarItemv2 : MenuItemv2
                 new (menuItems))
                 new (menuItems))
     { }
     { }
 
 
-    // TODO: Hide base.SubMenu?
+    /// <summary>
+    ///     Do not use this property. MenuBarItem does not support SubMenu. Use <see cref="PopoverMenu"/> instead.
+    /// </summary>
+    /// <exception cref="InvalidOperationException"></exception>
+    public new Menuv2? SubMenu
+    {
+        get => null;
+        set => throw new InvalidOperationException ("MenuBarItem does not support SubMenu. Use PopoverMenu instead.");
+    }
 
 
     /// <summary>
     /// <summary>
     ///     The Popover Menu that will be displayed when this item is selected.
     ///     The Popover Menu that will be displayed when this item is selected.
     /// </summary>
     /// </summary>
     public PopoverMenu? PopoverMenu { get; set; }
     public PopoverMenu? PopoverMenu { get; set; }
 
 
+    /// <inheritdoc />
+    protected override bool OnKeyDownNotHandled (Key key)
+    {
+        Logging.Trace ($"{key}");
+
+        if (PopoverMenu is { Visible: true } && HotKeyBindings.TryGet (key, out _))
+        {
+            // If the user presses the hotkey for a menu item that is already open,
+            // it should close the menu item (Test: MenuBarItem_HotKey_DeActivates)
+            if (SuperView is MenuBarv2 { } menuBar)
+            {
+                menuBar.HideActiveItem ();
+            }
+
+
+            return true;
+        }
+        return false;
+    }
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void Dispose (bool disposing)
     protected override void Dispose (bool disposing)
     {
     {

+ 238 - 9
Terminal.Gui/Views/Menu/MenuBarv2.cs

@@ -1,4 +1,7 @@
 #nullable enable
 #nullable enable
+using System.ComponentModel;
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
@@ -17,11 +20,56 @@ public class MenuBarv2 : Menuv2, IDesignable
     /// <inheritdoc/>
     /// <inheritdoc/>
     public MenuBarv2 (IEnumerable<MenuBarItemv2> menuBarItems) : base (menuBarItems)
     public MenuBarv2 (IEnumerable<MenuBarItemv2> menuBarItems) : base (menuBarItems)
     {
     {
+        CanFocus = false;
         TabStop = TabBehavior.TabGroup;
         TabStop = TabBehavior.TabGroup;
         Y = 0;
         Y = 0;
         Width = Dim.Fill ();
         Width = Dim.Fill ();
         Orientation = Orientation.Horizontal;
         Orientation = Orientation.Horizontal;
 
 
+        Key = DefaultKey;
+        AddCommand (Command.HotKey,
+                   () =>
+                   {
+                       if (HideActiveItem ())
+                       {
+                           return true;
+                       }
+
+                       if (SubViews.FirstOrDefault (sv => sv is MenuBarItemv2 { PopoverMenu: { } }) is MenuBarItemv2 { } first)
+                       {
+                           _active = true;
+                           ShowPopover (first);
+
+                           return true;
+                       }
+
+                       return false;
+                   });
+        HotKeyBindings.Add (Key, Command.HotKey);
+
+        KeyBindings.Add (Key, Command.Quit);
+        KeyBindings.ReplaceCommands (Application.QuitKey, Command.Quit);
+
+        AddCommand (
+                    Command.Quit,
+                    ctx =>
+                    {
+                        if (HideActiveItem ())
+                        {
+                            return true;
+                        }
+
+                        if (CanFocus)
+                        {
+                            CanFocus = false;
+                            _active = false;
+
+                            return true;
+                        }
+
+                        return false;//RaiseAccepted (ctx);
+                    });
+
         AddCommand (Command.Right, MoveRight);
         AddCommand (Command.Right, MoveRight);
         KeyBindings.Add (Key.CursorRight, Command.Right);
         KeyBindings.Add (Key.CursorRight, Command.Right);
 
 
@@ -35,10 +83,108 @@ public class MenuBarv2 : Menuv2, IDesignable
         bool? MoveRight (ICommandContext? ctx) { return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); }
         bool? MoveRight (ICommandContext? ctx) { return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); }
     }
     }
 
 
+    private Key _key = DefaultKey;
+
+    /// <summary>Specifies the key that will activate the context menu.</summary>
+    public Key Key
+    {
+        get => _key;
+        set
+        {
+            Key oldKey = _key;
+            _key = value;
+            KeyChanged?.Invoke (this, new (oldKey, _key));
+        }
+    }
+
+    /// <summary>
+    ///     Sets the Menu Bar Items for this Menu Bar. This will replace any existing Menu Bar Items.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This is a convenience property to help porting from the v1 MenuBar.
+    ///     </para>
+    /// </remarks>
+    public MenuBarItemv2 []? Menus
+    {
+        set
+        {
+            RemoveAll ();
+            if (value is null)
+            {
+                return;
+            }
+            foreach (MenuBarItemv2 mbi in value)
+            {
+                Add (mbi);
+            }
+        }
+    }
+
+    /// <summary>Raised when <see cref="Key"/> is changed.</summary>
+    public event EventHandler<KeyChangedEventArgs>? KeyChanged;
+
+    /// <summary>The default key for activating menu bars.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key DefaultKey { get; set; } = Key.F9;
+
+    /// <summary>
+    ///     Gets whether any of the menu bar items have a visible <see cref="PopoverMenu"/>.
+    /// </summary>
+    /// <exception cref="NotImplementedException"></exception>
+    public bool IsOpen ()
+    {
+        return SubViews.Count (sv => sv is MenuBarItemv2 { PopoverMenu: { Visible: true } }) > 0;
+    }
+
+    private bool _active;
+
+    /// <summary>
+    ///     Returns a value indicating whether the menu bar is active or not. When active, moving the mouse
+    ///     over a menu bar item will activate it.
+    /// </summary>
+    /// <returns></returns>
+    public bool IsActive ()
+    {
+        return _active;
+    }
+
+    /// <inheritdoc />
+    protected override bool OnMouseEnter (CancelEventArgs eventArgs)
+    {
+        // If the MenuBar does not have focus and the mouse enters: Enable CanFocus
+        // But do NOT show a Popover unless the user clicks or presses a hotkey
+        if (!HasFocus)
+        {
+            CanFocus = true;
+        }
+        return base.OnMouseEnter (eventArgs);
+    }
+
+    /// <inheritdoc />
+    protected override void OnMouseLeave ()
+    {
+        if (!IsOpen ())
+        {
+            CanFocus = false;
+        }
+        base.OnMouseLeave ();
+    }
+
+    /// <inheritdoc />
+    protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? focusedView)
+    {
+        if (!newHasFocus)
+        {
+            _active = false;
+            CanFocus = false;
+        }
+    }
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void OnSelectedMenuItemChanged (MenuItemv2? selected)
     protected override void OnSelectedMenuItemChanged (MenuItemv2? selected)
     {
     {
-        if (selected is MenuBarItemv2 { } selectedMenuBarItem)
+        if (selected is MenuBarItemv2 { PopoverMenu.Visible: false } selectedMenuBarItem)
         {
         {
             ShowPopover (selectedMenuBarItem);
             ShowPopover (selectedMenuBarItem);
         }
         }
@@ -56,7 +202,7 @@ public class MenuBarv2 : Menuv2, IDesignable
         }
         }
 
 
         // TODO: This needs to be done whenever a menuitem in any MenuBarItem changes
         // TODO: This needs to be done whenever a menuitem in any MenuBarItem changes
-        foreach (MenuBarItemv2? mbi in SubViews.Select(s => s as MenuBarItemv2))
+        foreach (MenuBarItemv2? mbi in SubViews.Select (s => s as MenuBarItemv2))
         {
         {
             Application.Popover?.Register (mbi?.PopoverMenu);
             Application.Popover?.Register (mbi?.PopoverMenu);
         }
         }
@@ -65,36 +211,119 @@ public class MenuBarv2 : Menuv2, IDesignable
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override bool OnAccepting (CommandEventArgs args)
     protected override bool OnAccepting (CommandEventArgs args)
     {
     {
-        if (args.Context?.Source is MenuBarItemv2 { PopoverMenu: { } } menuBarItem)
+        Logging.Trace ($"{args.Context?.Source?.Title}");
+
+        if (Visible && args.Context?.Source is MenuBarItemv2 { PopoverMenu.Visible: false } sourceMenuBarItem)
         {
         {
-            ShowPopover (menuBarItem);
+            _active = true;
+
+            if (!CanFocus)
+            {
+                // Enabling CanFocus will cause focus to change, which will cause OnSelectedMenuItem to change
+                // This will call ShowPopover
+                CanFocus = true;
+                sourceMenuBarItem.SetFocus ();
+            }
+            else
+            {
+                ShowPopover (sourceMenuBarItem);
+            }
+
+            return true;
         }
         }
 
 
         return base.OnAccepting (args);
         return base.OnAccepting (args);
     }
     }
 
 
+    /// <summary>
+    ///     Shows the specified popover, but only if the menu bar is active.
+    /// </summary>
+    /// <param name="menuBarItem"></param>
     private void ShowPopover (MenuBarItemv2? menuBarItem)
     private void ShowPopover (MenuBarItemv2? menuBarItem)
     {
     {
+        Logging.Trace ($"{menuBarItem?.Id}");
+
+        if (!_active || !Visible)
+        {
+            return;
+        }
+
+        //menuBarItem!.PopoverMenu.Id = menuBarItem.Id;
+
+        // TODO: We should init the PopoverMenu in a smarter way
         if (menuBarItem?.PopoverMenu is { IsInitialized: false })
         if (menuBarItem?.PopoverMenu is { IsInitialized: false })
         {
         {
             menuBarItem.PopoverMenu.BeginInit ();
             menuBarItem.PopoverMenu.BeginInit ();
             menuBarItem.PopoverMenu.EndInit ();
             menuBarItem.PopoverMenu.EndInit ();
         }
         }
 
 
-        // If the active popover is a PopoverMenu and part of this MenuBar...
-        if (menuBarItem?.PopoverMenu is null
-            && Application.Popover?.GetActivePopover () is PopoverMenu popoverMenu
+        // If the active Application Popover is part of this MenuBar, hide it.
+        //HideActivePopover ();
+        if (Application.Popover?.GetActivePopover () is PopoverMenu popoverMenu
             && popoverMenu?.Root?.SuperMenuItem?.SuperView == this)
             && popoverMenu?.Root?.SuperMenuItem?.SuperView == this)
         {
         {
             Application.Popover?.Hide (popoverMenu);
             Application.Popover?.Hide (popoverMenu);
         }
         }
 
 
-        menuBarItem?.PopoverMenu?.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom));
+        if (menuBarItem is null)
+        {
+            return;
+        }
+
+        if (menuBarItem.PopoverMenu is { })
+        {
+            menuBarItem.PopoverMenu.Accepted += (sender, args) =>
+                                                {
+                                                    if (HasFocus)
+                                                    {
+                                                        CanFocus = false;
+                                                    }
+                                                };
+        }
 
 
-        if (menuBarItem?.PopoverMenu?.Root is { })
+        _active = true;
+        CanFocus = true;
+        menuBarItem.SetFocus ();
+
+        if (menuBarItem.PopoverMenu?.Root is { })
         {
         {
             menuBarItem.PopoverMenu.Root.SuperMenuItem = menuBarItem;
             menuBarItem.PopoverMenu.Root.SuperMenuItem = menuBarItem;
         }
         }
+
+        menuBarItem.PopoverMenu?.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom));
+    }
+
+    private MenuBarItemv2? GetActiveItem ()
+    {
+        return SubViews.FirstOrDefault (sv => sv is MenuBarItemv2 { PopoverMenu: { Visible: true } }) as MenuBarItemv2;
+    }
+
+    /// <summary>
+    ///     Hides the popover menu associated with the active menu bar item and updates the focus state.
+    /// </summary>
+    /// <returns><see langword="true"/> if the popover was hidden</returns>
+    public bool HideActiveItem ()
+    {
+        return HideItem (GetActiveItem ());
+    }
+
+    /// <summary>
+    ///     Hides popover menu associated with the specified menu bar item and updates the focus state.
+    /// </summary>
+    /// <param name="activeItem"></param>
+    /// <returns><see langword="true"/> if the popover was hidden</returns>
+    public bool HideItem (MenuBarItemv2? activeItem)
+    {
+        if (activeItem is null || !activeItem.PopoverMenu!.Visible)
+        {
+            return false;
+        }
+        _active = false;
+        HasFocus = false;
+        activeItem.PopoverMenu!.Visible = false;
+        CanFocus = false;
+
+        return true;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>

+ 9 - 2
Terminal.Gui/Views/Menu/MenuItemv2.cs

@@ -7,6 +7,7 @@ namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     A <see cref="Shortcut"/>-derived object to be used as a menu item in a <see cref="Menuv2"/>. Has title, an
 ///     A <see cref="Shortcut"/>-derived object to be used as a menu item in a <see cref="Menuv2"/>. Has title, an
+///     A <see cref="Shortcut"/>-derived object to be used as a menu item in a <see cref="Menuv2"/>. Has title, an
 ///     associated help text, and an action to execute on activation.
 ///     associated help text, and an action to execute on activation.
 /// </summary>
 /// </summary>
 public class MenuItemv2 : Shortcut
 public class MenuItemv2 : Shortcut
@@ -53,6 +54,11 @@ public class MenuItemv2 : Shortcut
         : base (key ?? Key.Empty, commandText, action, helpText)
         : base (key ?? Key.Empty, commandText, action, helpText)
     { }
     { }
 
 
+    /// <inheritdoc/>
+    public MenuItemv2 (string commandText, Key key, Action ? action = null)
+        : base (key ?? Key.Empty, commandText, action, null)
+    { }
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     public MenuItemv2 (string? commandText = null, string? helpText = null, Menuv2? subMenu = null)
     public MenuItemv2 (string? commandText = null, string? helpText = null, Menuv2? subMenu = null)
         : base (Key.Empty, commandText, null, helpText)
         : base (Key.Empty, commandText, null, helpText)
@@ -98,6 +104,7 @@ public class MenuItemv2 : Shortcut
 
 
     internal override bool? DispatchCommand (ICommandContext? commandContext)
     internal override bool? DispatchCommand (ICommandContext? commandContext)
     {
     {
+        Logging.Trace($"{commandContext?.Source?.Title}");
         bool? ret = null;
         bool? ret = null;
 
 
         if (commandContext is { Command: not Command.HotKey })
         if (commandContext is { Command: not Command.HotKey })
@@ -116,11 +123,11 @@ public class MenuItemv2 : Shortcut
 
 
         if (ret is not true)
         if (ret is not true)
         {
         {
+            Logging.Trace($"Calling base.DispatchCommand");
             ret = base.DispatchCommand (commandContext);
             ret = base.DispatchCommand (commandContext);
         }
         }
 
 
-        //Logging.Trace ($"{commandContext?.Source?.Title}");
-
+        Logging.Trace($"Calling RaiseAccepted");
         RaiseAccepted (commandContext);
         RaiseAccepted (commandContext);
 
 
         return ret;
         return ret;

+ 25 - 11
Terminal.Gui/Views/Menu/Menuv2.cs

@@ -10,7 +10,7 @@ public class Menuv2 : Bar
     public Menuv2 () : this ([]) { }
     public Menuv2 () : this ([]) { }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public Menuv2 (IEnumerable<MenuItemv2>? shortcuts) : this (shortcuts?.Cast<View>()) { }
+    public Menuv2 (IEnumerable<MenuItemv2>? menuItems) : this (menuItems?.Cast<View> ()) { }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Menuv2 (IEnumerable<View>? shortcuts) : base (shortcuts)
     public Menuv2 (IEnumerable<View>? shortcuts) : base (shortcuts)
@@ -55,21 +55,21 @@ public class Menuv2 : Bar
         switch (view)
         switch (view)
         {
         {
             case MenuItemv2 menuItem:
             case MenuItemv2 menuItem:
-            {
-                menuItem.CanFocus = true;
+                {
+                    menuItem.CanFocus = true;
 
 
-                AddCommand (menuItem.Command, RaiseAccepted);
+                    AddCommand (menuItem.Command, RaiseAccepted);
 
 
-                menuItem.Accepted += MenuItemOnAccepted;
+                    menuItem.Accepted += MenuItemOnAccepted;
 
 
-                break;
+                    break;
 
 
-                void MenuItemOnAccepted (object? sender, CommandEventArgs e)
-                {
-                    //Logging.Trace ($"Accepted: {e.Context?.Source?.Title}");
-                    RaiseAccepted (e.Context);
+                    void MenuItemOnAccepted (object? sender, CommandEventArgs e)
+                    {
+                        Logging.Trace ($"MenuItemOnAccepted: {e.Context?.Source?.Title}");
+                        RaiseAccepted (e.Context);
+                    }
                 }
                 }
-            }
             case Line line:
             case Line line:
                 // Grow line so we get auto-join line
                 // Grow line so we get auto-join line
                 line.X = Pos.Func (() => -Border!.Thickness.Left);
                 line.X = Pos.Func (() => -Border!.Thickness.Left);
@@ -79,6 +79,19 @@ public class Menuv2 : Bar
         }
         }
     }
     }
 
 
+    /// <inheritdoc />
+    protected override bool OnAccepting (CommandEventArgs args)
+    {
+        Logging.Trace ($"{args.Context}");
+
+        if (SuperMenuItem is { })
+        {
+            Logging.Trace ($"Invoking Accept on SuperMenuItem: {SuperMenuItem.Title}...");
+            return SuperMenuItem?.SuperView?.InvokeCommand (Command.Accept, args.Context) is true;
+        }
+        return false;
+    }
+
     // TODO: Consider moving Accepted to Bar?
     // TODO: Consider moving Accepted to Bar?
 
 
     /// <summary>
     /// <summary>
@@ -120,6 +133,7 @@ public class Menuv2 : Bar
     protected override void OnFocusedChanged (View? previousFocused, View? focused)
     protected override void OnFocusedChanged (View? previousFocused, View? focused)
     {
     {
         base.OnFocusedChanged (previousFocused, focused);
         base.OnFocusedChanged (previousFocused, focused);
+
         SelectedMenuItem = focused as MenuItemv2;
         SelectedMenuItem = focused as MenuItemv2;
         RaiseSelectedMenuItemChanged (SelectedMenuItem);
         RaiseSelectedMenuItemChanged (SelectedMenuItem);
     }
     }

+ 35 - 24
Terminal.Gui/Views/Menu/PopoverMenu.cs

@@ -3,7 +3,8 @@ namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     Provides a cascading menu that pops over all other content. Can be used as a context menu or a drop-down
 ///     Provides a cascading menu that pops over all other content. Can be used as a context menu or a drop-down
-///     menu as part of <see cref="MenuBar"/>.
+///     all other content. Can be used as a context menu or a drop-down
+///     menu as part of <see cref="MenuBar"/> as part of <see cref="MenuBar"/>.
 /// </summary>
 /// </summary>
 /// <remarks>
 /// <remarks>
 ///     <para>
 ///     <para>
@@ -33,8 +34,6 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
 
         base.Visible = false;
         base.Visible = false;
 
 
-        //base.ColorScheme = Colors.ColorSchemes ["Menu"];
-
         Root = root;
         Root = root;
 
 
         AddCommand (Command.Right, MoveRight);
         AddCommand (Command.Right, MoveRight);
@@ -53,7 +52,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
                         return false;
                         return false;
                     });
                     });
 
 
-        KeyBindings.Add (DefaultKey, Command.Quit);
+        KeyBindings.Add (Key, Command.Quit);
         KeyBindings.ReplaceCommands (Application.QuitKey, Command.Quit);
         KeyBindings.ReplaceCommands (Application.QuitKey, Command.Quit);
 
 
         AddCommand (
         AddCommand (
@@ -67,7 +66,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
 
                         Visible = false;
                         Visible = false;
 
 
-                        return RaiseAccepted (ctx);
+                        return false;
                     });
                     });
 
 
         return;
         return;
@@ -98,7 +97,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
                 return true;
                 return true;
             }
             }
 
 
-            return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+            return false; //AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
         }
         }
     }
     }
 
 
@@ -220,8 +219,11 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
                 _root.Accepting += MenuOnAccepting;
                 _root.Accepting += MenuOnAccepting;
             }
             }
 
 
+            // TODO: This needs to be done whenever any MenuItem in the menu tree changes to support dynamic menus
+            // TODO: And it needs to clear the old bindings first
             UpdateKeyBindings ();
             UpdateKeyBindings ();
 
 
+            // TODO: This needs to be done whenever any MenuItem in the menu tree changes to support dynamic menus
             IEnumerable<Menuv2> allMenus = GetAllSubMenus ();
             IEnumerable<Menuv2> allMenus = GetAllSubMenus ();
 
 
             foreach (Menuv2 menu in allMenus)
             foreach (Menuv2 menu in allMenus)
@@ -235,13 +237,12 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
 
     private void UpdateKeyBindings ()
     private void UpdateKeyBindings ()
     {
     {
-        // TODO: This needs to be done whenever any MenuItem in the menu tree changes to support dynamic menus
-        // TODO: And it needs to clear them first
         IEnumerable<MenuItemv2> all = GetMenuItemsOfAllSubMenus ();
         IEnumerable<MenuItemv2> all = GetMenuItemsOfAllSubMenus ();
 
 
         foreach (MenuItemv2 menuItem in all.Where (mi => mi.Command != Command.NotBound))
         foreach (MenuItemv2 menuItem in all.Where (mi => mi.Command != Command.NotBound))
         {
         {
             Key? key;
             Key? key;
+
             if (menuItem.TargetView is { })
             if (menuItem.TargetView is { })
             {
             {
                 // A TargetView implies HotKey
                 // A TargetView implies HotKey
@@ -447,17 +448,16 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
 
     private void MenuOnAccepting (object? sender, CommandEventArgs e)
     private void MenuOnAccepting (object? sender, CommandEventArgs e)
     {
     {
+        var senderView = sender as View;
+        Logging.Trace ($"Sender: {senderView?.GetType ().Name}, {e.Context?.Source?.Title}");
+
         if (e.Context?.Command != Command.HotKey)
         if (e.Context?.Command != Command.HotKey)
         {
         {
             Visible = false;
             Visible = false;
         }
         }
-        else
-        {
-            // This supports the case when a hotkey of a menuitem with a submenu is pressed
-            e.Cancel = true;
-        }
 
 
-        //Logging.Trace ($"{e.Context?.Source?.Title}");
+        // This supports the case when a hotkey of a menuitem with a submenu is pressed
+        //e.Cancel = true;
     }
     }
 
 
     private void MenuAccepted (object? sender, CommandEventArgs e)
     private void MenuAccepted (object? sender, CommandEventArgs e)
@@ -514,10 +514,21 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
 
     private void MenuOnSelectedMenuItemChanged (object? sender, MenuItemv2? e)
     private void MenuOnSelectedMenuItemChanged (object? sender, MenuItemv2? e)
     {
     {
-        //Logging.Trace ($"{e}");
+        Logging.Trace ($"e: {e?.Title}");
         ShowSubMenu (e);
         ShowSubMenu (e);
     }
     }
 
 
+    /// <inheritdoc/>
+    protected override void OnSubViewAdded (View view)
+    {
+        if (Root is null && (view is Menuv2 || view is MenuItemv2))
+        {
+            throw new InvalidOperationException ("Do not add MenuItems or Menus directly to a PopoverMenu. Use the Root property.");
+        }
+
+        base.OnSubViewAdded (view);
+    }
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void Dispose (bool disposing)
     protected override void Dispose (bool disposing)
     {
     {
@@ -539,20 +550,20 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
         base.Dispose (disposing);
         base.Dispose (disposing);
     }
     }
 
 
-
     /// <inheritdoc/>
     /// <inheritdoc/>
     public bool EnableForDesign<TContext> (ref readonly TContext context) where TContext : notnull
     public bool EnableForDesign<TContext> (ref readonly TContext context) where TContext : notnull
     {
     {
-        Root = new Menuv2 (
-                           [
-                               new MenuItemv2 (this, Command.Cut),
-                               new MenuItemv2 (this, Command.Copy),
-                               new MenuItemv2 (this, Command.Paste),
-                               new Line (),
-                               new MenuItemv2 (this, Command.SelectAll)
-                           ]);
+        Root = new (
+                    [
+                        new MenuItemv2 (this, Command.Cut),
+                        new MenuItemv2 (this, Command.Copy),
+                        new MenuItemv2 (this, Command.Paste),
+                        new Line (),
+                        new MenuItemv2 (this, Command.SelectAll)
+                    ]);
 
 
         Visible = true;
         Visible = true;
+
         return true;
         return true;
     }
     }
 }
 }

+ 56 - 18
Terminal.Gui/Views/RadioGroup.cs

@@ -1,9 +1,7 @@
 #nullable enable
 #nullable enable
-using System.Diagnostics;
-
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Displays a group of labels with an idicator of which one is selected.</summary>
+/// <summary>Displays a list of mutually-exclusive items. Each items can have its own hotkey.</summary>
 public class RadioGroup : View, IDesignable, IOrientation
 public class RadioGroup : View, IDesignable, IOrientation
 {
 {
     /// <summary>
     /// <summary>
@@ -19,7 +17,7 @@ public class RadioGroup : View, IDesignable, IOrientation
         // Select (Space key or mouse click) - The default implementation sets focus. RadioGroup does not.
         // Select (Space key or mouse click) - The default implementation sets focus. RadioGroup does not.
         AddCommand (Command.Select, HandleSelectCommand);
         AddCommand (Command.Select, HandleSelectCommand);
 
 
-        // Accept (Enter key or Doubleclick) - Raise Accept event - DO NOT advance state
+        // Accept (Enter key or DoubleClick) - Raise Accept event - DO NOT advance state
         AddCommand (Command.Accept, HandleAcceptCommand);
         AddCommand (Command.Accept, HandleAcceptCommand);
 
 
         // Hotkey - ctx may indicate a radio item hotkey was pressed. Behavior depends on HasFocus
         // Hotkey - ctx may indicate a radio item hotkey was pressed. Behavior depends on HasFocus
@@ -59,7 +57,7 @@ public class RadioGroup : View, IDesignable, IOrientation
 
 
         if (HasFocus)
         if (HasFocus)
         {
         {
-            if ((item is null || HotKey == keyCommandContext.Binding.Key?.NoAlt.NoCtrl.NoShift!))
+            if (item is null || HotKey == keyCommandContext.Binding.Key?.NoAlt.NoCtrl.NoShift!)
             {
             {
                 // It's this.HotKey OR Another View (Label?) forwarded the hotkey command to us - Act just like `Space` (Select)
                 // It's this.HotKey OR Another View (Label?) forwarded the hotkey command to us - Act just like `Space` (Select)
                 return InvokeCommand (Command.Select);
                 return InvokeCommand (Command.Select);
@@ -145,14 +143,14 @@ public class RadioGroup : View, IDesignable, IOrientation
                 if (c > -1)
                 if (c > -1)
                 {
                 {
                     // Just like the user pressing the items' hotkey
                     // Just like the user pressing the items' hotkey
-                    return InvokeCommand<KeyBinding> (Command.HotKey, new KeyBinding ([Command.HotKey], target: this, data: c)) == true;
+                    return InvokeCommand (Command.HotKey, new KeyBinding ([Command.HotKey], this, c)) == true;
                 }
                 }
             }
             }
 
 
             return false;
             return false;
         }
         }
 
 
-        bool cursorChanged = false;
+        var cursorChanged = false;
 
 
         if (SelectedItem == Cursor)
         if (SelectedItem == Cursor)
         {
         {
@@ -164,7 +162,7 @@ public class RadioGroup : View, IDesignable, IOrientation
             }
             }
         }
         }
 
 
-        bool selectedItemChanged = false;
+        var selectedItemChanged = false;
 
 
         if (SelectedItem != Cursor)
         if (SelectedItem != Cursor)
         {
         {
@@ -209,7 +207,8 @@ public class RadioGroup : View, IDesignable, IOrientation
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Gets or sets whether double clicking on a Radio Item will cause the <see cref="View.Accepting"/> event to be raised.
+    ///     Gets or sets whether double-clicking on a Radio Item will cause the <see cref="View.Accepting"/> event to be
+    ///     raised.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
@@ -241,7 +240,21 @@ public class RadioGroup : View, IDesignable, IOrientation
         }
         }
     }
     }
 
 
-    private List<string> _radioLabels = [];
+    /// <summary>
+    ///     If <see langword="true"/> the <see cref="RadioLabels"/> will each be automatically assigned a hotkey.
+    ///     <see cref="UsedHotKeys"/> will be used to ensure unique keys are assigned. Set <see cref="UsedHotKeys"/>
+    ///     before setting <see cref="RadioLabels"/> with any hotkeys that may conflict with other Views.
+    /// </summary>
+    public bool AssignHotKeysToRadioLabels { get; set; }
+
+    /// <summary>
+    ///     Gets the list of hotkeys already used by <see cref="RadioLabels"/> or that should not be used if
+    ///     <see cref="AssignHotKeysToRadioLabels"/>
+    ///     is enabled.
+    /// </summary>
+    public List<Key> UsedHotKeys { get; } = [];
+
+    private readonly List<string> _radioLabels = [];
 
 
     /// <summary>
     /// <summary>
     ///     The radio labels to display. A <see cref="Command.HotKey"/> key binding will be added for each label enabling the
     ///     The radio labels to display. A <see cref="Command.HotKey"/> key binding will be added for each label enabling the
@@ -263,16 +276,40 @@ public class RadioGroup : View, IDesignable, IOrientation
                 }
                 }
             }
             }
 
 
-            int prevCount = _radioLabels.Count;
-            _radioLabels = value.ToList ();
+            _radioLabels.Clear ();
 
 
-            for (var index = 0; index < _radioLabels.Count; index++)
+            // Pick a unique hotkey for each radio label
+            for (var labelIndex = 0; labelIndex < value.Length; labelIndex++)
             {
             {
-                string label = _radioLabels [index];
+                string label = value [labelIndex];
+                string? newLabel = label;
 
 
-                if (TextFormatter.FindHotKey (label, HotKeySpecifier, out _, out Key hotKey))
+                if (AssignHotKeysToRadioLabels)
                 {
                 {
-                    AddKeyBindingsForHotKey (Key.Empty, hotKey, index);
+                    // Find the first char in label that is [a-z], [A-Z], or [0-9]
+                    for (var i = 0; i < label.Length; i++)
+                    {
+                        if (UsedHotKeys.Contains (new (label [i])) || !char.IsAsciiLetterOrDigit (label [i]))
+                        {
+                            continue;
+                        }
+
+                        if (char.IsAsciiLetterOrDigit (label [i]))
+                        {
+                            char? hotChar = label [i];
+                            newLabel = label.Insert (i, HotKeySpecifier.ToString ());
+                            UsedHotKeys.Add (new (hotChar));
+
+                            break;
+                        }
+                    }
+                }
+
+                _radioLabels.Add (newLabel);
+
+                if (TextFormatter.FindHotKey (newLabel, HotKeySpecifier, out _, out Key hotKey))
+                {
+                    AddKeyBindingsForHotKey (Key.Empty, hotKey, labelIndex);
                 }
                 }
             }
             }
 
 
@@ -351,7 +388,7 @@ public class RadioGroup : View, IDesignable, IOrientation
 
 
                     if (j == hotPos && i == Cursor)
                     if (j == hotPos && i == Cursor)
                     {
                     {
-                        SetAttribute (HasFocus ? GetHotFocusColor() : GetHotNormalColor ());
+                        SetAttribute (HasFocus ? GetHotFocusColor () : GetHotNormalColor ());
                     }
                     }
                     else if (j == hotPos && i != Cursor)
                     else if (j == hotPos && i != Cursor)
                     {
                     {
@@ -369,7 +406,7 @@ public class RadioGroup : View, IDesignable, IOrientation
 
 
                         if (i == Cursor)
                         if (i == Cursor)
                         {
                         {
-                            SetAttribute (HasFocus ? GetHotFocusColor() : GetHotNormalColor ());
+                            SetAttribute (HasFocus ? GetHotFocusColor () : GetHotNormalColor ());
                         }
                         }
                         else if (i != Cursor)
                         else if (i != Cursor)
                         {
                         {
@@ -386,6 +423,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                 DrawHotString (rl, HasFocus && i == Cursor);
                 DrawHotString (rl, HasFocus && i == Cursor);
             }
             }
         }
         }
+
         return true;
         return true;
     }
     }
 
 

+ 9 - 3
Terminal.Gui/Views/Shortcut.cs

@@ -103,9 +103,6 @@ public class Shortcut : View, IOrientation, IDesignable
         ShowHide ();
         ShowHide ();
     }
     }
 
 
-    /// <inheritdoc />
-    protected override bool OnClearingViewport () { return base.OnClearingViewport (); }
-
     // Helper to set Width consistently
     // Helper to set Width consistently
     internal Dim GetWidthDimAuto ()
     internal Dim GetWidthDimAuto ()
     {
     {
@@ -269,6 +266,7 @@ public class Shortcut : View, IOrientation, IDesignable
     /// <returns></returns>
     /// <returns></returns>
     internal virtual bool? DispatchCommand (ICommandContext? commandContext)
     internal virtual bool? DispatchCommand (ICommandContext? commandContext)
     {
     {
+        Logging.Trace($"{commandContext?.Source?.Title}");
         CommandContext<KeyBinding>? keyCommandContext = commandContext as CommandContext<KeyBinding>? ?? default (CommandContext<KeyBinding>);
         CommandContext<KeyBinding>? keyCommandContext = commandContext as CommandContext<KeyBinding>? ?? default (CommandContext<KeyBinding>);
 
 
         if (keyCommandContext?.Binding.Data != this)
         if (keyCommandContext?.Binding.Data != this)
@@ -276,16 +274,21 @@ public class Shortcut : View, IOrientation, IDesignable
             // Invoke Select on the CommandView to cause it to change state if it wants to
             // Invoke Select on the CommandView to cause it to change state if it wants to
             // If this causes CommandView to raise Accept, we eat it
             // If this causes CommandView to raise Accept, we eat it
             keyCommandContext = keyCommandContext!.Value with { Binding = keyCommandContext.Value.Binding with { Data = this } };
             keyCommandContext = keyCommandContext!.Value with { Binding = keyCommandContext.Value.Binding with { Data = this } };
+
+            Logging.Trace ($"Invoking Select on CommandView.");
+
             CommandView.InvokeCommand (Command.Select, keyCommandContext);
             CommandView.InvokeCommand (Command.Select, keyCommandContext);
         }
         }
 
 
         // BUGBUG: Why does this use keyCommandContext and not commandContext?
         // BUGBUG: Why does this use keyCommandContext and not commandContext?
+        Logging.Trace ($"RaiseSelecting ...");
         if (RaiseSelecting (keyCommandContext) is true)
         if (RaiseSelecting (keyCommandContext) is true)
         {
         {
             return true;
             return true;
         }
         }
 
 
         // The default HotKey handler sets Focus
         // The default HotKey handler sets Focus
+        Logging.Trace ($"SetFocus...");
         SetFocus ();
         SetFocus ();
 
 
         var cancel = false;
         var cancel = false;
@@ -294,6 +297,7 @@ public class Shortcut : View, IOrientation, IDesignable
         {
         {
             commandContext.Source = this;
             commandContext.Source = this;
         }
         }
+        Logging.Trace ($"RaiseAccepting...");
         cancel = RaiseAccepting (commandContext) is true;
         cancel = RaiseAccepting (commandContext) is true;
 
 
         if (cancel)
         if (cancel)
@@ -308,6 +312,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
 
         if (Action is { })
         if (Action is { })
         {
         {
+            Logging.Trace ($"Invoke Action...");
             Action.Invoke ();
             Action.Invoke ();
 
 
             // Assume if there's a subscriber to Action, it's handled.
             // Assume if there's a subscriber to Action, it's handled.
@@ -496,6 +501,7 @@ public class Shortcut : View, IOrientation, IDesignable
         // This is a helper to make it easier to set the CommandView text.
         // This is a helper to make it easier to set the CommandView text.
         // CommandView is public and replaceable, but this is a convenience.
         // CommandView is public and replaceable, but this is a convenience.
         _commandView.Text = Title;
         _commandView.Text = Title;
+        //_commandView.Title = Title;
     }
     }
 
 
     #endregion Command
     #endregion Command

+ 1 - 0
Terminal.sln.DotSettings

@@ -394,6 +394,7 @@
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=53eecf85_002Dd821_002D40e8_002Dac97_002Dfdb734542b84/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=53eecf85_002Dd821_002D40e8_002Dac97_002Dfdb734542b84/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=669e5282_002Dfb4b_002D4e90_002D91e7_002D07d269d04b60/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_CONSTANT" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_CONSTANT" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>

+ 1 - 1
Tests/UnitTests/Dialogs/MessageBoxTests.cs

@@ -462,7 +462,7 @@ public class MessageBoxTests
                                      {
                                      {
                                          MessageBox.Query (
                                          MessageBox.Query (
                                                            "",
                                                            "",
-                                                           UICatalogApp.GetAboutBoxMessage (),
+                                                           UICatalog.UICatalogTopLevel.GetAboutBoxMessage (),
                                                            wrapMessage: false,
                                                            wrapMessage: false,
                                                            buttons: "_Ok"
                                                            buttons: "_Ok"
                                                           );
                                                           );

+ 1 - 1
Tests/UnitTests/Text/TextFormatterTests.cs

@@ -4146,7 +4146,7 @@ Nice       Work")]
     {
     {
         TextFormatter tf = new ()
         TextFormatter tf = new ()
         {
         {
-            Text = UICatalogApp.GetAboutBoxMessage (),
+            Text = UICatalog.UICatalogTopLevel.GetAboutBoxMessage (),
             Alignment = Alignment.Center,
             Alignment = Alignment.Center,
             VerticalAlignment = Alignment.Start,
             VerticalAlignment = Alignment.Start,
             WordWrap = false,
             WordWrap = false,

+ 22 - 0
Tests/UnitTests/View/Keyboard/KeyBindingsTests.cs

@@ -129,6 +129,28 @@ public class KeyBindingsTests ()
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
+    [Fact]
+    [AutoInitShutdown]
+    public void HotKey_Enabled_False_Does_Not_Invoke ()
+    {
+        var view = new ScopedKeyBindingView ();
+        var keyWasHandled = false;
+        view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
+
+        var top = new Toplevel ();
+        top.Add (view);
+        Application.Begin (top);
+
+        Application.RaiseKeyDownEvent (Key.Z);
+        Assert.False (keyWasHandled);
+        Assert.False (view.HotKeyCommand);
+
+        keyWasHandled = false;
+        view.Enabled = false;
+        Application.RaiseKeyDownEvent (Key.F);
+        Assert.False (view.HotKeyCommand);
+        top.Dispose ();
+    }
     // tests that test KeyBindingScope.Focus and KeyBindingScope.HotKey (tests for KeyBindingScope.Application are in Application/KeyboardTests.cs)
     // tests that test KeyBindingScope.Focus and KeyBindingScope.HotKey (tests for KeyBindingScope.Application are in Application/KeyboardTests.cs)
 
 
     public class ScopedKeyBindingView : View
     public class ScopedKeyBindingView : View

+ 513 - 3706
Tests/UnitTests/Views/MenuBarTests.cs

@@ -3,3884 +3,691 @@ using Xunit.Abstractions;
 
 
 namespace Terminal.Gui.ViewsTests;
 namespace Terminal.Gui.ViewsTests;
 
 
-public class MenuBarTests (ITestOutputHelper output)
+public class MenuBarTests ()
 {
 {
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void AddMenuBarItem_RemoveMenuItem_Dynamically ()
+    public void DefaultKey_Activates ()
     {
     {
-        var menuBar = new MenuBar ();
-        var menuBarItem = new MenuBarItem { Title = "_New" };
-        var action = "";
-        var menuItem = new MenuItem { Title = "_Item", Action = () => action = "I", Parent = menuBarItem };
-        Assert.Equal ("n", menuBarItem.HotKey);
-        Assert.Equal ("i", menuItem.HotKey);
-        Assert.Empty (menuBar.Menus);
-        menuBarItem.AddMenuBarItem (menuBar, menuItem);
-        menuBar.Menus = [menuBarItem];
-        Assert.Single (menuBar.Menus);
-        Assert.Single (menuBar.Menus [0].Children!);
-
-        Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
-        Assert.False (menuBar.HotKeyBindings.TryGet (Key.I, out _));
-
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
         top.Add (menuBar);
         top.Add (menuBar);
-        Application.Begin (top);
-
-        top.NewKeyDownEvent (Key.N.WithAlt);
-        Application.MainLoop.RunIteration ();
-        Assert.True (menuBar.IsMenuOpen);
-        Assert.Equal ("", action);
-
-        top.NewKeyDownEvent (Key.I);
-        Application.MainLoop.RunIteration ();
-        Assert.False (menuBar.IsMenuOpen);
-        Assert.Equal ("I", action);
-
-        menuItem.RemoveMenuItem ();
-        Assert.Single (menuBar.Menus);
-        Assert.Null (menuBar.Menus [0].Children);
-        Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
-        Assert.False (menuBar.HotKeyBindings.TryGet (Key.I, out _));
-
-        menuBarItem.RemoveMenuItem ();
-        Assert.Empty (menuBar.Menus);
-        Assert.False (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
-
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void AllowNullChecked_Get_Set ()
-    {
-        var mi = new MenuItem ("Check this out 你", "", null) { CheckType = MenuItemCheckStyle.Checked };
-        mi.Action = mi.ToggleChecked;
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("Nullable Checked", new [] { mi })
-            ]
-        };
+        // Act
+        Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey);
+        Assert.True (menuBar.IsActive ());
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBar.HasFocus);
+        Assert.True (menuBar.CanFocus);
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+        Assert.True (menuBarItem.PopoverMenu.HasFocus);
 
 
-        //new CheckBox ();
-        Toplevel top = new ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.False (mi.Checked);
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter));
-        Application.MainLoop.RunIteration ();
-        Assert.True (mi.Checked);
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
-                                        )
-                    );
-
-        Assert.True (
-                     menu._openMenu.NewMouseEvent (
-                                                   new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu }
-                                                  )
-                    );
-        Application.MainLoop.RunIteration ();
-        Assert.False (mi.Checked);
-
-        mi.AllowNullChecked = true;
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter));
-        Application.MainLoop.RunIteration ();
-        Assert.Null (mi.Checked);
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
-                                        )
-                    );
-        Application.LayoutAndDraw ();
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @$"
- Nullable Checked       
-┌──────────────────────┐
-│ {Glyphs.CheckStateNone} Check this out 你  │
-└──────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (
-                     menu._openMenu.NewMouseEvent (
-                                                   new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu }
-                                                  )
-                    );
-        Application.MainLoop.RunIteration ();
-        Assert.True (mi.Checked);
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter));
-        Application.MainLoop.RunIteration ();
-        Assert.False (mi.Checked);
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
-                                        )
-                    );
-
-        Assert.True (
-                     menu._openMenu.NewMouseEvent (
-                                                   new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu }
-                                                  )
-                    );
-        Application.MainLoop.RunIteration ();
-        Assert.Null (mi.Checked);
-
-        mi.AllowNullChecked = false;
-        Assert.False (mi.Checked);
-
-        mi.CheckType = MenuItemCheckStyle.NoCheck;
-        Assert.Throws<InvalidOperationException> (mi.ToggleChecked);
-
-        mi.CheckType = MenuItemCheckStyle.Radio;
-        Assert.Throws<InvalidOperationException> (mi.ToggleChecked);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void CanExecute_False_Does_Not_Throws ()
+    public void DefaultKey_Deactivates ()
     {
     {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem []
-                {
-                    new ("New", "", null, () => false),
-                    null,
-                    new ("Quit", "", null)
-                })
-            ]
-        };
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void CanExecute_HotKey ()
-    {
-        Window win = null;
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "_File",
-                     new MenuItem []
-                     {
-                         new ("_New", "", New, CanExecuteNew),
-                         new (
-                              "_Close",
-                              "",
-                              Close,
-                              CanExecuteClose
-                             )
-                     }
-                    )
-            ]
-        };
-        Toplevel top = new ();
-        top.Add (menu);
-
-        bool CanExecuteNew () { return win == null; }
-
-        void New () { win = new (); }
-
-        bool CanExecuteClose () { return win != null; }
-
-        void Close () { win = null; }
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-        Application.Begin (top);
+        // Act
+        Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey);
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBarItem.PopoverMenu.Visible);
 
 
-        Assert.Null (win);
-        Assert.True (CanExecuteNew ());
-        Assert.False (CanExecuteClose ());
+        Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey);
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBar.HasFocus);
+        Assert.False (menuBar.CanFocus);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.HasFocus);
 
 
-        Assert.True (top.NewKeyDownEvent (Key.F.WithAlt));
-        Assert.True (top.NewKeyDownEvent (Key.N.WithAlt));
-        Application.MainLoop.RunIteration ();
-        Assert.NotNull (win);
-        Assert.False (CanExecuteNew ());
-        Assert.True (CanExecuteClose ());
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void Click_Another_View_Close_An_Open_Menu ()
+    public void QuitKey_DeActivates ()
     {
     {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) })
-            ]
-        };
-
-        var btnClicked = false;
-        var btn = new Button { Y = 4, Text = "Test" };
-        btn.Accepting += (s, e) => btnClicked = true;
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu, btn);
-        Application.Begin (top);
-
-        Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 4), Flags = MouseFlags.Button1Clicked });
-        Assert.True (btnClicked);
-        top.Dispose ();
-    }
-
-    // TODO: Lots of tests in here really test Menu and MenuItem - Move them to MenuTests.cs
-
-    [Fact]
-    public void Constructors_Defaults ()
-    {
-        var menuBar = new MenuBar ();
-        Assert.Equal (KeyCode.F9, menuBar.Key);
-        var menu = new Menu { Host = menuBar, X = 0, Y = 0, BarItems = new () };
-        Assert.Null (menu.ColorScheme);
-        Assert.False (menu.IsInitialized);
-        menu.BeginInit ();
-        menu.EndInit ();
-        Assert.Equal (Colors.ColorSchemes ["Menu"], menu.ColorScheme);
-        Assert.True (menu.CanFocus);
-        Assert.False (menu.WantContinuousButtonPressed);
-        Assert.Equal (LineStyle.Single, menuBar.MenusBorderStyle);
-
-        menuBar = new ();
-        Assert.Equal (0, menuBar.X);
-        Assert.Equal (0, menuBar.Y);
-        Assert.IsType<DimFill> (menuBar.Width);
-        Assert.Equal (1, menuBar.Height);
-        Assert.Empty (menuBar.Menus);
-        Assert.Equal (Colors.ColorSchemes ["Menu"], menuBar.ColorScheme);
-        Assert.True (menuBar.WantMousePositionReports);
-        Assert.False (menuBar.IsMenuOpen);
-
-        menuBar = new () { Menus = [] };
-        Assert.Equal (0, menuBar.X);
-        Assert.Equal (0, menuBar.Y);
-        Assert.IsType<DimFill> (menuBar.Width);
-        Assert.Equal (1, menuBar.Height);
-        Assert.Empty (menuBar.Menus);
-        Assert.Equal (Colors.ColorSchemes ["Menu"], menuBar.ColorScheme);
-        Assert.True (menuBar.WantMousePositionReports);
-        Assert.False (menuBar.IsMenuOpen);
-
-        var menuBarItem = new MenuBarItem ();
-        Assert.Equal ("", menuBarItem.Title);
-        Assert.Null (menuBarItem.Parent);
-        Assert.Empty (menuBarItem.Children);
-
-        menuBarItem = new (new MenuBarItem [] { });
-        Assert.Equal ("", menuBarItem.Title);
-        Assert.Null (menuBarItem.Parent);
-        Assert.Empty (menuBarItem.Children);
-
-        menuBarItem = new ("Test", new MenuBarItem [] { });
-        Assert.Equal ("Test", menuBarItem.Title);
-        Assert.Null (menuBarItem.Parent);
-        Assert.Empty (menuBarItem.Children);
-
-        menuBarItem = new ("Test", new List<MenuItem []> ());
-        Assert.Equal ("Test", menuBarItem.Title);
-        Assert.Null (menuBarItem.Parent);
-        Assert.Empty (menuBarItem.Children);
-
-        menuBarItem = new ("Test", "Help", null);
-        Assert.Equal ("Test", menuBarItem.Title);
-        Assert.Equal ("Help", menuBarItem.Help);
-        Assert.Null (menuBarItem.Action);
-        Assert.Null (menuBarItem.CanExecute);
-        Assert.Null (menuBarItem.Parent);
-        Assert.Equal (Key.Empty, menuBarItem.ShortcutKey);
-    }
-
-    [Fact]
-    [AutoInitShutdown (configLocation: ConfigLocations.Default)]
-    public void Disabled_MenuBar_Is_Never_Opened ()
-    {
-        Toplevel top = new ();
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) })
-            ]
-        };
-        top.Add (menu);
-        Application.Begin (top);
-        Assert.True (menu.Enabled);
-        menu.OpenMenu ();
-        Assert.True (menu.IsMenuOpen);
-
-        menu.Enabled = false;
-        menu.CloseAllMenus ();
-        menu.OpenMenu ();
-        Assert.False (menu.IsMenuOpen);
-        top.Dispose ();
-    }
-
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
-    [AutoInitShutdown (configLocation: ConfigLocations.Default)]
-    public void Disabled_MenuItem_Is_Never_Selected ()
-    {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "Menu",
-                     new MenuItem []
-                     {
-                         new ("Enabled 1", "", null),
-                         new ("Disabled", "", null, () => false),
-                         null,
-                         new ("Enabled 2", "", null)
-                     }
-                    )
-            ]
-        };
-
-        Toplevel top = new ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Attribute [] attributes =
-        {
-            // 0
-            menu.ColorScheme.Normal,
-
-            // 1
-            menu.ColorScheme.Focus,
-
-            // 2
-            menu.ColorScheme.Disabled
-        };
-
-        DriverAssert.AssertDriverAttributesAre (
-                                               @"
-00000000000000",
-                                               output,
-                                               Application.Driver,
-                                               attributes
-                                              );
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
-                                        )
-                    );
-        top.Draw ();
-
-        DriverAssert.AssertDriverAttributesAre (
-                                               @"
-11111100000000
-00000000000000
-01111111111110
-02222222222220
-00000000000000
-00000000000000
-00000000000000",
-                                               output,
-                                               Application.Driver,
-                                               attributes
-                                              );
-
-        Assert.True (
-                     top.SubViews.ElementAt (1)
-                        .NewMouseEvent (
-                                        new () { Position = new (0, 2), Flags = MouseFlags.Button1Clicked, View = top.SubViews.ElementAt (1) }
-                                       )
-                    );
-        top.SubViews.ElementAt (1).Layout();
-        top.SubViews.ElementAt (1).Draw ();
-
-        DriverAssert.AssertDriverAttributesAre (
-                                               @"
-11111100000000
-00000000000000
-01111111111110
-02222222222220
-00000000000000
-00000000000000
-00000000000000",
-                                               output,
-                                               Application.Driver,
-                                               attributes
-                                              );
-
-        Assert.True (
-                     top.SubViews.ElementAt (1)
-                        .NewMouseEvent (
-                                        new () { Position = new (0, 2), Flags = MouseFlags.ReportMousePosition, View = top.SubViews.ElementAt (1) }
-                                       )
-                    );
-        top.SubViews.ElementAt (1).Draw ();
-
-        DriverAssert.AssertDriverAttributesAre (
-                                               @"
-11111100000000
-00000000000000
-01111111111110
-02222222222220
-00000000000000
-00000000000000
-00000000000000",
-                                               output,
-                                               Application.Driver,
-                                               attributes
-                                              );
-        top.Dispose ();
-    }
-
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
-    [AutoInitShutdown]
-    public void Draw_A_Menu_Over_A_Dialog ()
-    {
-        // Override CM
-        Window.DefaultBorderStyle = LineStyle.Single;
-        Dialog.DefaultButtonAlignment = Alignment.Center;
-        Dialog.DefaultBorderStyle = LineStyle.Single;
-        Dialog.DefaultShadow = ShadowStyle.None;
-        Button.DefaultShadow = ShadowStyle.None;
-
-        Toplevel top = new ();
-        var win = new Window ();
-        top.Add (win);
-        RunState rsTop = Application.Begin (top);
-        ((FakeDriver)Application.Driver!).SetBufferSize (40, 15);
-
-        Assert.Equal (new (0, 0, 40, 15), win.Frame);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        List<string> items = new ()
-        {
-            "New",
-            "Open",
-            "Close",
-            "Save",
-            "Save As",
-            "Delete"
-        };
-        var dialog = new Dialog { X = 2, Y = 2, Width = 15, Height = 4 };
-        var menu = new MenuBar { X = Pos.Center (), Width = 10 };
-
-        menu.Menus = new MenuBarItem []
-        {
-            new (
-                 "File",
-                 new MenuItem []
-                 {
-                     new (
-                          items [0],
-                          "Create a new file",
-                          () => ChangeMenuTitle ("New"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.N
-                         ),
-                     new (
-                          items [1],
-                          "Open a file",
-                          () => ChangeMenuTitle ("Open"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.O
-                         ),
-                     new (
-                          items [2],
-                          "Close a file",
-                          () => ChangeMenuTitle ("Close"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.C
-                         ),
-                     new (
-                          items [3],
-                          "Save a file",
-                          () => ChangeMenuTitle ("Save"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.S
-                         ),
-                     new (
-                          items [4],
-                          "Save a file as",
-                          () => ChangeMenuTitle ("Save As"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.A
-                         ),
-                     new (
-                          items [5],
-                          "Delete a file",
-                          () => ChangeMenuTitle ("Delete"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.A
-                         )
-                 }
-                )
-        };
-        dialog.Add (menu);
-
-        void ChangeMenuTitle (string title)
-        {
-            menu.Menus [0].Title = title;
-            menu.SetNeedsDraw ();
-        }
-
-        RunState rsDialog = Application.Begin (dialog);
-        Application.RunIteration (ref rsDialog);
-
-        Assert.Equal (new (2, 2, 15, 4), dialog.Frame);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│                                      │
-│ ┌─────────────┐                      │
-│ │  File       │                      │
-│ │             │                      │
-│ └─────────────┘                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.Equal ("File", menu.Menus [0].Title);
-        menu.OpenMenu ();
-        Application.RunIteration (ref rsDialog);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│                                      │
-│ ┌─────────────┐                      │
-│ │  File       │                      │
-│ │ ┌──────────────────────────────────┐
-│ └─│ New    Create a new file  Ctrl+N │
-│   │ Open         Open a file  Ctrl+O │
-│   │ Close       Close a file  Ctrl+C │
-│   │ Save         Save a file  Ctrl+S │
-│   │ Save As   Save a file as  Ctrl+A │
-│   │ Delete     Delete a file  Ctrl+A │
-│   └──────────────────────────────────┘
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5), Flags = MouseFlags.Button1Clicked });
-
-        // Need to fool MainLoop into thinking it's running
-        Application.MainLoop.Running = true;
-        bool firstIteration = true;
-        Application.RunIteration (ref rsDialog, firstIteration);
-        Assert.Equal (items [0], menu.Menus [0].Title);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│                                      │
-│ ┌─────────────┐                      │
-│ │  New        │                      │
-│ │             │                      │
-│ └─────────────┘                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        for (var i = 0; i < items.Count; i++)
-        {
-            menu.OpenMenu ();
-
-            Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked });
-
-            Application.RunIteration (ref rsDialog);
-            Assert.Equal (items [i], menu.Menus [0].Title);
-        }
-
-        ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
-        menu.OpenMenu ();
-        Application.RunIteration (ref rsDialog);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────┐
-│                  │
-│ ┌─────────────┐  │
-│ │  Delete     │  │
-│ │ ┌───────────────
-│ └─│ New    Create 
-│   │ Open         O
-│   │ Close       Cl
-│   │ Save         S
-│   │ Save As   Save
-│   │ Delete     Del
-│   └───────────────
-│                  │
-│                  │
-└──────────────────┘",
-                                                      output
-                                                     );
-
-        Application.End (rsDialog);
-        Application.End (rsTop);
-        top.Dispose ();
-    }
-
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
-    [AutoInitShutdown]
-    public void Draw_A_Menu_Over_A_Top_Dialog ()
-    {
-        ((FakeDriver)Application.Driver).SetBufferSize (40, 15);
-
-        // Override CM
-        Window.DefaultBorderStyle = LineStyle.Single;
-        Dialog.DefaultButtonAlignment = Alignment.Center;
-        Dialog.DefaultBorderStyle = LineStyle.Single;
-        Dialog.DefaultShadow = ShadowStyle.None;
-        Button.DefaultShadow = ShadowStyle.None;
-
-        Assert.Equal (new (0, 0, 40, 15), View.GetClip ()!.GetBounds());
-        DriverAssert.AssertDriverContentsWithFrameAre (@"", output);
-
-        List<string> items = new ()
-        {
-            "New",
-            "Open",
-            "Close",
-            "Save",
-            "Save As",
-            "Delete"
-        };
-        var dialog = new Dialog { X = 2, Y = 2, Width = 15, Height = 4 };
-        var menu = new MenuBar { X = Pos.Center (), Width = 10 };
-
-        menu.Menus = new MenuBarItem []
-        {
-            new (
-                 "File",
-                 new MenuItem []
-                 {
-                     new (
-                          items [0],
-                          "Create a new file",
-                          () => ChangeMenuTitle ("New"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.N
-                         ),
-                     new (
-                          items [1],
-                          "Open a file",
-                          () => ChangeMenuTitle ("Open"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.O
-                         ),
-                     new (
-                          items [2],
-                          "Close a file",
-                          () => ChangeMenuTitle ("Close"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.C
-                         ),
-                     new (
-                          items [3],
-                          "Save a file",
-                          () => ChangeMenuTitle ("Save"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.S
-                         ),
-                     new (
-                          items [4],
-                          "Save a file as",
-                          () => ChangeMenuTitle ("Save As"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.A
-                         ),
-                     new (
-                          items [5],
-                          "Delete a file",
-                          () => ChangeMenuTitle ("Delete"),
-                          null,
-                          null,
-                          KeyCode.CtrlMask | KeyCode.A
-                         )
-                 }
-                )
-        };
-        dialog.Add (menu);
-
-        void ChangeMenuTitle (string title)
-        {
-            menu.Menus [0].Title = title;
-            menu.SetNeedsDraw ();
-        }
-
-        RunState rs = Application.Begin (dialog);
-        Application.RunIteration (ref rs);
-
-        Assert.Equal (new (2, 2, 15, 4), dialog.Frame);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-  ┌─────────────┐
-  │  File       │
-  │             │
-  └─────────────┘",
-                                                      output
-                                                     );
-
-        Assert.Equal ("File", menu.Menus [0].Title);
-        menu.OpenMenu ();
-        Application.RunIteration (ref rs);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-  ┌─────────────┐                       
-  │  File       │                       
-  │ ┌──────────────────────────────────┐
-  └─│ New    Create a new file  Ctrl+N │
-    │ Open         Open a file  Ctrl+O │
-    │ Close       Close a file  Ctrl+C │
-    │ Save         Save a file  Ctrl+S │
-    │ Save As   Save a file as  Ctrl+A │
-    │ Delete     Delete a file  Ctrl+A │
-    └──────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5), Flags = MouseFlags.Button1Clicked });
-
-        // Need to fool MainLoop into thinking it's running
-        Application.MainLoop.Running = true;
-        Application.RunIteration (ref rs);
-        Assert.Equal (items [0], menu.Menus [0].Title);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-  ┌─────────────┐
-  │  New        │
-  │             │
-  └─────────────┘",
-                                                      output
-                                                     );
-
-        for (var i = 1; i < items.Count; i++)
-        {
-            menu.OpenMenu ();
-
-            Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked });
-
-            Application.RunIteration (ref rs);
-            Assert.Equal (items [i], menu.Menus [0].Title);
-        }
-
-        ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
-        menu.OpenMenu ();
-        Application.RunIteration (ref rs);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-  ┌─────────────┐   
-  │  Delete     │   
-  │ ┌───────────────
-  └─│ New    Create 
-    │ Open         O
-    │ Close       Cl
-    │ Save         S
-    │ Save As   Save
-    │ Delete     Del
-    └───────────────",
-                                                      output
-                                                     );
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
+
+        // Act
+        Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey);
+        Assert.True (menuBar.IsActive ());
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+
+        Application.RaiseKeyDownEvent (Application.QuitKey);
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBar.HasFocus);
+        Assert.False (menuBar.CanFocus);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.HasFocus);
 
 
         Application.End (rs);
         Application.End (rs);
-        dialog.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void DrawFrame_With_Negative_Positions ()
-    {
-        var menu = new MenuBar
-        {
-            X = -1,
-            Y = -1,
-            Menus =
-            [
-                new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) })
-            ]
-        };
-        menu.Layout ();
-
-        Assert.Equal (new (-1, -1), new Point (menu.Frame.X, menu.Frame.Y));
-
-        Toplevel top = new ();
-        Application.Begin (top);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        var expected = @"
-──────┐
- One  │
- Two  │
-──────┘
-";
-
-        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (0, 0, 7, 4), pos);
-
-        menu.CloseAllMenus ();
-        menu.Frame = new (-1, -2, menu.Frame.Width, menu.Frame.Height);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        expected = @"
- One  │
- Two  │
-──────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 7, 3), pos);
-
-        menu.CloseAllMenus ();
-        menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height);
-        ((FakeDriver)Application.Driver!).SetBufferSize (7, 5);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        expected = @"
-┌──────
-│ One  
-│ Two  
-└──────
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (0, 1, 7, 4), pos);
-
-        menu.CloseAllMenus ();
-        menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height);
-        ((FakeDriver)Application.Driver!).SetBufferSize (7, 3);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        expected = @"
-┌──────
-│ One  
-│ Two  
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (0, 0, 7, 3), pos);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void DrawFrame_With_Negative_Positions_Disabled_Border ()
+    public void MenuBarItem_HotKey_Activates ()
     {
     {
-        var menu = new MenuBar
-        {
-            X = -2,
-            Y = -1,
-            MenusBorderStyle = LineStyle.None,
-            Menus =
-            [
-                new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) })
-            ]
-        };
-        menu.Layout ();
-
-        Assert.Equal (new (-2, -1), new Point (menu.Frame.X, menu.Frame.Y));
-
-        Toplevel top = new ();
-        Application.Begin (top);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        var expected = @"
-ne
-wo
-";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        menu.CloseAllMenus ();
-        menu.Frame = new (-2, -2, menu.Frame.Width, menu.Frame.Height);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        expected = @"
-wo
-";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        menu.CloseAllMenus ();
-        menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height);
-        ((FakeDriver)Application.Driver!).SetBufferSize (3, 2);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        expected = @"
- On
- Tw
-";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        menu.CloseAllMenus ();
-        menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height);
-        ((FakeDriver)Application.Driver!).SetBufferSize (3, 1);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        expected = @"
- On
-";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void DrawFrame_With_Positive_Positions ()
-    {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) })
-            ]
-        };
-
-        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
-
-        Toplevel top = new ();
-        Application.Begin (top);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        var expected = @"
-┌──────┐
-│ One  │
-│ Two  │
-└──────┘
-";
-
-        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (0, 1, 8, 4), pos);
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void DrawFrame_With_Positive_Positions_Disabled_Border ()
-    {
-        var menu = new MenuBar
-        {
-            MenusBorderStyle = LineStyle.None,
-            Menus =
-            [
-                new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) })
-            ]
-        };
-
-        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
-
-        Toplevel top = new ();
-        Application.Begin (top);
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        var expected = @"
- One
- Two
-";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        top.Dispose ();
-    }
-
-    [Fact]
-    public void Exceptions ()
-    {
-        Assert.Throws<ArgumentNullException> (() => new MenuBarItem ("Test", (MenuItem [])null));
-        Assert.Throws<ArgumentNullException> (() => new MenuBarItem ("Test", (List<MenuItem []>)null));
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void HotKey_MenuBar_OnKeyDown_OnKeyUp_ProcessKeyPressed ()
-    {
-        var newAction = false;
-        var copyAction = false;
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("_File", new MenuItem [] { new ("_New", "", () => newAction = true) }),
-                new (
-                     "_Edit",
-                     new MenuItem [] { new ("_Copy", "", () => copyAction = true) }
-                    )
-            ]
-        };
-
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.False (newAction);
-        Assert.False (copyAction);
-
-#if SUPPORT_ALT_TO_ACTIVATE_MENU
-        Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask)));
-        Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask)));
-        Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask)));
-        Assert.True (menu.IsMenuOpen);
-        Application.Top.Draw ();
-
-        string expected = @"
- File  Edit
-";
-
-        var pos = DriverAsserts.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 11, 1), pos);
-
-        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.N)));
-        Application.MainLoop.RunIteration ();
-        Assert.False (newAction); // not yet, hot keys don't work if the item is not visible
-
-        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.F)));
-        Application.MainLoop.RunIteration ();
-        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.N)));
-        Application.MainLoop.RunIteration ();
-        Assert.True (newAction);
-        Application.Top.Draw ();
-
-        expected = @"
- File  Edit
-";
-
-        Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask)));
-        Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask)));
-        Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask)));
-        Assert.True (menu.IsMenuOpen);
-        Application.Top.Draw ();
-
-        expected = @"
- File  Edit
-";
-
-        pos = DriverAsserts.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 11, 1), pos);
-
-        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.CursorRight)));
-        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.C)));
-        Application.MainLoop.RunIteration ();
-        Assert.True (copyAction);
-#endif
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void HotKey_MenuBar_ProcessKeyPressed_Menu_ProcessKey ()
-    {
-        var newAction = false;
-        var copyAction = false;
-
-        // Define the expected menu
-        var expectedMenu = new ExpectedMenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) }),
-                new (
-                     "Edit",
-                     new MenuItem [] { new ("Copy", "", null) }
-                    )
-            ]
-        };
-
-        // The real menu
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "_" + expectedMenu.Menus [0].Title,
-                     new MenuItem []
-                     {
-                         new (
-                              "_" + expectedMenu.Menus [0].Children [0].Title,
-                              "",
-                              () => newAction = true
-                             )
-                     }
-                    ),
-                new (
-                     "_" + expectedMenu.Menus [1].Title,
-                     new MenuItem []
-                     {
-                         new (
-                              "_"
-                              + expectedMenu.Menus [1]
-                                            .Children [0]
-                                            .Title,
-                              "",
-                              () => copyAction = true
-                             )
-                     }
-                    )
-            ]
-        };
-
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.False (newAction);
-        Assert.False (copyAction);
-
-        Assert.True (menu.NewKeyDownEvent (Key.F.WithAlt));
-        Assert.True (menu.IsMenuOpen);
-        Application.Top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
-
-        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.N));
-        Application.MainLoop.RunIteration ();
-        Assert.True (newAction);
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-        Assert.True (menu.NewKeyDownEvent (Key.E.WithAlt));
-        Assert.True (menu.IsMenuOpen);
-        Application.Top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
+        // Act
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBar.HasFocus);
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+        Assert.True (menuBarItem.PopoverMenu.HasFocus);
 
 
-        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.C));
-        Application.MainLoop.RunIteration ();
-        Assert.True (copyAction);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void Key_Open_And_Close_The_MenuBar ()
+    public void MenuBarItem_HotKey_Deactivates ()
     {
     {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) })
-            ]
-        };
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.True (top.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-        Assert.True (top.NewKeyDownEvent (menu.Key));
-        Assert.False (menu.IsMenuOpen);
-
-        menu.Key = Key.F10.WithShift;
-        Assert.False (top.NewKeyDownEvent (Key.F9));
-        Assert.False (menu.IsMenuOpen);
-
-        Assert.True (top.NewKeyDownEvent (Key.F10.WithShift));
-        Assert.True (menu.IsMenuOpen);
-        Assert.True (top.NewKeyDownEvent (Key.F10.WithShift));
-        Assert.False (menu.IsMenuOpen);
-        top.Dispose ();
-    }
-
-    [Theory]
-    [AutoInitShutdown]
-    [InlineData ("_File", "_New", "", KeyCode.Space | KeyCode.CtrlMask)]
-    [InlineData ("Closed", "None", "", KeyCode.Space | KeyCode.CtrlMask, KeyCode.Space | KeyCode.CtrlMask)]
-    [InlineData ("_File", "_New", "", KeyCode.F9)]
-    [InlineData ("Closed", "None", "", KeyCode.F9, KeyCode.F9)]
-    [InlineData ("_File", "_Open", "", KeyCode.F9, KeyCode.CursorDown)]
-    [InlineData ("_File", "_Save", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorDown)]
-    [InlineData ("_File", "_Quit", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorDown, KeyCode.CursorDown)]
-    [InlineData (
-                    "_File",
-                    "_New",
-                    "",
-                    KeyCode.F9,
-                    KeyCode.CursorDown,
-                    KeyCode.CursorDown,
-                    KeyCode.CursorDown,
-                    KeyCode.CursorDown
-                )]
-    [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorUp)]
-    [InlineData ("_File", "_Quit", "", KeyCode.F9, KeyCode.CursorUp)]
-    [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorUp, KeyCode.CursorDown)]
-    [InlineData ("Closed", "None", "Open", KeyCode.F9, KeyCode.CursorDown, KeyCode.Enter)]
-    [InlineData ("_Edit", "_Copy", "", KeyCode.F9, KeyCode.CursorRight)]
-    [InlineData ("_About", "_About", "", KeyCode.F9, KeyCode.CursorLeft)]
-    [InlineData ("_Edit", "_Copy", "", KeyCode.F9, KeyCode.CursorLeft, KeyCode.CursorLeft)]
-    [InlineData ("_Edit", "_Select All", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorUp)]
-    [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorDown, KeyCode.CursorLeft)]
-    [InlineData ("_About", "_About", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight)]
-    [InlineData ("Closed", "None", "New", KeyCode.F9, KeyCode.Enter)]
-    [InlineData ("Closed", "None", "Quit", KeyCode.F9, KeyCode.CursorUp, KeyCode.Enter)]
-    [InlineData ("Closed", "None", "Copy", KeyCode.F9, KeyCode.CursorRight, KeyCode.Enter)]
-    [InlineData (
-                    "Closed",
-                    "None",
-                    "Find",
-                    KeyCode.F9,
-                    KeyCode.CursorRight,
-                    KeyCode.CursorUp,
-                    KeyCode.CursorUp,
-                    KeyCode.Enter
-                )]
-    [InlineData (
-                    "Closed",
-                    "None",
-                    "Replace",
-                    KeyCode.F9,
-                    KeyCode.CursorRight,
-                    KeyCode.CursorUp,
-                    KeyCode.CursorUp,
-                    KeyCode.CursorDown,
-                    KeyCode.Enter
-                )]
-    [InlineData (
-                    "_Edit",
-                    "F_ind",
-                    "",
-                    KeyCode.F9,
-                    KeyCode.CursorRight,
-                    KeyCode.CursorUp,
-                    KeyCode.CursorUp,
-                    KeyCode.CursorLeft,
-                    KeyCode.Enter
-                )]
-    [InlineData ("Closed", "None", "About", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight, KeyCode.Enter)]
-
-    //// Hotkeys
-    [InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F)]
-    [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.ShiftMask | KeyCode.F)]
-    [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.Esc)]
-    [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.AltMask | KeyCode.F)]
-    [InlineData ("Closed", "None", "Open", KeyCode.AltMask | KeyCode.F, KeyCode.O)]
-    [InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F, KeyCode.ShiftMask | KeyCode.O)]
-    [InlineData ("Closed", "None", "Open", KeyCode.AltMask | KeyCode.F, KeyCode.AltMask | KeyCode.O)]
-    [InlineData ("_Edit", "_Copy", "", KeyCode.AltMask | KeyCode.E)]
-    [InlineData ("_Edit", "F_ind", "", KeyCode.AltMask | KeyCode.E, KeyCode.F)]
-    [InlineData ("_Edit", "F_ind", "", KeyCode.AltMask | KeyCode.E, KeyCode.AltMask | KeyCode.F)]
-    [InlineData ("Closed", "None", "Replace", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.R)]
-    [InlineData ("Closed", "None", "Copy", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.C)]
-    [InlineData ("_Edit", "_1st", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3)]
-    [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D1)]
-    [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.Enter)]
-    [InlineData ("Closed", "None", "2", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D2)]
-    [InlineData ("_Edit", "_5th", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D4)]
-    [InlineData ("Closed", "None", "5", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D4, KeyCode.D5)]
-    [InlineData ("Closed", "None", "About", KeyCode.AltMask | KeyCode.A)]
-    public void KeyBindings_Navigation_Commands (
-        string expectedBarTitle,
-        string expectedItemTitle,
-        string expectedAction,
-        params KeyCode [] keys
-    )
-    {
-        var miAction = "";
-        MenuItem mbiCurrent = null;
-        MenuItem miCurrent = null;
-
-        var menu = new MenuBar ();
-
-        Func<object, bool> fn = s =>
-                                {
-                                    miAction = s as string;
-
-                                    return true;
-                                };
-        menu.EnableForDesign (ref fn);
-
-        menu.Key = KeyCode.F9;
-        menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu;
-        menu.MenuOpened += (s, e) => { miCurrent = e.MenuItem; };
-
-        menu.MenuClosing += (s, e) =>
-                            {
-                                mbiCurrent = null;
-                                miCurrent = null;
-                            };
-        menu.UseKeysUpDownAsKeysLeftRight = true;
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        foreach (Key key in keys)
-        {
-            top.NewKeyDownEvent (key);
-            Application.MainLoop.RunIteration ();
-        }
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
+
+        // Act
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.True (menuBar.IsActive ());
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBar.HasFocus);
+        Assert.False (menuBar.CanFocus);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.HasFocus);
 
 
-        Assert.Equal (expectedBarTitle, mbiCurrent != null ? mbiCurrent.Title : "Closed");
-        Assert.Equal (expectedItemTitle, miCurrent != null ? miCurrent.Title : "None");
-        Assert.Equal (expectedAction, miAction);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
-    [Theory]
-    [AutoInitShutdown]
-    [InlineData ("New", KeyCode.CtrlMask | KeyCode.N)]
-    [InlineData ("Quit", KeyCode.CtrlMask | KeyCode.Q)]
-    [InlineData ("Copy", KeyCode.CtrlMask | KeyCode.C)]
-    [InlineData ("Replace", KeyCode.CtrlMask | KeyCode.H)]
-    [InlineData ("1", KeyCode.F1)]
-    [InlineData ("5", KeyCode.CtrlMask | KeyCode.D5)]
-    public void KeyBindings_Shortcut_Commands (string expectedAction, params KeyCode [] keys)
-    {
-        var miAction = "";
-        MenuItem mbiCurrent = null;
-        MenuItem miCurrent = null;
-
-        var menu = new MenuBar ();
-
-        bool FnAction (string s)
-        {
-            miAction = s;
-
-            return true;
-        }
-
-        // Declare a variable for the function
-        Func<string, bool> fnActionVariable = FnAction;
-
-        menu.EnableForDesign (ref fnActionVariable);
-
-        menu.Key = KeyCode.F9;
-        menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu;
-        menu.MenuOpened += (s, e) => { miCurrent = e.MenuItem; };
-
-        menu.MenuClosing += (s, e) =>
-                            {
-                                mbiCurrent = null;
-                                miCurrent = null;
-                            };
-        menu.UseKeysUpDownAsKeysLeftRight = true;
-
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        foreach (KeyCode key in keys)
-        {
-            Assert.True (top.NewKeyDownEvent (new (key)));
-            Application.MainLoop!.RunIteration ();
-        }
-
-        Assert.Equal (expectedAction, miAction);
-        top.Dispose ();
-    }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void Menu_With_Separator ()
+    public void MenuItem_HotKey_Deactivates ()
     {
     {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "File",
-                     new MenuItem []
-                     {
-                         new (
-                              "_Open",
-                              "Open a file",
-                              () => { },
-                              null,
-                              null,
-                              KeyCode.CtrlMask | KeyCode.O
-                             ),
-                         null,
-                         new ("_Quit", "", null)
-                     }
-                    )
-            ]
-        };
-
+        // Arrange
+        int action = 0;
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item", Action = () => action++ };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
- File                         
-┌────────────────────────────┐
-│ Open   Open a file  Ctrl+O │
-├────────────────────────────┤
-│ Quit                       │
-└────────────────────────────┘",
-                                                      output
-                                                     );
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void Menu_With_Separator_Disabled_Border ()
-    {
-        var menu = new MenuBar
-        {
-            MenusBorderStyle = LineStyle.None,
-            Menus =
-            [
-                new (
-                     "File",
-                     new MenuItem []
-                     {
-                         new (
-                              "_Open",
-                              "Open a file",
-                              () => { },
-                              null,
-                              null,
-                              KeyCode.CtrlMask | KeyCode.O
-                             ),
-                         null,
-                         new ("_Quit", "", null)
-                     }
-                    )
-            ]
-        };
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
+
+        // Act
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.True (menuBar.IsActive ());
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+
+        Application.RaiseKeyDownEvent (Key.I);
+        Assert.Equal (1, action);
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBar.HasFocus);
+        Assert.False (menuBar.CanFocus);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.HasFocus);
 
 
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        menu.OpenMenu ();
-        Application.LayoutAndDraw ();
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
- File                       
- Open   Open a file  Ctrl+O 
-────────────────────────────
- Quit                       ",
-                                                      output
-                                                     );
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void MenuBar_ButtonPressed_Open_The_Menu_ButtonPressed_Again_Close_The_Menu ()
+    public void HotKey_Activates_Only_Once ()
     {
     {
-        // Define the expected menu
-        var expectedMenu = new ExpectedMenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("Open", "", null) }),
-                new (
-                     "Edit",
-                     new MenuItem [] { new ("Copy", "", null) }
-                    )
-            ]
-        };
-
-        // Test without HotKeys first
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "_" + expectedMenu.Menus [0].Title,
-                     new MenuItem [] { new ("_" + expectedMenu.Menus [0].Children [0].Title, "", null) }
-                    ),
-                new (
-                     "_" + expectedMenu.Menus [1].Title,
-                     new MenuItem []
-                     {
-                         new (
-                              "_"
-                              + expectedMenu.Menus [1]
-                                            .Children [0]
-                                            .Title,
-                              "",
-                              null
-                             )
-                     }
-                    )
-            ]
-        };
-
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
-        Assert.True (menu.IsMenuOpen);
-        top.Draw ();
-
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
-
-        Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
-        Assert.False (menu.IsMenuOpen);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
-        top.Dispose ();
-    }
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void MenuBar_In_Window_Without_Other_Views_With_Top_Init ()
-    {
-        var win = new Window ();
+        int visibleChangeCount = 0;
+        menuBarItemPopover.VisibleChanged += (sender, args) =>
+                                             {
+                                                 if (menuBarItemPopover.Visible)
+                                                 {
+                                                     visibleChangeCount++;
+                                                 }
+                                             };
 
 
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) }),
-                new (
-                     "Edit",
-                     new MenuItem []
-                     {
-                         new MenuBarItem (
-                                          "Delete",
-                                          new MenuItem []
-                                              { new ("All", "", null), new ("Selected", "", null) }
-                                         )
-                     }
-                    )
-            ]
-        };
-        win.Add (menu);
-        Toplevel top = new ();
-        top.Add (win);
-        Application.Begin (top);
-        ((FakeDriver)Application.Driver!).SetBufferSize (40, 8);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (win.NewKeyDownEvent (menu.Key));
-        top.Draw ();
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│┌──────┐                              │
-││ New  │                              │
-│└──────┘                              │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
-        Application.LayoutAndDraw ();
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│      ┌─────────┐                     │
-│      │ Delete ►│                     │
-│      └─────────┘                     │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
-        top.Draw ();
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│      ┌─────────┐                     │
-│      │ Delete ►│┌───────────┐        │
-│      └─────────┘│ All       │        │
-│                 │ Selected  │        │
-│                 └───────────┘        │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
-        View.SetClipToScreen ();
-        top.Draw ();
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│┌──────┐                              │
-││ New  │                              │
-│└──────┘                              │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-        top.Dispose ();
-    }
+        // Act
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.Equal (1, visibleChangeCount);
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void MenuBar_In_Window_Without_Other_Views_With_Top_Init_With_Parameterless_Run ()
-    {
-        var win = new Window ();
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) }),
-                new (
-                     "Edit",
-                     new MenuItem []
-                     {
-                         new MenuBarItem (
-                                          "Delete",
-                                          new MenuItem []
-                                              { new ("All", "", null), new ("Selected", "", null) }
-                                         )
-                     }
-                    )
-            ]
-        };
-        win.Add (menu);
-        Toplevel top = new ();
-        top.Add (win);
-
-        Application.Iteration += (s, a) =>
-                                 {
-                                     ((FakeDriver)Application.Driver!).SetBufferSize (40, 8);
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Assert.True (win.NewKeyDownEvent (menu.Key));
-                                     top.Draw ();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│┌──────┐                              │
-││ New  │                              │
-│└──────┘                              │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
-                                     Application.LayoutAndDraw ();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│      ┌─────────┐                     │
-│      │ Delete ►│                     │
-│      └─────────┘                     │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
-                                     top.Draw ();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│      ┌─────────┐                     │
-│      │ Delete ►│┌───────────┐        │
-│      └─────────┘│ All       │        │
-│                 │ Selected  │        │
-│                 └───────────┘        │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
-                                     View.SetClipToScreen ();
-                                     top.Draw ();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│┌──────┐                              │
-││ New  │                              │
-│└──────┘                              │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Application.RequestStop ();
-                                 };
-
-        Application.Run (top);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init ()
-    {
-        var win = new Window ();
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) }),
-                new (
-                     "Edit",
-                     new MenuItem []
-                     {
-                         new MenuBarItem (
-                                          "Delete",
-                                          new MenuItem []
-                                              { new ("All", "", null), new ("Selected", "", null) }
-                                         )
-                     }
-                    )
-            ]
-        };
-        win.Add (menu);
-        ((FakeDriver)Application.Driver!).SetBufferSize (40, 8);
-        RunState rs = Application.Begin (win);
-        Application.RunIteration (ref rs);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (win.NewKeyDownEvent (menu.Key));
-        Application.RunIteration (ref rs);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│┌──────┐                              │
-││ New  │                              │
-│└──────┘                              │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
-        Application.RunIteration (ref rs);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│      ┌─────────┐                     │
-│      │ Delete ►│                     │
-│      └─────────┘                     │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
-        Application.RunIteration (ref rs);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│      ┌─────────┐                     │
-│      │ Delete ►│┌───────────┐        │
-│      └─────────┘│ All       │        │
-│                 │ Selected  │        │
-│                 └───────────┘        │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
-        Application.RunIteration (ref rs);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│┌──────┐                              │
-││ New  │                              │
-│└──────┘                              │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                      output
-                                                     );
-        win.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init_With_Run_T ()
+    public void WhenActive_Other_MenuBarItem_HotKey_Activates ()
     {
     {
-        ((FakeDriver)Application.Driver!).SetBufferSize (40, 8);
-
-        Application.Iteration += (s, a) =>
-                                 {
-                                     Toplevel top = Application.Top;
-                                     Application.LayoutAndDraw();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Assert.True (top.NewKeyDownEvent (Key.F9));
-                                     top.Draw ();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│┌──────┐                              │
-││ New  │                              │
-│└──────┘                              │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Assert.True (top.SubViews.ElementAt (0).NewKeyDownEvent (Key.CursorRight));
-                                     Application.LayoutAndDraw ();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│      ┌─────────┐                     │
-│      │ Delete ►│                     │
-│      └─────────┘                     │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Assert.True (
-                                                  ((MenuBar)top.SubViews.ElementAt (0))._openMenu.NewKeyDownEvent (Key.CursorRight)
-                                                 );
-                                     top.Draw ();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│      ┌─────────┐                     │
-│      │ Delete ►│┌───────────┐        │
-│      └─────────┘│ All       │        │
-│                 │ Selected  │        │
-│                 └───────────┘        │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Assert.True (
-                                                  ((MenuBar)top.SubViews.ElementAt (0))._openMenu.NewKeyDownEvent (Key.CursorRight)
-                                                 );
-                                     View.SetClipToScreen ();
-                                     top.Draw ();
-
-                                     DriverAssert.AssertDriverContentsWithFrameAre (
-                                                                                   @"
-┌──────────────────────────────────────┐
-│ File  Edit                           │
-│┌──────┐                              │
-││ New  │                              │
-│└──────┘                              │
-│                                      │
-│                                      │
-└──────────────────────────────────────┘",
-                                                                                   output
-                                                                                  );
-
-                                     Application.RequestStop ();
-                                 };
-
-        Application.Run<CustomWindow> ().Dispose ();
-    }
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void MenuBar_Position_And_Size_With_HotKeys_Is_The_Same_As_Without_HotKeys ()
-    {
-        // Define the expected menu
-        var expectedMenu = new ExpectedMenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("12", "", null) }),
-                new (
-                     "Edit",
-                     new MenuItem [] { new ("Copy", "", null) }
-                    )
-            ]
-        };
+        var menuItem2 = new MenuItemv2 { Id = "menuItem2", Title = "_Copy" };
+        var menu2 = new Menuv2 ([menuItem2]) { Id = "menu2" };
+        var menuBarItem2 = new MenuBarItemv2 () { Id = "menuBarItem2", Title = "_Edit" };
+        var menuBarItemPopover2 = new PopoverMenu () { Id = "menuBarItemPopover2" };
+        menuBarItem2.PopoverMenu = menuBarItemPopover2;
+        menuBarItemPopover2.Root = menu2;
 
 
-        // Test without HotKeys first
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     expectedMenu.Menus [0].Title,
-                     new MenuItem [] { new (expectedMenu.Menus [0].Children [0].Title, "", null) }
-                    ),
-                new (
-                     expectedMenu.Menus [1].Title,
-                     new MenuItem []
-                     {
-                         new (
-                              expectedMenu.Menus [1].Children [0].Title,
-                              "",
-                              null
-                             )
-                     }
-                    )
-            ]
-        };
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        menuBar.Add (menuBarItem2);
 
 
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        // Open first
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
-
-        // Open second
-        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight));
-        Assert.True (menu.IsMenuOpen);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
-
-        // Close menu
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.False (menu.IsMenuOpen);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
-
-        top.Remove (menu);
-
-        // Now test WITH HotKeys
-        menu = new ()
-        {
-            Menus =
-            [
-                new (
-                     "_" + expectedMenu.Menus [0].Title,
-                     new MenuItem [] { new ("_" + expectedMenu.Menus [0].Children [0].Title, "", null) }
-                    ),
-                new (
-                     "_" + expectedMenu.Menus [1].Title,
-                     new MenuItem []
-                     {
-                         new (
-                              "_" + expectedMenu.Menus [1].Children [0].Title,
-                              "",
-                              null
-                             )
-                     }
-                    )
-            ]
-        };
-
-        top.Add (menu);
-
-        // Open first
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
-
-        // Open second
-        Assert.True (top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight));
-        Assert.True (menu.IsMenuOpen);
-        View.SetClipToScreen ();
-        Application.Top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
-
-        // Close menu
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.False (menu.IsMenuOpen);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void MenuBar_Submenus_Alignment_Correct ()
-    {
-        // Define the expected menu
-        var expectedMenu = new ExpectedMenuBar
-        {
-            Menus =
-            [
-                new (
-                     "File",
-                     new MenuItem []
-                     {
-                         new (
-                              "Really Long Sub Menu",
-                              "",
-                              null
-                             )
-                     }
-                    ),
-                new (
-                     "123",
-                     new MenuItem [] { new ("Copy", "", null) }
-                    ),
-                new (
-                     "Format",
-                     new MenuItem [] { new ("Word Wrap", "", null) }
-                    ),
-                new (
-                     "Help",
-                     new MenuItem [] { new ("About", "", null) }
-                    ),
-                new (
-                     "1",
-                     new MenuItem [] { new ("2", "", null) }
-                    ),
-                new (
-                     "3",
-                     new MenuItem [] { new ("2", "", null) }
-                    ),
-                new (
-                     "Last one",
-                     new MenuItem [] { new ("Test", "", null) }
-                    )
-            ]
-        };
-
-        MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length];
-
-        for (var i = 0; i < expectedMenu.Menus.Length; i++)
-        {
-            items [i] = new (
-                             expectedMenu.Menus [i].Title,
-                             new MenuItem [] { new (expectedMenu.Menus [i].Children [0].Title, "", null) }
-                            );
-        }
-
-        var menu = new MenuBar { Menus = items };
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
 
 
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+        // Act
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.True (menuBar.IsActive ());
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBarItem.PopoverMenu.Visible);
 
 
-        for (var i = 0; i < expectedMenu.Menus.Length; i++)
-        {
-            menu.OpenMenu (i);
-            Assert.True (menu.IsMenuOpen);
-            View.SetClipToScreen ();
-            top.Draw ();
-            DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (i), output);
-        }
+        Application.RaiseKeyDownEvent (Key.E.WithAlt);
+        Assert.True (menuBar.IsActive ());
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBarItem2.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
 
 
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void MenuBar_With_Action_But_Without_MenuItems_Not_Throw ()
+    public void Mouse_Enter_Sets_Can_Focus_But_Does_Not_Activate ()
     {
     {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new () { Title = "Test 1", Action = () => { } },
-
-                new () { Title = "Test 2", Action = () => { } }
-            ]
-        };
-
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-#if SUPPORT_ALT_TO_ACTIVATE_MENU
-        Assert.True (
-                     Application.OnKeyUp (
-                                          new KeyEventArgs (
-                                                            Key.AltMask
-                                                           )
-                                         )
-                    ); // changed to true because Alt activates menu bar
-#endif
-        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
-        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
-        top.Dispose ();
-    }
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void MenuBarItem_Children_Null_Does_Not_Throw ()
-    {
-        var menu = new MenuBar
+        // Act
+        Application.RaiseMouseEvent (new ()
         {
         {
-            Menus =
-            [
-                new ("Test", "", null)
-            ]
-        };
-        var top = new Toplevel ();
-        top.Add (menu);
+            Flags = MouseFlags.ReportMousePosition
+        });
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.True (menuBar.HasFocus);
+        Assert.True (menuBar.CanFocus);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.HasFocus);
 
 
-        Exception exception = Record.Exception (() => menu.NewKeyDownEvent (Key.Space));
-        Assert.Null (exception);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void MenuOpened_On_Disabled_MenuItem ()
+    public void Mouse_Click_Activates ()
     {
     {
-        MenuItem parent = null;
-        MenuItem miCurrent = null;
-        Menu mCurrent = null;
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "_File",
-                     new MenuItem []
-                     {
-                         new MenuBarItem (
-                                          "_New",
-                                          new MenuItem []
-                                          {
-                                              new (
-                                                   "_New doc",
-                                                   "Creates new doc.",
-                                                   null,
-                                                   () => false
-                                                  )
-                                          }
-                                         ),
-                         null,
-                         new ("_Save", "Saves the file.", null)
-                     }
-                    )
-            ]
-        };
-
-        menu.MenuOpened += (s, e) =>
-                           {
-                               parent = e.Parent;
-                               miCurrent = e.MenuItem;
-                               mCurrent = menu._openMenu;
-                           };
-        menu.UseKeysUpDownAsKeysLeftRight = true;
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        // open the menu
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }
-                                        )
-                    );
-        Assert.True (menu.IsMenuOpen);
-        Assert.Equal ("_File", parent.Title);
-        Assert.Equal ("_File", miCurrent.Parent.Title);
-        Assert.Equal ("_New", miCurrent.Title);
-
-        Assert.True (
-                     mCurrent.NewMouseEvent (
-                                             new () { Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
-                                            )
-                    );
-        Assert.True (menu.IsMenuOpen);
-        Assert.Equal ("_File", parent.Title);
-        Assert.Equal ("_File", miCurrent.Parent.Title);
-        Assert.Equal ("_New", miCurrent.Title);
-
-        Assert.True (
-                     mCurrent.NewMouseEvent (
-                                             new () { Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
-                                            )
-                    );
-        Assert.True (menu.IsMenuOpen);
-        Assert.Equal ("_File", parent.Title);
-        Assert.Equal ("_File", miCurrent.Parent.Title);
-        Assert.Equal ("_New", miCurrent.Title);
-
-        Assert.True (
-                     mCurrent.NewMouseEvent (
-                                             new () { Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
-                                            )
-                    );
-        Assert.True (menu.IsMenuOpen);
-        Assert.Equal ("_File", parent.Title);
-        Assert.Equal ("_File", miCurrent.Parent.Title);
-        Assert.Equal ("_Save", miCurrent.Title);
-
-        // close the menu
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }
-                                        )
-                    );
-        Assert.False (menu.IsMenuOpen);
-
-        // open the menu
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-
-        // The _New doc is enabled but the sub-menu isn't enabled. Is show but can't be selected and executed
-        Assert.Equal ("_New", parent.Title);
-        Assert.Equal ("_New", miCurrent.Parent.Title);
-        Assert.Equal ("_New doc", miCurrent.Title);
-
-        Assert.True (mCurrent.NewKeyDownEvent (Key.CursorDown));
-        Assert.True (menu.IsMenuOpen);
-        Assert.Equal ("_File", parent.Title);
-        Assert.Equal ("_File", miCurrent.Parent.Title);
-        Assert.Equal ("_Save", miCurrent.Title);
-
-        Assert.True (mCurrent.NewKeyDownEvent (Key.CursorUp));
-        Assert.True (menu.IsMenuOpen);
-        Assert.Equal ("_File", parent.Title);
-        Assert.Null (miCurrent);
-
-        // close the menu
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.False (menu.IsMenuOpen);
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void MenuOpening_MenuOpened_MenuClosing_Events ()
-    {
-        var miAction = "";
-        var isMenuClosed = true;
-        var cancelClosing = false;
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-        var menu = new MenuBar
+        // Act
+        Application.RaiseMouseEvent (new ()
         {
         {
-            Menus =
-            [
-                new ("_File", new MenuItem [] { new ("_New", "Creates new file.", New) })
-            ]
-        };
-
-        menu.MenuOpening += (s, e) =>
-                            {
-                                Assert.Equal ("_File", e.CurrentMenu.Title);
-                                Assert.Equal ("_New", e.CurrentMenu.Children [0].Title);
-                                Assert.Equal ("Creates new file.", e.CurrentMenu.Children [0].Help);
-                                Assert.Equal (New, e.CurrentMenu.Children [0].Action);
-                                e.CurrentMenu.Children [0].Action ();
-                                Assert.Equal ("New", miAction);
-
-                                e.NewMenuBarItem = new (
-                                                        "_Edit",
-                                                        new MenuItem [] { new ("_Copy", "Copies the selection.", Copy) }
-                                                       );
-                            };
-
-        menu.MenuOpened += (s, e) =>
-                           {
-                               MenuItem mi = e.MenuItem;
-
-                               Assert.Equal ("_Edit", mi.Parent.Title);
-                               Assert.Equal ("_Copy", mi.Title);
-                               Assert.Equal ("Copies the selection.", mi.Help);
-                               Assert.Equal (Copy, mi.Action);
-                               mi.Action ();
-                               Assert.Equal ("Copy", miAction);
-                           };
-
-        menu.MenuClosing += (s, e) =>
-                            {
-                                Assert.False (isMenuClosed);
-
-                                if (cancelClosing)
-                                {
-                                    e.Cancel = true;
-                                    isMenuClosed = false;
-                                }
-                                else
-                                {
-                                    isMenuClosed = true;
-                                }
-                            };
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-        isMenuClosed = !menu.IsMenuOpen;
-        Assert.False (isMenuClosed);
-        top.Draw ();
-
-        var expected = @"
-Edit
-┌──────────────────────────────┐
-│ Copy   Copies the selection. │
-└──────────────────────────────┘
-";
-        DriverAssert.AssertDriverContentsAre (expected, output);
-
-        cancelClosing = true;
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (isMenuClosed);
-        View.SetClipToScreen ();
-        top.Draw ();
-
-        expected = @"
-Edit
-┌──────────────────────────────┐
-│ Copy   Copies the selection. │
-└──────────────────────────────┘
-";
-        DriverAssert.AssertDriverContentsAre (expected, output);
-
-        cancelClosing = false;
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.False (menu.IsMenuOpen);
-        Assert.True (isMenuClosed);
-        View.SetClipToScreen ();
-        top.Draw ();
-
-        expected = @"
-Edit
-";
-        DriverAssert.AssertDriverContentsAre (expected, output);
-
-        void New () { miAction = "New"; }
-
-        void Copy () { miAction = "Copy"; }
+            Flags = MouseFlags.Button1Clicked
+        });
+        Assert.True (menuBar.IsActive ());
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBar.HasFocus);
+        Assert.True (menuBar.CanFocus);
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+        Assert.True (menuBarItem.PopoverMenu.HasFocus);
 
 
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
-    [Fact]
+    // QUESTION: Windows' menus close the menu when you click on the menu bar item again.
+    // QUESTION: What does Mac do?
+    // QUESTION: How bad is it that this test is skipped?
+    // QUESTION: Fixing this could be challenging. Should we fix it?
+    [Fact (Skip = "Clicking outside Popover, passes mouse event to MenuBar, which activates the same item again.")]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void MouseEvent_Test ()
+    public void Mouse_Click_Deactivates ()
     {
     {
-        MenuItem miCurrent = null;
-        Menu mCurrent = null;
-
-        var menuBar = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "_File",
-                     new MenuItem [] { new ("_New", "", null), new ("_Open", "", null), new ("_Save", "", null) }
-                    ),
-                new (
-                     "_Edit",
-                     new MenuItem [] { new ("_Copy", "", null), new ("C_ut", "", null), new ("_Paste", "", null) }
-                    )
-            ]
-        };
-
-        menuBar.MenuOpened += (s, e) =>
-                           {
-                               miCurrent = e.MenuItem;
-                               mCurrent = menuBar.OpenCurrentMenu;
-                           };
+        // Arrange
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
         top.Add (menuBar);
         top.Add (menuBar);
-        Application.Begin (top);
-
-        // Click on Edit
-        Assert.True (
-                     menuBar.NewMouseEvent (
-                                         new () { Position = new (10, 0), Flags = MouseFlags.Button1Pressed, View = menuBar }
-                                        )
-                    );
-        Assert.True (menuBar.IsMenuOpen);
-        Assert.Equal ("_Edit", miCurrent.Parent.Title);
-        Assert.Equal ("_Copy", miCurrent.Title);
-
-        // Click on Paste
-        Assert.True (
-                     mCurrent.NewMouseEvent (
-                                             new () { Position = new (10, 2), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
-                                            )
-                    );
-        Assert.True (menuBar.IsMenuOpen);
-        Assert.Equal ("_Edit", miCurrent.Parent.Title);
-        Assert.Equal ("_Paste", miCurrent.Title);
-
-        for (var i = 4; i >= -1; i--)
-        {
-            Application.RaiseMouseEvent (
-                                         new () { ScreenPosition = new (10, i), Flags = MouseFlags.ReportMousePosition }
-                                        );
-
-            Assert.True (menuBar.IsMenuOpen);
-            Menu menu = (Menu)top.SubViews.First (v => v is Menu);
-
-            if (i is < 0 or > 0)
-            {
-                Assert.Equal (menu, Application.MouseGrabView);
-            }
-            else
-            {
-                Assert.Equal (menuBar, Application.MouseGrabView);
-            }
-
-            Assert.Equal ("_Edit", miCurrent.Parent.Title);
-
-            if (i == 4)
-            {
-                Assert.Equal ("_Paste", miCurrent.Title);
-            }
-            else if (i == 3)
-            {
-                Assert.Equal ("C_ut", miCurrent.Title);
-            }
-            else if (i == 2)
-            {
-                Assert.Equal ("_Copy", miCurrent.Title);
-            }
-            else
-            {
-                Assert.Equal ("_Copy", miCurrent.Title);
-            }
-        }
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
+
+        Application.RaiseMouseEvent (new ()
+        {
+            Flags = MouseFlags.Button1Clicked
+        });
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBar.HasFocus);
+        Assert.True (menuBar.CanFocus);
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+        Assert.True (menuBarItem.PopoverMenu.HasFocus);
+
+        // Act
+        Application.RaiseMouseEvent (new ()
+        {
+            Flags = MouseFlags.Button1Clicked
+        });
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBar.HasFocus);
+        Assert.False (menuBar.CanFocus);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.HasFocus);
 
 
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Keyboard ()
+    public void Dynamic_Change_MenuItem_Title ()
     {
     {
-        var expectedMenu = new ExpectedMenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) }),
-                new ("Edit", Array.Empty<MenuItem> ()),
-                new (
-                     "Format",
-                     new MenuItem [] { new ("Wrap", "", null) }
-                    )
-            ]
-        };
-
-        MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length];
-
-        for (var i = 0; i < expectedMenu.Menus.Length; i++)
-        {
-            items [i] = new (
-                             expectedMenu.Menus [i].Title,
-                             expectedMenu.Menus [i].Children.Length > 0
-                                 ? new MenuItem [] { new (expectedMenu.Menus [i].Children [0].Title, "", null) }
-                                 : Array.Empty<MenuItem> ()
-                            );
-        }
-
-        var menu = new MenuBar { Menus = items };
-
-        var tf = new TextField { Y = 2, Width = 10 };
+        // Arrange
+        int action = 0;
+        var menuItem = new MenuItemv2 { Title = "_Item", Action = () => action++ };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 ();
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu, tf);
-
-        Application.Begin (top);
-        Assert.True (tf.HasFocus);
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
-
-        // Right - Edit has no sub menu; this tests that no sub menu shows
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        Assert.Equal (1, menu._selected);
-        Assert.Equal (-1, menu._selectedSub);
-        Assert.Null (menu._openSubMenu);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
-
-        // Right - Format
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output);
-
-        // Left - Edit
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft));
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
-
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft));
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
-
-        Assert.True (Application.RaiseKeyDownEvent (menu.Key));
-        Assert.False (menu.IsMenuOpen);
-        Assert.True (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
-        top.Dispose ();
-    }
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse ()
-    {
-        // File  Edit  Format
-        //┌──────┐    ┌───────┐         
-        //│ New  │    │ Wrap  │         
-        //└──────┘    └───────┘         
+        Assert.False (menuBar.IsActive());
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.Equal (0, action);
 
 
-        // Define the expected menu
-        var expectedMenu = new ExpectedMenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) }),
-                new ("Edit", new MenuItem [] { }),
-                new (
-                     "Format",
-                     new MenuItem [] { new ("Wrap", "", null) }
-                    )
-            ]
-        };
+        Assert.Equal(Key.I, menuItem.HotKey);
+        Application.RaiseKeyDownEvent (Key.I);
+        Assert.Equal (1, action);
+        Assert.False (menuBar.IsActive ());
 
 
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     expectedMenu.Menus [0].Title,
-                     new MenuItem [] { new (expectedMenu.Menus [0].Children [0].Title, "", null) }
-                    ),
-                new (expectedMenu.Menus [1].Title, new MenuItem [] { }),
-                new (
-                     expectedMenu.Menus [2].Title,
-                     new MenuItem []
-                     {
-                         new (
-                              expectedMenu.Menus [2].Children [0].Title,
-                              "",
-                              null
-                             )
-                     }
-                    )
-            ]
-        };
+        menuItem.Title = "_Foo";
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.True (menuBar.IsActive ());
+        Application.RaiseKeyDownEvent (Key.I);
+        Assert.Equal (1, action);
+        Assert.True (menuBar.IsActive ());
 
 
-        var tf = new TextField { Y = 2, Width = 10 };
-        var top = new Toplevel ();
-        top.Add (menu, tf);
-        Application.Begin (top);
-
-        Assert.True (tf.HasFocus);
-        Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (8, 0), Flags = MouseFlags.ReportMousePosition, View = menu }
-                                        )
-                    );
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (15, 0), Flags = MouseFlags.ReportMousePosition, View = menu }
-                                        )
-                    );
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output);
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (8, 0), Flags = MouseFlags.ReportMousePosition, View = menu }
-                                        )
-                    );
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (1, 0), Flags = MouseFlags.ReportMousePosition, View = menu }
-                                        )
-                    );
-        Assert.True (menu.IsMenuOpen);
-        Assert.False (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
-
-        Assert.True (menu.NewMouseEvent (new () { Position = new (8, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
-        Assert.False (menu.IsMenuOpen);
-        Assert.True (tf.HasFocus);
-        View.SetClipToScreen ();
-        top.Draw ();
-        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
-        top.Dispose ();
-    }
+        Application.RaiseKeyDownEvent (Key.F);
+        Assert.Equal (2, action);
 
 
-    [Fact]
-    public void RemoveAndThenAddMenuBar_ShouldNotChangeWidth ()
-    {
-        MenuBar menuBar;
-        MenuBar menuBar2;
-
-        // TODO: When https: //github.com/gui-cs/Terminal.Gui/issues/3136 is fixed, 
-        // TODO: Change this to Window
-        var w = new View ();
-        menuBar2 = new ();
-        menuBar = new ();
-        w.Width = Dim.Fill ();
-        w.Height = Dim.Fill ();
-        w.X = 0;
-        w.Y = 0;
-
-        w.Visible = true;
-
-        // TODO: When https: //github.com/gui-cs/Terminal.Gui/issues/3136 is fixed, 
-        // TODO: uncomment this.
-        //w.Modal = false;
-        w.Title = "";
-        menuBar.Width = Dim.Fill ();
-        menuBar.Height = 1;
-        menuBar.X = 0;
-        menuBar.Y = 0;
-        menuBar.Visible = true;
-        w.Add (menuBar);
-
-        menuBar2.Width = Dim.Fill ();
-        menuBar2.Height = 1;
-        menuBar2.X = 0;
-        menuBar2.Y = 4;
-        menuBar2.Visible = true;
-        w.Add (menuBar2);
-
-        MenuBar [] menuBars = w.SubViews.OfType<MenuBar> ().ToArray ();
-        Assert.Equal (2, menuBars.Length);
-
-        Assert.Equal (Dim.Fill (), menuBars [0].Width);
-        Assert.Equal (Dim.Fill (), menuBars [1].Width);
-
-        // Goes wrong here
-        w.Remove (menuBar);
-        w.Remove (menuBar2);
-
-        w.Add (menuBar);
-        w.Add (menuBar2);
-
-        // These assertions fail
-        Assert.Equal (Dim.Fill (), menuBars [0].Width);
-        Assert.Equal (Dim.Fill (), menuBars [1].Width);
+        Application.End (rs);
+        top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
-    [AutoInitShutdown]
-    public void Resizing_Close_Menus ()
-    {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "File",
-                     new MenuItem []
-                     {
-                         new (
-                              "Open",
-                              "Open a file",
-                              () => { },
-                              null,
-                              null,
-                              KeyCode.CtrlMask | KeyCode.O
-                             )
-                     }
-                    )
-            ]
-        };
+    [AutoInitShutdown (configLocation: ConfigLocations.Default)]
+    public void Disabled_MenuBar_Is_Not_Activated ()
+    {
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
+        top.Add (menuBar);
         RunState rs = Application.Begin (top);
         RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-        menu.OpenMenu ();
-        var firstIteration = false;
-        Application.RunIteration (ref rs, firstIteration);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
- File                         
-┌────────────────────────────┐
-│ Open   Open a file  Ctrl+O │
-└────────────────────────────┘",
-                                                      output
-                                                     );
-
-        ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
-        firstIteration = false;
-        Application.RunIteration (ref rs, firstIteration);
-
-        DriverAssert.AssertDriverContentsWithFrameAre (
-                                                      @"
- File",
-                                                      output
-                                                     );
+        // Act
+        menuBar.Enabled = false;
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBarItem.PopoverMenu.Visible);
 
 
         Application.End (rs);
         Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
-    public void Separator_Does_Not_Throws_Pressing_Menu_Hotkey ()
-    {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "File",
-                     new MenuItem [] { new ("_New", "", null), null, new ("_Quit", "", null) }
-                    )
-            ]
-        };
-        Assert.False (menu.NewKeyDownEvent (Key.Q.WithAlt));
-    }
-
-    [Fact]
-    public void SetMenus_With_Same_HotKey_Does_Not_Throws ()
-    {
-        var mb = new MenuBar ();
-
-        var i1 = new MenuBarItem ("_heey", "fff", () => { }, () => true);
-
-        mb.Menus = new [] { i1 };
-        mb.Menus = new [] { i1 };
-
-        Assert.Equal (Key.H, mb.Menus [0].HotKey);
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void ShortCut_Activates ()
-    {
-        var saveAction = false;
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "_File",
-                     new MenuItem []
-                     {
-                         new (
-                              "_Save",
-                              "Saves the file.",
-                              () => { saveAction = true; },
-                              null,
-                              null,
-                              (KeyCode)Key.S.WithCtrl
-                             )
-                     }
-                    )
-            ]
-        };
-
+    [AutoInitShutdown (configLocation: ConfigLocations.Default)]
+    public void Disabled_MenuBarItem_Is_Not_Activated ()
+    {
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-        Application.RaiseKeyDownEvent (Key.S.WithCtrl);
-        Application.MainLoop.RunIteration ();
+        // Act
+        menuBarItem.Enabled = false;
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBarItem.PopoverMenu.Visible);
 
 
-        Assert.True (saveAction);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
-    [Fact]
-    public void Update_ShortcutKey_KeyBindings_Old_ShortcutKey_Is_Removed ()
-    {
-        var menuBar = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "_File",
-                     new MenuItem []
-                     {
-                         new ("New", "Create New", null, null, null, Key.A.WithCtrl)
-                     }
-                    )
-            ]
-        };
-
-        Assert.True (menuBar.HotKeyBindings.TryGet (Key.A.WithCtrl, out _));
-
-        menuBar.Menus [0].Children! [0].ShortcutKey = Key.B.WithCtrl;
-
-        Assert.False (menuBar.HotKeyBindings.TryGet (Key.A.WithCtrl, out _));
-        Assert.True (menuBar.HotKeyBindings.TryGet (Key.B.WithCtrl, out _));
-    }
 
 
     [Fact]
     [Fact]
-    public void UseKeysUpDownAsKeysLeftRight_And_UseSubMenusSingleFrame_Cannot_Be_Both_True ()
-    {
-        var menu = new MenuBar ();
-        Assert.False (menu.UseKeysUpDownAsKeysLeftRight);
-        Assert.False (menu.UseSubMenusSingleFrame);
-
-        menu.UseKeysUpDownAsKeysLeftRight = true;
-        Assert.True (menu.UseKeysUpDownAsKeysLeftRight);
-        Assert.False (menu.UseSubMenusSingleFrame);
-
-        menu.UseSubMenusSingleFrame = true;
-        Assert.False (menu.UseKeysUpDownAsKeysLeftRight);
-        Assert.True (menu.UseSubMenusSingleFrame);
-    }
-
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
-    [AutoInitShutdown]
-    public void UseSubMenusSingleFrame_False_By_Keyboard ()
-    {
-        var menu = new MenuBar
-        {
-            Menus = new MenuBarItem []
-            {
-                new (
-                     "Numbers",
-                     new MenuItem []
-                     {
-                         new ("One", "", null),
-                         new MenuBarItem (
-                                          "Two",
-                                          new MenuItem []
-                                          {
-                                              new ("Sub-Menu 1", "", null),
-                                              new ("Sub-Menu 2", "", null)
-                                          }
-                                         ),
-                         new ("Three", "", null)
-                     }
-                    )
-            }
-        };
-        menu.UseKeysUpDownAsKeysLeftRight = true;
+    [AutoInitShutdown (configLocation: ConfigLocations.Default)]
+    public void Disabled_MenuBarItem_Popover_Is_Activated ()
+    {
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
-        Assert.False (menu.UseSubMenusSingleFrame);
-
-        top.Draw ();
-
-        var expected = @"
- Numbers
-";
-
-        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        top.Draw ();
-
-        expected = @"
- Numbers  
-┌────────┐
-│ One    │
-│ Two   ►│
-│ Three  │
-└────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorDown));
-        top.Draw ();
-
-        expected = @"
- Numbers                 
-┌────────┐               
-│ One    │               
-│ Two   ►│┌─────────────┐
-│ Three  ││ Sub-Menu 1  │
-└────────┘│ Sub-Menu 2  │
-          └─────────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        Assert.True (Application.Top.SubViews.ElementAt (2).NewKeyDownEvent (Key.CursorLeft));
-        top.Draw ();
-
-        expected = @"
- Numbers  
-┌────────┐
-│ One    │
-│ Two   ►│
-│ Three  │
-└────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.Esc));
-        top.Draw ();
-
-        expected = @"
- Numbers
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        top.Dispose ();
-    }
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
-    [AutoInitShutdown]
-    public void UseSubMenusSingleFrame_False_By_Mouse ()
-    {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "Numbers",
-                     new MenuItem []
-                     {
-                         new ("One", "", null),
-                         new MenuBarItem (
-                                          "Two",
-                                          new MenuItem []
-                                          {
-                                              new (
-                                                   "Sub-Menu 1",
-                                                   "",
-                                                   null
-                                                  ),
-                                              new (
-                                                   "Sub-Menu 2",
-                                                   "",
-                                                   null
-                                                  )
-                                          }
-                                         ),
-                         new ("Three", "", null)
-                     }
-                    )
-            ]
-        };
+        // Act
+        menuBarItem.PopoverMenu.Enabled = false;
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.True (menuBar.IsActive ());
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBarItem.PopoverMenu.Visible);
 
 
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
-        Assert.False (menu.UseSubMenusSingleFrame);
-
-        top.Draw ();
-
-        var expected = @"
- Numbers
-";
-
-        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 1), pos);
-
-        menu.NewMouseEvent (
-                            new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }
-                           );
-        top.Draw ();
-
-        expected = @"
- Numbers  
-┌────────┐
-│ One    │
-│ Two   ►│
-│ Three  │
-└────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 10, 6), pos);
-
-        menu.NewMouseEvent (
-                            new ()
-                            {
-                                Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = Application.Top.SubViews.ElementAt (1)
-                            }
-                           );
-        top.Draw ();
-
-        expected = @"
- Numbers                 
-┌────────┐               
-│ One    │               
-│ Two   ►│┌─────────────┐
-│ Three  ││ Sub-Menu 1  │
-└────────┘│ Sub-Menu 2  │
-          └─────────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 25, 7), pos);
-
-        Assert.False (
-                      menu.NewMouseEvent (
-                                          new ()
-                                          {
-                                              Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = Application.Top.SubViews.ElementAt (1)
-                                          }
-                                         )
-                     );
-        top.Draw ();
-
-        expected = @"
- Numbers  
-┌────────┐
-│ One    │
-│ Two   ►│
-│ Three  │
-└────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 10, 6), pos);
-
-        menu.NewMouseEvent (
-                            new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top }
-                           );
-        top.Draw ();
-
-        expected = @"
- Numbers
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 1), pos);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
-    [AutoInitShutdown]
-    public void UseSubMenusSingleFrame_False_Disabled_Border ()
-    {
-        var menu = new MenuBar
-        {
-            MenusBorderStyle = LineStyle.None,
-            Menus =
-            [
-                new (
-                     "Numbers",
-                     new MenuItem []
-                     {
-                         new ("One", "", null),
-                         new MenuBarItem (
-                                          "Two",
-                                          new MenuItem []
-                                          {
-                                              new (
-                                                   "Sub-Menu 1",
-                                                   "",
-                                                   null
-                                                  ),
-                                              new (
-                                                   "Sub-Menu 2",
-                                                   "",
-                                                   null
-                                                  )
-                                          }
-                                         ),
-                         new ("Three", "", null)
-                     }
-                    )
-            ]
-        };
-
-        menu.UseKeysUpDownAsKeysLeftRight = true;
-        menu.BeginInit ();
-        menu.EndInit ();
-
-        menu.OpenMenu ();
-        menu.ColorScheme = menu._openMenu.ColorScheme = new (Attribute.Default);
-        Assert.True (menu.IsMenuOpen);
-
-        menu.Draw ();
-        menu._openMenu.Draw ();
-
-        var expected = @"
- Numbers
- One    
- Two   ►
- Three  ";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorDown));
-        menu.Draw ();
-        menu._openMenu.Draw ();
-        menu.OpenCurrentMenu.Draw ();
-
-        expected = @"
- Numbers           
- One               
- Two   ► Sub-Menu 1
- Three   Sub-Menu 2";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-    }
-
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [Fact (Skip = "For v2, should the menu close on resize?")]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void UseSubMenusSingleFrame_True_By_Keyboard ()
+    public void Resizing_Closes_Menus ()
     {
     {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "Numbers",
-                     new MenuItem []
-                     {
-                         new ("One", "", null),
-                         new MenuBarItem (
-                                          "Two",
-                                          new MenuItem []
-                                          {
-                                              new (
-                                                   "Sub-Menu 1",
-                                                   "",
-                                                   null
-                                                  ),
-                                              new (
-                                                   "Sub-Menu 2",
-                                                   "",
-                                                   null
-                                                  )
-                                          }
-                                         ),
-                         new ("Three", "", null)
-                     }
-                    )
-            ]
-        };
 
 
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
-        Assert.False (menu.UseSubMenusSingleFrame);
-        menu.UseSubMenusSingleFrame = true;
-        Assert.True (menu.UseSubMenusSingleFrame);
-
-        top.Draw ();
-
-        var expected = @"
- Numbers
-";
-
-        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 1), pos);
-
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        top.Draw ();
-
-        expected = @"
- Numbers  
-┌────────┐
-│ One    │
-│ Two   ►│
-│ Three  │
-└────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 10, 6), pos);
-
-        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorDown));
-        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.Enter));
-        top.Draw ();
-
-        expected = @"
- Numbers       
-┌─────────────┐
-│◄    Two     │
-├─────────────┤
-│ Sub-Menu 1  │
-│ Sub-Menu 2  │
-└─────────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 15, 7), pos);
-
-        Assert.True (Application.Top.SubViews.ElementAt (2).NewKeyDownEvent (Key.Enter));
-        top.Draw ();
-
-        expected = @"
- Numbers  
-┌────────┐
-│ One    │
-│ Two   ►│
-│ Three  │
-└────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 10, 6), pos);
-
-        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.Esc));
-        top.Draw ();
-
-        expected = @"
- Numbers
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 1), pos);
-        top.Dispose ();
     }
     }
 
 
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void UseSubMenusSingleFrame_True_By_Mouse ()
+    public void Update_MenuBarItem_HotKey_Works ()
     {
     {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "Numbers",
-                     new MenuItem []
-                     {
-                         new ("One", "", null),
-                         new MenuBarItem (
-                                          "Two",
-                                          new MenuItem []
-                                          {
-                                              new (
-                                                   "Sub-Menu 1",
-                                                   "",
-                                                   null
-                                                  ),
-                                              new (
-                                                   "Sub-Menu 2",
-                                                   "",
-                                                   null
-                                                  )
-                                          }
-                                         ),
-                         new ("Three", "", null)
-                     }
-                    )
-            ]
-        };
-
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
-        Assert.False (menu.UseSubMenusSingleFrame);
-        menu.UseSubMenusSingleFrame = true;
-        Assert.True (menu.UseSubMenusSingleFrame);
-
-        top.Draw ();
-
-        var expected = @"
- Numbers
-";
-
-        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 1), pos);
-
-        Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
-        top.Draw ();
-
-        expected = @"
- Numbers  
-┌────────┐
-│ One    │
-│ Two   ►│
-│ Three  │
-└────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 10, 6), pos);
-
-        Assert.False (menu.NewMouseEvent (new () { Position = new (1, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top.SubViews.ElementAt (1) }));
-        top.Draw ();
-
-        expected = @"
- Numbers       
-┌─────────────┐
-│◄    Two     │
-├─────────────┤
-│ Sub-Menu 1  │
-│ Sub-Menu 2  │
-└─────────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 15, 7), pos);
-
-        menu.NewMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Clicked, View = Application.Top.SubViews.ElementAt (2) });
-        top.Draw ();
-
-        expected = @"
- Numbers  
-┌────────┐
-│ One    │
-│ Two   ►│
-│ Three  │
-└────────┘
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 10, 6), pos);
-
-        Assert.False (menu.NewMouseEvent (new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top }));
-        top.Draw ();
-
-        expected = @"
- Numbers
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 1), pos);
-        top.Dispose ();
-    }
-
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
-    [AutoInitShutdown]
-    public void UseSubMenusSingleFrame_True_Disabled_Border ()
-    {
-        var menu = new MenuBar
-        {
-            MenusBorderStyle = LineStyle.None,
-            Menus =
-            [
-                new (
-                     "Numbers",
-                     new MenuItem []
-                     {
-                         new ("One", "", null),
-                         new MenuBarItem (
-                                          "Two",
-                                          new MenuItem []
-                                          {
-                                              new (
-                                                   "Sub-Menu 1",
-                                                   "",
-                                                   null
-                                                  ),
-                                              new (
-                                                   "Sub-Menu 2",
-                                                   "",
-                                                   null
-                                                  )
-                                          }
-                                         ),
-                         new ("Three", "", null)
-                     }
-                    )
-            ]
-        };
-
-        menu.UseSubMenusSingleFrame = true;
-        menu.BeginInit ();
-        menu.EndInit ();
-
-        menu.OpenMenu ();
-        Assert.True (menu.IsMenuOpen);
-
-        menu.Draw ();
-        menu.ColorScheme = menu._openMenu.ColorScheme = new (Attribute.Default);
-        menu._openMenu.Draw ();
-
-        var expected = @"
- Numbers
- One    
- Two   ►
- Three  ";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorDown));
-        Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter));
-        menu.Draw ();
-        menu._openMenu.Draw ();
-        menu.OpenCurrentMenu.Draw ();
-
-        expected = @"
- Numbers     
-◄    Two     
-─────────────
- Sub-Menu 1  
- Sub-Menu 2  ";
-
-        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-    }
-
-    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
-    [AutoInitShutdown]
-    public void UseSubMenusSingleFrame_True_Without_Border ()
-    {
-        var menu = new MenuBar
-        {
-            UseSubMenusSingleFrame = true,
-            MenusBorderStyle = LineStyle.None,
-            Menus =
-            [
-                new (
-                     "Numbers",
-                     new MenuItem []
-                     {
-                         new ("One", "", null),
-                         new MenuBarItem (
-                                          "Two",
-                                          new MenuItem []
-                                          {
-                                              new (
-                                                   "Sub-Menu 1",
-                                                   "",
-                                                   null
-                                                  ),
-                                              new (
-                                                   "Sub-Menu 2",
-                                                   "",
-                                                   null
-                                                  )
-                                          }
-                                         ),
-                         new ("Three", "", null)
-                     }
-                    )
-            ]
-        };
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
+
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBar.HasFocus);
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+        Assert.True (menuBarItem.PopoverMenu.HasFocus);
+
+        // Act
+        menuBarItem.HotKey = Key.E.WithAlt;
+
+        // old key should do nothing
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBar.HasFocus);
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+        Assert.True (menuBarItem.PopoverMenu.HasFocus);
+
+        // use new key
+        Application.RaiseKeyDownEvent (Key.E.WithAlt);
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBar.HasFocus);
+        Assert.False (menuBar.CanFocus);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.HasFocus);
 
 
-        var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
-        Assert.True (menu.UseSubMenusSingleFrame);
-        Assert.Equal (LineStyle.None, menu.MenusBorderStyle);
-
-        top.Draw ();
-
-        var expected = @"
- Numbers
-";
-
-        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 1), pos);
-
-        Assert.True (
-                     menu.NewMouseEvent (
-                                         new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }
-                                        )
-                    );
-        top.Draw ();
-
-        expected = @"
- Numbers
- One    
- Two   ►
- Three  
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 4), pos);
-
-        menu.NewMouseEvent (
-                            new () { Position = new (1, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top.SubViews.ElementAt (1) }
-                           );
-        top.Draw ();
-
-        expected = @"
- Numbers     
-◄    Two     
-─────────────
- Sub-Menu 1  
- Sub-Menu 2  
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 13, 5), pos);
-
-        menu.NewMouseEvent (
-                            new () { Position = new (1, 1), Flags = MouseFlags.Button1Clicked, View = Application.Top.SubViews.ElementAt (2) }
-                           );
-        top.Draw ();
-
-        expected = @"
- Numbers
- One    
- Two   ►
- Three  
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 4), pos);
-
-        menu.NewMouseEvent (
-                            new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top }
-                           );
-        top.Draw ();
-
-        expected = @"
- Numbers
-";
-
-        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (1, 0, 8, 1), pos);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void Visible_False_Key_Does_Not_Open_And_Close_All_Opened_Menus ()
+    public void Visible_False_HotKey_Does_Not_Activate ()
     {
     {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) })
-            ]
-        };
+        // Arrange
+        var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
-
-        Assert.True (menu.Visible);
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
-
-        menu.Visible = false;
-        Assert.False (menu.IsMenuOpen);
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
+
+        // Act
+        menuBar.Visible = false;
+        Application.RaiseKeyDownEvent (Key.N.WithAlt);
+        Assert.False (menuBar.IsActive ());
+        Assert.False (menuBar.IsOpen ());
+        Assert.False (menuBar.HasFocus);
+        Assert.False (menuBar.CanFocus);
+        Assert.False (menuBarItem.PopoverMenu.Visible);
+        Assert.False (menuBarItem.PopoverMenu.HasFocus);
 
 
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.False (menu.IsMenuOpen);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void CanFocus_True_Key_Esc_Exit_Toplevel_If_IsMenuOpen_False ()
+    public void Visible_False_MenuItem_Key_Does_Action ()
     {
     {
-        var menu = new MenuBar
+        // Arrange
+        int action = 0;
+        var menuItem = new MenuItemv2 ()
         {
         {
-            Menus =
-            [
-                new ("File", new MenuItem [] { new ("New", "", null) })
-            ],
-            CanFocus = true
+            Id = "menuItem",
+            Title = "_Item",
+            Key = Key.F1,
+            Action = () => action++
         };
         };
+        var menu = new Menuv2 ([menuItem]) { Id = "menu" };
+        var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" };
+        var menuBarItemPopover = new PopoverMenu ();
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+        var menuBar = new MenuBarv2 () { Id = "menuBar" };
+        menuBar.Add (menuBarItem);
+        Assert.Single (menuBar.SubViews);
+        Assert.Single (menuBarItem.SubViews);
         var top = new Toplevel ();
         var top = new Toplevel ();
-        top.Add (menu);
-        Application.Begin (top);
+        top.Add (menuBar);
+        RunState rs = Application.Begin (top);
+        Assert.False (menuBar.IsActive ());
 
 
-        Assert.True (menu.CanFocus);
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
-        Assert.True (menu.IsMenuOpen);
+        // Act
+        menuBar.Visible = false;
+        Application.RaiseKeyDownEvent (Key.F1);
 
 
-        Assert.True (menu.NewKeyDownEvent (Key.Esc));
-        Assert.False (menu.IsMenuOpen);
+        Assert.Equal (1, action);
 
 
-        Assert.False (menu.NewKeyDownEvent (Key.Esc));
-        Assert.False (menu.IsMenuOpen);
+        Application.End (rs);
         top.Dispose ();
         top.Dispose ();
     }
     }
-
-    // Defines the expected strings for a Menu. Currently supports 
-    //   - MenuBar with any number of MenuItems 
-    //   - Each top-level MenuItem can have a SINGLE sub-menu
-    //
-    // TODO: Enable multiple sub-menus
-    // TODO: Enable checked sub-menus
-    // TODO: Enable sub-menus with sub-menus (perhaps better to put this in a separate class with focused unit tests?)
-    //
-    // E.g: 
-    //
-    // File  Edit
-    //  New    Copy
-    public class ExpectedMenuBar : MenuBar
-    {
-        private FakeDriver _d = (FakeDriver)Application.Driver;
-
-        // The expected strings when the menu is closed
-        public string ClosedMenuText => MenuBarText + "\n";
-
-        public string ExpectedBottomRow (int i)
-        {
-            return $"{Glyphs.LLCorner}{new (Glyphs.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{Glyphs.LRCorner}  \n";
-        }
-
-        // The 3 spaces at end are a result of Menu.cs line 1062 where `pos` is calculated (` + spacesAfterTitle`)
-        public string ExpectedMenuItemRow (int i) { return $"{Glyphs.VLine} {Menus [i].Children [0].Title}  {Glyphs.VLine}   \n"; }
-
-        // The full expected string for an open sub menu
-        public string ExpectedSubMenuOpen (int i)
-        {
-            return ClosedMenuText
-                   + (Menus [i].Children.Length > 0
-                          ? ExpectedPadding (i)
-                            + ExpectedTopRow (i)
-                            + ExpectedPadding (i)
-                            + ExpectedMenuItemRow (i)
-                            + ExpectedPadding (i)
-                            + ExpectedBottomRow (i)
-                          : "");
-        }
-
-        // Define expected menu frame
-        // "┌──────┐"
-        // "│ New  │"
-        // "└──────┘"
-        // 
-        // The width of the Frame is determined in Menu.cs line 144, where `Width` is calculated
-        //   1 space before the Title and 2 spaces after the Title/Check/Help
-        public string ExpectedTopRow (int i)
-        {
-            return $"{Glyphs.ULCorner}{new (Glyphs.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{Glyphs.URCorner}  \n";
-        }
-
-        // Each MenuBar title has a 1 space pad on each side
-        // See `static int leftPadding` and `static int rightPadding` on line 1037 of Menu.cs
-        public string MenuBarText
-        {
-            get
-            {
-                var txt = string.Empty;
-
-                foreach (MenuBarItem m in Menus)
-                {
-                    txt += " " + m.Title + " ";
-                }
-
-                return txt;
-            }
-        }
-
-        // Padding for the X of the sub menu Frame
-        // Menu.cs - Line 1239 in `internal void OpenMenu` is where the Menu is created
-        private string ExpectedPadding (int i)
-        {
-            var n = 0;
-
-            while (i > 0)
-            {
-                n += Menus [i - 1].TitleLength + 2;
-                i--;
-            }
-
-            return new (' ', n);
-        }
-    }
-
-    private class CustomWindow : Window
-    {
-        public CustomWindow ()
-        {
-            var menu = new MenuBar
-            {
-                Menus =
-                [
-                    new ("File", new MenuItem [] { new ("New", "", null) }),
-                    new (
-                         "Edit",
-                         new MenuItem []
-                         {
-                             new MenuBarItem (
-                                              "Delete",
-                                              new MenuItem []
-                                                  { new ("All", "", null), new ("Selected", "", null) }
-                                             )
-                         }
-                        )
-                ]
-            };
-            Add (menu);
-        }
-    }
 }
 }

+ 3886 - 0
Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs

@@ -0,0 +1,3886 @@
+using UnitTests;
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewsTests;
+
+public class MenuBarv1Tests (ITestOutputHelper output)
+{
+    [Fact]
+    [AutoInitShutdown]
+    public void AddMenuBarItem_RemoveMenuItem_Dynamically ()
+    {
+        var menuBar = new MenuBar ();
+        var menuBarItem = new MenuBarItem { Title = "_New" };
+        var action = "";
+        var menuItem = new MenuItem { Title = "_Item", Action = () => action = "I", Parent = menuBarItem };
+        Assert.Equal ("n", menuBarItem.HotKey);
+        Assert.Equal ("i", menuItem.HotKey);
+        Assert.Empty (menuBar.Menus);
+        menuBarItem.AddMenuBarItem (menuBar, menuItem);
+        menuBar.Menus = [menuBarItem];
+        Assert.Single (menuBar.Menus);
+        Assert.Single (menuBar.Menus [0].Children!);
+
+        Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
+        Assert.False (menuBar.HotKeyBindings.TryGet (Key.I, out _));
+
+        var top = new Toplevel ();
+        top.Add (menuBar);
+        Application.Begin (top);
+
+        top.NewKeyDownEvent (Key.N.WithAlt);
+        Application.MainLoop.RunIteration ();
+        Assert.True (menuBar.IsMenuOpen);
+        Assert.Equal ("", action);
+
+        top.NewKeyDownEvent (Key.I);
+        Application.MainLoop.RunIteration ();
+        Assert.False (menuBar.IsMenuOpen);
+        Assert.Equal ("I", action);
+
+        menuItem.RemoveMenuItem ();
+        Assert.Single (menuBar.Menus);
+        Assert.Null (menuBar.Menus [0].Children);
+        Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
+        Assert.False (menuBar.HotKeyBindings.TryGet (Key.I, out _));
+
+        menuBarItem.RemoveMenuItem ();
+        Assert.Empty (menuBar.Menus);
+        Assert.False (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
+
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void AllowNullChecked_Get_Set ()
+    {
+        var mi = new MenuItem ("Check this out 你", "", null) { CheckType = MenuItemCheckStyle.Checked };
+        mi.Action = mi.ToggleChecked;
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("Nullable Checked", new [] { mi })
+            ]
+        };
+
+        //new CheckBox ();
+        Toplevel top = new ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.False (mi.Checked);
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter));
+        Application.MainLoop.RunIteration ();
+        Assert.True (mi.Checked);
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
+                                        )
+                    );
+
+        Assert.True (
+                     menu._openMenu.NewMouseEvent (
+                                                   new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu }
+                                                  )
+                    );
+        Application.MainLoop.RunIteration ();
+        Assert.False (mi.Checked);
+
+        mi.AllowNullChecked = true;
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter));
+        Application.MainLoop.RunIteration ();
+        Assert.Null (mi.Checked);
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
+                                        )
+                    );
+        Application.LayoutAndDraw ();
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @$"
+ Nullable Checked       
+┌──────────────────────┐
+│ {Glyphs.CheckStateNone} Check this out 你  │
+└──────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (
+                     menu._openMenu.NewMouseEvent (
+                                                   new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu }
+                                                  )
+                    );
+        Application.MainLoop.RunIteration ();
+        Assert.True (mi.Checked);
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter));
+        Application.MainLoop.RunIteration ();
+        Assert.False (mi.Checked);
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
+                                        )
+                    );
+
+        Assert.True (
+                     menu._openMenu.NewMouseEvent (
+                                                   new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu }
+                                                  )
+                    );
+        Application.MainLoop.RunIteration ();
+        Assert.Null (mi.Checked);
+
+        mi.AllowNullChecked = false;
+        Assert.False (mi.Checked);
+
+        mi.CheckType = MenuItemCheckStyle.NoCheck;
+        Assert.Throws<InvalidOperationException> (mi.ToggleChecked);
+
+        mi.CheckType = MenuItemCheckStyle.Radio;
+        Assert.Throws<InvalidOperationException> (mi.ToggleChecked);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void CanExecute_False_Does_Not_Throws ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem []
+                {
+                    new ("New", "", null, () => false),
+                    null,
+                    new ("Quit", "", null)
+                })
+            ]
+        };
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void CanExecute_HotKey ()
+    {
+        Window win = null;
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "_File",
+                     new MenuItem []
+                     {
+                         new ("_New", "", New, CanExecuteNew),
+                         new (
+                              "_Close",
+                              "",
+                              Close,
+                              CanExecuteClose
+                             )
+                     }
+                    )
+            ]
+        };
+        Toplevel top = new ();
+        top.Add (menu);
+
+        bool CanExecuteNew () { return win == null; }
+
+        void New () { win = new (); }
+
+        bool CanExecuteClose () { return win != null; }
+
+        void Close () { win = null; }
+
+        Application.Begin (top);
+
+        Assert.Null (win);
+        Assert.True (CanExecuteNew ());
+        Assert.False (CanExecuteClose ());
+
+        Assert.True (top.NewKeyDownEvent (Key.F.WithAlt));
+        Assert.True (top.NewKeyDownEvent (Key.N.WithAlt));
+        Application.MainLoop.RunIteration ();
+        Assert.NotNull (win);
+        Assert.False (CanExecuteNew ());
+        Assert.True (CanExecuteClose ());
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Click_Another_View_Close_An_Open_Menu ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) })
+            ]
+        };
+
+        var btnClicked = false;
+        var btn = new Button { Y = 4, Text = "Test" };
+        btn.Accepting += (s, e) => btnClicked = true;
+        var top = new Toplevel ();
+        top.Add (menu, btn);
+        Application.Begin (top);
+
+        Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 4), Flags = MouseFlags.Button1Clicked });
+        Assert.True (btnClicked);
+        top.Dispose ();
+    }
+
+    // TODO: Lots of tests in here really test Menu and MenuItem - Move them to MenuTests.cs
+
+    [Fact]
+    public void Constructors_Defaults ()
+    {
+        var menuBar = new MenuBar ();
+        Assert.Equal (KeyCode.F9, menuBar.Key);
+        var menu = new Menu { Host = menuBar, X = 0, Y = 0, BarItems = new () };
+        Assert.Null (menu.ColorScheme);
+        Assert.False (menu.IsInitialized);
+        menu.BeginInit ();
+        menu.EndInit ();
+        Assert.Equal (Colors.ColorSchemes ["Menu"], menu.ColorScheme);
+        Assert.True (menu.CanFocus);
+        Assert.False (menu.WantContinuousButtonPressed);
+        Assert.Equal (LineStyle.Single, menuBar.MenusBorderStyle);
+
+        menuBar = new ();
+        Assert.Equal (0, menuBar.X);
+        Assert.Equal (0, menuBar.Y);
+        Assert.IsType<DimFill> (menuBar.Width);
+        Assert.Equal (1, menuBar.Height);
+        Assert.Empty (menuBar.Menus);
+        Assert.Equal (Colors.ColorSchemes ["Menu"], menuBar.ColorScheme);
+        Assert.True (menuBar.WantMousePositionReports);
+        Assert.False (menuBar.IsMenuOpen);
+
+        menuBar = new () { Menus = [] };
+        Assert.Equal (0, menuBar.X);
+        Assert.Equal (0, menuBar.Y);
+        Assert.IsType<DimFill> (menuBar.Width);
+        Assert.Equal (1, menuBar.Height);
+        Assert.Empty (menuBar.Menus);
+        Assert.Equal (Colors.ColorSchemes ["Menu"], menuBar.ColorScheme);
+        Assert.True (menuBar.WantMousePositionReports);
+        Assert.False (menuBar.IsMenuOpen);
+
+        var menuBarItem = new MenuBarItem ();
+        Assert.Equal ("", menuBarItem.Title);
+        Assert.Null (menuBarItem.Parent);
+        Assert.Empty (menuBarItem.Children);
+
+        menuBarItem = new (new MenuBarItem [] { });
+        Assert.Equal ("", menuBarItem.Title);
+        Assert.Null (menuBarItem.Parent);
+        Assert.Empty (menuBarItem.Children);
+
+        menuBarItem = new ("Test", new MenuBarItem [] { });
+        Assert.Equal ("Test", menuBarItem.Title);
+        Assert.Null (menuBarItem.Parent);
+        Assert.Empty (menuBarItem.Children);
+
+        menuBarItem = new ("Test", new List<MenuItem []> ());
+        Assert.Equal ("Test", menuBarItem.Title);
+        Assert.Null (menuBarItem.Parent);
+        Assert.Empty (menuBarItem.Children);
+
+        menuBarItem = new ("Test", "Help", null);
+        Assert.Equal ("Test", menuBarItem.Title);
+        Assert.Equal ("Help", menuBarItem.Help);
+        Assert.Null (menuBarItem.Action);
+        Assert.Null (menuBarItem.CanExecute);
+        Assert.Null (menuBarItem.Parent);
+        Assert.Equal (Key.Empty, menuBarItem.ShortcutKey);
+    }
+
+    [Fact]
+    [AutoInitShutdown (configLocation: ConfigLocations.Default)]
+    public void Disabled_MenuBar_Is_Never_Opened ()
+    {
+        Toplevel top = new ();
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) })
+            ]
+        };
+        top.Add (menu);
+        Application.Begin (top);
+        Assert.True (menu.Enabled);
+        menu.OpenMenu ();
+        Assert.True (menu.IsMenuOpen);
+
+        menu.Enabled = false;
+        menu.CloseAllMenus ();
+        menu.OpenMenu ();
+        Assert.False (menu.IsMenuOpen);
+        top.Dispose ();
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown (configLocation: ConfigLocations.Default)]
+    public void Disabled_MenuItem_Is_Never_Selected ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "Menu",
+                     new MenuItem []
+                     {
+                         new ("Enabled 1", "", null),
+                         new ("Disabled", "", null, () => false),
+                         null,
+                         new ("Enabled 2", "", null)
+                     }
+                    )
+            ]
+        };
+
+        Toplevel top = new ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Attribute [] attributes =
+        {
+            // 0
+            menu.ColorScheme.Normal,
+
+            // 1
+            menu.ColorScheme.Focus,
+
+            // 2
+            menu.ColorScheme.Disabled
+        };
+
+        DriverAssert.AssertDriverAttributesAre (
+                                               @"
+00000000000000",
+                                               output,
+                                               Application.Driver,
+                                               attributes
+                                              );
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
+                                        )
+                    );
+        top.Draw ();
+
+        DriverAssert.AssertDriverAttributesAre (
+                                               @"
+11111100000000
+00000000000000
+01111111111110
+02222222222220
+00000000000000
+00000000000000
+00000000000000",
+                                               output,
+                                               Application.Driver,
+                                               attributes
+                                              );
+
+        Assert.True (
+                     top.SubViews.ElementAt (1)
+                        .NewMouseEvent (
+                                        new () { Position = new (0, 2), Flags = MouseFlags.Button1Clicked, View = top.SubViews.ElementAt (1) }
+                                       )
+                    );
+        top.SubViews.ElementAt (1).Layout();
+        top.SubViews.ElementAt (1).Draw ();
+
+        DriverAssert.AssertDriverAttributesAre (
+                                               @"
+11111100000000
+00000000000000
+01111111111110
+02222222222220
+00000000000000
+00000000000000
+00000000000000",
+                                               output,
+                                               Application.Driver,
+                                               attributes
+                                              );
+
+        Assert.True (
+                     top.SubViews.ElementAt (1)
+                        .NewMouseEvent (
+                                        new () { Position = new (0, 2), Flags = MouseFlags.ReportMousePosition, View = top.SubViews.ElementAt (1) }
+                                       )
+                    );
+        top.SubViews.ElementAt (1).Draw ();
+
+        DriverAssert.AssertDriverAttributesAre (
+                                               @"
+11111100000000
+00000000000000
+01111111111110
+02222222222220
+00000000000000
+00000000000000
+00000000000000",
+                                               output,
+                                               Application.Driver,
+                                               attributes
+                                              );
+        top.Dispose ();
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void Draw_A_Menu_Over_A_Dialog ()
+    {
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
+
+        Toplevel top = new ();
+        var win = new Window ();
+        top.Add (win);
+        RunState rsTop = Application.Begin (top);
+        ((FakeDriver)Application.Driver!).SetBufferSize (40, 15);
+
+        Assert.Equal (new (0, 0, 40, 15), win.Frame);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        List<string> items = new ()
+        {
+            "New",
+            "Open",
+            "Close",
+            "Save",
+            "Save As",
+            "Delete"
+        };
+        var dialog = new Dialog { X = 2, Y = 2, Width = 15, Height = 4 };
+        var menu = new MenuBar { X = Pos.Center (), Width = 10 };
+
+        menu.Menus = new MenuBarItem []
+        {
+            new (
+                 "File",
+                 new MenuItem []
+                 {
+                     new (
+                          items [0],
+                          "Create a new file",
+                          () => ChangeMenuTitle ("New"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.N
+                         ),
+                     new (
+                          items [1],
+                          "Open a file",
+                          () => ChangeMenuTitle ("Open"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.O
+                         ),
+                     new (
+                          items [2],
+                          "Close a file",
+                          () => ChangeMenuTitle ("Close"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.C
+                         ),
+                     new (
+                          items [3],
+                          "Save a file",
+                          () => ChangeMenuTitle ("Save"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.S
+                         ),
+                     new (
+                          items [4],
+                          "Save a file as",
+                          () => ChangeMenuTitle ("Save As"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.A
+                         ),
+                     new (
+                          items [5],
+                          "Delete a file",
+                          () => ChangeMenuTitle ("Delete"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.A
+                         )
+                 }
+                )
+        };
+        dialog.Add (menu);
+
+        void ChangeMenuTitle (string title)
+        {
+            menu.Menus [0].Title = title;
+            menu.SetNeedsDraw ();
+        }
+
+        RunState rsDialog = Application.Begin (dialog);
+        Application.RunIteration (ref rsDialog);
+
+        Assert.Equal (new (2, 2, 15, 4), dialog.Frame);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│                                      │
+│ ┌─────────────┐                      │
+│ │  File       │                      │
+│ │             │                      │
+│ └─────────────┘                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.Equal ("File", menu.Menus [0].Title);
+        menu.OpenMenu ();
+        Application.RunIteration (ref rsDialog);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│                                      │
+│ ┌─────────────┐                      │
+│ │  File       │                      │
+│ │ ┌──────────────────────────────────┐
+│ └─│ New    Create a new file  Ctrl+N │
+│   │ Open         Open a file  Ctrl+O │
+│   │ Close       Close a file  Ctrl+C │
+│   │ Save         Save a file  Ctrl+S │
+│   │ Save As   Save a file as  Ctrl+A │
+│   │ Delete     Delete a file  Ctrl+A │
+│   └──────────────────────────────────┘
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5), Flags = MouseFlags.Button1Clicked });
+
+        // Need to fool MainLoop into thinking it's running
+        Application.MainLoop.Running = true;
+        bool firstIteration = true;
+        Application.RunIteration (ref rsDialog, firstIteration);
+        Assert.Equal (items [0], menu.Menus [0].Title);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│                                      │
+│ ┌─────────────┐                      │
+│ │  New        │                      │
+│ │             │                      │
+│ └─────────────┘                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        for (var i = 0; i < items.Count; i++)
+        {
+            menu.OpenMenu ();
+
+            Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked });
+
+            Application.RunIteration (ref rsDialog);
+            Assert.Equal (items [i], menu.Menus [0].Title);
+        }
+
+        ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
+        menu.OpenMenu ();
+        Application.RunIteration (ref rsDialog);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────┐
+│                  │
+│ ┌─────────────┐  │
+│ │  Delete     │  │
+│ │ ┌───────────────
+│ └─│ New    Create 
+│   │ Open         O
+│   │ Close       Cl
+│   │ Save         S
+│   │ Save As   Save
+│   │ Delete     Del
+│   └───────────────
+│                  │
+│                  │
+└──────────────────┘",
+                                                      output
+                                                     );
+
+        Application.End (rsDialog);
+        Application.End (rsTop);
+        top.Dispose ();
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void Draw_A_Menu_Over_A_Top_Dialog ()
+    {
+        ((FakeDriver)Application.Driver).SetBufferSize (40, 15);
+
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
+
+        Assert.Equal (new (0, 0, 40, 15), View.GetClip ()!.GetBounds());
+        DriverAssert.AssertDriverContentsWithFrameAre (@"", output);
+
+        List<string> items = new ()
+        {
+            "New",
+            "Open",
+            "Close",
+            "Save",
+            "Save As",
+            "Delete"
+        };
+        var dialog = new Dialog { X = 2, Y = 2, Width = 15, Height = 4 };
+        var menu = new MenuBar { X = Pos.Center (), Width = 10 };
+
+        menu.Menus = new MenuBarItem []
+        {
+            new (
+                 "File",
+                 new MenuItem []
+                 {
+                     new (
+                          items [0],
+                          "Create a new file",
+                          () => ChangeMenuTitle ("New"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.N
+                         ),
+                     new (
+                          items [1],
+                          "Open a file",
+                          () => ChangeMenuTitle ("Open"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.O
+                         ),
+                     new (
+                          items [2],
+                          "Close a file",
+                          () => ChangeMenuTitle ("Close"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.C
+                         ),
+                     new (
+                          items [3],
+                          "Save a file",
+                          () => ChangeMenuTitle ("Save"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.S
+                         ),
+                     new (
+                          items [4],
+                          "Save a file as",
+                          () => ChangeMenuTitle ("Save As"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.A
+                         ),
+                     new (
+                          items [5],
+                          "Delete a file",
+                          () => ChangeMenuTitle ("Delete"),
+                          null,
+                          null,
+                          KeyCode.CtrlMask | KeyCode.A
+                         )
+                 }
+                )
+        };
+        dialog.Add (menu);
+
+        void ChangeMenuTitle (string title)
+        {
+            menu.Menus [0].Title = title;
+            menu.SetNeedsDraw ();
+        }
+
+        RunState rs = Application.Begin (dialog);
+        Application.RunIteration (ref rs);
+
+        Assert.Equal (new (2, 2, 15, 4), dialog.Frame);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+  ┌─────────────┐
+  │  File       │
+  │             │
+  └─────────────┘",
+                                                      output
+                                                     );
+
+        Assert.Equal ("File", menu.Menus [0].Title);
+        menu.OpenMenu ();
+        Application.RunIteration (ref rs);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+  ┌─────────────┐                       
+  │  File       │                       
+  │ ┌──────────────────────────────────┐
+  └─│ New    Create a new file  Ctrl+N │
+    │ Open         Open a file  Ctrl+O │
+    │ Close       Close a file  Ctrl+C │
+    │ Save         Save a file  Ctrl+S │
+    │ Save As   Save a file as  Ctrl+A │
+    │ Delete     Delete a file  Ctrl+A │
+    └──────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5), Flags = MouseFlags.Button1Clicked });
+
+        // Need to fool MainLoop into thinking it's running
+        Application.MainLoop.Running = true;
+        Application.RunIteration (ref rs);
+        Assert.Equal (items [0], menu.Menus [0].Title);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+  ┌─────────────┐
+  │  New        │
+  │             │
+  └─────────────┘",
+                                                      output
+                                                     );
+
+        for (var i = 1; i < items.Count; i++)
+        {
+            menu.OpenMenu ();
+
+            Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked });
+
+            Application.RunIteration (ref rs);
+            Assert.Equal (items [i], menu.Menus [0].Title);
+        }
+
+        ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
+        menu.OpenMenu ();
+        Application.RunIteration (ref rs);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+  ┌─────────────┐   
+  │  Delete     │   
+  │ ┌───────────────
+  └─│ New    Create 
+    │ Open         O
+    │ Close       Cl
+    │ Save         S
+    │ Save As   Save
+    │ Delete     Del
+    └───────────────",
+                                                      output
+                                                     );
+
+        Application.End (rs);
+        dialog.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void DrawFrame_With_Negative_Positions ()
+    {
+        var menu = new MenuBar
+        {
+            X = -1,
+            Y = -1,
+            Menus =
+            [
+                new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) })
+            ]
+        };
+        menu.Layout ();
+
+        Assert.Equal (new (-1, -1), new Point (menu.Frame.X, menu.Frame.Y));
+
+        Toplevel top = new ();
+        Application.Begin (top);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        var expected = @"
+──────┐
+ One  │
+ Two  │
+──────┘
+";
+
+        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (0, 0, 7, 4), pos);
+
+        menu.CloseAllMenus ();
+        menu.Frame = new (-1, -2, menu.Frame.Width, menu.Frame.Height);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        expected = @"
+ One  │
+ Two  │
+──────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 7, 3), pos);
+
+        menu.CloseAllMenus ();
+        menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height);
+        ((FakeDriver)Application.Driver!).SetBufferSize (7, 5);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        expected = @"
+┌──────
+│ One  
+│ Two  
+└──────
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (0, 1, 7, 4), pos);
+
+        menu.CloseAllMenus ();
+        menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height);
+        ((FakeDriver)Application.Driver!).SetBufferSize (7, 3);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        expected = @"
+┌──────
+│ One  
+│ Two  
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (0, 0, 7, 3), pos);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void DrawFrame_With_Negative_Positions_Disabled_Border ()
+    {
+        var menu = new MenuBar
+        {
+            X = -2,
+            Y = -1,
+            MenusBorderStyle = LineStyle.None,
+            Menus =
+            [
+                new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) })
+            ]
+        };
+        menu.Layout ();
+
+        Assert.Equal (new (-2, -1), new Point (menu.Frame.X, menu.Frame.Y));
+
+        Toplevel top = new ();
+        Application.Begin (top);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        var expected = @"
+ne
+wo
+";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        menu.CloseAllMenus ();
+        menu.Frame = new (-2, -2, menu.Frame.Width, menu.Frame.Height);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        expected = @"
+wo
+";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        menu.CloseAllMenus ();
+        menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height);
+        ((FakeDriver)Application.Driver!).SetBufferSize (3, 2);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        expected = @"
+ On
+ Tw
+";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        menu.CloseAllMenus ();
+        menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height);
+        ((FakeDriver)Application.Driver!).SetBufferSize (3, 1);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        expected = @"
+ On
+";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void DrawFrame_With_Positive_Positions ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) })
+            ]
+        };
+
+        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
+
+        Toplevel top = new ();
+        Application.Begin (top);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        var expected = @"
+┌──────┐
+│ One  │
+│ Two  │
+└──────┘
+";
+
+        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (0, 1, 8, 4), pos);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void DrawFrame_With_Positive_Positions_Disabled_Border ()
+    {
+        var menu = new MenuBar
+        {
+            MenusBorderStyle = LineStyle.None,
+            Menus =
+            [
+                new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) })
+            ]
+        };
+
+        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
+
+        Toplevel top = new ();
+        Application.Begin (top);
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        var expected = @"
+ One
+ Two
+";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        top.Dispose ();
+    }
+
+    [Fact]
+    public void Exceptions ()
+    {
+        Assert.Throws<ArgumentNullException> (() => new MenuBarItem ("Test", (MenuItem [])null));
+        Assert.Throws<ArgumentNullException> (() => new MenuBarItem ("Test", (List<MenuItem []>)null));
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void HotKey_MenuBar_OnKeyDown_OnKeyUp_ProcessKeyPressed ()
+    {
+        var newAction = false;
+        var copyAction = false;
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("_File", new MenuItem [] { new ("_New", "", () => newAction = true) }),
+                new (
+                     "_Edit",
+                     new MenuItem [] { new ("_Copy", "", () => copyAction = true) }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.False (newAction);
+        Assert.False (copyAction);
+
+#if SUPPORT_ALT_TO_ACTIVATE_MENU
+        Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask)));
+        Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask)));
+        Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask)));
+        Assert.True (menu.IsMenuOpen);
+        Application.Top.Draw ();
+
+        string expected = @"
+ File  Edit
+";
+
+        var pos = DriverAsserts.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 11, 1), pos);
+
+        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.N)));
+        Application.MainLoop.RunIteration ();
+        Assert.False (newAction); // not yet, hot keys don't work if the item is not visible
+
+        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.F)));
+        Application.MainLoop.RunIteration ();
+        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.N)));
+        Application.MainLoop.RunIteration ();
+        Assert.True (newAction);
+        Application.Top.Draw ();
+
+        expected = @"
+ File  Edit
+";
+
+        Assert.False (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.AltMask)));
+        Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask)));
+        Assert.True (Application.Top.ProcessKeyUp (new KeyEventArgs (Key.AltMask)));
+        Assert.True (menu.IsMenuOpen);
+        Application.Top.Draw ();
+
+        expected = @"
+ File  Edit
+";
+
+        pos = DriverAsserts.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 11, 1), pos);
+
+        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.CursorRight)));
+        Assert.True (Application.Top.ProcessKeyDown (new KeyEventArgs (Key.C)));
+        Application.MainLoop.RunIteration ();
+        Assert.True (copyAction);
+#endif
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void HotKey_MenuBar_ProcessKeyPressed_Menu_ProcessKey ()
+    {
+        var newAction = false;
+        var copyAction = false;
+
+        // Define the expected menu
+        var expectedMenu = new ExpectedMenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) }),
+                new (
+                     "Edit",
+                     new MenuItem [] { new ("Copy", "", null) }
+                    )
+            ]
+        };
+
+        // The real menu
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "_" + expectedMenu.Menus [0].Title,
+                     new MenuItem []
+                     {
+                         new (
+                              "_" + expectedMenu.Menus [0].Children [0].Title,
+                              "",
+                              () => newAction = true
+                             )
+                     }
+                    ),
+                new (
+                     "_" + expectedMenu.Menus [1].Title,
+                     new MenuItem []
+                     {
+                         new (
+                              "_"
+                              + expectedMenu.Menus [1]
+                                            .Children [0]
+                                            .Title,
+                              "",
+                              () => copyAction = true
+                             )
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.False (newAction);
+        Assert.False (copyAction);
+
+        Assert.True (menu.NewKeyDownEvent (Key.F.WithAlt));
+        Assert.True (menu.IsMenuOpen);
+        Application.Top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
+
+        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.N));
+        Application.MainLoop.RunIteration ();
+        Assert.True (newAction);
+
+        Assert.True (menu.NewKeyDownEvent (Key.E.WithAlt));
+        Assert.True (menu.IsMenuOpen);
+        Application.Top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
+
+        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.C));
+        Application.MainLoop.RunIteration ();
+        Assert.True (copyAction);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Key_Open_And_Close_The_MenuBar ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) })
+            ]
+        };
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.True (top.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+        Assert.True (top.NewKeyDownEvent (menu.Key));
+        Assert.False (menu.IsMenuOpen);
+
+        menu.Key = Key.F10.WithShift;
+        Assert.False (top.NewKeyDownEvent (Key.F9));
+        Assert.False (menu.IsMenuOpen);
+
+        Assert.True (top.NewKeyDownEvent (Key.F10.WithShift));
+        Assert.True (menu.IsMenuOpen);
+        Assert.True (top.NewKeyDownEvent (Key.F10.WithShift));
+        Assert.False (menu.IsMenuOpen);
+        top.Dispose ();
+    }
+
+    [Theory]
+    [AutoInitShutdown]
+    [InlineData ("_File", "_New", "", KeyCode.Space | KeyCode.CtrlMask)]
+    [InlineData ("Closed", "None", "", KeyCode.Space | KeyCode.CtrlMask, KeyCode.Space | KeyCode.CtrlMask)]
+    [InlineData ("_File", "_New", "", KeyCode.F9)]
+    [InlineData ("Closed", "None", "", KeyCode.F9, KeyCode.F9)]
+    [InlineData ("_File", "_Open", "", KeyCode.F9, KeyCode.CursorDown)]
+    [InlineData ("_File", "_Save", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorDown)]
+    [InlineData ("_File", "_Quit", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorDown, KeyCode.CursorDown)]
+    [InlineData (
+                    "_File",
+                    "_New",
+                    "",
+                    KeyCode.F9,
+                    KeyCode.CursorDown,
+                    KeyCode.CursorDown,
+                    KeyCode.CursorDown,
+                    KeyCode.CursorDown
+                )]
+    [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorUp)]
+    [InlineData ("_File", "_Quit", "", KeyCode.F9, KeyCode.CursorUp)]
+    [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorUp, KeyCode.CursorDown)]
+    [InlineData ("Closed", "None", "Open", KeyCode.F9, KeyCode.CursorDown, KeyCode.Enter)]
+    [InlineData ("_Edit", "_Copy", "", KeyCode.F9, KeyCode.CursorRight)]
+    [InlineData ("_About", "_About", "", KeyCode.F9, KeyCode.CursorLeft)]
+    [InlineData ("_Edit", "_Copy", "", KeyCode.F9, KeyCode.CursorLeft, KeyCode.CursorLeft)]
+    [InlineData ("_Edit", "_Select All", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorUp)]
+    [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorDown, KeyCode.CursorLeft)]
+    [InlineData ("_About", "_About", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight)]
+    [InlineData ("Closed", "None", "New", KeyCode.F9, KeyCode.Enter)]
+    [InlineData ("Closed", "None", "Quit", KeyCode.F9, KeyCode.CursorUp, KeyCode.Enter)]
+    [InlineData ("Closed", "None", "Copy", KeyCode.F9, KeyCode.CursorRight, KeyCode.Enter)]
+    [InlineData (
+                    "Closed",
+                    "None",
+                    "Find",
+                    KeyCode.F9,
+                    KeyCode.CursorRight,
+                    KeyCode.CursorUp,
+                    KeyCode.CursorUp,
+                    KeyCode.Enter
+                )]
+    [InlineData (
+                    "Closed",
+                    "None",
+                    "Replace",
+                    KeyCode.F9,
+                    KeyCode.CursorRight,
+                    KeyCode.CursorUp,
+                    KeyCode.CursorUp,
+                    KeyCode.CursorDown,
+                    KeyCode.Enter
+                )]
+    [InlineData (
+                    "_Edit",
+                    "F_ind",
+                    "",
+                    KeyCode.F9,
+                    KeyCode.CursorRight,
+                    KeyCode.CursorUp,
+                    KeyCode.CursorUp,
+                    KeyCode.CursorLeft,
+                    KeyCode.Enter
+                )]
+    [InlineData ("Closed", "None", "About", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight, KeyCode.Enter)]
+
+    //// Hotkeys
+    [InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F)]
+    [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.ShiftMask | KeyCode.F)]
+    [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.Esc)]
+    [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.AltMask | KeyCode.F)]
+    [InlineData ("Closed", "None", "Open", KeyCode.AltMask | KeyCode.F, KeyCode.O)]
+    [InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F, KeyCode.ShiftMask | KeyCode.O)]
+    [InlineData ("Closed", "None", "Open", KeyCode.AltMask | KeyCode.F, KeyCode.AltMask | KeyCode.O)]
+    [InlineData ("_Edit", "_Copy", "", KeyCode.AltMask | KeyCode.E)]
+    [InlineData ("_Edit", "F_ind", "", KeyCode.AltMask | KeyCode.E, KeyCode.F)]
+    [InlineData ("_Edit", "F_ind", "", KeyCode.AltMask | KeyCode.E, KeyCode.AltMask | KeyCode.F)]
+    [InlineData ("Closed", "None", "Replace", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.R)]
+    [InlineData ("Closed", "None", "Copy", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.C)]
+    [InlineData ("_Edit", "_1st", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3)]
+    [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D1)]
+    [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.Enter)]
+    [InlineData ("Closed", "None", "2", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D2)]
+    [InlineData ("_Edit", "_5th", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D4)]
+    [InlineData ("Closed", "None", "5", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D4, KeyCode.D5)]
+    [InlineData ("Closed", "None", "About", KeyCode.AltMask | KeyCode.A)]
+    public void KeyBindings_Navigation_Commands (
+        string expectedBarTitle,
+        string expectedItemTitle,
+        string expectedAction,
+        params KeyCode [] keys
+    )
+    {
+        var miAction = "";
+        MenuItem mbiCurrent = null;
+        MenuItem miCurrent = null;
+
+        var menu = new MenuBar ();
+
+        Func<object, bool> fn = s =>
+                                {
+                                    miAction = s as string;
+
+                                    return true;
+                                };
+        menu.EnableForDesign (ref fn);
+
+        menu.Key = KeyCode.F9;
+        menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu;
+        menu.MenuOpened += (s, e) => { miCurrent = e.MenuItem; };
+
+        menu.MenuClosing += (s, e) =>
+                            {
+                                mbiCurrent = null;
+                                miCurrent = null;
+                            };
+        menu.UseKeysUpDownAsKeysLeftRight = true;
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        foreach (Key key in keys)
+        {
+            top.NewKeyDownEvent (key);
+            Application.MainLoop.RunIteration ();
+        }
+
+        Assert.Equal (expectedBarTitle, mbiCurrent != null ? mbiCurrent.Title : "Closed");
+        Assert.Equal (expectedItemTitle, miCurrent != null ? miCurrent.Title : "None");
+        Assert.Equal (expectedAction, miAction);
+        top.Dispose ();
+    }
+
+    [Theory]
+    [AutoInitShutdown]
+    [InlineData ("New", KeyCode.CtrlMask | KeyCode.N)]
+    [InlineData ("Quit", KeyCode.CtrlMask | KeyCode.Q)]
+    [InlineData ("Copy", KeyCode.CtrlMask | KeyCode.C)]
+    [InlineData ("Replace", KeyCode.CtrlMask | KeyCode.H)]
+    [InlineData ("1", KeyCode.F1)]
+    [InlineData ("5", KeyCode.CtrlMask | KeyCode.D5)]
+    public void KeyBindings_Shortcut_Commands (string expectedAction, params KeyCode [] keys)
+    {
+        var miAction = "";
+        MenuItem mbiCurrent = null;
+        MenuItem miCurrent = null;
+
+        var menu = new MenuBar ();
+
+        bool FnAction (string s)
+        {
+            miAction = s;
+
+            return true;
+        }
+
+        // Declare a variable for the function
+        Func<string, bool> fnActionVariable = FnAction;
+
+        menu.EnableForDesign (ref fnActionVariable);
+
+        menu.Key = KeyCode.F9;
+        menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu;
+        menu.MenuOpened += (s, e) => { miCurrent = e.MenuItem; };
+
+        menu.MenuClosing += (s, e) =>
+                            {
+                                mbiCurrent = null;
+                                miCurrent = null;
+                            };
+        menu.UseKeysUpDownAsKeysLeftRight = true;
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        foreach (KeyCode key in keys)
+        {
+            Assert.True (top.NewKeyDownEvent (new (key)));
+            Application.MainLoop!.RunIteration ();
+        }
+
+        Assert.Equal (expectedAction, miAction);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Menu_With_Separator ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "File",
+                     new MenuItem []
+                     {
+                         new (
+                              "_Open",
+                              "Open a file",
+                              () => { },
+                              null,
+                              null,
+                              KeyCode.CtrlMask | KeyCode.O
+                             ),
+                         null,
+                         new ("_Quit", "", null)
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+ File                         
+┌────────────────────────────┐
+│ Open   Open a file  Ctrl+O │
+├────────────────────────────┤
+│ Quit                       │
+└────────────────────────────┘",
+                                                      output
+                                                     );
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Menu_With_Separator_Disabled_Border ()
+    {
+        var menu = new MenuBar
+        {
+            MenusBorderStyle = LineStyle.None,
+            Menus =
+            [
+                new (
+                     "File",
+                     new MenuItem []
+                     {
+                         new (
+                              "_Open",
+                              "Open a file",
+                              () => { },
+                              null,
+                              null,
+                              KeyCode.CtrlMask | KeyCode.O
+                             ),
+                         null,
+                         new ("_Quit", "", null)
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        menu.OpenMenu ();
+        Application.LayoutAndDraw ();
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+ File                       
+ Open   Open a file  Ctrl+O 
+────────────────────────────
+ Quit                       ",
+                                                      output
+                                                     );
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBar_ButtonPressed_Open_The_Menu_ButtonPressed_Again_Close_The_Menu ()
+    {
+        // Define the expected menu
+        var expectedMenu = new ExpectedMenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("Open", "", null) }),
+                new (
+                     "Edit",
+                     new MenuItem [] { new ("Copy", "", null) }
+                    )
+            ]
+        };
+
+        // Test without HotKeys first
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "_" + expectedMenu.Menus [0].Title,
+                     new MenuItem [] { new ("_" + expectedMenu.Menus [0].Children [0].Title, "", null) }
+                    ),
+                new (
+                     "_" + expectedMenu.Menus [1].Title,
+                     new MenuItem []
+                     {
+                         new (
+                              "_"
+                              + expectedMenu.Menus [1]
+                                            .Children [0]
+                                            .Title,
+                              "",
+                              null
+                             )
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
+        Assert.True (menu.IsMenuOpen);
+        top.Draw ();
+
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
+
+        Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
+        Assert.False (menu.IsMenuOpen);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBar_In_Window_Without_Other_Views_With_Top_Init ()
+    {
+        var win = new Window ();
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) }),
+                new (
+                     "Edit",
+                     new MenuItem []
+                     {
+                         new MenuBarItem (
+                                          "Delete",
+                                          new MenuItem []
+                                              { new ("All", "", null), new ("Selected", "", null) }
+                                         )
+                     }
+                    )
+            ]
+        };
+        win.Add (menu);
+        Toplevel top = new ();
+        top.Add (win);
+        Application.Begin (top);
+        ((FakeDriver)Application.Driver!).SetBufferSize (40, 8);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (win.NewKeyDownEvent (menu.Key));
+        top.Draw ();
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│┌──────┐                              │
+││ New  │                              │
+│└──────┘                              │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
+        Application.LayoutAndDraw ();
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│      ┌─────────┐                     │
+│      │ Delete ►│                     │
+│      └─────────┘                     │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
+        top.Draw ();
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│      ┌─────────┐                     │
+│      │ Delete ►│┌───────────┐        │
+│      └─────────┘│ All       │        │
+│                 │ Selected  │        │
+│                 └───────────┘        │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
+        View.SetClipToScreen ();
+        top.Draw ();
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│┌──────┐                              │
+││ New  │                              │
+│└──────┘                              │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBar_In_Window_Without_Other_Views_With_Top_Init_With_Parameterless_Run ()
+    {
+        var win = new Window ();
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) }),
+                new (
+                     "Edit",
+                     new MenuItem []
+                     {
+                         new MenuBarItem (
+                                          "Delete",
+                                          new MenuItem []
+                                              { new ("All", "", null), new ("Selected", "", null) }
+                                         )
+                     }
+                    )
+            ]
+        };
+        win.Add (menu);
+        Toplevel top = new ();
+        top.Add (win);
+
+        Application.Iteration += (s, a) =>
+                                 {
+                                     ((FakeDriver)Application.Driver!).SetBufferSize (40, 8);
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Assert.True (win.NewKeyDownEvent (menu.Key));
+                                     top.Draw ();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│┌──────┐                              │
+││ New  │                              │
+│└──────┘                              │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
+                                     Application.LayoutAndDraw ();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│      ┌─────────┐                     │
+│      │ Delete ►│                     │
+│      └─────────┘                     │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
+                                     top.Draw ();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│      ┌─────────┐                     │
+│      │ Delete ►│┌───────────┐        │
+│      └─────────┘│ All       │        │
+│                 │ Selected  │        │
+│                 └───────────┘        │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
+                                     View.SetClipToScreen ();
+                                     top.Draw ();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│┌──────┐                              │
+││ New  │                              │
+│└──────┘                              │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Application.RequestStop ();
+                                 };
+
+        Application.Run (top);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init ()
+    {
+        var win = new Window ();
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) }),
+                new (
+                     "Edit",
+                     new MenuItem []
+                     {
+                         new MenuBarItem (
+                                          "Delete",
+                                          new MenuItem []
+                                              { new ("All", "", null), new ("Selected", "", null) }
+                                         )
+                     }
+                    )
+            ]
+        };
+        win.Add (menu);
+        ((FakeDriver)Application.Driver!).SetBufferSize (40, 8);
+        RunState rs = Application.Begin (win);
+        Application.RunIteration (ref rs);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (win.NewKeyDownEvent (menu.Key));
+        Application.RunIteration (ref rs);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│┌──────┐                              │
+││ New  │                              │
+│└──────┘                              │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
+        Application.RunIteration (ref rs);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│      ┌─────────┐                     │
+│      │ Delete ►│                     │
+│      └─────────┘                     │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
+        Application.RunIteration (ref rs);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│      ┌─────────┐                     │
+│      │ Delete ►│┌───────────┐        │
+│      └─────────┘│ All       │        │
+│                 │ Selected  │        │
+│                 └───────────┘        │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
+        Application.RunIteration (ref rs);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│┌──────┐                              │
+││ New  │                              │
+│└──────┘                              │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                      output
+                                                     );
+        win.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init_With_Run_T ()
+    {
+        ((FakeDriver)Application.Driver!).SetBufferSize (40, 8);
+
+        Application.Iteration += (s, a) =>
+                                 {
+                                     Toplevel top = Application.Top;
+                                     Application.LayoutAndDraw();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Assert.True (top.NewKeyDownEvent (Key.F9));
+                                     top.Draw ();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│┌──────┐                              │
+││ New  │                              │
+│└──────┘                              │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Assert.True (top.SubViews.ElementAt (0).NewKeyDownEvent (Key.CursorRight));
+                                     Application.LayoutAndDraw ();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│      ┌─────────┐                     │
+│      │ Delete ►│                     │
+│      └─────────┘                     │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Assert.True (
+                                                  ((MenuBar)top.SubViews.ElementAt (0))._openMenu.NewKeyDownEvent (Key.CursorRight)
+                                                 );
+                                     top.Draw ();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│      ┌─────────┐                     │
+│      │ Delete ►│┌───────────┐        │
+│      └─────────┘│ All       │        │
+│                 │ Selected  │        │
+│                 └───────────┘        │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Assert.True (
+                                                  ((MenuBar)top.SubViews.ElementAt (0))._openMenu.NewKeyDownEvent (Key.CursorRight)
+                                                 );
+                                     View.SetClipToScreen ();
+                                     top.Draw ();
+
+                                     DriverAssert.AssertDriverContentsWithFrameAre (
+                                                                                   @"
+┌──────────────────────────────────────┐
+│ File  Edit                           │
+│┌──────┐                              │
+││ New  │                              │
+│└──────┘                              │
+│                                      │
+│                                      │
+└──────────────────────────────────────┘",
+                                                                                   output
+                                                                                  );
+
+                                     Application.RequestStop ();
+                                 };
+
+        Application.Run<CustomWindow> ().Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBar_Position_And_Size_With_HotKeys_Is_The_Same_As_Without_HotKeys ()
+    {
+        // Define the expected menu
+        var expectedMenu = new ExpectedMenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("12", "", null) }),
+                new (
+                     "Edit",
+                     new MenuItem [] { new ("Copy", "", null) }
+                    )
+            ]
+        };
+
+        // Test without HotKeys first
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     expectedMenu.Menus [0].Title,
+                     new MenuItem [] { new (expectedMenu.Menus [0].Children [0].Title, "", null) }
+                    ),
+                new (
+                     expectedMenu.Menus [1].Title,
+                     new MenuItem []
+                     {
+                         new (
+                              expectedMenu.Menus [1].Children [0].Title,
+                              "",
+                              null
+                             )
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        // Open first
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
+
+        // Open second
+        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight));
+        Assert.True (menu.IsMenuOpen);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
+
+        // Close menu
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.False (menu.IsMenuOpen);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+
+        top.Remove (menu);
+
+        // Now test WITH HotKeys
+        menu = new ()
+        {
+            Menus =
+            [
+                new (
+                     "_" + expectedMenu.Menus [0].Title,
+                     new MenuItem [] { new ("_" + expectedMenu.Menus [0].Children [0].Title, "", null) }
+                    ),
+                new (
+                     "_" + expectedMenu.Menus [1].Title,
+                     new MenuItem []
+                     {
+                         new (
+                              "_" + expectedMenu.Menus [1].Children [0].Title,
+                              "",
+                              null
+                             )
+                     }
+                    )
+            ]
+        };
+
+        top.Add (menu);
+
+        // Open first
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
+
+        // Open second
+        Assert.True (top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight));
+        Assert.True (menu.IsMenuOpen);
+        View.SetClipToScreen ();
+        Application.Top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
+
+        // Close menu
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.False (menu.IsMenuOpen);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBar_Submenus_Alignment_Correct ()
+    {
+        // Define the expected menu
+        var expectedMenu = new ExpectedMenuBar
+        {
+            Menus =
+            [
+                new (
+                     "File",
+                     new MenuItem []
+                     {
+                         new (
+                              "Really Long Sub Menu",
+                              "",
+                              null
+                             )
+                     }
+                    ),
+                new (
+                     "123",
+                     new MenuItem [] { new ("Copy", "", null) }
+                    ),
+                new (
+                     "Format",
+                     new MenuItem [] { new ("Word Wrap", "", null) }
+                    ),
+                new (
+                     "Help",
+                     new MenuItem [] { new ("About", "", null) }
+                    ),
+                new (
+                     "1",
+                     new MenuItem [] { new ("2", "", null) }
+                    ),
+                new (
+                     "3",
+                     new MenuItem [] { new ("2", "", null) }
+                    ),
+                new (
+                     "Last one",
+                     new MenuItem [] { new ("Test", "", null) }
+                    )
+            ]
+        };
+
+        MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length];
+
+        for (var i = 0; i < expectedMenu.Menus.Length; i++)
+        {
+            items [i] = new (
+                             expectedMenu.Menus [i].Title,
+                             new MenuItem [] { new (expectedMenu.Menus [i].Children [0].Title, "", null) }
+                            );
+        }
+
+        var menu = new MenuBar { Menus = items };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+
+        for (var i = 0; i < expectedMenu.Menus.Length; i++)
+        {
+            menu.OpenMenu (i);
+            Assert.True (menu.IsMenuOpen);
+            View.SetClipToScreen ();
+            top.Draw ();
+            DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (i), output);
+        }
+
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBar_With_Action_But_Without_MenuItems_Not_Throw ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new () { Title = "Test 1", Action = () => { } },
+
+                new () { Title = "Test 2", Action = () => { } }
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+#if SUPPORT_ALT_TO_ACTIVATE_MENU
+        Assert.True (
+                     Application.OnKeyUp (
+                                          new KeyEventArgs (
+                                                            Key.AltMask
+                                                           )
+                                         )
+                    ); // changed to true because Alt activates menu bar
+#endif
+        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
+        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuBarItem_Children_Null_Does_Not_Throw ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("Test", "", null)
+            ]
+        };
+        var top = new Toplevel ();
+        top.Add (menu);
+
+        Exception exception = Record.Exception (() => menu.NewKeyDownEvent (Key.Space));
+        Assert.Null (exception);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuOpened_On_Disabled_MenuItem ()
+    {
+        MenuItem parent = null;
+        MenuItem miCurrent = null;
+        Menu mCurrent = null;
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "_File",
+                     new MenuItem []
+                     {
+                         new MenuBarItem (
+                                          "_New",
+                                          new MenuItem []
+                                          {
+                                              new (
+                                                   "_New doc",
+                                                   "Creates new doc.",
+                                                   null,
+                                                   () => false
+                                                  )
+                                          }
+                                         ),
+                         null,
+                         new ("_Save", "Saves the file.", null)
+                     }
+                    )
+            ]
+        };
+
+        menu.MenuOpened += (s, e) =>
+                           {
+                               parent = e.Parent;
+                               miCurrent = e.MenuItem;
+                               mCurrent = menu._openMenu;
+                           };
+        menu.UseKeysUpDownAsKeysLeftRight = true;
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        // open the menu
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }
+                                        )
+                    );
+        Assert.True (menu.IsMenuOpen);
+        Assert.Equal ("_File", parent.Title);
+        Assert.Equal ("_File", miCurrent.Parent.Title);
+        Assert.Equal ("_New", miCurrent.Title);
+
+        Assert.True (
+                     mCurrent.NewMouseEvent (
+                                             new () { Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
+                                            )
+                    );
+        Assert.True (menu.IsMenuOpen);
+        Assert.Equal ("_File", parent.Title);
+        Assert.Equal ("_File", miCurrent.Parent.Title);
+        Assert.Equal ("_New", miCurrent.Title);
+
+        Assert.True (
+                     mCurrent.NewMouseEvent (
+                                             new () { Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
+                                            )
+                    );
+        Assert.True (menu.IsMenuOpen);
+        Assert.Equal ("_File", parent.Title);
+        Assert.Equal ("_File", miCurrent.Parent.Title);
+        Assert.Equal ("_New", miCurrent.Title);
+
+        Assert.True (
+                     mCurrent.NewMouseEvent (
+                                             new () { Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
+                                            )
+                    );
+        Assert.True (menu.IsMenuOpen);
+        Assert.Equal ("_File", parent.Title);
+        Assert.Equal ("_File", miCurrent.Parent.Title);
+        Assert.Equal ("_Save", miCurrent.Title);
+
+        // close the menu
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }
+                                        )
+                    );
+        Assert.False (menu.IsMenuOpen);
+
+        // open the menu
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+
+        // The _New doc is enabled but the sub-menu isn't enabled. Is show but can't be selected and executed
+        Assert.Equal ("_New", parent.Title);
+        Assert.Equal ("_New", miCurrent.Parent.Title);
+        Assert.Equal ("_New doc", miCurrent.Title);
+
+        Assert.True (mCurrent.NewKeyDownEvent (Key.CursorDown));
+        Assert.True (menu.IsMenuOpen);
+        Assert.Equal ("_File", parent.Title);
+        Assert.Equal ("_File", miCurrent.Parent.Title);
+        Assert.Equal ("_Save", miCurrent.Title);
+
+        Assert.True (mCurrent.NewKeyDownEvent (Key.CursorUp));
+        Assert.True (menu.IsMenuOpen);
+        Assert.Equal ("_File", parent.Title);
+        Assert.Null (miCurrent);
+
+        // close the menu
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.False (menu.IsMenuOpen);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MenuOpening_MenuOpened_MenuClosing_Events ()
+    {
+        var miAction = "";
+        var isMenuClosed = true;
+        var cancelClosing = false;
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("_File", new MenuItem [] { new ("_New", "Creates new file.", New) })
+            ]
+        };
+
+        menu.MenuOpening += (s, e) =>
+                            {
+                                Assert.Equal ("_File", e.CurrentMenu.Title);
+                                Assert.Equal ("_New", e.CurrentMenu.Children [0].Title);
+                                Assert.Equal ("Creates new file.", e.CurrentMenu.Children [0].Help);
+                                Assert.Equal (New, e.CurrentMenu.Children [0].Action);
+                                e.CurrentMenu.Children [0].Action ();
+                                Assert.Equal ("New", miAction);
+
+                                e.NewMenuBarItem = new (
+                                                        "_Edit",
+                                                        new MenuItem [] { new ("_Copy", "Copies the selection.", Copy) }
+                                                       );
+                            };
+
+        menu.MenuOpened += (s, e) =>
+                           {
+                               MenuItem mi = e.MenuItem;
+
+                               Assert.Equal ("_Edit", mi.Parent.Title);
+                               Assert.Equal ("_Copy", mi.Title);
+                               Assert.Equal ("Copies the selection.", mi.Help);
+                               Assert.Equal (Copy, mi.Action);
+                               mi.Action ();
+                               Assert.Equal ("Copy", miAction);
+                           };
+
+        menu.MenuClosing += (s, e) =>
+                            {
+                                Assert.False (isMenuClosed);
+
+                                if (cancelClosing)
+                                {
+                                    e.Cancel = true;
+                                    isMenuClosed = false;
+                                }
+                                else
+                                {
+                                    isMenuClosed = true;
+                                }
+                            };
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+        isMenuClosed = !menu.IsMenuOpen;
+        Assert.False (isMenuClosed);
+        top.Draw ();
+
+        var expected = @"
+Edit
+┌──────────────────────────────┐
+│ Copy   Copies the selection. │
+└──────────────────────────────┘
+";
+        DriverAssert.AssertDriverContentsAre (expected, output);
+
+        cancelClosing = true;
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (isMenuClosed);
+        View.SetClipToScreen ();
+        top.Draw ();
+
+        expected = @"
+Edit
+┌──────────────────────────────┐
+│ Copy   Copies the selection. │
+└──────────────────────────────┘
+";
+        DriverAssert.AssertDriverContentsAre (expected, output);
+
+        cancelClosing = false;
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.False (menu.IsMenuOpen);
+        Assert.True (isMenuClosed);
+        View.SetClipToScreen ();
+        top.Draw ();
+
+        expected = @"
+Edit
+";
+        DriverAssert.AssertDriverContentsAre (expected, output);
+
+        void New () { miAction = "New"; }
+
+        void Copy () { miAction = "Copy"; }
+
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void MouseEvent_Test ()
+    {
+        MenuItem miCurrent = null;
+        Menu mCurrent = null;
+
+        var menuBar = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "_File",
+                     new MenuItem [] { new ("_New", "", null), new ("_Open", "", null), new ("_Save", "", null) }
+                    ),
+                new (
+                     "_Edit",
+                     new MenuItem [] { new ("_Copy", "", null), new ("C_ut", "", null), new ("_Paste", "", null) }
+                    )
+            ]
+        };
+
+        menuBar.MenuOpened += (s, e) =>
+                           {
+                               miCurrent = e.MenuItem;
+                               mCurrent = menuBar.OpenCurrentMenu;
+                           };
+        var top = new Toplevel ();
+        top.Add (menuBar);
+        Application.Begin (top);
+
+        // Click on Edit
+        Assert.True (
+                     menuBar.NewMouseEvent (
+                                         new () { Position = new (10, 0), Flags = MouseFlags.Button1Pressed, View = menuBar }
+                                        )
+                    );
+        Assert.True (menuBar.IsMenuOpen);
+        Assert.Equal ("_Edit", miCurrent.Parent.Title);
+        Assert.Equal ("_Copy", miCurrent.Title);
+
+        // Click on Paste
+        Assert.True (
+                     mCurrent.NewMouseEvent (
+                                             new () { Position = new (10, 2), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
+                                            )
+                    );
+        Assert.True (menuBar.IsMenuOpen);
+        Assert.Equal ("_Edit", miCurrent.Parent.Title);
+        Assert.Equal ("_Paste", miCurrent.Title);
+
+        for (var i = 4; i >= -1; i--)
+        {
+            Application.RaiseMouseEvent (
+                                         new () { ScreenPosition = new (10, i), Flags = MouseFlags.ReportMousePosition }
+                                        );
+
+            Assert.True (menuBar.IsMenuOpen);
+            Menu menu = (Menu)top.SubViews.First (v => v is Menu);
+
+            if (i is < 0 or > 0)
+            {
+                Assert.Equal (menu, Application.MouseGrabView);
+            }
+            else
+            {
+                Assert.Equal (menuBar, Application.MouseGrabView);
+            }
+
+            Assert.Equal ("_Edit", miCurrent.Parent.Title);
+
+            if (i == 4)
+            {
+                Assert.Equal ("_Paste", miCurrent.Title);
+            }
+            else if (i == 3)
+            {
+                Assert.Equal ("C_ut", miCurrent.Title);
+            }
+            else if (i == 2)
+            {
+                Assert.Equal ("_Copy", miCurrent.Title);
+            }
+            else
+            {
+                Assert.Equal ("_Copy", miCurrent.Title);
+            }
+        }
+
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Keyboard ()
+    {
+        var expectedMenu = new ExpectedMenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) }),
+                new ("Edit", Array.Empty<MenuItem> ()),
+                new (
+                     "Format",
+                     new MenuItem [] { new ("Wrap", "", null) }
+                    )
+            ]
+        };
+
+        MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length];
+
+        for (var i = 0; i < expectedMenu.Menus.Length; i++)
+        {
+            items [i] = new (
+                             expectedMenu.Menus [i].Title,
+                             expectedMenu.Menus [i].Children.Length > 0
+                                 ? new MenuItem [] { new (expectedMenu.Menus [i].Children [0].Title, "", null) }
+                                 : Array.Empty<MenuItem> ()
+                            );
+        }
+
+        var menu = new MenuBar { Menus = items };
+
+        var tf = new TextField { Y = 2, Width = 10 };
+        var top = new Toplevel ();
+        top.Add (menu, tf);
+
+        Application.Begin (top);
+        Assert.True (tf.HasFocus);
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
+
+        // Right - Edit has no sub menu; this tests that no sub menu shows
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        Assert.Equal (1, menu._selected);
+        Assert.Equal (-1, menu._selectedSub);
+        Assert.Null (menu._openSubMenu);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
+
+        // Right - Format
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output);
+
+        // Left - Edit
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft));
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
+
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft));
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
+
+        Assert.True (Application.RaiseKeyDownEvent (menu.Key));
+        Assert.False (menu.IsMenuOpen);
+        Assert.True (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse ()
+    {
+        // File  Edit  Format
+        //┌──────┐    ┌───────┐         
+        //│ New  │    │ Wrap  │         
+        //└──────┘    └───────┘         
+
+        // Define the expected menu
+        var expectedMenu = new ExpectedMenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) }),
+                new ("Edit", new MenuItem [] { }),
+                new (
+                     "Format",
+                     new MenuItem [] { new ("Wrap", "", null) }
+                    )
+            ]
+        };
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     expectedMenu.Menus [0].Title,
+                     new MenuItem [] { new (expectedMenu.Menus [0].Children [0].Title, "", null) }
+                    ),
+                new (expectedMenu.Menus [1].Title, new MenuItem [] { }),
+                new (
+                     expectedMenu.Menus [2].Title,
+                     new MenuItem []
+                     {
+                         new (
+                              expectedMenu.Menus [2].Children [0].Title,
+                              "",
+                              null
+                             )
+                     }
+                    )
+            ]
+        };
+
+        var tf = new TextField { Y = 2, Width = 10 };
+        var top = new Toplevel ();
+        top.Add (menu, tf);
+        Application.Begin (top);
+
+        Assert.True (tf.HasFocus);
+        Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (8, 0), Flags = MouseFlags.ReportMousePosition, View = menu }
+                                        )
+                    );
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (15, 0), Flags = MouseFlags.ReportMousePosition, View = menu }
+                                        )
+                    );
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output);
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (8, 0), Flags = MouseFlags.ReportMousePosition, View = menu }
+                                        )
+                    );
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (1, 0), Flags = MouseFlags.ReportMousePosition, View = menu }
+                                        )
+                    );
+        Assert.True (menu.IsMenuOpen);
+        Assert.False (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
+
+        Assert.True (menu.NewMouseEvent (new () { Position = new (8, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
+        Assert.False (menu.IsMenuOpen);
+        Assert.True (tf.HasFocus);
+        View.SetClipToScreen ();
+        top.Draw ();
+        DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+        top.Dispose ();
+    }
+
+    [Fact]
+    public void RemoveAndThenAddMenuBar_ShouldNotChangeWidth ()
+    {
+        MenuBar menuBar;
+        MenuBar menuBar2;
+
+        // TODO: When https: //github.com/gui-cs/Terminal.Gui/issues/3136 is fixed, 
+        // TODO: Change this to Window
+        var w = new View ();
+        menuBar2 = new ();
+        menuBar = new ();
+        w.Width = Dim.Fill ();
+        w.Height = Dim.Fill ();
+        w.X = 0;
+        w.Y = 0;
+
+        w.Visible = true;
+
+        // TODO: When https: //github.com/gui-cs/Terminal.Gui/issues/3136 is fixed, 
+        // TODO: uncomment this.
+        //w.Modal = false;
+        w.Title = "";
+        menuBar.Width = Dim.Fill ();
+        menuBar.Height = 1;
+        menuBar.X = 0;
+        menuBar.Y = 0;
+        menuBar.Visible = true;
+        w.Add (menuBar);
+
+        menuBar2.Width = Dim.Fill ();
+        menuBar2.Height = 1;
+        menuBar2.X = 0;
+        menuBar2.Y = 4;
+        menuBar2.Visible = true;
+        w.Add (menuBar2);
+
+        MenuBar [] menuBars = w.SubViews.OfType<MenuBar> ().ToArray ();
+        Assert.Equal (2, menuBars.Length);
+
+        Assert.Equal (Dim.Fill (), menuBars [0].Width);
+        Assert.Equal (Dim.Fill (), menuBars [1].Width);
+
+        // Goes wrong here
+        w.Remove (menuBar);
+        w.Remove (menuBar2);
+
+        w.Add (menuBar);
+        w.Add (menuBar2);
+
+        // These assertions fail
+        Assert.Equal (Dim.Fill (), menuBars [0].Width);
+        Assert.Equal (Dim.Fill (), menuBars [1].Width);
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Resizing_Close_Menus ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "File",
+                     new MenuItem []
+                     {
+                         new (
+                              "Open",
+                              "Open a file",
+                              () => { },
+                              null,
+                              null,
+                              KeyCode.CtrlMask | KeyCode.O
+                             )
+                     }
+                    )
+            ]
+        };
+        var top = new Toplevel ();
+        top.Add (menu);
+        RunState rs = Application.Begin (top);
+
+        menu.OpenMenu ();
+        var firstIteration = false;
+        Application.RunIteration (ref rs, firstIteration);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+ File                         
+┌────────────────────────────┐
+│ Open   Open a file  Ctrl+O │
+└────────────────────────────┘",
+                                                      output
+                                                     );
+
+        ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
+        firstIteration = false;
+        Application.RunIteration (ref rs, firstIteration);
+
+        DriverAssert.AssertDriverContentsWithFrameAre (
+                                                      @"
+ File",
+                                                      output
+                                                     );
+
+        Application.End (rs);
+        top.Dispose ();
+    }
+
+    [Fact]
+    public void Separator_Does_Not_Throws_Pressing_Menu_Hotkey ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "File",
+                     new MenuItem [] { new ("_New", "", null), null, new ("_Quit", "", null) }
+                    )
+            ]
+        };
+        Assert.False (menu.NewKeyDownEvent (Key.Q.WithAlt));
+    }
+
+    [Fact]
+    public void SetMenus_With_Same_HotKey_Does_Not_Throws ()
+    {
+        var mb = new MenuBar ();
+
+        var i1 = new MenuBarItem ("_heey", "fff", () => { }, () => true);
+
+        mb.Menus = new [] { i1 };
+        mb.Menus = new [] { i1 };
+
+        Assert.Equal (Key.H, mb.Menus [0].HotKey);
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void ShortCut_Activates ()
+    {
+        var saveAction = false;
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "_File",
+                     new MenuItem []
+                     {
+                         new (
+                              "_Save",
+                              "Saves the file.",
+                              () => { saveAction = true; },
+                              null,
+                              null,
+                              (KeyCode)Key.S.WithCtrl
+                             )
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Application.RaiseKeyDownEvent (Key.S.WithCtrl);
+        Application.MainLoop.RunIteration ();
+
+        Assert.True (saveAction);
+        top.Dispose ();
+    }
+
+    [Fact]
+    public void Update_ShortcutKey_KeyBindings_Old_ShortcutKey_Is_Removed ()
+    {
+        var menuBar = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "_File",
+                     new MenuItem []
+                     {
+                         new ("New", "Create New", null, null, null, Key.A.WithCtrl)
+                     }
+                    )
+            ]
+        };
+
+        Assert.True (menuBar.HotKeyBindings.TryGet (Key.A.WithCtrl, out _));
+
+        menuBar.Menus [0].Children! [0].ShortcutKey = Key.B.WithCtrl;
+
+        Assert.False (menuBar.HotKeyBindings.TryGet (Key.A.WithCtrl, out _));
+        Assert.True (menuBar.HotKeyBindings.TryGet (Key.B.WithCtrl, out _));
+    }
+
+    [Fact]
+    public void UseKeysUpDownAsKeysLeftRight_And_UseSubMenusSingleFrame_Cannot_Be_Both_True ()
+    {
+        var menu = new MenuBar ();
+        Assert.False (menu.UseKeysUpDownAsKeysLeftRight);
+        Assert.False (menu.UseSubMenusSingleFrame);
+
+        menu.UseKeysUpDownAsKeysLeftRight = true;
+        Assert.True (menu.UseKeysUpDownAsKeysLeftRight);
+        Assert.False (menu.UseSubMenusSingleFrame);
+
+        menu.UseSubMenusSingleFrame = true;
+        Assert.False (menu.UseKeysUpDownAsKeysLeftRight);
+        Assert.True (menu.UseSubMenusSingleFrame);
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void UseSubMenusSingleFrame_False_By_Keyboard ()
+    {
+        var menu = new MenuBar
+        {
+            Menus = new MenuBarItem []
+            {
+                new (
+                     "Numbers",
+                     new MenuItem []
+                     {
+                         new ("One", "", null),
+                         new MenuBarItem (
+                                          "Two",
+                                          new MenuItem []
+                                          {
+                                              new ("Sub-Menu 1", "", null),
+                                              new ("Sub-Menu 2", "", null)
+                                          }
+                                         ),
+                         new ("Three", "", null)
+                     }
+                    )
+            }
+        };
+        menu.UseKeysUpDownAsKeysLeftRight = true;
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
+        Assert.False (menu.UseSubMenusSingleFrame);
+
+        top.Draw ();
+
+        var expected = @"
+ Numbers
+";
+
+        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        top.Draw ();
+
+        expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorDown));
+        top.Draw ();
+
+        expected = @"
+ Numbers                 
+┌────────┐               
+│ One    │               
+│ Two   ►│┌─────────────┐
+│ Three  ││ Sub-Menu 1  │
+└────────┘│ Sub-Menu 2  │
+          └─────────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        Assert.True (Application.Top.SubViews.ElementAt (2).NewKeyDownEvent (Key.CursorLeft));
+        top.Draw ();
+
+        expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.Esc));
+        top.Draw ();
+
+        expected = @"
+ Numbers
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        top.Dispose ();
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void UseSubMenusSingleFrame_False_By_Mouse ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "Numbers",
+                     new MenuItem []
+                     {
+                         new ("One", "", null),
+                         new MenuBarItem (
+                                          "Two",
+                                          new MenuItem []
+                                          {
+                                              new (
+                                                   "Sub-Menu 1",
+                                                   "",
+                                                   null
+                                                  ),
+                                              new (
+                                                   "Sub-Menu 2",
+                                                   "",
+                                                   null
+                                                  )
+                                          }
+                                         ),
+                         new ("Three", "", null)
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
+        Assert.False (menu.UseSubMenusSingleFrame);
+
+        top.Draw ();
+
+        var expected = @"
+ Numbers
+";
+
+        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 1), pos);
+
+        menu.NewMouseEvent (
+                            new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }
+                           );
+        top.Draw ();
+
+        expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 10, 6), pos);
+
+        menu.NewMouseEvent (
+                            new ()
+                            {
+                                Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = Application.Top.SubViews.ElementAt (1)
+                            }
+                           );
+        top.Draw ();
+
+        expected = @"
+ Numbers                 
+┌────────┐               
+│ One    │               
+│ Two   ►│┌─────────────┐
+│ Three  ││ Sub-Menu 1  │
+└────────┘│ Sub-Menu 2  │
+          └─────────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 25, 7), pos);
+
+        Assert.False (
+                      menu.NewMouseEvent (
+                                          new ()
+                                          {
+                                              Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = Application.Top.SubViews.ElementAt (1)
+                                          }
+                                         )
+                     );
+        top.Draw ();
+
+        expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 10, 6), pos);
+
+        menu.NewMouseEvent (
+                            new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top }
+                           );
+        top.Draw ();
+
+        expected = @"
+ Numbers
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 1), pos);
+        top.Dispose ();
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void UseSubMenusSingleFrame_False_Disabled_Border ()
+    {
+        var menu = new MenuBar
+        {
+            MenusBorderStyle = LineStyle.None,
+            Menus =
+            [
+                new (
+                     "Numbers",
+                     new MenuItem []
+                     {
+                         new ("One", "", null),
+                         new MenuBarItem (
+                                          "Two",
+                                          new MenuItem []
+                                          {
+                                              new (
+                                                   "Sub-Menu 1",
+                                                   "",
+                                                   null
+                                                  ),
+                                              new (
+                                                   "Sub-Menu 2",
+                                                   "",
+                                                   null
+                                                  )
+                                          }
+                                         ),
+                         new ("Three", "", null)
+                     }
+                    )
+            ]
+        };
+
+        menu.UseKeysUpDownAsKeysLeftRight = true;
+        menu.BeginInit ();
+        menu.EndInit ();
+
+        menu.OpenMenu ();
+        menu.ColorScheme = menu._openMenu.ColorScheme = new (Attribute.Default);
+        Assert.True (menu.IsMenuOpen);
+
+        menu.Draw ();
+        menu._openMenu.Draw ();
+
+        var expected = @"
+ Numbers
+ One    
+ Two   ►
+ Three  ";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorDown));
+        menu.Draw ();
+        menu._openMenu.Draw ();
+        menu.OpenCurrentMenu.Draw ();
+
+        expected = @"
+ Numbers           
+ One               
+ Two   ► Sub-Menu 1
+ Three   Sub-Menu 2";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void UseSubMenusSingleFrame_True_By_Keyboard ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "Numbers",
+                     new MenuItem []
+                     {
+                         new ("One", "", null),
+                         new MenuBarItem (
+                                          "Two",
+                                          new MenuItem []
+                                          {
+                                              new (
+                                                   "Sub-Menu 1",
+                                                   "",
+                                                   null
+                                                  ),
+                                              new (
+                                                   "Sub-Menu 2",
+                                                   "",
+                                                   null
+                                                  )
+                                          }
+                                         ),
+                         new ("Three", "", null)
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
+        Assert.False (menu.UseSubMenusSingleFrame);
+        menu.UseSubMenusSingleFrame = true;
+        Assert.True (menu.UseSubMenusSingleFrame);
+
+        top.Draw ();
+
+        var expected = @"
+ Numbers
+";
+
+        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 1), pos);
+
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        top.Draw ();
+
+        expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 10, 6), pos);
+
+        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorDown));
+        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.Enter));
+        top.Draw ();
+
+        expected = @"
+ Numbers       
+┌─────────────┐
+│◄    Two     │
+├─────────────┤
+│ Sub-Menu 1  │
+│ Sub-Menu 2  │
+└─────────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 15, 7), pos);
+
+        Assert.True (Application.Top.SubViews.ElementAt (2).NewKeyDownEvent (Key.Enter));
+        top.Draw ();
+
+        expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 10, 6), pos);
+
+        Assert.True (Application.Top.SubViews.ElementAt (1).NewKeyDownEvent (Key.Esc));
+        top.Draw ();
+
+        expected = @"
+ Numbers
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 1), pos);
+        top.Dispose ();
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void UseSubMenusSingleFrame_True_By_Mouse ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "Numbers",
+                     new MenuItem []
+                     {
+                         new ("One", "", null),
+                         new MenuBarItem (
+                                          "Two",
+                                          new MenuItem []
+                                          {
+                                              new (
+                                                   "Sub-Menu 1",
+                                                   "",
+                                                   null
+                                                  ),
+                                              new (
+                                                   "Sub-Menu 2",
+                                                   "",
+                                                   null
+                                                  )
+                                          }
+                                         ),
+                         new ("Three", "", null)
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
+        Assert.False (menu.UseSubMenusSingleFrame);
+        menu.UseSubMenusSingleFrame = true;
+        Assert.True (menu.UseSubMenusSingleFrame);
+
+        top.Draw ();
+
+        var expected = @"
+ Numbers
+";
+
+        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 1), pos);
+
+        Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
+        top.Draw ();
+
+        expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 10, 6), pos);
+
+        Assert.False (menu.NewMouseEvent (new () { Position = new (1, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top.SubViews.ElementAt (1) }));
+        top.Draw ();
+
+        expected = @"
+ Numbers       
+┌─────────────┐
+│◄    Two     │
+├─────────────┤
+│ Sub-Menu 1  │
+│ Sub-Menu 2  │
+└─────────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 15, 7), pos);
+
+        menu.NewMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Clicked, View = Application.Top.SubViews.ElementAt (2) });
+        top.Draw ();
+
+        expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 10, 6), pos);
+
+        Assert.False (menu.NewMouseEvent (new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top }));
+        top.Draw ();
+
+        expected = @"
+ Numbers
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 1), pos);
+        top.Dispose ();
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void UseSubMenusSingleFrame_True_Disabled_Border ()
+    {
+        var menu = new MenuBar
+        {
+            MenusBorderStyle = LineStyle.None,
+            Menus =
+            [
+                new (
+                     "Numbers",
+                     new MenuItem []
+                     {
+                         new ("One", "", null),
+                         new MenuBarItem (
+                                          "Two",
+                                          new MenuItem []
+                                          {
+                                              new (
+                                                   "Sub-Menu 1",
+                                                   "",
+                                                   null
+                                                  ),
+                                              new (
+                                                   "Sub-Menu 2",
+                                                   "",
+                                                   null
+                                                  )
+                                          }
+                                         ),
+                         new ("Three", "", null)
+                     }
+                    )
+            ]
+        };
+
+        menu.UseSubMenusSingleFrame = true;
+        menu.BeginInit ();
+        menu.EndInit ();
+
+        menu.OpenMenu ();
+        Assert.True (menu.IsMenuOpen);
+
+        menu.Draw ();
+        menu.ColorScheme = menu._openMenu.ColorScheme = new (Attribute.Default);
+        menu._openMenu.Draw ();
+
+        var expected = @"
+ Numbers
+ One    
+ Two   ►
+ Three  ";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorDown));
+        Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter));
+        menu.Draw ();
+        menu._openMenu.Draw ();
+        menu.OpenCurrentMenu.Draw ();
+
+        expected = @"
+ Numbers     
+◄    Two     
+─────────────
+ Sub-Menu 1  
+ Sub-Menu 2  ";
+
+        _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+    }
+
+    [Fact (Skip = "#3798 Broke. Will fix in #2975")]
+    [AutoInitShutdown]
+    public void UseSubMenusSingleFrame_True_Without_Border ()
+    {
+        var menu = new MenuBar
+        {
+            UseSubMenusSingleFrame = true,
+            MenusBorderStyle = LineStyle.None,
+            Menus =
+            [
+                new (
+                     "Numbers",
+                     new MenuItem []
+                     {
+                         new ("One", "", null),
+                         new MenuBarItem (
+                                          "Two",
+                                          new MenuItem []
+                                          {
+                                              new (
+                                                   "Sub-Menu 1",
+                                                   "",
+                                                   null
+                                                  ),
+                                              new (
+                                                   "Sub-Menu 2",
+                                                   "",
+                                                   null
+                                                  )
+                                          }
+                                         ),
+                         new ("Three", "", null)
+                     }
+                    )
+            ]
+        };
+
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y));
+        Assert.True (menu.UseSubMenusSingleFrame);
+        Assert.Equal (LineStyle.None, menu.MenusBorderStyle);
+
+        top.Draw ();
+
+        var expected = @"
+ Numbers
+";
+
+        Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 1), pos);
+
+        Assert.True (
+                     menu.NewMouseEvent (
+                                         new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }
+                                        )
+                    );
+        top.Draw ();
+
+        expected = @"
+ Numbers
+ One    
+ Two   ►
+ Three  
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 4), pos);
+
+        menu.NewMouseEvent (
+                            new () { Position = new (1, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top.SubViews.ElementAt (1) }
+                           );
+        top.Draw ();
+
+        expected = @"
+ Numbers     
+◄    Two     
+─────────────
+ Sub-Menu 1  
+ Sub-Menu 2  
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 13, 5), pos);
+
+        menu.NewMouseEvent (
+                            new () { Position = new (1, 1), Flags = MouseFlags.Button1Clicked, View = Application.Top.SubViews.ElementAt (2) }
+                           );
+        top.Draw ();
+
+        expected = @"
+ Numbers
+ One    
+ Two   ►
+ Three  
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 4), pos);
+
+        menu.NewMouseEvent (
+                            new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top }
+                           );
+        top.Draw ();
+
+        expected = @"
+ Numbers
+";
+
+        pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
+        Assert.Equal (new (1, 0, 8, 1), pos);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Visible_False_Key_Does_Not_Open_And_Close_All_Opened_Menus ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) })
+            ]
+        };
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.True (menu.Visible);
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+
+        menu.Visible = false;
+        Assert.False (menu.IsMenuOpen);
+
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.False (menu.IsMenuOpen);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void CanFocus_True_Key_Esc_Exit_Toplevel_If_IsMenuOpen_False ()
+    {
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new ("File", new MenuItem [] { new ("New", "", null) })
+            ],
+            CanFocus = true
+        };
+        var top = new Toplevel ();
+        top.Add (menu);
+        Application.Begin (top);
+
+        Assert.True (menu.CanFocus);
+        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (menu.IsMenuOpen);
+
+        Assert.True (menu.NewKeyDownEvent (Key.Esc));
+        Assert.False (menu.IsMenuOpen);
+
+        Assert.False (menu.NewKeyDownEvent (Key.Esc));
+        Assert.False (menu.IsMenuOpen);
+        top.Dispose ();
+    }
+
+    // Defines the expected strings for a Menu. Currently supports 
+    //   - MenuBar with any number of MenuItems 
+    //   - Each top-level MenuItem can have a SINGLE sub-menu
+    //
+    // TODO: Enable multiple sub-menus
+    // TODO: Enable checked sub-menus
+    // TODO: Enable sub-menus with sub-menus (perhaps better to put this in a separate class with focused unit tests?)
+    //
+    // E.g: 
+    //
+    // File  Edit
+    //  New    Copy
+    public class ExpectedMenuBar : MenuBar
+    {
+        private FakeDriver _d = (FakeDriver)Application.Driver;
+
+        // The expected strings when the menu is closed
+        public string ClosedMenuText => MenuBarText + "\n";
+
+        public string ExpectedBottomRow (int i)
+        {
+            return $"{Glyphs.LLCorner}{new (Glyphs.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{Glyphs.LRCorner}  \n";
+        }
+
+        // The 3 spaces at end are a result of Menu.cs line 1062 where `pos` is calculated (` + spacesAfterTitle`)
+        public string ExpectedMenuItemRow (int i) { return $"{Glyphs.VLine} {Menus [i].Children [0].Title}  {Glyphs.VLine}   \n"; }
+
+        // The full expected string for an open sub menu
+        public string ExpectedSubMenuOpen (int i)
+        {
+            return ClosedMenuText
+                   + (Menus [i].Children.Length > 0
+                          ? ExpectedPadding (i)
+                            + ExpectedTopRow (i)
+                            + ExpectedPadding (i)
+                            + ExpectedMenuItemRow (i)
+                            + ExpectedPadding (i)
+                            + ExpectedBottomRow (i)
+                          : "");
+        }
+
+        // Define expected menu frame
+        // "┌──────┐"
+        // "│ New  │"
+        // "└──────┘"
+        // 
+        // The width of the Frame is determined in Menu.cs line 144, where `Width` is calculated
+        //   1 space before the Title and 2 spaces after the Title/Check/Help
+        public string ExpectedTopRow (int i)
+        {
+            return $"{Glyphs.ULCorner}{new (Glyphs.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{Glyphs.URCorner}  \n";
+        }
+
+        // Each MenuBar title has a 1 space pad on each side
+        // See `static int leftPadding` and `static int rightPadding` on line 1037 of Menu.cs
+        public string MenuBarText
+        {
+            get
+            {
+                var txt = string.Empty;
+
+                foreach (MenuBarItem m in Menus)
+                {
+                    txt += " " + m.Title + " ";
+                }
+
+                return txt;
+            }
+        }
+
+        // Padding for the X of the sub menu Frame
+        // Menu.cs - Line 1239 in `internal void OpenMenu` is where the Menu is created
+        private string ExpectedPadding (int i)
+        {
+            var n = 0;
+
+            while (i > 0)
+            {
+                n += Menus [i - 1].TitleLength + 2;
+                i--;
+            }
+
+            return new (' ', n);
+        }
+    }
+
+    private class CustomWindow : Window
+    {
+        public CustomWindow ()
+        {
+            var menu = new MenuBar
+            {
+                Menus =
+                [
+                    new ("File", new MenuItem [] { new ("New", "", null) }),
+                    new (
+                         "Edit",
+                         new MenuItem []
+                         {
+                             new MenuBarItem (
+                                              "Delete",
+                                              new MenuItem []
+                                                  { new ("All", "", null), new ("Selected", "", null) }
+                                             )
+                         }
+                        )
+                ]
+            };
+            Add (menu);
+        }
+    }
+}

+ 2 - 2
Tests/UnitTests/Views/MenuTests.cs → Tests/UnitTests/Views/Menuv1/Menuv1Tests.cs

@@ -4,10 +4,10 @@
 
 
 namespace Terminal.Gui.ViewsTests;
 namespace Terminal.Gui.ViewsTests;
 
 
-public class MenuTests
+public class Menuv1Tests
 {
 {
     private readonly ITestOutputHelper _output;
     private readonly ITestOutputHelper _output;
-    public MenuTests (ITestOutputHelper output) { _output = output; }
+    public Menuv1Tests (ITestOutputHelper output) { _output = output; }
 
 
     // TODO: Create more low-level unit tests for Menu and MenuItem
     // TODO: Create more low-level unit tests for Menu and MenuItem
 
 

+ 124 - 0
Tests/UnitTestsParallelizable/Views/FlagSelectorTests.cs

@@ -0,0 +1,124 @@
+
+namespace Terminal.Gui.ViewsTests;
+
+public class FlagSelectorTests
+{
+    [Fact]
+    public void Initialization_ShouldSetDefaults()
+    {
+        var flagSelector = new FlagSelector();
+
+        Assert.True(flagSelector.CanFocus);
+        Assert.Equal(Dim.Auto(DimAutoStyle.Content), flagSelector.Width);
+        Assert.Equal(Dim.Auto(DimAutoStyle.Content), flagSelector.Height);
+        Assert.Equal(Orientation.Vertical, flagSelector.Orientation);
+    }
+
+    [Fact]
+    public void SetFlags_WithDictionary_ShouldSetFlags()
+    {
+        var flagSelector = new FlagSelector();
+        var flags = new Dictionary<uint, string>
+        {
+            { 1, "Flag1" },
+            { 2, "Flag2" }
+        };
+
+        flagSelector.SetFlags(flags);
+
+        Assert.Equal(flags, flagSelector.Flags);
+    }
+
+    [Fact]
+    public void SetFlags_WithEnum_ShouldSetFlags()
+    {
+        var flagSelector = new FlagSelector();
+
+        flagSelector.SetFlags<FlagSelectorStyles>();
+
+        var expectedFlags = Enum.GetValues<FlagSelectorStyles>()
+                                .ToDictionary(f => Convert.ToUInt32(f), f => f.ToString());
+
+        Assert.Equal(expectedFlags, flagSelector.Flags);
+    }
+
+    [Fact]
+    public void SetFlags_WithEnumAndCustomNames_ShouldSetFlags()
+    {
+        var flagSelector = new FlagSelector();
+
+        flagSelector.SetFlags<FlagSelectorStyles>(f => f switch
+        {
+            FlagSelectorStyles.ShowNone => "Show None Value",
+            FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
+            FlagSelectorStyles.All => "Everything",
+            _ => f.ToString()
+        });
+
+        var expectedFlags = Enum.GetValues<FlagSelectorStyles>()
+                                .ToDictionary(f => Convert.ToUInt32(f), f => f switch
+                                {
+                                    FlagSelectorStyles.ShowNone => "Show None Value",
+                                    FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
+                                    FlagSelectorStyles.All => "Everything",
+                                    _ => f.ToString()
+                                });
+
+        Assert.Equal(expectedFlags, flagSelector.Flags);
+    }
+
+    [Fact]
+    public void Value_Set_ShouldUpdateCheckedState()
+    {
+        var flagSelector = new FlagSelector();
+        var flags = new Dictionary<uint, string>
+        {
+            { 1, "Flag1" },
+            { 2, "Flag2" }
+        };
+
+        flagSelector.SetFlags(flags);
+        flagSelector.Value = 1;
+
+        var checkBox = flagSelector.SubViews.OfType<CheckBox>().First(cb => (uint)cb.Data == 1);
+        Assert.Equal(CheckState.Checked, checkBox.CheckedState);
+
+        checkBox = flagSelector.SubViews.OfType<CheckBox>().First(cb => (uint)cb.Data == 2);
+        Assert.Equal(CheckState.UnChecked, checkBox.CheckedState);
+    }
+
+    [Fact]
+    public void Styles_Set_ShouldCreateSubViews()
+    {
+        var flagSelector = new FlagSelector();
+        var flags = new Dictionary<uint, string>
+        {
+            { 1, "Flag1" },
+            { 2, "Flag2" }
+        };
+
+        flagSelector.SetFlags(flags);
+        flagSelector.Styles = FlagSelectorStyles.ShowNone;
+
+        Assert.Contains(flagSelector.SubViews, sv => sv is CheckBox cb && cb.Title == "None");
+    }
+
+    [Fact]
+    public void ValueChanged_Event_ShouldBeRaised()
+    {
+        var flagSelector = new FlagSelector();
+        var flags = new Dictionary<uint, string>
+        {
+            { 1, "Flag1" },
+            { 2, "Flag2" }
+        };
+
+        flagSelector.SetFlags(flags);
+        bool eventRaised = false;
+        flagSelector.ValueChanged += (sender, args) => eventRaised = true;
+
+        flagSelector.Value = 1;
+
+        Assert.True(eventRaised);
+    }
+}

+ 22 - 0
Tests/UnitTestsParallelizable/Views/MenuBarItemTests.cs

@@ -0,0 +1,22 @@
+using Xunit.Abstractions;
+
+//using static Terminal.Gui.ViewTests.MenuTests;
+
+namespace Terminal.Gui.ViewsTests;
+
+public class MenuBarItemTests ()
+{
+    [Fact]
+    public void Constructors_Defaults ()
+    {
+        var menuBarItem = new MenuBarItemv2 ();
+        Assert.Null (menuBarItem.PopoverMenu);
+        Assert.Null (menuBarItem.TargetView);
+
+        menuBarItem = new MenuBarItemv2 (targetView: null, command: Command.NotBound, commandText: null, popoverMenu: null);
+        Assert.Null (menuBarItem.PopoverMenu);
+        Assert.Null (menuBarItem.TargetView);
+
+
+    }
+}

+ 14 - 0
Tests/UnitTestsParallelizable/Views/MenuItemTests.cs

@@ -0,0 +1,14 @@
+using Xunit.Abstractions;
+
+//using static Terminal.Gui.ViewTests.MenuTests;
+
+namespace Terminal.Gui.ViewsTests;
+
+public class MenuItemTests ()
+{
+    [Fact]
+    public void Constructors_Defaults ()
+    {
+
+    }
+}

+ 17 - 0
Tests/UnitTestsParallelizable/Views/MenuTests.cs

@@ -0,0 +1,17 @@
+using Xunit.Abstractions;
+
+//using static Terminal.Gui.ViewTests.MenuTests;
+
+namespace Terminal.Gui.ViewsTests;
+
+public class MenuTests ()
+{
+    [Fact]
+    public void Constructors_Defaults ()
+    {
+        var menu = new Menuv2 { };
+        Assert.Empty (menu.Title);
+        Assert.Empty (menu.Text);
+    }
+
+}

+ 0 - 211
UICatalog/KeyBindingsDialog.cs

@@ -1,211 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using Terminal.Gui;
-
-namespace UICatalog;
-
-internal class KeyBindingsDialog : Dialog
-{
-    // TODO: Update to use Key instead of KeyCode
-    private static readonly Dictionary<Command, KeyCode> CurrentBindings = new ();
-
-    private readonly ObservableCollection<Command> _commands;
-    private readonly ListView _commandsListView;
-    private readonly Label _keyLabel;
-
-    public KeyBindingsDialog ()
-    {
-        Title = "Keybindings";
-
-        //Height = Dim.Percent (80);
-        //Width = Dim.Percent (80);
-        if (ViewTracker.Instance == null)
-        {
-            ViewTracker.Initialize ();
-        }
-
-        // known commands that views can support
-        _commands = new (Enum.GetValues (typeof (Command)).Cast<Command> ().ToArray ());
-
-        _commandsListView = new ListView
-        {
-            Width = Dim.Percent (50),
-            Height = Dim.Fill (Dim.Func (() => IsInitialized ? SubViews.First (view => view.Y.Has<PosAnchorEnd> (out _)).Frame.Height : 1)),
-            Source = new ListWrapper<Command> (_commands),
-            SelectedItem = 0
-        };
-
-        Add (_commandsListView);
-
-        _keyLabel = new Label { Text = "Key: None", Width = Dim.Fill (), X = Pos.Percent (50), Y = 0 };
-        Add (_keyLabel);
-
-        var btnChange = new Button { X = Pos.Percent (50), Y = 1, Text = "Ch_ange" };
-        Add (btnChange);
-        btnChange.Accepting += RemapKey;
-
-        var close = new Button { Text = "Ok" };
-
-        close.Accepting += (s, e) =>
-                         {
-                             Application.RequestStop ();
-                             ViewTracker.Instance.StartUsingNewKeyMap (CurrentBindings);
-                         };
-        AddButton (close);
-
-        var cancel = new Button { Text = "Cancel" };
-        cancel.Accepting += (s, e) => Application.RequestStop ();
-        AddButton (cancel);
-
-        // Register event handler as the last thing in constructor to prevent early calls
-        // before it is even shown (e.g. OnHasFocusChanging)
-        _commandsListView.SelectedItemChanged += CommandsListView_SelectedItemChanged;
-
-        // Setup to show first ListView entry
-        SetTextBoxToShowBinding (_commands.First ());
-    }
-
-    private void CommandsListView_SelectedItemChanged (object sender, ListViewItemEventArgs obj) { SetTextBoxToShowBinding ((Command)obj.Value); }
-
-    private void RemapKey (object sender, EventArgs e)
-    {
-        Command cmd = _commands [_commandsListView.SelectedItem];
-        KeyCode? key = null;
-
-        // prompt user to hit a key
-        var dlg = new Dialog { Title = "Enter Key" };
-
-        dlg.KeyDown += (s, k) =>
-                       {
-                           key = k.KeyCode;
-                           Application.RequestStop ();
-                       };
-        Application.Run (dlg);
-        dlg.Dispose ();
-
-        if (key.HasValue)
-        {
-            CurrentBindings [cmd] = key.Value;
-            SetTextBoxToShowBinding (cmd);
-        }
-    }
-
-    private void SetTextBoxToShowBinding (Command cmd)
-    {
-        if (CurrentBindings.ContainsKey (cmd))
-        {
-            _keyLabel.Text = "Key: " + CurrentBindings [cmd];
-        }
-        else
-        {
-            _keyLabel.Text = "Key: None";
-        }
-
-        SetNeedsDraw ();
-    }
-
-    /// <summary>Tracks views as they are created in UICatalog so that their keybindings can be managed.</summary>
-    private class ViewTracker
-    {
-        /// <summary>All views seen so far and a bool to indicate if we have applied keybindings to them</summary>
-        private readonly Dictionary<View, bool> _knownViews = new ();
-
-        private readonly object _lockKnownViews = new ();
-        private Dictionary<Command, KeyCode> _keybindings;
-
-        private ViewTracker (View top)
-        {
-            RecordView (top);
-
-            // Refresh known windows
-            Application.AddTimeout (
-                                    TimeSpan.FromMilliseconds (100),
-                                    () =>
-                                    {
-                                        lock (_lockKnownViews)
-                                        {
-                                            RecordView (Application.Top);
-
-                                            ApplyKeyBindingsToAllKnownViews ();
-                                        }
-
-                                        return true;
-                                    }
-                                   );
-        }
-
-        public static ViewTracker Instance { get; private set; }
-        internal static void Initialize () { Instance = new ViewTracker (Application.Top); }
-
-        internal void StartUsingNewKeyMap (Dictionary<Command, KeyCode> currentBindings)
-        {
-            lock (_lockKnownViews)
-            {
-                // change our knowledge of what keys to bind
-                _keybindings = currentBindings;
-
-                // Mark that we have not applied the key bindings yet to any views
-                foreach (View view in _knownViews.Keys)
-                {
-                    _knownViews [view] = false;
-                }
-            }
-        }
-
-        private void ApplyKeyBindingsToAllKnownViews ()
-        {
-            if (_keybindings == null)
-            {
-                return;
-            }
-
-            // Key is the view Value is whether we have already done it
-            foreach (KeyValuePair<View, bool> viewDone in _knownViews)
-            {
-                View view = viewDone.Key;
-                bool done = viewDone.Value;
-
-                if (done)
-                {
-                    // we have already applied keybindings to this view
-                    continue;
-                }
-
-                HashSet<Command> supported = new (view.GetSupportedCommands ());
-
-                foreach (KeyValuePair<Command, KeyCode> kvp in _keybindings)
-                {
-                    // if the view supports the keybinding
-                    if (supported.Contains (kvp.Key))
-                    {
-                        // if the key was bound to any other commands clear that
-                        view.KeyBindings.Remove (kvp.Value);
-                        view.KeyBindings.Add (kvp.Value, kvp.Key);
-                    }
-
-                    // mark that we have done this view so don't need to set keybindings again on it
-                    _knownViews [view] = true;
-                }
-            }
-        }
-
-        private void RecordView (View view)
-        {
-            if (!_knownViews.ContainsKey (view))
-            {
-                _knownViews.Add (view, false);
-            }
-
-            // may already have subviews that were added to it
-            // before we got to it
-            foreach (View sub in view.SubViews)
-            {
-                RecordView (sub);
-            }
-
-            view.SubViewAdded += (s, e) => RecordView (e.SubView);
-        }
-    }
-}

+ 11 - 8
UICatalog/Scenarios/CharacterMap/CharacterMap.cs

@@ -146,13 +146,13 @@ public class CharacterMap : Scenario
 
 
         top.Add (_categoryList);
         top.Add (_categoryList);
 
 
-        var menu = new MenuBar
+        var menu = new MenuBarv2
         {
         {
             Menus =
             Menus =
             [
             [
                 new (
                 new (
                      "_File",
                      "_File",
-                     new MenuItem []
+                     new MenuItemv2 []
                      {
                      {
                          new (
                          new (
                               "_Quit",
                               "_Quit",
@@ -163,7 +163,7 @@ public class CharacterMap : Scenario
                     ),
                     ),
                 new (
                 new (
                      "_Options",
                      "_Options",
-                     new [] { CreateMenuShowWidth () }
+                     new MenuItemv2 [] { CreateMenuShowWidth () }
                     )
                     )
             ]
             ]
         };
         };
@@ -305,16 +305,19 @@ public class CharacterMap : Scenario
                    );
                    );
     }
     }
 
 
-    private MenuItem CreateMenuShowWidth ()
+    private MenuItemv2 CreateMenuShowWidth ()
     {
     {
-        var item = new MenuItem { Title = "_Show Glyph Width" };
-        item.CheckType |= MenuItemCheckStyle.Checked;
-        item.Checked = _charMap?.ShowGlyphWidths;
+        CheckBox cb = new ()
+        {
+            Title = "_Show Glyph Width",
+            CheckedState = _charMap!.ShowGlyphWidths ? CheckState.Checked : CheckState.None
+        };
+        var item = new MenuItemv2 { CommandView = cb };
         item.Action += () =>
         item.Action += () =>
                        {
                        {
                            if (_charMap is { })
                            if (_charMap is { })
                            {
                            {
-                               _charMap.ShowGlyphWidths = (bool)(item.Checked = !item.Checked)!;
+                               _charMap.ShowGlyphWidths = cb.CheckedState == CheckState.Checked;
                            }
                            }
                        };
                        };
 
 

+ 24 - 11
UICatalog/Scenarios/HexEditor.cs

@@ -1,5 +1,4 @@
-using System.IO;
-using System.Text;
+using System.Text;
 using Terminal.Gui;
 using Terminal.Gui;
 
 
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
@@ -14,7 +13,7 @@ public class HexEditor : Scenario
 {
 {
     private string _fileName = "demo.bin";
     private string _fileName = "demo.bin";
     private HexView _hexView;
     private HexView _hexView;
-    private MenuItem _miAllowEdits;
+    private MenuItemv2 _miAllowEdits;
     private bool _saved = true;
     private bool _saved = true;
     private Shortcut _scAddress;
     private Shortcut _scAddress;
     private Shortcut _scInfo;
     private Shortcut _scInfo;
@@ -48,13 +47,13 @@ public class HexEditor : Scenario
 
 
         app.Add (_hexView);
         app.Add (_hexView);
 
 
-        var menu = new MenuBar
+        var menu = new MenuBarv2
         {
         {
             Menus =
             Menus =
             [
             [
                 new (
                 new (
                      "_File",
                      "_File",
-                     new MenuItem []
+                     new MenuItemv2 []
                      {
                      {
                          new ("_New", "", () => New ()),
                          new ("_New", "", () => New ()),
                          new ("_Open", "", () => Open ()),
                          new ("_Open", "", () => Open ()),
@@ -65,7 +64,7 @@ public class HexEditor : Scenario
                     ),
                     ),
                 new (
                 new (
                      "_Edit",
                      "_Edit",
-                     new MenuItem []
+                     new MenuItemv2 []
                      {
                      {
                          new ("_Copy", "", () => Copy ()),
                          new ("_Copy", "", () => Copy ()),
                          new ("C_ut", "", () => Cut ()),
                          new ("C_ut", "", () => Cut ()),
@@ -74,7 +73,7 @@ public class HexEditor : Scenario
                     ),
                     ),
                 new (
                 new (
                      "_Options",
                      "_Options",
-                     new []
+                     new MenuItemv2 []
                      {
                      {
                          _miAllowEdits = new (
                          _miAllowEdits = new (
                                               "_AllowEdits",
                                               "_AllowEdits",
@@ -82,14 +81,19 @@ public class HexEditor : Scenario
                                               () => ToggleAllowEdits ()
                                               () => ToggleAllowEdits ()
                                              )
                                              )
                          {
                          {
-                             Checked = _hexView.AllowEdits,
-                             CheckType = MenuItemCheckStyle
-                                 .Checked
+
                          }
                          }
                      }
                      }
                     )
                     )
             ]
             ]
         };
         };
+
+        CheckBox cb = new CheckBox ()
+        {
+            Title = _miAllowEdits.Title,
+            CheckedState = _hexView.AllowEdits ? CheckState.Checked : CheckState.None,
+        };
+        _miAllowEdits.CommandView = cb;
         app.Add (menu);
         app.Add (menu);
 
 
         var addressWidthUpDown = new NumericUpDown
         var addressWidthUpDown = new NumericUpDown
@@ -285,5 +289,14 @@ public class HexEditor : Scenario
         }
         }
     }
     }
 
 
-    private void ToggleAllowEdits () { _hexView.AllowEdits = (bool)(_miAllowEdits.Checked = !_miAllowEdits.Checked); }
+    private void ToggleAllowEdits ()
+    {
+        CheckBox? cb = _miAllowEdits.CommandView as CheckBox;
+        if (cb is null)
+        {
+            return;
+        }
+
+        _hexView.AllowEdits = cb.CheckedState == CheckState.Checked;
+    }
 }
 }

+ 1 - 1
UICatalog/Scenarios/MenusV2.cs

@@ -439,7 +439,7 @@ public class MenusV2 : Scenario
 
 
             _menuBgColorCp.ColorChanged += (sender, args) =>
             _menuBgColorCp.ColorChanged += (sender, args) =>
                                            {
                                            {
-                                               menu.ColorScheme = menu.ColorScheme with
+                                               menu.ColorScheme = menu.ColorScheme! with
                                                {
                                                {
                                                    Normal = new (menu.ColorScheme.Normal.Foreground, args.CurrentValue)
                                                    Normal = new (menu.ColorScheme.Normal.Foreground, args.CurrentValue)
                                                };
                                                };

+ 193 - 1124
UICatalog/UICatalog.cs

@@ -1,7 +1,4 @@
 global using Attribute = Terminal.Gui.Attribute;
 global using Attribute = Terminal.Gui.Attribute;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.CommandLine;
 using System.CommandLine;
 using System.CommandLine.Builder;
 using System.CommandLine.Builder;
 using System.CommandLine.Parsing;
 using System.CommandLine.Parsing;
@@ -9,29 +6,25 @@ using System.Data;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Globalization;
-using System.IO;
-using System.Linq;
 using System.Reflection;
 using System.Reflection;
-using System.Runtime.InteropServices;
 using System.Text;
 using System.Text;
 using System.Text.Json;
 using System.Text.Json;
-using System.Text.Json.Serialization;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
-using static Terminal.Gui.ConfigurationManager;
-using Command = Terminal.Gui.Command;
 using Serilog;
 using Serilog;
 using Serilog.Core;
 using Serilog.Core;
 using Serilog.Events;
 using Serilog.Events;
-using ILogger = Microsoft.Extensions.Logging.ILogger;
-using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
 using Terminal.Gui;
 using Terminal.Gui;
+using static Terminal.Gui.ConfigurationManager;
+using Command = Terminal.Gui.Command;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
 
 
 #nullable enable
 #nullable enable
 
 
 namespace UICatalog;
 namespace UICatalog;
 
 
 /// <summary>
 /// <summary>
-///     UI Catalog is a comprehensive sample library and test app for Terminal.Gui. It provides a simple UI for adding to the
+///     UI Catalog is a comprehensive sample library and test app for Terminal.Gui. It provides a simple UI for adding to
+///     the
 ///     catalog of scenarios.
 ///     catalog of scenarios.
 /// </summary>
 /// </summary>
 /// <remarks>
 /// <remarks>
@@ -50,85 +43,14 @@ namespace UICatalog;
 ///         </list>
 ///         </list>
 ///     </para>
 ///     </para>
 /// </remarks>
 /// </remarks>
-public class UICatalogApp
+public class UICatalog
 {
 {
-    private static int _cachedCategoryIndex;
-
-    // When a scenario is run, the main app is killed. These items
-    // are therefore cached so that when the scenario exits the
-    // main app UI can be restored to previous state
-    private static int _cachedScenarioIndex;
-    private static string? _cachedTheme = string.Empty;
-    private static ObservableCollection<string>? _categories;
-
-    [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
-    private static readonly FileSystemWatcher _currentDirWatcher = new ();
-
-    private static ViewDiagnosticFlags _diagnosticFlags;
     private static string _forceDriver = string.Empty;
     private static string _forceDriver = string.Empty;
 
 
-    [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
-    private static readonly FileSystemWatcher _homeDirWatcher = new ();
-
-    private static bool _isFirstRunning = true;
-    private static Options _options;
-    private static ObservableCollection<Scenario>? _scenarios;
-
-    private const string LOGFILE_LOCATION = "logs";
-    private static string _logFilePath = string.Empty;
-    private static readonly LoggingLevelSwitch _logLevelSwitch = new ();
-
-    // If set, holds the scenario the user selected
-    private static Scenario? _selectedScenario;
-    private static MenuBarItem? _themeMenuBarItem;
-    private static MenuItem []? _themeMenuItems;
-    private static string _topLevelColorScheme = string.Empty;
-
-    [SerializableConfigurationProperty (Scope = typeof (AppScope), OmitClassName = true)]
-    [JsonPropertyName ("UICatalog.StatusBar")]
-    public static bool ShowStatusBar { get; set; } = true;
-
-    /// <summary>
-    ///     Gets the message displayed in the About Box. `public` so it can be used from Unit tests.
-    /// </summary>
-    /// <returns></returns>
-    public static string GetAboutBoxMessage ()
-    {
-        // NOTE: Do not use multiline verbatim strings here.
-        // WSL gets all confused.
-        StringBuilder msg = new ();
-        msg.AppendLine ("UI Catalog: A comprehensive sample library and test app for");
-        msg.AppendLine ();
-
-        msg.AppendLine (
-                        """
-                         _______                  _             _   _____       _ 
-                        |__   __|                (_)           | | / ____|     (_)
-                           | | ___ _ __ _ __ ___  _ _ __   __ _| || |  __ _   _ _ 
-                           | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | |
-                           | |  __/ |  | | | | | | | | | | (_| | || |__| | |_| | |
-                           |_|\___|_|  |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_|
-                        """);
-        msg.AppendLine ();
-        msg.AppendLine ("v2 - Pre-Alpha");
-        msg.AppendLine ();
-        msg.AppendLine ("https://github.com/gui-cs/Terminal.Gui");
-
-        return msg.ToString ();
-    }
-
-    private static void ConfigFileChanged (object sender, FileSystemEventArgs e)
-    {
-        if (Application.Top == null)
-        {
-            return;
-        }
-
-        // TODO: This is a hack. Figure out how to ensure that the file is fully written before reading it.
-        //Thread.Sleep (500);
-        Load ();
-        Apply ();
-    }
+    public static string LogFilePath { get; set; } = string.Empty;
+    public static LoggingLevelSwitch LogLevelSwitch { get; } = new ();
+    public const string LOGFILE_LOCATION = "logs";
+    public static UICatalogCommandLineOptions Options { get; set; }
 
 
     private static int Main (string [] args)
     private static int Main (string [] args)
     {
     {
@@ -139,18 +61,18 @@ public class UICatalogApp
             CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
             CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
         }
         }
 
 
-        _scenarios = Scenario.GetScenarios ();
-        _categories = Scenario.GetAllCategories ();
+        UICatalogTopLevel.CachedScenarios = Scenario.GetScenarios ();
+        UICatalogTopLevel.CachedCategories = Scenario.GetAllCategories ();
 
 
         // Process command line args
         // Process command line args
 
 
         // If no driver is provided, the default driver is used.
         // If no driver is provided, the default driver is used.
         Option<string> driverOption = new Option<string> ("--driver", "The IConsoleDriver to use.").FromAmong (
         Option<string> driverOption = new Option<string> ("--driver", "The IConsoleDriver to use.").FromAmong (
              Application.GetDriverTypes ()
              Application.GetDriverTypes ()
-                            .Where (d=>!typeof (IConsoleDriverFacade).IsAssignableFrom (d))
-                            .Select (d => d!.Name)
-                            .Union (["v2","v2win","v2net"])
-                            .ToArray ()
+                        .Where (d => !typeof (IConsoleDriverFacade).IsAssignableFrom (d))
+                        .Select (d => d!.Name)
+                        .Union (["v2", "v2win", "v2net"])
+                        .ToArray ()
             );
             );
         driverOption.AddAlias ("-d");
         driverOption.AddAlias ("-d");
         driverOption.AddAlias ("--d");
         driverOption.AddAlias ("--d");
@@ -171,11 +93,12 @@ public class UICatalogApp
         resultsFile.AddAlias ("--f");
         resultsFile.AddAlias ("--f");
 
 
         // what's the app name?
         // what's the app name?
-        _logFilePath = $"{LOGFILE_LOCATION}/{Assembly.GetExecutingAssembly ().GetName ().Name}";
-        Option<string> debugLogLevel = new Option<string> ("--debug-log-level", $"The level to use for logging (debug console and {_logFilePath})").FromAmong (
+        LogFilePath = $"{LOGFILE_LOCATION}/{Assembly.GetExecutingAssembly ().GetName ().Name}";
+
+        Option<string> debugLogLevel = new Option<string> ("--debug-log-level", $"The level to use for logging (debug console and {LogFilePath})").FromAmong (
              Enum.GetNames<LogLevel> ()
              Enum.GetNames<LogLevel> ()
             );
             );
-        debugLogLevel.SetDefaultValue("Warning");
+        debugLogLevel.SetDefaultValue ("Warning");
         debugLogLevel.AddAlias ("-dl");
         debugLogLevel.AddAlias ("-dl");
         debugLogLevel.AddAlias ("--dl");
         debugLogLevel.AddAlias ("--dl");
 
 
@@ -185,9 +108,9 @@ public class UICatalogApp
                                                                   "The name of the Scenario to run. If not provided, the UI Catalog UI will be shown.",
                                                                   "The name of the Scenario to run. If not provided, the UI Catalog UI will be shown.",
                                                                   getDefaultValue: () => "none"
                                                                   getDefaultValue: () => "none"
                                                                  ).FromAmong (
                                                                  ).FromAmong (
-                                                                              _scenarios.Select (s => s.GetName ())
-                                                                                        .Append ("none")
-                                                                                        .ToArray ()
+                                                                              UICatalogTopLevel.CachedScenarios.Select (s => s.GetName ())
+                                                                                               .Append ("none")
+                                                                                               .ToArray ()
                                                                              );
                                                                              );
 
 
         var rootCommand = new RootCommand ("A comprehensive sample library and test app for Terminal.Gui")
         var rootCommand = new RootCommand ("A comprehensive sample library and test app for Terminal.Gui")
@@ -198,7 +121,7 @@ public class UICatalogApp
         rootCommand.SetHandler (
         rootCommand.SetHandler (
                                 context =>
                                 context =>
                                 {
                                 {
-                                    var options = new Options
+                                    var options = new UICatalogCommandLineOptions
                                     {
                                     {
                                         Scenario = context.ParseResult.GetValueForArgument (scenarioArgument),
                                         Scenario = context.ParseResult.GetValueForArgument (scenarioArgument),
                                         Driver = context.ParseResult.GetValueForOption (driverOption) ?? string.Empty,
                                         Driver = context.ParseResult.GetValueForOption (driverOption) ?? string.Empty,
@@ -210,7 +133,7 @@ public class UICatalogApp
                                     };
                                     };
 
 
                                     // See https://github.com/dotnet/command-line-api/issues/796 for the rationale behind this hackery
                                     // See https://github.com/dotnet/command-line-api/issues/796 for the rationale behind this hackery
-                                    _options = options;
+                                    Options = options;
                                 }
                                 }
                                );
                                );
 
 
@@ -227,16 +150,16 @@ public class UICatalogApp
             return 0;
             return 0;
         }
         }
 
 
-        Scenario.BenchmarkTimeout = _options.BenchmarkTimeout;
+        Scenario.BenchmarkTimeout = Options.BenchmarkTimeout;
 
 
         Logging.Logger = CreateLogger ();
         Logging.Logger = CreateLogger ();
 
 
-        UICatalogMain (_options);
+        UICatalogMain (Options);
 
 
         return 0;
         return 0;
     }
     }
 
 
-    private static LogEventLevel LogLevelToLogEventLevel (LogLevel logLevel)
+    public static LogEventLevel LogLevelToLogEventLevel (LogLevel logLevel)
     {
     {
         return logLevel switch
         return logLevel switch
                {
                {
@@ -254,13 +177,14 @@ public class UICatalogApp
     private static ILogger CreateLogger ()
     private static ILogger CreateLogger ()
     {
     {
         // Configure Serilog to write logs to a file
         // Configure Serilog to write logs to a file
-        _logLevelSwitch.MinimumLevel = LogLevelToLogEventLevel(Enum.Parse<LogLevel> (_options.DebugLogLevel));
+        LogLevelSwitch.MinimumLevel = LogLevelToLogEventLevel (Enum.Parse<LogLevel> (Options.DebugLogLevel));
+
         Log.Logger = new LoggerConfiguration ()
         Log.Logger = new LoggerConfiguration ()
-                     .MinimumLevel.ControlledBy (_logLevelSwitch)
+                     .MinimumLevel.ControlledBy (LogLevelSwitch)
                      .Enrich.FromLogContext () // Enables dynamic enrichment
                      .Enrich.FromLogContext () // Enables dynamic enrichment
                      .WriteTo.Debug ()
                      .WriteTo.Debug ()
                      .WriteTo.File (
                      .WriteTo.File (
-                                    _logFilePath,
+                                    LogFilePath,
                                     rollingInterval: RollingInterval.Day,
                                     rollingInterval: RollingInterval.Day,
                                     outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
                                     outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
                      .CreateLogger ();
                      .CreateLogger ();
@@ -278,35 +202,6 @@ public class UICatalogApp
         return loggerFactory.CreateLogger ("Global Logger");
         return loggerFactory.CreateLogger ("Global Logger");
     }
     }
 
 
-    public static void OpenUrl (string url)
-    {
-        if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows))
-        {
-            url = url.Replace ("&", "^&");
-            Process.Start (new ProcessStartInfo ("cmd", $"/c start {url}") { CreateNoWindow = true });
-        }
-        else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux))
-        {
-            using var process = new Process
-            {
-                StartInfo = new ()
-                {
-                    FileName = "xdg-open",
-                    Arguments = url,
-                    RedirectStandardError = true,
-                    RedirectStandardOutput = true,
-                    CreateNoWindow = true,
-                    UseShellExecute = false
-                }
-            };
-            process.Start ();
-        }
-        else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX))
-        {
-            Process.Start ("open", url);
-        }
-    }
-
     /// <summary>
     /// <summary>
     ///     Shows the UI Catalog selection UI. When the user selects a Scenario to run, the UI Catalog main app UI is
     ///     Shows the UI Catalog selection UI. When the user selects a Scenario to run, the UI Catalog main app UI is
     ///     killed and the Scenario is run as though it were Application.Top. When the Scenario exits, this function exits.
     ///     killed and the Scenario is run as though it were Application.Top. When the Scenario exits, this function exits.
@@ -322,25 +217,31 @@ public class UICatalogApp
 
 
         Application.Init (driverName: _forceDriver);
         Application.Init (driverName: _forceDriver);
 
 
-        if (_cachedTheme is null)
+        if (string.IsNullOrWhiteSpace (UICatalogTopLevel.CachedTheme))
         {
         {
-            _cachedTheme = Themes?.Theme;
+            UICatalogTopLevel.CachedTheme = Themes?.Theme;
         }
         }
         else
         else
         {
         {
-            Themes!.Theme = _cachedTheme;
+            Themes!.Theme = UICatalogTopLevel.CachedTheme;
             Apply ();
             Apply ();
         }
         }
 
 
         Application.Run<UICatalogTopLevel> ().Dispose ();
         Application.Run<UICatalogTopLevel> ().Dispose ();
         Application.Shutdown ();
         Application.Shutdown ();
 
 
-        return _selectedScenario!;
+        return UICatalogTopLevel.CachedSelectedScenario!;
     }
     }
 
 
+    [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
+    private static readonly FileSystemWatcher _currentDirWatcher = new ();
+
+    [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
+    private static readonly FileSystemWatcher _homeDirWatcher = new ();
+
     private static void StartConfigFileWatcher ()
     private static void StartConfigFileWatcher ()
     {
     {
-        // Setup a file system watcher for `./.tui/`
+        // Set up a file system watcher for `./.tui/`
         _currentDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
         _currentDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
 
 
         string assemblyLocation = Assembly.GetExecutingAssembly ().Location;
         string assemblyLocation = Assembly.GetExecutingAssembly ().Location;
@@ -364,7 +265,7 @@ public class UICatalogApp
         _currentDirWatcher.Path = tuiDir;
         _currentDirWatcher.Path = tuiDir;
         _currentDirWatcher.Filter = "*config.json";
         _currentDirWatcher.Filter = "*config.json";
 
 
-        // Setup a file system watcher for `~/.tui/`
+        // Set up a file system watcher for `~/.tui/`
         _homeDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
         _homeDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
         var f = new FileInfo (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile));
         var f = new FileInfo (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile));
         tuiDir = Path.Combine (f.FullName, ".tui");
         tuiDir = Path.Combine (f.FullName, ".tui");
@@ -399,7 +300,18 @@ public class UICatalogApp
         _homeDirWatcher.Created -= ConfigFileChanged;
         _homeDirWatcher.Created -= ConfigFileChanged;
     }
     }
 
 
-    private static void UICatalogMain (Options options)
+    private static void ConfigFileChanged (object sender, FileSystemEventArgs e)
+    {
+        if (Application.Top == null)
+        {
+            return;
+        }
+
+        Load ();
+        Apply ();
+    }
+
+    private static void UICatalogMain (UICatalogCommandLineOptions options)
     {
     {
         StartConfigFileWatcher ();
         StartConfigFileWatcher ();
 
 
@@ -411,17 +323,15 @@ public class UICatalogApp
         // run it and exit when done.
         // run it and exit when done.
         if (options.Scenario != "none")
         if (options.Scenario != "none")
         {
         {
-            _topLevelColorScheme = "Base";
-
-            int item = _scenarios!.IndexOf (
-                                            _scenarios!.FirstOrDefault (
+            int item = UICatalogTopLevel.CachedScenarios!.IndexOf (
+                                                                   UICatalogTopLevel.CachedScenarios!.FirstOrDefault (
                                                                         s =>
                                                                         s =>
                                                                             s.GetName ()
                                                                             s.GetName ()
                                                                              .Equals (options.Scenario, StringComparison.OrdinalIgnoreCase)
                                                                              .Equals (options.Scenario, StringComparison.OrdinalIgnoreCase)
                                                                        )!);
                                                                        )!);
-            _selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ())!;
+            UICatalogTopLevel.CachedSelectedScenario = (Scenario)Activator.CreateInstance (UICatalogTopLevel.CachedScenarios [item].GetType ())!;
 
 
-            BenchmarkResults? results = RunScenario (_selectedScenario, options.Benchmark);
+            BenchmarkResults? results = RunScenario (UICatalogTopLevel.CachedSelectedScenario, options.Benchmark);
 
 
             if (results is { })
             if (results is { })
             {
             {
@@ -450,12 +360,13 @@ public class UICatalogApp
         while (RunUICatalogTopLevel () is { } scenario)
         while (RunUICatalogTopLevel () is { } scenario)
         {
         {
             VerifyObjectsWereDisposed ();
             VerifyObjectsWereDisposed ();
-            Themes!.Theme = _cachedTheme!;
+            Themes!.Theme = UICatalogTopLevel.CachedTheme!;
             Apply ();
             Apply ();
-            scenario.TopLevelColorScheme = _topLevelColorScheme;
+            scenario.TopLevelColorScheme = UICatalogTopLevel.CachedTopLevelColorScheme!;
 
 
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
             View.DebugIDisposable = true;
             View.DebugIDisposable = true;
+
             // Measure how long it takes for the app to shut down
             // Measure how long it takes for the app to shut down
             var sw = new Stopwatch ();
             var sw = new Stopwatch ();
             string scenarioName = scenario.GetName ();
             string scenarioName = scenario.GetName ();
@@ -501,7 +412,7 @@ public class UICatalogApp
         }
         }
 
 
         Application.Init (driverName: _forceDriver);
         Application.Init (driverName: _forceDriver);
-        scenario.TopLevelColorScheme = _topLevelColorScheme;
+        scenario.TopLevelColorScheme = UICatalogTopLevel.CachedTopLevelColorScheme!;
 
 
         if (benchmark)
         if (benchmark)
         {
         {
@@ -527,11 +438,11 @@ public class UICatalogApp
 
 
     private static void BenchmarkAllScenarios ()
     private static void BenchmarkAllScenarios ()
     {
     {
-        List<BenchmarkResults> resultsList = new ();
+        List<BenchmarkResults> resultsList = [];
 
 
         var maxScenarios = 5;
         var maxScenarios = 5;
 
 
-        foreach (Scenario s in _scenarios!)
+        foreach (Scenario s in UICatalogTopLevel.CachedScenarios!)
         {
         {
             resultsList.Add (RunScenario (s, true)!);
             resultsList.Add (RunScenario (s, true)!);
             maxScenarios--;
             maxScenarios--;
@@ -542,144 +453,146 @@ public class UICatalogApp
             }
             }
         }
         }
 
 
-        if (resultsList.Count > 0)
+        if (resultsList.Count <= 0)
         {
         {
-            if (!string.IsNullOrEmpty (_options.ResultsFile))
-            {
-                string output = JsonSerializer.Serialize (
-                                                          resultsList,
-                                                          new JsonSerializerOptions
-                                                          {
-                                                              WriteIndented = true
-                                                          });
-
-                using StreamWriter file = File.CreateText (_options.ResultsFile);
-                file.Write (output);
-                file.Close ();
-
-                return;
-            }
+            return;
+        }
 
 
-            Application.Init ();
+        if (!string.IsNullOrEmpty (Options.ResultsFile))
+        {
+            string output = JsonSerializer.Serialize (
+                                                      resultsList,
+                                                      new JsonSerializerOptions
+                                                      {
+                                                          WriteIndented = true
+                                                      });
 
 
-            var benchmarkWindow = new Window
-            {
-                Title = "Benchmark Results"
-            };
+            using StreamWriter file = File.CreateText (Options.ResultsFile);
+            file.Write (output);
+            file.Close ();
 
 
-            if (benchmarkWindow.Border is { })
-            {
-                benchmarkWindow.Border.Thickness = new (0, 0, 0, 0);
-            }
+            return;
+        }
 
 
-            TableView resultsTableView = new ()
-            {
-                Width = Dim.Fill (),
-                Height = Dim.Fill ()
-            };
-
-            // TableView provides many options for table headers. For simplicity we turn all 
-            // of these off. By enabling FullRowSelect and turning off headers, TableView looks just
-            // like a ListView
-            resultsTableView.FullRowSelect = true;
-            resultsTableView.Style.ShowHeaders = true;
-            resultsTableView.Style.ShowHorizontalHeaderOverline = false;
-            resultsTableView.Style.ShowHorizontalHeaderUnderline = true;
-            resultsTableView.Style.ShowHorizontalBottomline = false;
-            resultsTableView.Style.ShowVerticalCellLines = true;
-            resultsTableView.Style.ShowVerticalHeaderLines = true;
-
-            /* By default TableView lays out columns at render time and only
-             * measures y rows of data at a time.  Where y is the height of the
-             * console. This is for the following reasons:
-             *
-             * - Performance, when tables have a large amount of data
-             * - Defensive, prevents a single wide cell value pushing other
-             *   columns off screen (requiring horizontal scrolling
-             *
-             * In the case of UICatalog here, such an approach is overkill so
-             * we just measure all the data ourselves and set the appropriate
-             * max widths as ColumnStyles
-             */
-            //int longestName = _scenarios!.Max (s => s.GetName ().Length);
-
-            //resultsTableView.Style.ColumnStyles.Add (
-            //                                     0,
-            //                                     new () { MaxWidth = longestName, MinWidth = longestName, MinAcceptableWidth = longestName }
-            //                                    );
-            //resultsTableView.Style.ColumnStyles.Add (1, new () { MaxWidth = 1 });
-            //resultsTableView.CellActivated += ScenarioView_OpenSelectedItem;
-
-            // TableView typically is a grid where nav keys are biased for moving left/right.
-            resultsTableView.KeyBindings.Remove (Key.Home);
-            resultsTableView.KeyBindings.Add (Key.Home, Command.Start);
-            resultsTableView.KeyBindings.Remove (Key.End);
-            resultsTableView.KeyBindings.Add (Key.End, Command.End);
-
-            // Ideally, TableView.MultiSelect = false would turn off any keybindings for
-            // multi-select options. But it currently does not. UI Catalog uses Ctrl-A for
-            // a shortcut to About.
-            resultsTableView.MultiSelect = false;
-
-            var dt = new DataTable ();
-
-            dt.Columns.Add (new DataColumn ("Scenario", typeof (string)));
-            dt.Columns.Add (new DataColumn ("Duration", typeof (TimeSpan)));
-            dt.Columns.Add (new DataColumn ("Refreshed", typeof (int)));
-            dt.Columns.Add (new DataColumn ("LaidOut", typeof (int)));
-            dt.Columns.Add (new DataColumn ("ClearedContent", typeof (int)));
-            dt.Columns.Add (new DataColumn ("DrawComplete", typeof (int)));
-            dt.Columns.Add (new DataColumn ("Updated", typeof (int)));
-            dt.Columns.Add (new DataColumn ("Iterations", typeof (int)));
-
-            foreach (BenchmarkResults r in resultsList)
-            {
-                dt.Rows.Add (
-                             r.Scenario,
-                             r.Duration,
-                             r.RefreshedCount,
-                             r.LaidOutCount,
-                             r.ClearedContentCount,
-                             r.DrawCompleteCount,
-                             r.UpdatedCount,
-                             r.IterationCount
-                            );
-            }
+        Application.Init ();
 
 
-            BenchmarkResults totalRow = new ()
-            {
-                Scenario = "TOTAL",
-                Duration = new (resultsList.Sum (r => r.Duration.Ticks)),
-                RefreshedCount = resultsList.Sum (r => r.RefreshedCount),
-                LaidOutCount = resultsList.Sum (r => r.LaidOutCount),
-                ClearedContentCount = resultsList.Sum (r => r.ClearedContentCount),
-                DrawCompleteCount = resultsList.Sum (r => r.DrawCompleteCount),
-                UpdatedCount = resultsList.Sum (r => r.UpdatedCount),
-                IterationCount = resultsList.Sum (r => r.IterationCount)
-            };
+        var benchmarkWindow = new Window
+        {
+            Title = "Benchmark Results"
+        };
+
+        if (benchmarkWindow.Border is { })
+        {
+            benchmarkWindow.Border.Thickness = new (0, 0, 0, 0);
+        }
+
+        TableView resultsTableView = new ()
+        {
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+        };
 
 
+        // TableView provides many options for table headers. For simplicity we turn all 
+        // of these off. By enabling FullRowSelect and turning off headers, TableView looks just
+        // like a ListView
+        resultsTableView.FullRowSelect = true;
+        resultsTableView.Style.ShowHeaders = true;
+        resultsTableView.Style.ShowHorizontalHeaderOverline = false;
+        resultsTableView.Style.ShowHorizontalHeaderUnderline = true;
+        resultsTableView.Style.ShowHorizontalBottomline = false;
+        resultsTableView.Style.ShowVerticalCellLines = true;
+        resultsTableView.Style.ShowVerticalHeaderLines = true;
+
+        /* By default, TableView lays out columns at render time and only
+         * measures y rows of data at a time.  Where y is the height of the
+         * console. This is for the following reasons:
+         *
+         * - Performance, when tables have a large amount of data
+         * - Defensive, prevents a single wide cell value pushing other
+         *   columns off-screen (requiring horizontal scrolling
+         *
+         * In the case of UICatalog here, such an approach is overkill so
+         * we just measure all the data ourselves and set the appropriate
+         * max widths as ColumnStyles
+         */
+        //int longestName = _scenarios!.Max (s => s.GetName ().Length);
+
+        //resultsTableView.Style.ColumnStyles.Add (
+        //                                     0,
+        //                                     new () { MaxWidth = longestName, MinWidth = longestName, MinAcceptableWidth = longestName }
+        //                                    );
+        //resultsTableView.Style.ColumnStyles.Add (1, new () { MaxWidth = 1 });
+        //resultsTableView.CellActivated += ScenarioView_OpenSelectedItem;
+
+        // TableView typically is a grid where nav keys are biased for moving left/right.
+        resultsTableView.KeyBindings.Remove (Key.Home);
+        resultsTableView.KeyBindings.Add (Key.Home, Command.Start);
+        resultsTableView.KeyBindings.Remove (Key.End);
+        resultsTableView.KeyBindings.Add (Key.End, Command.End);
+
+        // Ideally, TableView.MultiSelect = false would turn off any keybindings for
+        // multi-select options. But it currently does not. UI Catalog uses Ctrl-A for
+        // a shortcut to About.
+        resultsTableView.MultiSelect = false;
+
+        var dt = new DataTable ();
+
+        dt.Columns.Add (new DataColumn ("Scenario", typeof (string)));
+        dt.Columns.Add (new DataColumn ("Duration", typeof (TimeSpan)));
+        dt.Columns.Add (new DataColumn ("Refreshed", typeof (int)));
+        dt.Columns.Add (new DataColumn ("LaidOut", typeof (int)));
+        dt.Columns.Add (new DataColumn ("ClearedContent", typeof (int)));
+        dt.Columns.Add (new DataColumn ("DrawComplete", typeof (int)));
+        dt.Columns.Add (new DataColumn ("Updated", typeof (int)));
+        dt.Columns.Add (new DataColumn ("Iterations", typeof (int)));
+
+        foreach (BenchmarkResults r in resultsList)
+        {
             dt.Rows.Add (
             dt.Rows.Add (
-                         totalRow.Scenario,
-                         totalRow.Duration,
-                         totalRow.RefreshedCount,
-                         totalRow.LaidOutCount,
-                         totalRow.ClearedContentCount,
-                         totalRow.DrawCompleteCount,
-                         totalRow.UpdatedCount,
-                         totalRow.IterationCount
+                         r.Scenario,
+                         r.Duration,
+                         r.RefreshedCount,
+                         r.LaidOutCount,
+                         r.ClearedContentCount,
+                         r.DrawCompleteCount,
+                         r.UpdatedCount,
+                         r.IterationCount
                         );
                         );
+        }
 
 
-            dt.DefaultView.Sort = "Duration";
-            DataTable sortedCopy = dt.DefaultView.ToTable ();
+        BenchmarkResults totalRow = new ()
+        {
+            Scenario = "TOTAL",
+            Duration = new (resultsList.Sum (r => r.Duration.Ticks)),
+            RefreshedCount = resultsList.Sum (r => r.RefreshedCount),
+            LaidOutCount = resultsList.Sum (r => r.LaidOutCount),
+            ClearedContentCount = resultsList.Sum (r => r.ClearedContentCount),
+            DrawCompleteCount = resultsList.Sum (r => r.DrawCompleteCount),
+            UpdatedCount = resultsList.Sum (r => r.UpdatedCount),
+            IterationCount = resultsList.Sum (r => r.IterationCount)
+        };
 
 
-            resultsTableView.Table = new DataTableSource (sortedCopy);
+        dt.Rows.Add (
+                     totalRow.Scenario,
+                     totalRow.Duration,
+                     totalRow.RefreshedCount,
+                     totalRow.LaidOutCount,
+                     totalRow.ClearedContentCount,
+                     totalRow.DrawCompleteCount,
+                     totalRow.UpdatedCount,
+                     totalRow.IterationCount
+                    );
 
 
-            benchmarkWindow.Add (resultsTableView);
+        dt.DefaultView.Sort = "Duration";
+        DataTable sortedCopy = dt.DefaultView.ToTable ();
 
 
-            Application.Run (benchmarkWindow);
-            benchmarkWindow.Dispose ();
-            Application.Shutdown ();
-        }
+        resultsTableView.Table = new DataTableSource (sortedCopy);
+
+        benchmarkWindow.Add (resultsTableView);
+
+        Application.Run (benchmarkWindow);
+        benchmarkWindow.Dispose ();
+        Application.Shutdown ();
     }
     }
 
 
     private static void VerifyObjectsWereDisposed ()
     private static void VerifyObjectsWereDisposed ()
@@ -711,848 +624,4 @@ public class UICatalogApp
         RunState.Instances.Clear ();
         RunState.Instances.Clear ();
 #endif
 #endif
     }
     }
-
-    /// <summary>
-    ///     This is the main UI Catalog app view. It is run fresh when the app loads (if a Scenario has not been passed on
-    ///     the command line) and each time a Scenario ends.
-    /// </summary>
-    public class UICatalogTopLevel : Toplevel
-    {
-        public ListView? CategoryList;
-        public MenuItem? MiForce16Colors;
-        public MenuItem? MiIsMenuBorderDisabled;
-        public MenuItem? MiIsMouseDisabled;
-        public MenuItem? MiUseSubMenusSingleFrame;
-
-        public Shortcut? ShForce16Colors;
-
-        //public Shortcut? ShDiagnostics;
-        public Shortcut? ShVersion;
-
-        // UI Catalog uses TableView for the scenario list instead of a ListView to demonstate how
-        // TableView works. There's no real reason not to use ListView. Because we use TableView, and TableView
-        // doesn't (currently) have CollectionNavigator support built in, we implement it here, within the app.
-        public TableView ScenarioList;
-
-        private readonly StatusBar? _statusBar;
-
-        private readonly CollectionNavigator _scenarioCollectionNav = new ();
-
-        public UICatalogTopLevel ()
-        {
-            _diagnosticFlags = Diagnostics;
-
-            _themeMenuItems = CreateThemeMenuItems ();
-            _themeMenuBarItem = new ("_Themes", _themeMenuItems!);
-
-            MenuBar menuBar = new ()
-            {
-                Menus =
-                [
-                    new (
-                         "_File",
-                         new MenuItem []
-                         {
-                             new (
-                                  "_Quit",
-                                  "Quit UI Catalog",
-                                  RequestStop
-                                 )
-                         }
-                        ),
-                    _themeMenuBarItem,
-                    new ("Diag_nostics", CreateDiagnosticMenuItems ()),
-                    new ("_Logging", CreateLoggingMenuItems ()),
-                    new (
-                         "_Help",
-                         new MenuItem []
-                         {
-                             new (
-                                  "_Documentation",
-                                  "",
-                                  () => OpenUrl ("https://gui-cs.github.io/Terminal.GuiV2Docs"),
-                                  null,
-                                  null,
-                                  (KeyCode)Key.F1
-                                 ),
-                             new (
-                                  "_README",
-                                  "",
-                                  () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"),
-                                  null,
-                                  null,
-                                  (KeyCode)Key.F2
-                                 ),
-                             new (
-                                  "_About...",
-                                  "About UI Catalog",
-                                  () => MessageBox.Query (
-                                                          "",
-                                                          GetAboutBoxMessage (),
-                                                          wrapMessage: false,
-                                                          buttons: "_Ok"
-                                                         ),
-                                  null,
-                                  null,
-                                  (KeyCode)Key.A.WithCtrl
-                                 )
-                         }
-                        )
-                ]
-            };
-
-            _statusBar = new ()
-            {
-                Visible = ShowStatusBar,
-                AlignmentModes = AlignmentModes.IgnoreFirstOrLast,
-                CanFocus = false
-            };
-            _statusBar.Height = Dim.Auto (DimAutoStyle.Auto, minimumContentDim: Dim.Func (() => _statusBar.Visible ? 1 : 0), maximumContentDim: Dim.Func (() => _statusBar.Visible ? 1 : 0));
-
-            ShVersion = new ()
-            {
-                Title = "Version Info",
-                CanFocus = false
-            };
-
-            var statusBarShortcut = new Shortcut
-            {
-                Key = Key.F10,
-                Title = "Show/Hide Status Bar",
-                CanFocus = false
-            };
-
-            statusBarShortcut.Accepting += (sender, args) =>
-                                           {
-                                               _statusBar.Visible = !_statusBar.Visible;
-                                               args.Cancel = true;
-                                           };
-
-            ShForce16Colors = new ()
-            {
-                CanFocus = false,
-                CommandView = new CheckBox
-                {
-                    Title = "16 color mode",
-                    CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
-                    CanFocus = false
-                },
-                HelpText = "",
-                BindKeyToApplication = true,
-                Key = Key.F7
-            };
-
-            ((CheckBox)ShForce16Colors.CommandView).CheckedStateChanging += (sender, args) =>
-                                                                            {
-                                                                                Application.Force16Colors = args.NewValue == CheckState.Checked;
-                                                                                MiForce16Colors!.Checked = Application.Force16Colors;
-                                                                                Application.LayoutAndDraw ();
-                                                                            };
-
-            _statusBar.Add (
-                            new Shortcut
-                            {
-                                CanFocus = false,
-                                Title = "Quit",
-                                Key = Application.QuitKey
-                            },
-                            statusBarShortcut,
-                            ShForce16Colors,
-
-                            //ShDiagnostics,
-                            ShVersion
-                           );
-
-            // Create the Category list view. This list never changes.
-            CategoryList = new ()
-            {
-                X = 0,
-                Y = Pos.Bottom (menuBar),
-                Width = Dim.Auto (),
-                Height = Dim.Fill (
-                                   Dim.Func (
-                                             () =>
-                                             {
-                                                 if (_statusBar.NeedsLayout)
-                                                 {
-                                                     throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
-
-                                                     //_statusBar.Layout ();
-                                                 }
-
-                                                 return _statusBar.Frame.Height;
-                                             })),
-                AllowsMarking = false,
-                CanFocus = true,
-                Title = "_Categories",
-                BorderStyle = LineStyle.Rounded,
-                SuperViewRendersLineCanvas = true,
-                Source = new ListWrapper<string> (_categories)
-            };
-            CategoryList.OpenSelectedItem += (s, a) => { ScenarioList!.SetFocus (); };
-            CategoryList.SelectedItemChanged += CategoryView_SelectedChanged;
-
-            // This enables the scrollbar by causing lazy instantiation to happen
-            CategoryList.VerticalScrollBar.AutoShow = true;
-
-            // Create the scenario list. The contents of the scenario list changes whenever the
-            // Category list selection changes (to show just the scenarios that belong to the selected
-            // category).
-            ScenarioList = new ()
-            {
-                X = Pos.Right (CategoryList) - 1,
-                Y = Pos.Bottom (menuBar),
-                Width = Dim.Fill (),
-                Height = Dim.Fill (
-                                   Dim.Func (
-                                             () =>
-                                             {
-                                                 if (_statusBar.NeedsLayout)
-                                                 {
-                                                     throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
-
-                                                     //_statusBar.Layout ();
-                                                 }
-
-                                                 return _statusBar.Frame.Height;
-                                             })),
-
-                //AllowsMarking = false,
-                CanFocus = true,
-                Title = "_Scenarios",
-                BorderStyle = CategoryList.BorderStyle,
-                SuperViewRendersLineCanvas = true
-            };
-
-            //ScenarioList.VerticalScrollBar.AutoHide = false;
-            //ScenarioList.HorizontalScrollBar.AutoHide = false;
-
-            // TableView provides many options for table headers. For simplicity we turn all 
-            // of these off. By enabling FullRowSelect and turning off headers, TableView looks just
-            // like a ListView
-            ScenarioList.FullRowSelect = true;
-            ScenarioList.Style.ShowHeaders = false;
-            ScenarioList.Style.ShowHorizontalHeaderOverline = false;
-            ScenarioList.Style.ShowHorizontalHeaderUnderline = false;
-            ScenarioList.Style.ShowHorizontalBottomline = false;
-            ScenarioList.Style.ShowVerticalCellLines = false;
-            ScenarioList.Style.ShowVerticalHeaderLines = false;
-
-            /* By default TableView lays out columns at render time and only
-             * measures y rows of data at a time.  Where y is the height of the
-             * console. This is for the following reasons:
-             *
-             * - Performance, when tables have a large amount of data
-             * - Defensive, prevents a single wide cell value pushing other
-             *   columns off screen (requiring horizontal scrolling
-             *
-             * In the case of UICatalog here, such an approach is overkill so
-             * we just measure all the data ourselves and set the appropriate
-             * max widths as ColumnStyles
-             */
-            int longestName = _scenarios!.Max (s => s.GetName ().Length);
-
-            ScenarioList.Style.ColumnStyles.Add (
-                                                 0,
-                                                 new () { MaxWidth = longestName, MinWidth = longestName, MinAcceptableWidth = longestName }
-                                                );
-            ScenarioList.Style.ColumnStyles.Add (1, new () { MaxWidth = 1 });
-            ScenarioList.CellActivated += ScenarioView_OpenSelectedItem;
-
-            // TableView typically is a grid where nav keys are biased for moving left/right.
-            ScenarioList.KeyBindings.Remove (Key.Home);
-            ScenarioList.KeyBindings.Add (Key.Home, Command.Start);
-            ScenarioList.KeyBindings.Remove (Key.End);
-            ScenarioList.KeyBindings.Add (Key.End, Command.End);
-
-            // Ideally, TableView.MultiSelect = false would turn off any keybindings for
-            // multi-select options. But it currently does not. UI Catalog uses Ctrl-A for
-            // a shortcut to About.
-            ScenarioList.MultiSelect = false;
-            ScenarioList.KeyBindings.Remove (Key.A.WithCtrl);
-
-            Add (menuBar);
-            Add (CategoryList);
-            Add (ScenarioList);
-            Add (_statusBar);
-
-            Loaded += LoadedHandler;
-            Unloaded += UnloadedHandler;
-
-            // Restore previous selections
-            CategoryList.SelectedItem = _cachedCategoryIndex;
-            ScenarioList.SelectedRow = _cachedScenarioIndex;
-
-            Applied += ConfigAppliedHandler;
-        }
-
-        public void ConfigChanged ()
-        {
-            if (MenuBar == null)
-            {
-                // View is probably disposed
-                return;
-            }
-
-            if (_topLevelColorScheme == null || !Colors.ColorSchemes.ContainsKey (_topLevelColorScheme))
-            {
-                _topLevelColorScheme = "Base";
-            }
-
-            _cachedTheme = Themes?.Theme;
-
-            _themeMenuItems = CreateThemeMenuItems ();
-            _themeMenuBarItem!.Children = _themeMenuItems;
-
-            foreach (MenuItem mi in _themeMenuItems!)
-            {
-                if (mi is { Parent: null })
-                {
-                    mi.Parent = _themeMenuBarItem;
-                }
-            }
-
-            ColorScheme = Colors.ColorSchemes [_topLevelColorScheme];
-
-            MenuBar!.Menus [0].Children! [0]!.ShortcutKey = Application.QuitKey;
-
-            ((Shortcut)_statusBar!.SubViews.ElementAt (0)).Key = Application.QuitKey;
-            _statusBar.Visible = ShowStatusBar;
-
-            MiIsMouseDisabled!.Checked = Application.IsMouseDisabled;
-
-            ((CheckBox)ShForce16Colors!.CommandView!).CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
-
-            Application.Top!.SetNeedsDraw ();
-        }
-
-        public MenuItem []? CreateThemeMenuItems ()
-        {
-            List<MenuItem> menuItems = CreateForce16ColorItems ().ToList ();
-            menuItems.Add (null!);
-
-            var schemeCount = 0;
-
-            foreach (KeyValuePair<string, ThemeScope> theme in Themes!)
-            {
-                var item = new MenuItem
-                {
-                    Title = theme.Key == "Dark" ? $"{theme.Key.Substring (0, 3)}_{theme.Key.Substring (3, 1)}" : $"_{theme.Key}",
-                    ShortcutKey = new Key ((KeyCode)((uint)KeyCode.D1 + schemeCount++))
-                        .WithCtrl
-                };
-                item.CheckType |= MenuItemCheckStyle.Checked;
-                item.Checked = theme.Key == _cachedTheme; // CM.Themes.Theme;
-
-                item.Action += () =>
-                               {
-                                   Themes.Theme = _cachedTheme = theme.Key;
-                                   Apply ();
-                               };
-                menuItems.Add (item);
-            }
-
-            List<MenuItem> schemeMenuItems = new ();
-
-            foreach (KeyValuePair<string, ColorScheme?> sc in Colors.ColorSchemes)
-            {
-                var item = new MenuItem { Title = $"_{sc.Key}", Data = sc.Key };
-                item.CheckType |= MenuItemCheckStyle.Radio;
-                item.Checked = sc.Key == _topLevelColorScheme;
-
-                item.Action += () =>
-                               {
-                                   _topLevelColorScheme = (string)item.Data;
-
-                                   foreach (MenuItem schemeMenuItem in schemeMenuItems)
-                                   {
-                                       schemeMenuItem.Checked = (string)schemeMenuItem.Data == _topLevelColorScheme;
-                                   }
-
-                                   ColorScheme = Colors.ColorSchemes [_topLevelColorScheme];
-                               };
-                item.ShortcutKey = ((Key)sc.Key [0].ToString ().ToLower ()).WithCtrl;
-                schemeMenuItems.Add (item);
-            }
-
-            menuItems.Add (null!);
-            var mbi = new MenuBarItem ("_Color Scheme for Application.Top", schemeMenuItems.ToArray ());
-            menuItems.Add (mbi);
-
-            return menuItems.ToArray ();
-        }
-
-        private void CategoryView_SelectedChanged (object? sender, ListViewItemEventArgs? e)
-        {
-            string item = _categories! [e!.Item];
-            ObservableCollection<Scenario> newlist;
-
-            if (e.Item == 0)
-            {
-                // First category is "All"
-                newlist = _scenarios!;
-            }
-            else
-            {
-                newlist = new (_scenarios!.Where (s => s.GetCategories ().Contains (item)).ToList ());
-            }
-
-            ScenarioList.Table = new EnumerableTableSource<Scenario> (
-                                                                      newlist,
-                                                                      new ()
-                                                                      {
-                                                                          { "Name", s => s.GetName () }, { "Description", s => s.GetDescription () }
-                                                                      }
-                                                                     );
-
-            // Create a collection of just the scenario names (the 1st column in our TableView)
-            // for CollectionNavigator. 
-            List<object> firstColumnList = new ();
-
-            for (var i = 0; i < ScenarioList.Table.Rows; i++)
-            {
-                firstColumnList.Add (ScenarioList.Table [i, 0]);
-            }
-
-            _scenarioCollectionNav.Collection = firstColumnList;
-        }
-
-        private void ConfigAppliedHandler (object? sender, ConfigurationManagerEventArgs? a) { ConfigChanged (); }
-
-        [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
-        private MenuItem [] CreateDiagnosticFlagsMenuItems ()
-        {
-            const string OFF = "View Diagnostics: _Off";
-            const string RULER = "View Diagnostics: _Ruler";
-            const string THICKNESS = "View Diagnostics: _Thickness";
-            const string HOVER = "View Diagnostics: _Hover";
-            const string DRAWINDICATOR = "View Diagnostics: _DrawIndicator";
-            var index = 0;
-
-            List<MenuItem> menuItems = new ();
-
-            foreach (Enum diag in Enum.GetValues (_diagnosticFlags.GetType ()))
-            {
-                var item = new MenuItem
-                {
-                    Title = GetDiagnosticsTitle (diag), ShortcutKey = new Key (index.ToString () [0]).WithAlt
-                };
-                index++;
-                item.CheckType |= MenuItemCheckStyle.Checked;
-
-                if (GetDiagnosticsTitle (ViewDiagnosticFlags.Off) == item.Title)
-                {
-                    item.Checked = !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Thickness)
-                                   && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler)
-                                   && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Hover)
-                                   && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.DrawIndicator);
-                }
-                else
-                {
-                    item.Checked = _diagnosticFlags.HasFlag (diag);
-                }
-
-                item.Action += () =>
-                               {
-                                   string t = GetDiagnosticsTitle (ViewDiagnosticFlags.Off);
-
-                                   if (item.Title == t && item.Checked == false)
-                                   {
-                                       _diagnosticFlags &= ~(ViewDiagnosticFlags.Thickness
-                                                             | ViewDiagnosticFlags.Ruler
-                                                             | ViewDiagnosticFlags.Hover
-                                                             | ViewDiagnosticFlags.DrawIndicator);
-                                       item.Checked = true;
-                                   }
-                                   else if (item.Title == t && item.Checked == true)
-                                   {
-                                       _diagnosticFlags |= ViewDiagnosticFlags.Thickness
-                                                           | ViewDiagnosticFlags.Ruler
-                                                           | ViewDiagnosticFlags.Hover
-                                                           | ViewDiagnosticFlags.DrawIndicator;
-                                       item.Checked = false;
-                                   }
-                                   else
-                                   {
-                                       Enum f = GetDiagnosticsEnumValue (item.Title);
-
-                                       if (_diagnosticFlags.HasFlag (f))
-                                       {
-                                           SetDiagnosticsFlag (f, false);
-                                       }
-                                       else
-                                       {
-                                           SetDiagnosticsFlag (f, true);
-                                       }
-                                   }
-
-                                   foreach (MenuItem menuItem in menuItems)
-                                   {
-                                       if (menuItem.Title == t)
-                                       {
-                                           menuItem.Checked = !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler)
-                                                              && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Thickness)
-                                                              && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Hover)
-                                                              && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.DrawIndicator);
-                                       }
-                                       else if (menuItem.Title != t)
-                                       {
-                                           menuItem.Checked = _diagnosticFlags.HasFlag (GetDiagnosticsEnumValue (menuItem.Title));
-                                       }
-                                   }
-
-                                   Diagnostics = _diagnosticFlags;
-                               };
-                menuItems.Add (item);
-            }
-
-            return menuItems.ToArray ();
-
-            string GetDiagnosticsTitle (Enum diag)
-            {
-                return Enum.GetName (_diagnosticFlags.GetType (), diag) switch
-                {
-                    "Off" => OFF,
-                    "Ruler" => RULER,
-                    "Thickness" => THICKNESS,
-                    "Hover" => HOVER,
-                    "DrawIndicator" => DRAWINDICATOR,
-                    _ => ""
-                };
-            }
-
-            Enum GetDiagnosticsEnumValue (string? title)
-            {
-                return title switch
-                {
-                    RULER => ViewDiagnosticFlags.Ruler,
-                    THICKNESS => ViewDiagnosticFlags.Thickness,
-                    HOVER => ViewDiagnosticFlags.Hover,
-                    DRAWINDICATOR => ViewDiagnosticFlags.DrawIndicator,
-                    _ => null!
-                };
-            }
-
-            void SetDiagnosticsFlag (Enum diag, bool add)
-            {
-                switch (diag)
-                {
-                    case ViewDiagnosticFlags.Ruler:
-                        if (add)
-                        {
-                            _diagnosticFlags |= ViewDiagnosticFlags.Ruler;
-                        }
-                        else
-                        {
-                            _diagnosticFlags &= ~ViewDiagnosticFlags.Ruler;
-                        }
-
-                        break;
-                    case ViewDiagnosticFlags.Thickness:
-                        if (add)
-                        {
-                            _diagnosticFlags |= ViewDiagnosticFlags.Thickness;
-                        }
-                        else
-                        {
-                            _diagnosticFlags &= ~ViewDiagnosticFlags.Thickness;
-                        }
-
-                        break;
-                    case ViewDiagnosticFlags.Hover:
-                        if (add)
-                        {
-                            _diagnosticFlags |= ViewDiagnosticFlags.Hover;
-                        }
-                        else
-                        {
-                            _diagnosticFlags &= ~ViewDiagnosticFlags.Hover;
-                        }
-
-                        break;
-                    case ViewDiagnosticFlags.DrawIndicator:
-                        if (add)
-                        {
-                            _diagnosticFlags |= ViewDiagnosticFlags.DrawIndicator;
-                        }
-                        else
-                        {
-                            _diagnosticFlags &= ~ViewDiagnosticFlags.DrawIndicator;
-                        }
-
-                        break;
-                    default:
-                        _diagnosticFlags = default (ViewDiagnosticFlags);
-
-                        break;
-                }
-            }
-        }
-
-        private List<MenuItem []> CreateDiagnosticMenuItems ()
-        {
-            List<MenuItem []> menuItems = new ()
-            {
-                CreateDiagnosticFlagsMenuItems (),
-                new MenuItem [] { null! },
-                CreateDisabledEnabledMouseItems (),
-                CreateDisabledEnabledMenuBorder (),
-                CreateDisabledEnableUseSubMenusSingleFrame (),
-                CreateKeyBindingsMenuItems ()
-            };
-
-            return menuItems;
-        }
-
-        private List<MenuItem []> CreateLoggingMenuItems ()
-        {
-            List<MenuItem []> menuItems = new ()
-            {
-                CreateLoggingFlagsMenuItems ()!
-            };
-
-            return menuItems;
-        }
-
-        [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
-        private MenuItem? [] CreateLoggingFlagsMenuItems ()
-        {
-            string [] logLevelMenuStrings = Enum.GetNames<LogLevel> ().Select (n => n = "_" + n).ToArray ();
-            LogLevel [] logLevels = Enum.GetValues<LogLevel> ();
-
-            List<MenuItem?> menuItems = new ();
-
-            foreach (LogLevel logLevel in logLevels)
-            {
-                var item = new MenuItem
-                {
-                    Title = logLevelMenuStrings [(int)logLevel]
-                };
-                item.CheckType |= MenuItemCheckStyle.Checked;
-                item.Checked = Enum.Parse<LogLevel> (_options.DebugLogLevel) == logLevel;
-
-                item.Action += () =>
-                               {
-                                   foreach (MenuItem? menuItem in menuItems.Where (mi => mi is { } && logLevelMenuStrings.Contains (mi.Title)))
-                                   {
-                                       menuItem!.Checked = false;
-                                   }
-
-                                   if (item.Title == logLevelMenuStrings [(int)logLevel] && item.Checked == false)
-                                   {
-                                       _options.DebugLogLevel = Enum.GetName (logLevel)!;
-                                       _logLevelSwitch.MinimumLevel = LogLevelToLogEventLevel (Enum.Parse<LogLevel> (_options.DebugLogLevel)); 
-                                       item.Checked = true;
-                                   }
-
-                                   Diagnostics = _diagnosticFlags;
-                               };
-                menuItems.Add (item);
-            }
-
-            // add a separator
-            menuItems.Add (null!);
-
-            menuItems.Add (
-                           new (
-                                $"_Open Log Folder",
-                                "",
-                                () => OpenUrl (LOGFILE_LOCATION),
-                                null,
-                                null,
-                                null
-                               ));
-
-            return menuItems.ToArray ()!;
-        }
-
-        // TODO: This should be an ConfigurationManager setting
-        private MenuItem [] CreateDisabledEnabledMenuBorder ()
-        {
-            List<MenuItem> menuItems = new ();
-            MiIsMenuBorderDisabled = new () { Title = "Disable Menu _Border" };
-
-            MiIsMenuBorderDisabled.ShortcutKey =
-                new Key (MiIsMenuBorderDisabled!.Title!.Substring (14, 1) [0]).WithAlt.WithCtrl.NoShift;
-            MiIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked;
-
-            MiIsMenuBorderDisabled.Action += () =>
-                                             {
-                                                 MiIsMenuBorderDisabled.Checked = (bool)!MiIsMenuBorderDisabled.Checked!;
-
-                                                 MenuBar!.MenusBorderStyle = !(bool)MiIsMenuBorderDisabled.Checked
-                                                                                 ? LineStyle.Single
-                                                                                 : LineStyle.None;
-                                             };
-            menuItems.Add (MiIsMenuBorderDisabled);
-
-            return menuItems.ToArray ();
-        }
-
-        private MenuItem [] CreateDisabledEnabledMouseItems ()
-        {
-            List<MenuItem> menuItems = new ();
-            MiIsMouseDisabled = new () { Title = "_Disable Mouse" };
-
-            MiIsMouseDisabled.ShortcutKey =
-                new Key (MiIsMouseDisabled!.Title!.Substring (1, 1) [0]).WithAlt.WithCtrl.NoShift;
-            MiIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked;
-
-            MiIsMouseDisabled.Action += () =>
-                                        {
-                                            MiIsMouseDisabled.Checked =
-                                                Application.IsMouseDisabled = (bool)!MiIsMouseDisabled.Checked!;
-                                        };
-            menuItems.Add (MiIsMouseDisabled);
-
-            return menuItems.ToArray ();
-        }
-
-        // TODO: This should be an ConfigurationManager setting
-        private MenuItem [] CreateDisabledEnableUseSubMenusSingleFrame ()
-        {
-            List<MenuItem> menuItems = new ();
-            MiUseSubMenusSingleFrame = new () { Title = "Enable _Sub-Menus Single Frame" };
-
-            MiUseSubMenusSingleFrame.ShortcutKey = KeyCode.CtrlMask
-                                                   | KeyCode.AltMask
-                                                   | (KeyCode)MiUseSubMenusSingleFrame!.Title!.Substring (8, 1) [
-                                                    0];
-            MiUseSubMenusSingleFrame.CheckType |= MenuItemCheckStyle.Checked;
-
-            MiUseSubMenusSingleFrame.Action += () =>
-                                               {
-                                                   MiUseSubMenusSingleFrame.Checked = (bool)!MiUseSubMenusSingleFrame.Checked!;
-                                                   MenuBar!.UseSubMenusSingleFrame = (bool)MiUseSubMenusSingleFrame.Checked;
-                                               };
-            menuItems.Add (MiUseSubMenusSingleFrame);
-
-            return menuItems.ToArray ();
-        }
-
-        private MenuItem [] CreateForce16ColorItems ()
-        {
-            List<MenuItem> menuItems = new ();
-
-            MiForce16Colors = new ()
-            {
-                Title = "Force _16 Colors",
-                ShortcutKey = Key.F6,
-                Checked = Application.Force16Colors,
-                CanExecute = () => Application.Driver?.SupportsTrueColor ?? false
-            };
-            MiForce16Colors.CheckType |= MenuItemCheckStyle.Checked;
-
-            MiForce16Colors.Action += () =>
-                                      {
-                                          MiForce16Colors.Checked = Application.Force16Colors = (bool)!MiForce16Colors.Checked!;
-
-                                          ((CheckBox)ShForce16Colors!.CommandView!).CheckedState =
-                                              Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
-                                          Application.LayoutAndDraw ();
-                                      };
-            menuItems.Add (MiForce16Colors);
-
-            return menuItems.ToArray ();
-        }
-
-        private MenuItem [] CreateKeyBindingsMenuItems ()
-        {
-            List<MenuItem> menuItems = new ();
-            var item = new MenuItem { Title = "_Key Bindings", Help = "Change which keys do what" };
-
-            item.Action += () =>
-                           {
-                               var dlg = new KeyBindingsDialog ();
-                               Application.Run (dlg);
-                               dlg.Dispose ();
-                           };
-
-            menuItems.Add (null!);
-            menuItems.Add (item);
-
-            return menuItems.ToArray ();
-        }
-
-        private void LoadedHandler (object? sender, EventArgs? args)
-        {
-            ConfigChanged ();
-
-            MiIsMouseDisabled!.Checked = Application.IsMouseDisabled;
-
-            if (ShVersion is { })
-            {
-                ShVersion.Title = $"{RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}, {Driver!.GetVersionInfo ()}";
-            }
-
-            if (_selectedScenario != null)
-            {
-                _selectedScenario = null;
-                _isFirstRunning = false;
-            }
-
-            if (!_isFirstRunning)
-            {
-                ScenarioList.SetFocus ();
-            }
-
-            if (_statusBar is { })
-            {
-                _statusBar.VisibleChanged += (s, e) => { ShowStatusBar = _statusBar.Visible; };
-            }
-
-            Loaded -= LoadedHandler;
-            CategoryList!.EnsureSelectedItemVisible ();
-            ScenarioList.EnsureSelectedCellIsVisible ();
-        }
-
-        /// <summary>Launches the selected scenario, setting the global _selectedScenario</summary>
-        /// <param name="e"></param>
-        private void ScenarioView_OpenSelectedItem (object? sender, EventArgs? e)
-        {
-            if (_selectedScenario is null)
-            {
-                // Save selected item state
-                _cachedCategoryIndex = CategoryList!.SelectedItem;
-                _cachedScenarioIndex = ScenarioList.SelectedRow;
-
-                // Create new instance of scenario (even though Scenarios contains instances)
-                var selectedScenarioName = (string)ScenarioList.Table [ScenarioList.SelectedRow, 0];
-
-                _selectedScenario = (Scenario)Activator.CreateInstance (
-                                                                        _scenarios!.FirstOrDefault (
-                                                                                                    s => s.GetName ()
-                                                                                                         == selectedScenarioName
-                                                                                                   )!
-                                                                                   .GetType ()
-                                                                       )!;
-
-                // Tell the main app to stop
-                Application.RequestStop ();
-            }
-        }
-
-        private void UnloadedHandler (object? sender, EventArgs? args)
-        {
-            Applied -= ConfigAppliedHandler;
-            Unloaded -= UnloadedHandler;
-            Dispose ();
-        }
-    }
-
-    private struct Options
-    {
-        public string Driver;
-
-        public string Scenario;
-
-        public uint BenchmarkTimeout;
-
-        public bool Benchmark;
-
-        public string ResultsFile;
-
-        public string DebugLogLevel;
-        /* etc. */
-    }
 }
 }

+ 1 - 1
UICatalog/UICatalog.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
     <PropertyGroup>
-	    <StartupObject>UICatalog.UICatalogApp</StartupObject>
+	    <StartupObject>UICatalog.UICatalog</StartupObject>
         <OutputType>Exe</OutputType>
         <OutputType>Exe</OutputType>
         <!-- Version numbers are automatically updated by gitversion when a release is released -->
         <!-- Version numbers are automatically updated by gitversion when a release is released -->
         <!-- In the source tree the version will always be 2.0 for all projects. -->
         <!-- In the source tree the version will always be 2.0 for all projects. -->

+ 18 - 0
UICatalog/UICatalogCommandLineOptions.cs

@@ -0,0 +1,18 @@
+#nullable enable
+namespace UICatalog;
+
+public struct UICatalogCommandLineOptions
+{
+    public string Driver { get; set; }
+
+    public string Scenario { get; set; }
+
+    public uint BenchmarkTimeout { get; set; }
+
+    public bool Benchmark { get; set; }
+
+    public string ResultsFile { get; set; }
+
+    public string DebugLogLevel { get; set; }
+    /* etc. */
+}

+ 721 - 0
UICatalog/UICatalogTopLevel.cs

@@ -0,0 +1,721 @@
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.Json.Serialization;
+using Microsoft.Extensions.Logging;
+using Terminal.Gui;
+using static Terminal.Gui.ConfigurationManager;
+using Command = Terminal.Gui.Command;
+using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
+
+#nullable enable
+
+namespace UICatalog;
+
+/// <summary>
+///     This is the main UI Catalog app view. It is run fresh when the app loads (if a Scenario has not been passed on
+///     the command line) and each time a Scenario ends.
+/// </summary>
+public class UICatalogTopLevel : Toplevel
+{
+    // When a scenario is run, the main app is killed. The static
+    // members are cached so that when the scenario exits the
+    // main app UI can be restored to previous state
+
+    // Theme Management
+    public static string? CachedTheme { get; set; }
+
+    public static string? CachedTopLevelColorScheme { get; set; }
+
+    // Diagnostics
+    private static ViewDiagnosticFlags _diagnosticFlags;
+
+    public UICatalogTopLevel ()
+    {
+        _diagnosticFlags = Diagnostics;
+
+        _menuBar = CreateMenuBar ();
+        _statusBar = CreateStatusBar ();
+        _categoryList = CreateCategoryList ();
+        _scenarioList = CreateScenarioList ();
+
+        Add (_menuBar, _categoryList, _scenarioList, _statusBar);
+
+        Loaded += LoadedHandler;
+        Unloaded += UnloadedHandler;
+
+        // Restore previous selections
+        _categoryList.SelectedItem = _cachedCategoryIndex;
+        _scenarioList.SelectedRow = _cachedScenarioIndex;
+
+        Applied += ConfigAppliedHandler;
+    }
+
+
+    private static bool _isFirstRunning = true;
+
+    private void LoadedHandler (object? sender, EventArgs? args)
+    {
+        ConfigChanged ();
+
+        if (_disableMouseCb is { })
+        {
+            _disableMouseCb.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked;
+        }
+
+        if (_shVersion is { })
+        {
+            _shVersion.Title = $"{RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}, {Driver!.GetVersionInfo ()}";
+        }
+
+        if (CachedSelectedScenario != null)
+        {
+            CachedSelectedScenario = null;
+            _isFirstRunning = false;
+        }
+
+        if (!_isFirstRunning)
+        {
+            _scenarioList.SetFocus ();
+        }
+
+        if (_statusBar is { })
+        {
+            _statusBar.VisibleChanged += (s, e) => { ShowStatusBar = _statusBar.Visible; };
+        }
+
+        Loaded -= LoadedHandler;
+        _categoryList!.EnsureSelectedItemVisible ();
+        _scenarioList.EnsureSelectedCellIsVisible ();
+    }
+
+    private void UnloadedHandler (object? sender, EventArgs? args)
+    {
+        Applied -= ConfigAppliedHandler;
+        Unloaded -= UnloadedHandler;
+        Dispose ();
+    }
+
+    #region MenuBar
+
+    private readonly MenuBarv2? _menuBar;
+    private CheckBox? _force16ColorsMenuItemCb;
+    private RadioGroup? _themesRg;
+    private RadioGroup? _topSchemeRg;
+    private RadioGroup? _logLevelRg;
+    private FlagSelector? _diagnosticFlagsSelector;
+    private CheckBox? _disableMouseCb;
+
+    private MenuBarv2 CreateMenuBar ()
+    {
+        MenuBarv2 menuBar = new (
+                                 [
+                                     new (
+                                          "_File",
+                                          [
+                                              new MenuItemv2 (
+                                                              "_Quit",
+                                                              "Quit UI Catalog",
+                                                              RequestStop
+                                                             )
+                                          ]),
+                                     new ("_Themes", CreateThemeMenuItems ()),
+                                     new ("Diag_nostics", CreateDiagnosticMenuItems ()),
+                                     new ("_Logging", CreateLoggingMenuItems ()),
+                                     new (
+                                          "_Help",
+                                          [
+                                              new MenuItemv2 (
+                                                              "_Documentation",
+                                                              "",
+                                                              () => OpenUrl ("https://gui-cs.github.io/Terminal.GuiV2Docs"),
+                                                              Key.F1
+                                                             ),
+                                              new MenuItemv2 (
+                                                              "_README",
+                                                              "",
+                                                              () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"),
+                                                              Key.F2
+                                                             ),
+                                              new MenuItemv2 (
+                                                              "_About...",
+                                                              "About UI Catalog",
+                                                              () => MessageBox.Query (
+                                                                                      "",
+                                                                                      GetAboutBoxMessage (),
+                                                                                      wrapMessage: false,
+                                                                                      buttons: "_Ok"
+                                                                                     ),
+                                                              Key.A.WithCtrl
+                                                             )
+                                          ])
+                                 ]);
+
+        return menuBar;
+
+        View [] CreateThemeMenuItems ()
+        {
+            List<View> menuItems = [];
+
+            _force16ColorsMenuItemCb = new ()
+            {
+                Title = "Force _16 Colors",
+                CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked
+            };
+
+            _force16ColorsMenuItemCb.CheckedStateChanged += (sender, args) =>
+            {
+                Application.Force16Colors = args.CurrentValue == CheckState.Checked;
+
+                _force16ColorsShortcutCb!.CheckedState = args.CurrentValue;
+                Application.LayoutAndDraw ();
+            };
+
+            menuItems.Add (
+                           new MenuItemv2
+                           {
+                               CommandView = _force16ColorsMenuItemCb
+                           });
+
+            menuItems.Add (new Line ());
+
+            _themesRg = new ();
+
+            _themesRg.SelectedItemChanged += (_, args) =>
+            {
+                Themes!.Theme = Themes!.Keys.ToArray () [args.SelectedItem];
+                CachedTheme = Themes!.Keys.ToArray () [args.SelectedItem];
+                Apply ();
+                SetNeedsDraw ();
+            };
+
+            var menuItem = new MenuItemv2
+            {
+                CommandView = _themesRg,
+                HelpText = "Cycle Through Themes",
+                Key = Key.T.WithCtrl
+            };
+            menuItems.Add (menuItem);
+
+            menuItems.Add (new Line ());
+
+            _topSchemeRg = new ();
+
+            _topSchemeRg.SelectedItemChanged += (_, args) =>
+            {
+                CachedTopLevelColorScheme = Colors.ColorSchemes.Keys.ToArray () [args.SelectedItem];
+                ColorScheme = Colors.ColorSchemes [CachedTopLevelColorScheme];
+                SetNeedsDraw ();
+            };
+
+            menuItem = new ()
+            {
+                Title = "Color Scheme for Application._Top",
+                SubMenu = new (
+                               [
+                                   new ()
+                               {
+                                   CommandView = _topSchemeRg,
+                                   HelpText = "Cycle Through Color Schemes",
+                                   Key = Key.S.WithCtrl
+                               }
+                               ])
+            };
+            menuItems.Add (menuItem);
+
+            UpdateThemesMenu ();
+
+            return menuItems.ToArray ();
+        }
+
+        View [] CreateDiagnosticMenuItems ()
+        {
+            List<View> menuItems = [];
+
+            _diagnosticFlagsSelector = new ()
+            {
+                CanFocus = false,
+                Styles = FlagSelectorStyles.ShowNone,
+                HighlightStyle = HighlightStyle.None
+            };
+            _diagnosticFlagsSelector.SetFlags<ViewDiagnosticFlags> ();
+
+            _diagnosticFlagsSelector.ValueChanged += (sender, args) =>
+            {
+                _diagnosticFlags = (ViewDiagnosticFlags)_diagnosticFlagsSelector.Value;
+                Diagnostics = _diagnosticFlags;
+            };
+
+            menuItems.Add (
+                           new MenuItemv2
+                           {
+                               CommandView = _diagnosticFlagsSelector,
+                               HelpText = "View Diagnostics"
+                           });
+
+            menuItems.Add (new Line ());
+
+            _disableMouseCb = new ()
+            {
+                Title = "_Disable Mouse",
+                CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked
+            };
+
+            _disableMouseCb.CheckedStateChanged += (_, args) => { Application.IsMouseDisabled = args.CurrentValue == CheckState.Checked; };
+
+            menuItems.Add (
+                           new MenuItemv2
+                           {
+                               CommandView = _disableMouseCb,
+                               HelpText = "Disable Mouse"
+                           });
+
+            return menuItems.ToArray ();
+        }
+
+        View [] CreateLoggingMenuItems ()
+        {
+            List<View?> menuItems = [];
+
+            LogLevel [] logLevels = Enum.GetValues<LogLevel> ();
+
+            _logLevelRg = new ()
+            {
+                AssignHotKeysToRadioLabels = true,
+                RadioLabels = Enum.GetNames<LogLevel> (),
+                SelectedItem = logLevels.ToList ().IndexOf (Enum.Parse<LogLevel> (UICatalog.Options.DebugLogLevel))
+            };
+
+            _logLevelRg.SelectedItemChanged += (_, args) =>
+            {
+                UICatalog.Options = UICatalog.Options with { DebugLogLevel = Enum.GetName (logLevels [args.SelectedItem])! };
+
+                UICatalog.LogLevelSwitch.MinimumLevel =
+                    UICatalog.LogLevelToLogEventLevel (Enum.Parse<LogLevel> (UICatalog.Options.DebugLogLevel));
+            };
+
+            menuItems.Add (
+                           new MenuItemv2
+                           {
+                               CommandView = _logLevelRg,
+                               HelpText = "Cycle Through Log Levels",
+                               Key = Key.L.WithCtrl
+                           });
+
+            // add a separator
+            menuItems.Add (new Line ());
+
+            menuItems.Add (
+                           new MenuItemv2 (
+                                           "_Open Log Folder",
+                                           string.Empty,
+                                           () => OpenUrl (UICatalog.LOGFILE_LOCATION)
+                                          ));
+
+            return menuItems.ToArray ()!;
+        }
+
+    }
+
+    private void UpdateThemesMenu ()
+    {
+        if (_themesRg is null)
+        {
+            return;
+        }
+
+        _themesRg.AssignHotKeysToRadioLabels = true;
+        _themesRg.UsedHotKeys.Clear ();
+        _themesRg.RadioLabels = Themes!.Keys.ToArray ();
+        _themesRg.SelectedItem = Themes.Keys.ToList ().IndexOf (CachedTheme!.Replace ("_", string.Empty));
+
+        if (_topSchemeRg is null)
+        {
+            return;
+        }
+
+        _topSchemeRg.AssignHotKeysToRadioLabels = true;
+        _topSchemeRg.UsedHotKeys.Clear ();
+        int selected = _topSchemeRg.SelectedItem;
+        _topSchemeRg.RadioLabels = Colors.ColorSchemes.Keys.ToArray ();
+        _topSchemeRg.SelectedItem = selected;
+
+        if (CachedTopLevelColorScheme is null || !Colors.ColorSchemes.ContainsKey (CachedTopLevelColorScheme))
+        {
+            CachedTopLevelColorScheme = "Base";
+        }
+
+        _topSchemeRg.SelectedItem = Array.IndexOf (Colors.ColorSchemes.Keys.ToArray (), CachedTopLevelColorScheme);
+    }
+
+    #endregion MenuBar
+
+    #region Scenario List
+
+    private readonly TableView _scenarioList;
+
+    private static int _cachedScenarioIndex;
+
+    public static ObservableCollection<Scenario>? CachedScenarios { get; set; }
+
+    // UI Catalog uses TableView for the scenario list instead of a ListView to demonstrate how
+    // TableView works. There's no real reason not to use ListView. Because we use TableView, and TableView
+    // doesn't (currently) have CollectionNavigator support built in, we implement it here, within the app.
+    private readonly CollectionNavigator _scenarioCollectionNav = new ();
+
+    // If set, holds the scenario the user selected to run
+    public static Scenario? CachedSelectedScenario { get; set; }
+
+    private TableView CreateScenarioList ()
+    {
+        // Create the scenario list. The contents of the scenario list changes whenever the
+        // Category list selection changes (to show just the scenarios that belong to the selected
+        // category).
+        TableView scenarioList = new ()
+        {
+            X = Pos.Right (_categoryList!) - 1,
+            Y = Pos.Bottom (_menuBar!),
+            Width = Dim.Fill (),
+            Height = Dim.Fill (
+                               Dim.Func (
+                                         () =>
+                                         {
+                                             if (_statusBar!.NeedsLayout)
+                                             {
+                                                 throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
+
+                                                 //_statusBar.Layout ();
+                                             }
+
+                                             return _statusBar.Frame.Height;
+                                         })),
+
+            //AllowsMarking = false,
+            CanFocus = true,
+            Title = "_Scenarios",
+            BorderStyle = _categoryList!.BorderStyle,
+            SuperViewRendersLineCanvas = true
+        };
+
+        // TableView provides many options for table headers. For simplicity, we turn all
+        // of these off. By enabling FullRowSelect and turning off headers, TableView looks just
+        // like a ListView
+        scenarioList.FullRowSelect = true;
+        scenarioList.Style.ShowHeaders = false;
+        scenarioList.Style.ShowHorizontalHeaderOverline = false;
+        scenarioList.Style.ShowHorizontalHeaderUnderline = false;
+        scenarioList.Style.ShowHorizontalBottomline = false;
+        scenarioList.Style.ShowVerticalCellLines = false;
+        scenarioList.Style.ShowVerticalHeaderLines = false;
+
+        /* By default, TableView lays out columns at render time and only
+         * measures y rows of data at a time.  Where y is the height of the
+         * console. This is for the following reasons:
+         *
+         * - Performance, when tables have a large amount of data
+         * - Defensive, prevents a single wide cell value pushing other
+         *   columns off-screen (requiring horizontal scrolling
+         *
+         * In the case of UICatalog here, such an approach is overkill so
+         * we just measure all the data ourselves and set the appropriate
+         * max widths as ColumnStyles
+         */
+        int longestName = CachedScenarios!.Max (s => s.GetName ().Length);
+
+        scenarioList.Style.ColumnStyles.Add (
+                                             0,
+                                             new () { MaxWidth = longestName, MinWidth = longestName, MinAcceptableWidth = longestName }
+                                            );
+        scenarioList.Style.ColumnStyles.Add (1, new () { MaxWidth = 1 });
+        scenarioList.CellActivated += ScenarioView_OpenSelectedItem;
+
+        // TableView typically is a grid where nav keys are biased for moving left/right.
+        scenarioList.KeyBindings.Remove (Key.Home);
+        scenarioList.KeyBindings.Add (Key.Home, Command.Start);
+        scenarioList.KeyBindings.Remove (Key.End);
+        scenarioList.KeyBindings.Add (Key.End, Command.End);
+
+        // Ideally, TableView.MultiSelect = false would turn off any keybindings for
+        // multi-select options. But it currently does not. UI Catalog uses Ctrl-A for
+        // a shortcut to About.
+        scenarioList.MultiSelect = false;
+        scenarioList.KeyBindings.Remove (Key.A.WithCtrl);
+
+        return scenarioList;
+    }
+
+
+    /// <summary>Launches the selected scenario, setting the global _selectedScenario</summary>
+    /// <param name="e"></param>
+    private void ScenarioView_OpenSelectedItem (object? sender, EventArgs? e)
+    {
+        if (CachedSelectedScenario is null)
+        {
+            // Save selected item state
+            _cachedCategoryIndex = _categoryList!.SelectedItem;
+            _cachedScenarioIndex = _scenarioList.SelectedRow;
+
+            // Create new instance of scenario (even though Scenarios contains instances)
+            var selectedScenarioName = (string)_scenarioList.Table [_scenarioList.SelectedRow, 0];
+
+            CachedSelectedScenario = (Scenario)Activator.CreateInstance (
+                                                                         CachedScenarios!.FirstOrDefault (
+                                                                                                          s => s.GetName ()
+                                                                                                              == selectedScenarioName
+                                                                                                         )!
+                                                                                         .GetType ()
+                                                                        )!;
+
+            // Tell the main app to stop
+            Application.RequestStop ();
+        }
+    }
+
+    #endregion Scenario List
+
+    #region Category List
+
+    private readonly ListView? _categoryList;
+    private static int _cachedCategoryIndex;
+    public static ObservableCollection<string>? CachedCategories { get; set; }
+
+    private ListView CreateCategoryList ()
+    {
+        // Create the Category list view. This list never changes.
+        ListView categoryList = new ()
+        {
+            X = 0,
+            Y = Pos.Bottom (_menuBar!),
+            Width = Dim.Auto (),
+            Height = Dim.Fill (
+                               Dim.Func (
+                                         () =>
+                                         {
+                                             if (_statusBar!.NeedsLayout)
+                                             {
+                                                 throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
+
+                                                 //_statusBar.Layout ();
+                                             }
+
+                                             return _statusBar.Frame.Height;
+                                         })),
+            AllowsMarking = false,
+            CanFocus = true,
+            Title = "_Categories",
+            BorderStyle = LineStyle.Rounded,
+            SuperViewRendersLineCanvas = true,
+            Source = new ListWrapper<string> (CachedCategories)
+        };
+        categoryList.OpenSelectedItem += (s, a) => { _scenarioList!.SetFocus (); };
+        categoryList.SelectedItemChanged += CategoryView_SelectedChanged;
+
+        // This enables the scrollbar by causing lazy instantiation to happen
+        categoryList.VerticalScrollBar.AutoShow = true;
+
+        return categoryList;
+    }
+
+    private void CategoryView_SelectedChanged (object? sender, ListViewItemEventArgs? e)
+    {
+        string item = CachedCategories! [e!.Item];
+        ObservableCollection<Scenario> newScenarioList;
+
+        if (e.Item == 0)
+        {
+            // First category is "All"
+            newScenarioList = CachedScenarios!;
+        }
+        else
+        {
+            newScenarioList = new (CachedScenarios!.Where (s => s.GetCategories ().Contains (item)).ToList ());
+        }
+
+        _scenarioList.Table = new EnumerableTableSource<Scenario> (
+                                                                   newScenarioList,
+                                                                   new ()
+                                                                   {
+                                                                       { "Name", s => s.GetName () }, { "Description", s => s.GetDescription () }
+                                                                   }
+                                                                  );
+
+        // Create a collection of just the scenario names (the 1st column in our TableView)
+        // for CollectionNavigator. 
+        List<object> firstColumnList = [];
+
+        for (var i = 0; i < _scenarioList.Table.Rows; i++)
+        {
+            firstColumnList.Add (_scenarioList.Table [i, 0]);
+        }
+
+        _scenarioCollectionNav.Collection = firstColumnList;
+    }
+
+    #endregion Category List
+
+    #region StatusBar
+
+    private readonly StatusBar? _statusBar;
+
+    [SerializableConfigurationProperty (Scope = typeof (AppScope), OmitClassName = true)]
+    [JsonPropertyName ("UICatalog.StatusBar")]
+    public static bool ShowStatusBar { get; set; } = true;
+
+    private Shortcut? _shVersion;
+    private CheckBox? _force16ColorsShortcutCb;
+
+    private StatusBar CreateStatusBar ()
+    {
+        StatusBar statusBar = new ()
+        {
+            Visible = ShowStatusBar,
+            AlignmentModes = AlignmentModes.IgnoreFirstOrLast,
+            CanFocus = false
+        };
+
+        // ReSharper disable All
+        statusBar.Height = Dim.Auto (
+                                     DimAutoStyle.Auto,
+                                     minimumContentDim: Dim.Func (() => statusBar.Visible ? 1 : 0),
+                                     maximumContentDim: Dim.Func (() => statusBar.Visible ? 1 : 0));
+        // ReSharper restore All
+
+        _shVersion = new ()
+        {
+            Title = "Version Info",
+            CanFocus = false
+        };
+
+        var statusBarShortcut = new Shortcut
+        {
+            Key = Key.F10,
+            Title = "Show/Hide Status Bar",
+            CanFocus = false
+        };
+
+        statusBarShortcut.Accepting += (sender, args) =>
+        {
+            statusBar.Visible = !_statusBar!.Visible;
+            args.Cancel = true;
+        };
+
+        _force16ColorsShortcutCb = new ()
+        {
+            Title = "16 color mode",
+            CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
+            CanFocus = false
+        };
+
+        _force16ColorsShortcutCb.CheckedStateChanging += (sender, args) =>
+        {
+            Application.Force16Colors = args.NewValue == CheckState.Checked;
+            _force16ColorsMenuItemCb!.CheckedState = args.NewValue;
+            Application.LayoutAndDraw ();
+        };
+
+        statusBar.Add (
+                       new Shortcut
+                       {
+                           CanFocus = false,
+                           Title = "Quit",
+                           Key = Application.QuitKey
+                       },
+                       statusBarShortcut,
+                       new Shortcut
+                       {
+                           CanFocus = false,
+                           CommandView = _force16ColorsShortcutCb,
+                           HelpText = "",
+                           BindKeyToApplication = true,
+                           Key = Key.F7
+                       },
+                       _shVersion
+                      );
+
+        return statusBar;
+    }
+
+    #endregion StatusBar
+
+    #region Configuration Manager
+    public void ConfigChanged ()
+    {
+        CachedTheme = Themes?.Theme;
+
+        UpdateThemesMenu ();
+
+        ColorScheme = Colors.ColorSchemes [CachedTopLevelColorScheme!];
+
+        ((Shortcut)_statusBar!.SubViews.ElementAt (0)).Key = Application.QuitKey;
+        _statusBar.Visible = ShowStatusBar;
+
+        _disableMouseCb!.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked;
+        _force16ColorsShortcutCb!.CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
+
+        Application.Top!.SetNeedsDraw ();
+    }
+
+    private void ConfigAppliedHandler (object? sender, ConfigurationManagerEventArgs? a) { ConfigChanged (); }
+
+    #endregion Configuration Manager
+
+    /// <summary>
+    ///     Gets the message displayed in the About Box. `public` so it can be used from Unit tests.
+    /// </summary>
+    /// <returns></returns>
+    public static string GetAboutBoxMessage ()
+    {
+        // NOTE: Do not use multiline verbatim strings here.
+        // WSL gets all confused.
+        StringBuilder msg = new ();
+        msg.AppendLine ("UI Catalog: A comprehensive sample library and test app for");
+        msg.AppendLine ();
+
+        msg.AppendLine (
+                        """
+                         _______                  _             _   _____       _ 
+                        |__   __|                (_)           | | / ____|     (_)
+                           | | ___ _ __ _ __ ___  _ _ __   __ _| || |  __ _   _ _ 
+                           | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | |
+                           | |  __/ |  | | | | | | | | | | (_| | || |__| | |_| | |
+                           |_|\___|_|  |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_|
+                        """);
+        msg.AppendLine ();
+        msg.AppendLine ("v2 - Pre-Alpha");
+        msg.AppendLine ();
+        msg.AppendLine ("https://github.com/gui-cs/Terminal.Gui");
+
+        return msg.ToString ();
+    }
+
+    public static void OpenUrl (string url)
+    {
+        if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows))
+        {
+            url = url.Replace ("&", "^&");
+            Process.Start (new ProcessStartInfo ("cmd", $"/c start {url}") { CreateNoWindow = true });
+        }
+        else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux))
+        {
+            using var process = new Process
+            {
+                StartInfo = new ()
+                {
+                    FileName = "xdg-open",
+                    Arguments = url,
+                    RedirectStandardError = true,
+                    RedirectStandardOutput = true,
+                    CreateNoWindow = true,
+                    UseShellExecute = false
+                }
+            };
+            process.Start ();
+        }
+        else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX))
+        {
+            Process.Start ("open", url);
+        }
+    }
+}