using UICatalog.Scenarios;
using Xunit.Abstractions;


namespace Terminal.Gui.ViewsTests;

public class MenuBarTests (ITestOutputHelper output)
{
    [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.Refresh ();

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @$"
 Nullable Checked       
┌──────────────────────┐
│ {
    CM.Glyphs.NullChecked
} 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_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 ();
    }

    // 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 (KeyCode.Null, menuBarItem.Shortcut);
    }

    [Fact]
    [AutoInitShutdown]
    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]
    [AutoInitShutdown]
    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
        };

        TestHelpers.AssertDriverAttributesAre (
                                               @"
00000000000000",
                                               Application.Driver,
                                               attributes
                                              );

        Assert.True (
                     menu.NewMouseEvent (
                                         new() { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu }
                                        )
                    );
        top.Draw ();

        TestHelpers.AssertDriverAttributesAre (
                                               @"
11111100000000
00000000000000
01111111111110
02222222222220
00000000000000
00000000000000
00000000000000",
                                               Application.Driver,
                                               attributes
                                              );

        Assert.True (
                     top.Subviews [1]
                        .NewMouseEvent (
                                        new() { Position = new (0, 2), Flags = MouseFlags.Button1Clicked, View = top.Subviews [1] }
                                       )
                    );
        top.Subviews [1].Draw ();

        TestHelpers.AssertDriverAttributesAre (
                                               @"
11111100000000
00000000000000
01111111111110
02222222222220
00000000000000
00000000000000
00000000000000",
                                               Application.Driver,
                                               attributes
                                              );

        Assert.True (
                     top.Subviews [1]
                        .NewMouseEvent (
                                        new() { Position = new (0, 2), Flags = MouseFlags.ReportMousePosition, View = top.Subviews [1] }
                                       )
                    );
        top.Subviews [1].Draw ();

        TestHelpers.AssertDriverAttributesAre (
                                               @"
11111100000000
00000000000000
01111111111110
02222222222220
00000000000000
00000000000000
00000000000000",
                                               Application.Driver,
                                               attributes
                                              );
        top.Dispose ();
    }

    [Fact]
    [AutoInitShutdown]
    public void Draw_A_Menu_Over_A_Dialog ()
    {
        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);

        TestHelpers.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.SetNeedsDisplay ();
        }

        RunState rsDialog = Application.Begin (dialog);

        Assert.Equal (new (2, 2, 15, 4), dialog.Frame);

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│                                      │
│ ┌─────────────┐                      │
│ │  File       │                      │
│ │             │                      │
│ └─────────────┘                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.Equal ("File", menu.Menus [0].Title);
        menu.OpenMenu ();
        var firstIteration = false;
        Application.RunIteration (ref rsDialog, ref firstIteration);

        TestHelpers.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.OnMouseEvent (new() { Position = new (20, 5), Flags = MouseFlags.Button1Clicked });

        firstIteration = false;

        // Need to fool MainLoop into thinking it's running
        Application.MainLoop.Running = true;
        Application.RunIteration (ref rsDialog, ref firstIteration);
        Assert.Equal (items [0], menu.Menus [0].Title);

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│                                      │
│ ┌─────────────┐                      │
│ │  New        │                      │
│ │             │                      │
│ └─────────────┘                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        for (var i = 0; i < items.Count; i++)
        {
            menu.OpenMenu ();

            Application.OnMouseEvent (new() { Position = new (20, 5 + i), Flags = MouseFlags.Button1Clicked });

            firstIteration = false;
            Application.RunIteration (ref rsDialog, ref firstIteration);
            Assert.Equal (items [i], menu.Menus [0].Title);
        }

        ((FakeDriver)Application.Driver).SetBufferSize (20, 15);
        menu.OpenMenu ();
        firstIteration = false;
        Application.RunIteration (ref rsDialog, ref firstIteration);

        TestHelpers.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]
    [AutoInitShutdown]
    public void Draw_A_Menu_Over_A_Top_Dialog ()
    {
        ((FakeDriver)Application.Driver).SetBufferSize (40, 15);

        Assert.Equal (new (0, 0, 40, 15), Application.Driver.Clip);
        TestHelpers.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.SetNeedsDisplay ();
        }

        RunState rs = Application.Begin (dialog);

        Assert.Equal (new (2, 2, 15, 4), dialog.Frame);

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
  ┌─────────────┐
  │  File       │
  │             │
  └─────────────┘",
                                                      output
                                                     );

        Assert.Equal ("File", menu.Menus [0].Title);
        menu.OpenMenu ();
        var firstIteration = false;
        Application.RunIteration (ref rs, ref firstIteration);

        TestHelpers.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.OnMouseEvent (new() { Position = new (20, 5), Flags = MouseFlags.Button1Clicked });

        firstIteration = false;

        // Need to fool MainLoop into thinking it's running
        Application.MainLoop.Running = true;
        Application.RunIteration (ref rs, ref firstIteration);
        Assert.Equal (items [0], menu.Menus [0].Title);

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
  ┌─────────────┐
  │  New        │
  │             │
  └─────────────┘",
                                                      output
                                                     );

        for (var i = 1; i < items.Count; i++)
        {
            menu.OpenMenu ();

            Application.OnMouseEvent (new() { Position = new (20, 5 + i), Flags = MouseFlags.Button1Clicked });

            firstIteration = false;
            Application.RunIteration (ref rs, ref firstIteration);
            Assert.Equal (items [i], menu.Menus [0].Title);
        }

        ((FakeDriver)Application.Driver).SetBufferSize (20, 15);
        menu.OpenMenu ();
        firstIteration = false;
        Application.RunIteration (ref rs, ref firstIteration);

        TestHelpers.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) })
            ]
        };

        Assert.Equal (new (-1, -1), new Point (menu.Frame.X, menu.Frame.Y));

        Toplevel top = new ();
        Application.Begin (top);
        menu.OpenMenu ();
        Application.Refresh ();

        var expected = @"
