Search code examples
javajavafx

How to open JavaFX Menu upwards rather than down


Any way to make the menu open to the up direction in JavaFX? Normally the menu bar is always at the top of a window and the menu is opening down-ways. I was wondering, if I could have my menu bar at the bottom of the window and open the menu up?

I tried the JavaFX CSS reference and the JavaFX Javadocs, and Google search. But I don't think there is anything about it.

JavaFX menu bottom


Solution

  • If you're okay with relying on implementation details, then you can utilize the fact that each menu in the menu bar is an instance of MenuButton (at least when using the default skin). That class defines the popupSide property which you can set to TOP.

    Here's an example:

    import javafx.application.Application;
    import javafx.geometry.Side;
    import javafx.scene.Scene;
    import javafx.scene.control.Menu;
    import javafx.scene.control.MenuBar;
    import javafx.scene.control.MenuButton;
    import javafx.scene.control.MenuItem;
    import javafx.scene.layout.BorderPane;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
      @Override
      public void start(Stage primaryStage) {
        var fileMenu = new Menu("File");
        for (int i = 1; i <= 5; i++) {
          fileMenu.getItems().add(new MenuItem("Item #" + i));
        }
        var menuBar = new MenuBar(fileMenu);
    
        var root = new BorderPane();
        root.setBottom(menuBar);
    
        primaryStage.setScene(new Scene(root, 500, 300));
        primaryStage.show();
    
        for (var menu : menuBar.lookupAll(".menu")) {
          ((MenuButton) menu).setPopupSide(Side.TOP);
        }
      }
    
      public static void main(String[] args) {
        launch(Main.class);
      }
    }
    
    

    Which results in the following on Windows 10 using JavaFX 23.0.1:

    GIF demonstrating result of example code

    This example uses Node#lookupAll(String) to get the underlying MenuButton instances. That should work for any number of menus in the menu bar. Unfortunately, the popupSide property does not appear to be styleable, so you can't set it from CSS.

    Note you will have to:

    1. Wait to execute the solution until after you added all your menus and the menu bar's skin has been initialized. One way to wait for the skin to be initialized is to wait until after the menu bar's window has been shown at least once (like in the example).

    2. Based on the implementation of MenuBarSkin, you'll have to re-execute the solution if you:

      • Modify the menus list of the MenuBar in any way.

      • Change the visibility of a menu currently in the menu bar.

      The reason is because the skin apparently rebuilds the entire UI of the menu bar in those cases, which results in new menu buttons being created. I'm not sure if modifying the menus themselves (beyond their visibility) requires you to re-execute the solution.

    And keep in mind this solution relies on implementation details, which means it may not work in other versions or break in a future version. I only tested the example with JavaFX 23.0.1.


    That all said, it may be best to not use a MenuBar in your case. Consider directly using multiple MenuButton in something like an HBox instead. That way you don't have to rely on implementation details. Though you may have to style each MenuButton to fit your needs.