diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs
index f446a1157..5b3d36f02 100644
--- a/Terminal.Gui/Views/Menu/MenuBar.cs
+++ b/Terminal.Gui/Views/Menu/MenuBar.cs
@@ -236,12 +236,33 @@ public class MenuBar : Menu, IDesignable
/// The first menu item in the PopoverMenu will be selected and focused.
///
///
- public bool OpenMenu ()
+ public bool OpenMenu () { return OpenMenu (null); }
+
+ ///
+ /// Opens the first menu item with a at the specified screen position.
+ /// This is useful for programmatically opening the menu, for example when using the MenuBar as a dropdown list.
+ ///
+ ///
+ /// The screen position at which to open the menu. If , the menu will be positioned
+ /// at the default location (bottom-left of the first MenuBarItem).
+ ///
+ /// if a menu was opened; otherwise.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// When using MenuBar as a dropdown button next to a TextField, you can position the menu
+ /// to align with the left edge of the TextField by passing the TextField's screen position.
+ ///
+ ///
+ public bool OpenMenu (Point? position)
{
if (SubViews.OfType ().FirstOrDefault (mbi => mbi.PopoverMenu is { }) is { } first)
{
Active = true;
- ShowItem (first);
+ ShowItem (first, position);
return true;
}
@@ -401,7 +422,11 @@ public class MenuBar : Menu, IDesignable
/// Shows the specified popover, but only if the menu bar is active.
///
///
- private void ShowItem (MenuBarItem? menuBarItem)
+ ///
+ /// The screen position at which to show the popover. If , the menu will be positioned
+ /// at the default location (bottom-left of the MenuBarItem).
+ ///
+ private void ShowItem (MenuBarItem? menuBarItem, Point? position = null)
{
// Logging.Debug ($"{Title} - {menuBarItem?.Id}");
@@ -447,7 +472,8 @@ public class MenuBar : Menu, IDesignable
if (menuBarItem.PopoverMenu is { })
{
menuBarItem.PopoverMenu.App ??= App;
- menuBarItem.PopoverMenu.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom));
+ Point menuPosition = position ?? new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom);
+ menuBarItem.PopoverMenu.MakeVisible (menuPosition);
}
menuBarItem.Accepting += OnMenuItemAccepted;
diff --git a/Tests/UnitTests/Views/MenuBarTests.cs b/Tests/UnitTests/Views/MenuBarTests.cs
index f67fcb779..25273bc9f 100644
--- a/Tests/UnitTests/Views/MenuBarTests.cs
+++ b/Tests/UnitTests/Views/MenuBarTests.cs
@@ -794,4 +794,48 @@ public class MenuBarTests ()
Application.End (rs);
top.Dispose ();
}
+
+ [Fact]
+ [AutoInitShutdown]
+ public void OpenMenu_With_Position_Opens_At_Specified_Location ()
+ {
+ // Arrange
+ var top = new Toplevel ()
+ {
+ App = ApplicationImpl.Instance
+ };
+
+ var menuBar = new MenuBar () { Id = "menuBar", X = 10, Y = 0 };
+ top.Add (menuBar);
+
+ var menuItem1 = new MenuItem { Id = "menuItem1", Title = "Item _1" };
+ var menu = new Menu ([menuItem1]) { 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 - Open menu at custom position (0, 1)
+ Point customPosition = new Point (0, 1);
+ bool result = menuBar.OpenMenu (customPosition);
+
+ // Assert
+ Assert.True (result);
+ Assert.True (menuBar.Active);
+ Assert.True (menuBar.IsOpen ());
+ Assert.True (menuBarItem.PopoverMenu.Visible);
+
+ // The menu's Root should be positioned at or near the custom position
+ // (GetMostVisibleLocationForSubMenu may adjust it to fit on screen)
+ Assert.NotNull (menuBarItemPopover.Root);
+
+ Application.End (rs);
+ top.Dispose ();
+ }
}