──────┐
 One  │
 Two  │
──────┘
";

        Rectangle pos = TestHelpers.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.Refresh ();

        expected = @"
 One  │
 Two  │
──────┘
";

        pos = TestHelpers.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.Refresh ();

        expected = @"
┌──────
│ One  
│ Two  
└──────
";

        pos = TestHelpers.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.Refresh ();

        expected = @"
┌──────
│ One  
│ Two  
";

        pos = TestHelpers.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) })
            ]
        };

        Assert.Equal (new (-2, -1), new Point (menu.Frame.X, menu.Frame.Y));

        Toplevel top = new ();
        Application.Begin (top);
        menu.OpenMenu ();
        Application.Refresh ();

        var expected = @"
ne
wo
";

        _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);

        menu.CloseAllMenus ();
        menu.Frame = new (-2, -2, menu.Frame.Width, menu.Frame.Height);
        menu.OpenMenu ();
        Application.Refresh ();

        expected = @"
wo
";

        _ = TestHelpers.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.Refresh ();

        expected = @"
 On
 Tw
";

        _ = TestHelpers.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.Refresh ();

        expected = @"
 On
";

        _ = TestHelpers.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.Refresh ();

        var expected = @"
┌──────┐
│ One  │
│ Two  │
└──────┘
";

        Rectangle pos = TestHelpers.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.Refresh ();

        var expected = @"
 One
 Two
