Browse Source

Add OpenMenu() method to MenuBar for dropdown list support

Co-authored-by: tig <[email protected]>
copilot-swe-agent[bot] 3 weeks ago
parent
commit
40e777acc1
2 changed files with 100 additions and 0 deletions
  1. 24 0
      Terminal.Gui/Views/Menu/MenuBar.cs
  2. 76 0
      Tests/UnitTests/Views/MenuBarTests.cs

+ 24 - 0
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -225,6 +225,30 @@ public class MenuBar : Menu, IDesignable
     /// <exception cref="NotImplementedException"></exception>
     public bool IsOpen () { return SubViews.OfType<MenuBarItem> ().Count (sv => sv is { PopoverMenuOpen: true }) > 0; }
 
+    /// <summary>
+    ///     Opens the first menu item with a <see cref="PopoverMenu"/>. This is useful for programmatically opening
+    ///     the menu, for example when using the MenuBar as a dropdown list.
+    /// </summary>
+    /// <returns><see langword="true"/> if a menu was opened; <see langword="false"/> otherwise.</returns>
+    /// <remarks>
+    ///     <para>
+    ///         This method activates the MenuBar and shows the first MenuBarItem that has a PopoverMenu.
+    ///         The first menu item in the PopoverMenu will be selected and focused.
+    ///     </para>
+    /// </remarks>
+    public bool OpenMenu ()
+    {
+        if (SubViews.OfType<MenuBarItem> ().FirstOrDefault (mbi => mbi.PopoverMenu is { }) is { } first)
+        {
+            Active = true;
+            ShowItem (first);
+
+            return true;
+        }
+
+        return false;
+    }
+
     private bool _active;
 
     /// <summary>

+ 76 - 0
Tests/UnitTests/Views/MenuBarTests.cs

@@ -718,4 +718,80 @@ public class MenuBarTests ()
         Application.End (rs);
         top.Dispose ();
     }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void OpenMenu_Opens_First_MenuBarItem ()
+    {
+        // Arrange
+        var top = new Toplevel ()
+        {
+            App = ApplicationImpl.Instance
+        };
+
+        var menuBar = new MenuBar () { Id = "menuBar" };
+        top.Add (menuBar);
+
+        var menuItem1 = new MenuItem { Id = "menuItem1", Title = "Item _1" };
+        var menuItem2 = new MenuItem { Id = "menuItem2", Title = "Item _2" };
+        var menu = new Menu ([menuItem1, menuItem2]) { Id = "menu" };
+        var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_File" };
+        var menuBarItemPopover = new PopoverMenu ();
+
+        menuBar.Add (menuBarItem);
+        menuBarItem.PopoverMenu = menuBarItemPopover;
+        menuBarItemPopover.Root = menu;
+
+        SessionToken rs = Application.Begin (top);
+        Assert.False (menuBar.Active);
+        Assert.False (menuBar.IsOpen ());
+
+        // Act
+        bool result = menuBar.OpenMenu ();
+
+        // Assert
+        Assert.True (result);
+        Assert.True (menuBar.Active);
+        Assert.True (menuBar.IsOpen ());
+        Assert.True (menuBar.CanFocus);
+        Assert.True (menuBarItem.PopoverMenu.Visible);
+        Assert.True (menuBarItem.PopoverMenu.HasFocus);
+        Assert.Equal (menuItem1, menu.SelectedMenuItem);
+
+        Application.End (rs);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void OpenMenu_Returns_False_When_No_MenuBarItem_With_PopoverMenu ()
+    {
+        // Arrange
+        var top = new Toplevel ()
+        {
+            App = ApplicationImpl.Instance
+        };
+
+        var menuBar = new MenuBar () { Id = "menuBar" };
+        top.Add (menuBar);
+
+        var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_File" };
+        menuBar.Add (menuBarItem);
+        // Note: No PopoverMenu set
+
+        SessionToken rs = Application.Begin (top);
+        Assert.False (menuBar.Active);
+        Assert.False (menuBar.IsOpen ());
+
+        // Act
+        bool result = menuBar.OpenMenu ();
+
+        // Assert
+        Assert.False (result);
+        Assert.False (menuBar.Active);
+        Assert.False (menuBar.IsOpen ());
+
+        Application.End (rs);
+        top.Dispose ();
+    }
 }