소스 검색

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

Tig 5 달 전
부모
커밋
47833bfc80
31개의 변경된 파일6388개의 추가작업 그리고 5137개의 파일을 삭제
  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 (_activePopover is View popoverView && popoverView == popover)
         {
-            popoverView.Visible = false;
             _activePopover = null;
+            popoverView.Visible = false;
             Application.Top?.SetNeedsDraw ();
         }
     }

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

@@ -115,15 +115,18 @@ public partial class View // Command APIs
     /// </returns>
     protected bool? RaiseAccepting (ICommandContext? ctx)
     {
+        Logging.Trace($"{ctx?.Source?.Title}");
         CommandEventArgs args = new () { Context = ctx };
 
         // Best practice is to invoke the virtual method first.
         // This allows derived classes to handle the event and potentially cancel it.
+        Logging.Trace ($"Calling OnAccepting...");
         args.Cancel = OnAccepting (args) || args.Cancel;
 
         if (!args.Cancel)
         {
             // 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);
         }
 
@@ -148,11 +151,12 @@ public partial class View // Command APIs
 
             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>

+ 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)
     {
+        if (!adornment.Enabled)
+        {
+            return false;
+        }
+
         bool? adornmentHandled = adornment.InvokeCommands (key);
 
         if (adornmentHandled is true)
@@ -599,6 +604,11 @@ public partial class View // Keyboard APIs
     /// </returns>
     internal bool? InvokeCommandsBoundToHotKey (Key hotKey)
     {
+        if (!Enabled)
+        {
+            return false;
+        }
+
         bool? handled = null;
         // Process this View
         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))
     { }
 
-    // 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>
     ///     The Popover Menu that will be displayed when this item is selected.
     /// </summary>
     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/>
     protected override void Dispose (bool disposing)
     {

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

@@ -1,4 +1,7 @@
 #nullable enable
+using System.ComponentModel;
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 
 /// <summary>
@@ -17,11 +20,56 @@ public class MenuBarv2 : Menuv2, IDesignable
     /// <inheritdoc/>
     public MenuBarv2 (IEnumerable<MenuBarItemv2> menuBarItems) : base (menuBarItems)
     {
+        CanFocus = false;
         TabStop = TabBehavior.TabGroup;
         Y = 0;
         Width = Dim.Fill ();
         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);
         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); }
     }
 
+    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/>
     protected override void OnSelectedMenuItemChanged (MenuItemv2? selected)
     {
-        if (selected is MenuBarItemv2 { } selectedMenuBarItem)
+        if (selected is MenuBarItemv2 { PopoverMenu.Visible: false } 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
-        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);
         }
@@ -65,36 +211,119 @@ public class MenuBarv2 : Menuv2, IDesignable
     /// <inheritdoc/>
     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);
     }
 