";

        _ = TestHelpers.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 = TestHelpers.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 = TestHelpers.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 ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);

        Assert.True (Application.Top.Subviews [1].NewKeyDownEvent (Key.N));
        Application.MainLoop.RunIteration ();
        Assert.True (newAction);

        Assert.True (menu.NewKeyDownEvent (Key.E.WithAlt));
        Assert.True (menu.IsMenuOpen);
        Application.Top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);

        Assert.True (Application.Top.Subviews [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 ("_Edit", "_3rd Level", "", 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 ("_About", "_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;

        MenuBar menu = MenuBarScenario.CreateTestMenu (
                                                       s =>
                                                       {
                                                           miAction = s;

                                                           return true;
                                                       }
                                                      );
        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)
        {
            top.NewKeyDownEvent (new (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.AltMask | 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;

        MenuBar menu = MenuBarScenario.CreateTestMenu (
                                                       s =>
                                                       {
                                                           miAction = s;

                                                           return true;
                                                       }
                                                      );
        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.Refresh ();

        TestHelpers.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.Refresh ();

        TestHelpers.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 ();

        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);

        Assert.True (menu.NewMouseEvent (new() { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu }));
        Assert.False (menu.IsMenuOpen);
        top.Draw ();
        TestHelpers.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);

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.True (win.NewKeyDownEvent (menu.Key));
        top.Draw ();

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│┌──────┐                              │
││ New  │                              │
│└──────┘                              │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
        Application.Refresh ();

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│      ┌─────────┐                     │
│      │ Delete ►│                     │
│      └─────────┘                     │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
        top.Draw ();

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│      ┌─────────┐                     │
│      │ Delete ►│┌───────────┐        │
│      └─────────┘│ All       │        │
│                 │ Selected  │        │
│                 └───────────┘        │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
        top.Draw ();

        TestHelpers.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);

                                     TestHelpers.AssertDriverContentsWithFrameAre (
                                                                                   @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                                                   output
                                                                                  );

                                     Assert.True (win.NewKeyDownEvent (menu.Key));
                                     top.Draw ();

                                     TestHelpers.AssertDriverContentsWithFrameAre (
                                                                                   @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│┌──────┐                              │
││ New  │                              │
│└──────┘                              │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                                                   output
                                                                                  );

                                     Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
                                     Application.Refresh ();

                                     TestHelpers.AssertDriverContentsWithFrameAre (
                                                                                   @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│      ┌─────────┐                     │
│      │ Delete ►│                     │
│      └─────────┘                     │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                                                   output
                                                                                  );

                                     Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
                                     top.Draw ();

                                     TestHelpers.AssertDriverContentsWithFrameAre (
                                                                                   @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│      ┌─────────┐                     │
│      │ Delete ►│┌───────────┐        │
│      └─────────┘│ All       │        │
│                 │ Selected  │        │
│                 └───────────┘        │
└──────────────────────────────────────┘",
                                                                                   output
                                                                                  );

                                     Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
                                     top.Draw ();

                                     TestHelpers.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);
        Application.Begin (win);

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.True (win.NewKeyDownEvent (menu.Key));
        win.Draw ();

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│┌──────┐                              │
││ New  │                              │
│└──────┘                              │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.True (menu.NewKeyDownEvent (Key.CursorRight));
        Application.Refresh ();

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│      ┌─────────┐                     │
│      │ Delete ►│                     │
│      └─────────┘                     │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
        win.Draw ();

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│      ┌─────────┐                     │
│      │ Delete ►│┌───────────┐        │
│      └─────────┘│ All       │        │
│                 │ Selected  │        │
│                 └───────────┘        │
└──────────────────────────────────────┘",
                                                      output
                                                     );

        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
        win.Draw ();

        TestHelpers.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;

                                     TestHelpers.AssertDriverContentsWithFrameAre (
                                                                                   @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                                                   output
                                                                                  );

                                     Assert.True (top.NewKeyDownEvent (Key.F9));
                                     top.Draw ();

                                     TestHelpers.AssertDriverContentsWithFrameAre (
                                                                                   @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│┌──────┐                              │
││ New  │                              │
│└──────┘                              │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                                                   output
                                                                                  );

                                     Assert.True (top.Subviews [0].NewKeyDownEvent (Key.CursorRight));
                                     Application.Refresh ();

                                     TestHelpers.AssertDriverContentsWithFrameAre (
                                                                                   @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│      ┌─────────┐                     │
│      │ Delete ►│                     │
│      └─────────┘                     │
│                                      │
│                                      │
└──────────────────────────────────────┘",
                                                                                   output
                                                                                  );

                                     Assert.True (
                                                  ((MenuBar)top.Subviews [0])._openMenu.NewKeyDownEvent (Key.CursorRight)
                                                 );
                                     top.Draw ();

                                     TestHelpers.AssertDriverContentsWithFrameAre (
                                                                                   @"
┌──────────────────────────────────────┐
│ File  Edit                           │
│      ┌─────────┐                     │
│      │ Delete ►│┌───────────┐        │
│      └─────────┘│ All       │        │
│                 │ Selected  │        │
│                 └───────────┘        │
└──────────────────────────────────────┘",
                                                                                   output
                                                                                  );

                                     Assert.True (
                                                  ((MenuBar)top.Subviews [0])._openMenu.NewKeyDownEvent (Key.CursorRight)
                                                 );
                                     top.Draw ();

                                     TestHelpers.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 ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);

        // Open second
        Assert.True (Application.Top.Subviews [1].NewKeyDownEvent (Key.CursorRight));
        Assert.True (menu.IsMenuOpen);
        top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);

        // Close menu
        Assert.True (menu.NewKeyDownEvent (menu.Key));
        Assert.False (menu.IsMenuOpen);
        top.Draw ();
        TestHelpers.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);
        top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);

        // Open second
        Assert.True (top.Subviews [1].NewKeyDownEvent (Key.CursorRight));
        Assert.True (menu.IsMenuOpen);
        Application.Top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);

        // Close menu
        Assert.True (menu.NewKeyDownEvent (menu.Key));
        Assert.False (menu.IsMenuOpen);
        top.Draw ();
        TestHelpers.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);

        top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);

        for (var i = 0; i < expectedMenu.Menus.Length; i++)
        {
            menu.OpenMenu (i);
            Assert.True (menu.IsMenuOpen);
            top.Draw ();
            TestHelpers.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 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) =>
                           {
                               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", 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", 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", 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", 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", miCurrent.Parent.Title);
        Assert.Equal ("_New doc", miCurrent.Title);

        Assert.True (mCurrent.NewKeyDownEvent (Key.CursorDown));
        Assert.True (menu.IsMenuOpen);
        Assert.Equal ("_File", miCurrent.Parent.Title);
        Assert.Equal ("_Save", miCurrent.Title);

        Assert.True (mCurrent.NewKeyDownEvent (Key.CursorUp));
        Assert.True (menu.IsMenuOpen);
        Assert.Equal ("_File", miCurrent.Parent.Title);
        Assert.Equal ("_New", miCurrent.Title);

        // 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. │
└──────────────────────────────┘
";
        TestHelpers.AssertDriverContentsAre (expected, output);

        cancelClosing = true;
        Assert.True (menu.NewKeyDownEvent (menu.Key));
        Assert.True (menu.IsMenuOpen);
        Assert.False (isMenuClosed);
        top.Draw ();

        expected = @"
Edit
┌──────────────────────────────┐
│ Copy   Copies the selection. │
└──────────────────────────────┘
";
        TestHelpers.AssertDriverContentsAre (expected, output);

        cancelClosing = false;
        Assert.True (menu.NewKeyDownEvent (menu.Key));
        Assert.False (menu.IsMenuOpen);
        Assert.True (isMenuClosed);
        top.Draw ();

        expected = @"
Edit
";
        TestHelpers.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 menu = 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) }
                    )
            ]
        };

        menu.MenuOpened += (s, e) =>
                           {
                               miCurrent = e.MenuItem;
                               mCurrent = menu.OpenCurrentMenu;
                           };
        var top = new Toplevel ();
        top.Add (menu);
        Application.Begin (top);

        // Click on Edit
        Assert.True (
                     menu.NewMouseEvent (
                                         new() { Position = new (10, 0), Flags = MouseFlags.Button1Pressed, View = menu }
                                        )
                    );
        Assert.True (menu.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 (menu.IsMenuOpen);
        Assert.Equal ("_Edit", miCurrent.Parent.Title);
        Assert.Equal ("_Paste", miCurrent.Title);

        for (var i = 2; i >= -1; i--)
        {
            if (i == -1)
            {
                // Edit menu is open. Click on the menu at Y = -1, which is outside the menu.
                Assert.False (
                              mCurrent.NewMouseEvent (
                                                      new() { Position = new (10, i), Flags = MouseFlags.ReportMousePosition, View = menu }
                                                     )
                             );
            }
            else
            {
                // Edit menu is open. Click on the menu at Y = i.
                Assert.True (
                             mCurrent.NewMouseEvent (
                                                     new() { Position = new (10, i), Flags = MouseFlags.ReportMousePosition, View = mCurrent }
                                                    )
                            );
            }

            Assert.True (menu.IsMenuOpen);

            if (i == 2)
            {
                Assert.Equal ("_Edit", miCurrent.Parent.Title);
                Assert.Equal ("_Paste", miCurrent.Title);
            }
            else if (i == 1)
            {
                Assert.Equal ("_Edit", miCurrent.Parent.Title);
                Assert.Equal ("C_ut", miCurrent.Title);
            }
            else if (i == 0)
            {
                Assert.Equal ("_Edit", miCurrent.Parent.Title);
                Assert.Equal ("_Copy", miCurrent.Title);
            }
            else
            {
                Assert.Equal ("_Edit", miCurrent.Parent.Title);
                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 ();
        TestHelpers.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);
        top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);

        // Right - Format
        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight));
        Assert.True (menu.IsMenuOpen);
        Assert.False (tf.HasFocus);
        top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output);

        // Left - Edit
        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft));
        Assert.True (menu.IsMenuOpen);
        Assert.False (tf.HasFocus);
        top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output);

        Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft));
        Assert.True (menu.IsMenuOpen);
        Assert.False (tf.HasFocus);
        top.Draw ();
        TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);

        Assert.True (menu.NewKeyDownEvent (menu.Key));
        Assert.False (menu.IsMenuOpen);
        Assert.True (tf.HasFocus);
        top.Draw ();
        TestHelpers.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 ();
        TestHelpers.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);
        top.Draw ();
        TestHelpers.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);
        top.Draw ();
        TestHelpers.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);
        top.Draw ();
        TestHelpers.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);
        top.Draw ();
        TestHelpers.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);
        top.Draw ();
        TestHelpers.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, ref firstIteration);

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
 File                         
