I’ve been thinking about this some more, and I really didn’t like the idea of adding a new method to the Application class for each application menu thing we wanted to support. I think it is much better to add a new, final ApplicationMenu class to javafx.application.
I have also added what I think is support for GTK applications, but I cannot test this because I only have an M1 Mac Mini. Is anyone with a Linux box interested in testing this? Code is at https://github.com/dkkopp/jfx/tree/appmenu So, here is take 2: Added to ConditionalFeature: /** * Indicates whether or not an application menu exists. * * @since JavaFX 24.0 */ APPLICATION_MENU And in PlatformImpl: case APPLICATION_MENU: return PlatformUtil.isMac() || PlatformUtil.isLinux(); Added to javafx.application.Application: private ApplicationMenu applicationMenu = null; /** * Gets the ApplicationMenu for this application on platforms which support this concept. * * @return the ApplicationMenu * * @throws java.lang.UnsupportedOperationException if the current platform does not support the * ConditionalFeature.APPLICATION_MENU feature */ public final ApplicationMenu getApplicationMenu() { if (!PlatformImpl.isSupported(ConditionalFeature.APPLICATION_MENU)) { throw new UnsupportedOperationException("ConditionalFeature.APPLICATION_MENU is not" + " supported on this platform"); } synchronized (this) { if (applicationMenu == null) { applicationMenu = new ApplicationMenu(); } return applicationMenu; } } New class added to package javafx.application: /** * This class provides application menu integration for an Application on platforms which support * this concept. * * @since JavaFX 24.0 * * @author David Kopp */ public final class ApplicationMenu { /** * Package scope constructor to create the ApplicationMenu object. */ ApplicationMenu() { } /** * Installs a handler to show a custom about window for your application. * <p> * Setting the handler to null reverts it to the default behavior. * * @param handler the handler */ public void setAboutHandler(Runnable handler) { PlatformImpl.setAboutHandler(handler); } /** * Installs a handler to show a custom settings window for your application. * <p> * Setting the handler to null reverts it to the default behavior. * * @param handler the handler */ public void setSettingsHandler(Runnable handler) { PlatformImpl.setSettingsHandler(handler); } } Added to GtkApplication: @Override public void setAboutHandler(Runnable handler) { aboutHandler = handler; rebuildGtkMenu(); } @Override public void setSettingsHandler(Runnable handler) { settingsHandler = handler; rebuildGtkMenu(); } private void rebuildGtkMenu() { for (int index = this.gtkMenu.getItems().size() - 1; index >= 0; index--) { this.gtkMenu.remove(index); } if (null != aboutHandler) { MenuItem aboutMenu = createMenuItem("About " + getName(), new MenuItem.Callback() { @Override public void action() { aboutHandler.run(); } @Override public void validate() { } }); this.gtkMenu.add(aboutMenu); this.gtkMenu.add(MenuItem.Separator); } if (null != settingsHandler) { MenuItem preferencesMenu = createMenuItem("Settings...", new MenuItem.Callback() { @Override public void action() { settingsHandler.run(); } @Override public void validate() { } }, ',', KeyEvent.MODIFIER_COMMAND); this.gtkMenu.add(preferencesMenu); this.gtkMenu.add(MenuItem.Separator); } } public void installGtkMenu(MenuBar menubar) { this.gtkMenu = createMenu("GTK"); rebuildGtkMenu(); menubar.add(this.gtkMenu); } @Override public void installDefaultMenus(MenuBar menubar) { checkEventThread(); installGtkMenu(menubar); } MacApplication stays the same.