+    /// <summary>
+    ///     Shows the specified popover, but only if the menu bar is active.
+    /// </summary>
+    /// <param name="menuBarItem"></param>
     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 })
         {
             menuBarItem.PopoverMenu.BeginInit ();
             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)
         {
             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?.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/>

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

@@ -7,6 +7,7 @@ namespace Terminal.Gui;
 
 /// <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
 ///     associated help text, and an action to execute on activation.
 /// </summary>
 public class MenuItemv2 : Shortcut
@@ -53,6 +54,11 @@ public class MenuItemv2 : Shortcut
         : 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/>
     public MenuItemv2 (string? commandText = null, string? helpText = null, Menuv2? subMenu = null)
         : base (Key.Empty, commandText, null, helpText)
@@ -98,6 +104,7 @@ public class MenuItemv2 : Shortcut
 
     internal override bool? DispatchCommand (ICommandContext? commandContext)
     {
+        Logging.Trace($"{commandContext?.Source?.Title}");
         bool? ret = null;
 
         if (commandContext is { Command: not Command.HotKey })
@@ -116,11 +123,11 @@ public class MenuItemv2 : Shortcut
 
         if (ret is not true)
         {
+            Logging.Trace($"Calling base.DispatchCommand");
             ret = base.DispatchCommand (commandContext);
         }
 
-        //Logging.Trace ($"{commandContext?.Source?.Title}");
-
+        Logging.Trace($"Calling RaiseAccepted");
         RaiseAccepted (commandContext);
 
         return ret;

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

@@ -10,7 +10,7 @@ public class Menuv2 : Bar
     public Menuv2 () : this ([]) { }
 
     /// <inheritdoc/>
-    public Menuv2 (IEnumerable<MenuItemv2>? shortcuts) : this (shortcuts?.Cast<View>()) { }
+    public Menuv2 (IEnumerable<MenuItemv2>? menuItems) : this (menuItems?.Cast<View> ()) { }
 
     /// <inheritdoc/>
     public Menuv2 (IEnumerable<View>? shortcuts) : base (shortcuts)
@@ -55,21 +55,21 @@ public class Menuv2 : Bar
         switch (view)
         {
             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:
                 // Grow line so we get auto-join line
                 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?
 
     /// <summary>
@@ -120,6 +133,7 @@ public class Menuv2 : Bar
     protected override void OnFocusedChanged (View? previousFocused, View? focused)
     {
         base.OnFocusedChanged (previousFocused, focused);
+
         SelectedMenuItem = focused as MenuItemv2;
         RaiseSelectedMenuItemChanged (SelectedMenuItem);
     }

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

@@ -3,7 +3,8 @@ namespace Terminal.Gui;
 
 /// <summary>
 ///     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>
 /// <remarks>
 ///     <para>
@@ -33,8 +34,6 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
         base.Visible = false;
 
-        //base.ColorScheme = Colors.ColorSchemes ["Menu"];
-
         Root = root;
 
         AddCommand (Command.Right, MoveRight);
@@ -53,7 +52,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
                         return false;
                     });
 
-        KeyBindings.Add (DefaultKey, Command.Quit);
+        KeyBindings.Add (Key, Command.Quit);
         KeyBindings.ReplaceCommands (Application.QuitKey, Command.Quit);
 
         AddCommand (
@@ -67,7 +66,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
                         Visible = false;
 
-                        return RaiseAccepted (ctx);
+                        return false;
                     });
 
         return;
@@ -98,7 +97,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
                 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;
             }
 
+            // 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 ();
 
+            // TODO: This needs to be done whenever any MenuItem in the menu tree changes to support dynamic menus
             IEnumerable<Menuv2> allMenus = GetAllSubMenus ();
 
             foreach (Menuv2 menu in allMenus)
@@ -235,13 +237,12 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
     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 ();
 
         foreach (MenuItemv2 menuItem in all.Where (mi => mi.Command != Command.NotBound))
         {
             Key? key;
+
             if (menuItem.TargetView is { })
             {
                 // A TargetView implies HotKey
@@ -447,17 +448,16 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
     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)
         {
             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)
@@ -514,10 +514,21 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
 
     private void MenuOnSelectedMenuItemChanged (object? sender, MenuItemv2? e)
     {
-        //Logging.Trace ($"{e}");
+        Logging.Trace ($"e: {e?.Title}");
         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/>
     protected override void Dispose (bool disposing)
     {
@@ -539,20 +550,20 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable
         base.Dispose (disposing);
     }
 
-
     /// <inheritdoc/>
     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;
+
         return true;
     }
 }

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

@@ -1,9 +1,7 @@
 #nullable enable
-using System.Diagnostics;
-
 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
 {
     /// <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.
         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);
 
         // 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 ((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)
                 return InvokeCommand (Command.Select);
@@ -145,14 +143,14 @@ public class RadioGroup : View, IDesignable, IOrientation
                 if (c > -1)
                 {
                     // 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;
         }
 
-        bool cursorChanged = false;
+        var cursorChanged = false;
 
         if (SelectedItem == Cursor)
         {
@@ -164,7 +162,7 @@ public class RadioGroup : View, IDesignable, IOrientation
             }
         }
 