┌────────────────────────────┐
│ Open   Open a file  Ctrl+O │
└────────────────────────────┘",
                                                      output
                                                     );

        ((FakeDriver)Application.Driver).SetBufferSize (20, 15);
        firstIteration = false;
        Application.RunIteration (ref rs, ref firstIteration);

        TestHelpers.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]
    [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.OnKeyDown (Key.S.WithCtrl);
        Application.MainLoop.RunIteration ();

        Assert.True (saveAction);
        top.Dispose ();
    }

    [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]
    [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 = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);

        Assert.True (menu.NewKeyDownEvent (menu.Key));
        top.Draw ();

        expected = @"
 Numbers  
┌────────┐
│ One    │
│ Two   ►│
│ Three  │
└────────┘
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);

        Assert.True (Application.Top.Subviews [1].NewKeyDownEvent (Key.CursorDown));
        top.Draw ();

        expected = @"
 Numbers                 
┌────────┐               
│ One    │               
│ Two   ►│┌─────────────┐
│ Three  ││ Sub-Menu 1  │
└────────┘│ Sub-Menu 2  │
          └─────────────┘
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);

        Assert.True (Application.Top.Subviews [2].NewKeyDownEvent (Key.CursorLeft));
        top.Draw ();

        expected = @"
 Numbers  
┌────────┐
│ One    │
│ Two   ►│
│ Three  │
└────────┘
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);

        Assert.True (Application.Top.Subviews [1].NewKeyDownEvent (Key.Esc));
        top.Draw ();

        expected = @"
 Numbers
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        top.Dispose ();
    }

    [Fact]
    [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 = TestHelpers.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 = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 10, 6), pos);

        Assert.False (
                      menu.NewMouseEvent (
                                          new()
                                          {
                                              Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = Application.Top.Subviews [1]
                                          }
                                         )
                     );
        top.Draw ();

        expected = @"
 Numbers                 
┌────────┐               
│ One    │               
│ Two   ►│┌─────────────┐
│ Three  ││ Sub-Menu 1  │
└────────┘│ Sub-Menu 2  │
          └─────────────┘
";

        pos = TestHelpers.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 [1]
                                          }
                                         )
                     );
        top.Draw ();

        expected = @"
 Numbers  
┌────────┐
│ One    │
│ Two   ►│
│ Three  │
└────────┘
";

        pos = TestHelpers.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 = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 8, 1), pos);
        top.Dispose ();
    }

    [Fact]
    [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  ";

        _ = TestHelpers.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";

        _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
    }

    [Fact]
    [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 = TestHelpers.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 = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 10, 6), pos);

        Assert.True (Application.Top.Subviews [1].NewKeyDownEvent (Key.CursorDown));
        Assert.True (Application.Top.Subviews [1].NewKeyDownEvent (Key.Enter));
        top.Draw ();

        expected = @"
 Numbers       