-        bool selectedItemChanged = false;
+        var selectedItemChanged = false;
 
         if (SelectedItem != Cursor)
         {
@@ -209,7 +207,8 @@ public class RadioGroup : View, IDesignable, IOrientation
     }
 
     /// <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>
     /// <remarks>
     ///     <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>
     ///     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)
                     {
-                        SetAttribute (HasFocus ? GetHotFocusColor() : GetHotNormalColor ());
+                        SetAttribute (HasFocus ? GetHotFocusColor () : GetHotNormalColor ());
                     }
                     else if (j == hotPos && i != Cursor)
                     {
@@ -369,7 +406,7 @@ public class RadioGroup : View, IDesignable, IOrientation
 
                         if (i == Cursor)
                         {
-                            SetAttribute (HasFocus ? GetHotFocusColor() : GetHotNormalColor ());
+                            SetAttribute (HasFocus ? GetHotFocusColor () : GetHotNormalColor ());
                         }
                         else if (i != Cursor)
                         {
@@ -386,6 +423,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                 DrawHotString (rl, HasFocus && i == Cursor);
             }
         }
+
         return true;
     }
 

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

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

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

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

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

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

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

@@ -129,6 +129,28 @@ public class KeyBindingsTests ()
         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)
 
     public class ScopedKeyBindingView : View

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

@@ -3,3884 +3,691 @@ using Xunit.Abstractions;
 
 namespace Terminal.Gui.ViewsTests;
 
-public class MenuBarTests (ITestOutputHelper output)
+public class MenuBarTests ()
 {
     [Fact]
     [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 ();
         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 ();
     }
 
     [Fact]
     [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 ();
-        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 ();
     }
 
     [Fact]
     [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 ();
-        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);
-        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 ()
+    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 ();
-        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 ();
     }
 
     [Fact]
     [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 ();
-        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 ();
     }
 
-    [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 ()
+    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 ();
-        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 ();
     }
 
     [Fact]
     [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 ();
-        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 ();
     }
 
     [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 ()
+    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 ();
-        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 ();
     }
 
     [Fact]
     [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 ();
-        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 ();
     }
 
     [Fact]
     [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 ();
-        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 ();
     }
 
-    [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]
-    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 ();
         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 ();
     }
 
     [Fact]
     [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 ();
-        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]
-    [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 ();
-        top.Add (menu);
+        top.Add (menuBar);
         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);
         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
-                             )
-                     }
-                    )
-            ]
-        };
-
+    [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 ();
-        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 ();
     }
 
-    [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;
+    [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 ();
-        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 ();
     }
 
-    [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]
-    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]
-    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 ();
-        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 ();
     }
 
     [Fact]
     [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 ();
-        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 ();
     }
 
     [Fact]
     [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 ();
-        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 ();
     }
-
-    // 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;
 
-public class MenuTests
+public class Menuv1Tests
 {
     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
 

+ 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);
 