┌─────────────┐
│◄    Two     │
├─────────────┤
│ Sub-Menu 1  │
│ Sub-Menu 2  │
└─────────────┘
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 15, 7), pos);

        Assert.True (Application.Top.Subviews [2].NewKeyDownEvent (Key.Enter));
        top.Draw ();

        expected = @"
 Numbers  
┌────────┐
│ One    │
│ Two   ►│
│ Three  │
└────────┘
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 10, 6), pos);

        Assert.True (Application.Top.Subviews [1].NewKeyDownEvent (Key.Esc));
        top.Draw ();

        expected = @"
 Numbers
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 8, 1), pos);
        top.Dispose ();
    }

    [Fact]
    [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 = TestHelpers.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 = TestHelpers.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 [1] }));
        top.Draw ();

        expected = @"
 Numbers       
┌─────────────┐
│◄    Two     │
├─────────────┤
│ Sub-Menu 1  │
│ Sub-Menu 2  │
└─────────────┘
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 15, 7), pos);

        Assert.False (menu.NewMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Clicked, View = Application.Top.Subviews [2] }));
        top.Draw ();

        expected = @"
 Numbers  
┌────────┐
│ One    │
│ Two   ►│
│ Three  │
└────────┘
";

        pos = TestHelpers.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 = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 8, 1), pos);
        top.Dispose ();
    }

    [Fact]
    [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  ";

        _ = TestHelpers.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  ";

        _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
    }

    [Fact]
    [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 = TestHelpers.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 = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 8, 4), pos);

        Assert.False (
                      menu.NewMouseEvent (
                                          new() { Position = new (1, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top.Subviews [1] }
                                         )
                     );
        top.Draw ();

        expected = @"
 Numbers     
◄    Two     
─────────────
 Sub-Menu 1  
 Sub-Menu 2  
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 13, 5), pos);

        Assert.False (
                      menu.NewMouseEvent (
                                          new() { Position = new (1, 1), Flags = MouseFlags.Button1Clicked, View = Application.Top.Subviews [2] }
                                         )
                     );
        top.Draw ();

        expected = @"
 Numbers
 One    
 Two   ►
 Three  
";

        pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
        Assert.Equal (new (1, 0, 8, 4), pos);

        Assert.False (
                      menu.NewMouseEvent (
                                          new() { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.Top }
                                         )
                     );
        top.Draw ();

        expected = @"
 Numbers
";

        pos = TestHelpers.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 ();
    }

    // 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";

        // 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;
            }
        }

        public string ExpectedBottomRow (int i)
        {
            return $"{
                CM.Glyphs.LLCorner
            }{
                new (CM.Glyphs.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)
            }{
                CM.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 $"{CM.Glyphs.VLine} {Menus [i].Children [0].Title}  {CM.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 $"{
                CM.Glyphs.ULCorner
            }{
                new (CM.Glyphs.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)
            }{
                CM.Glyphs.URCorner
            }  \n";
        }

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

    [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.Accept += (s, e) => btnClicked = true;
        var top = new Toplevel ();
        top.Add (menu, btn);
        Application.Begin (top);

        Application.OnMouseEvent (new () { Position = new (0, 4), Flags = MouseFlags.Button1Clicked });
        Assert.True (btnClicked);
        top.Dispose ();
    }
}