-        var menu = new MenuBar
+        var menu = new MenuBarv2
         {
             Menus =
             [
                 new (
                      "_File",
-                     new MenuItem []
+                     new MenuItemv2 []
                      {
                          new (
                               "_Quit",
@@ -163,7 +163,7 @@ public class CharacterMap : Scenario
                     ),
                 new (
                      "_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 += () =>
                        {
                            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;
 
 namespace UICatalog.Scenarios;
@@ -14,7 +13,7 @@ public class HexEditor : Scenario
 {
     private string _fileName = "demo.bin";
     private HexView _hexView;
-    private MenuItem _miAllowEdits;
+    private MenuItemv2 _miAllowEdits;
     private bool _saved = true;
     private Shortcut _scAddress;
     private Shortcut _scInfo;
@@ -48,13 +47,13 @@ public class HexEditor : Scenario
 
         app.Add (_hexView);
 
-        var menu = new MenuBar
+        var menu = new MenuBarv2
         {
             Menus =
             [
                 new (
                      "_File",
-                     new MenuItem []
+                     new MenuItemv2 []
                      {
                          new ("_New", "", () => New ()),
                          new ("_Open", "", () => Open ()),
@@ -65,7 +64,7 @@ public class HexEditor : Scenario
                     ),
                 new (
                      "_Edit",
-                     new MenuItem []
+                     new MenuItemv2 []
                      {
                          new ("_Copy", "", () => Copy ()),
                          new ("C_ut", "", () => Cut ()),
@@ -74,7 +73,7 @@ public class HexEditor : Scenario
                     ),
                 new (
                      "_Options",
-                     new []
+                     new MenuItemv2 []
                      {
                          _miAllowEdits = new (
                                               "_AllowEdits",
@@ -82,14 +81,19 @@ public class HexEditor : Scenario
                                               () => 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);
 
         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) =>
                                            {
-                                               menu.ColorScheme = menu.ColorScheme with
+                                               menu.ColorScheme = menu.ColorScheme! with
                                                {
                                                    Normal = new (menu.ColorScheme.Normal.Foreground, args.CurrentValue)
                                                };

+ 193 - 1124
UICatalog/UICatalog.cs

@@ -1,7 +1,4 @@
 global using Attribute = Terminal.Gui.Attribute;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.CommandLine;
 using System.CommandLine.Builder;
 using System.CommandLine.Parsing;
@@ -9,29 +6,25 @@ using System.Data;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
-using System.IO;
-using System.Linq;
 using System.Reflection;
-using System.Runtime.InteropServices;
 using System.Text;
 using System.Text.Json;
-using System.Text.Json.Serialization;
 using Microsoft.Extensions.Logging;
-using static Terminal.Gui.ConfigurationManager;
-using Command = Terminal.Gui.Command;
 using Serilog;
 using Serilog.Core;
 using Serilog.Events;
-using ILogger = Microsoft.Extensions.Logging.ILogger;
-using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
 using Terminal.Gui;
+using static Terminal.Gui.ConfigurationManager;
+using Command = Terminal.Gui.Command;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
 
 #nullable enable
 
 namespace UICatalog;
 
 /// <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.
 /// </summary>
 /// <remarks>
@@ -50,85 +43,14 @@ namespace UICatalog;
 ///         </list>
 ///     </para>
 /// </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;
 
-    [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)
     {
@@ -139,18 +61,18 @@ public class UICatalogApp
             CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
         }
 
-        _scenarios = Scenario.GetScenarios ();
-        _categories = Scenario.GetAllCategories ();
+        UICatalogTopLevel.CachedScenarios = Scenario.GetScenarios ();
+        UICatalogTopLevel.CachedCategories = Scenario.GetAllCategories ();
 
         // Process command line args
 
         // If no driver is provided, the default driver is used.
         Option<string> driverOption = new Option<string> ("--driver", "The IConsoleDriver to use.").FromAmong (
              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");
@@ -171,11 +93,12 @@ public class UICatalogApp
         resultsFile.AddAlias ("--f");
 
         // 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> ()
             );
-        debugLogLevel.SetDefaultValue("Warning");
+        debugLogLevel.SetDefaultValue ("Warning");
         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.",
                                                                   getDefaultValue: () => "none"
                                                                  ).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")
@@ -198,7 +121,7 @@ public class UICatalogApp
         rootCommand.SetHandler (
                                 context =>
                                 {
-                                    var options = new Options
+                                    var options = new UICatalogCommandLineOptions
                                     {
                                         Scenario = context.ParseResult.GetValueForArgument (scenarioArgument),
                                         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
-                                    _options = options;
+                                    Options = options;
                                 }
                                );
 
@@ -227,16 +150,16 @@ public class UICatalogApp
             return 0;
         }
 
-        Scenario.BenchmarkTimeout = _options.BenchmarkTimeout;
+        Scenario.BenchmarkTimeout = Options.BenchmarkTimeout;
 
         Logging.Logger = CreateLogger ();
 
-        UICatalogMain (_options);
+        UICatalogMain (Options);
 
         return 0;
     }
 
-    private static LogEventLevel LogLevelToLogEventLevel (LogLevel logLevel)
+    public static LogEventLevel LogLevelToLogEventLevel (LogLevel logLevel)
     {
         return logLevel switch
                {
@@ -254,13 +177,14 @@ public class UICatalogApp
     private static ILogger CreateLogger ()
     {
         // 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 ()
-                     .MinimumLevel.ControlledBy (_logLevelSwitch)
+                     .MinimumLevel.ControlledBy (LogLevelSwitch)
                      .Enrich.FromLogContext () // Enables dynamic enrichment
                      .WriteTo.Debug ()
                      .WriteTo.File (
-                                    _logFilePath,
+                                    LogFilePath,
                                     rollingInterval: RollingInterval.Day,
                                     outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
                      .CreateLogger ();
@@ -278,35 +202,6 @@ public class UICatalogApp
         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>
     ///     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.
@@ -322,25 +217,31 @@ public class UICatalogApp
 
         Application.Init (driverName: _forceDriver);
 
-        if (_cachedTheme is null)
+        if (string.IsNullOrWhiteSpace (UICatalogTopLevel.CachedTheme))
         {
-            _cachedTheme = Themes?.Theme;
+            UICatalogTopLevel.CachedTheme = Themes?.Theme;
         }
         else
         {
-            Themes!.Theme = _cachedTheme;
+            Themes!.Theme = UICatalogTopLevel.CachedTheme;
             Apply ();
         }
 
         Application.Run<UICatalogTopLevel> ().Dispose ();
         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 ()
     {
-        // Setup a file system watcher for `./.tui/`
+        // Set up a file system watcher for `./.tui/`
         _currentDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
 
         string assemblyLocation = Assembly.GetExecutingAssembly ().Location;
@@ -364,7 +265,7 @@ public class UICatalogApp
         _currentDirWatcher.Path = tuiDir;
         _currentDirWatcher.Filter = "*config.json";
 
-        // Setup a file system watcher for `~/.tui/`
+        // Set up a file system watcher for `~/.tui/`
         _homeDirWatcher.NotifyFilter = NotifyFilters.LastWrite;
         var f = new FileInfo (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile));
         tuiDir = Path.Combine (f.FullName, ".tui");
@@ -399,7 +300,18 @@ public class UICatalogApp
         _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 ();
 
@@ -411,17 +323,15 @@ public class UICatalogApp
         // run it and exit when done.
         if (options.Scenario != "none")
         {
-            _topLevelColorScheme = "Base";
-
-            int item = _scenarios!.IndexOf (
-                                            _scenarios!.FirstOrDefault (
+            int item = UICatalogTopLevel.CachedScenarios!.IndexOf (
+                                                                   UICatalogTopLevel.CachedScenarios!.FirstOrDefault (
                                                                         s =>
                                                                             s.GetName ()
                                                                              .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 { })
             {
@@ -450,12 +360,13 @@ public class UICatalogApp
         while (RunUICatalogTopLevel () is { } scenario)
         {
             VerifyObjectsWereDisposed ();
-            Themes!.Theme = _cachedTheme!;
+            Themes!.Theme = UICatalogTopLevel.CachedTheme!;
             Apply ();
-            scenario.TopLevelColorScheme = _topLevelColorScheme;
+            scenario.TopLevelColorScheme = UICatalogTopLevel.CachedTopLevelColorScheme!;
 
 #if DEBUG_IDISPOSABLE
             View.DebugIDisposable = true;
+
             // Measure how long it takes for the app to shut down
             var sw = new Stopwatch ();
             string scenarioName = scenario.GetName ();
@@ -501,7 +412,7 @@ public class UICatalogApp
         }
 
         Application.Init (driverName: _forceDriver);
-        scenario.TopLevelColorScheme = _topLevelColorScheme;
+        scenario.TopLevelColorScheme = UICatalogTopLevel.CachedTopLevelColorScheme!;
 
         if (benchmark)
         {
@@ -527,11 +438,11 @@ public class UICatalogApp
 
     private static void BenchmarkAllScenarios ()
     {
-        List<BenchmarkResults> resultsList = new ();
+        List<BenchmarkResults> resultsList = [];
 
         var maxScenarios = 5;
 
-        foreach (Scenario s in _scenarios!)
+        foreach (Scenario s in UICatalogTopLevel.CachedScenarios!)
         {
             resultsList.Add (RunScenario (s, true)!);
             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 (
-                         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 ()
@@ -711,848 +624,4 @@ public class UICatalogApp
         RunState.Instances.Clear ();
 #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">
     <PropertyGroup>
-	    <StartupObject>UICatalog.UICatalogApp</StartupObject>
+	    <StartupObject>UICatalog.UICatalog</StartupObject>
         <OutputType>Exe</OutputType>
         <!-- 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. -->

+ 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);
+        }
+    }
+}