UPDATE 9/9/2017
For JavaFX, set the useSystemMenuBar property. This will take the MenuBar and move it off of the top of the Stage and put it in the Finder.
I'll keep the rest of the post as-is in case someone has been using it as a reference. -Carl
----
All source code in this post is available at GitHub under examples-javafx-macmenu.
This is a JavaFX app running on a Mac. Notice that the top-level menubar denotes the FX executable process "java". Also, notice that the menubar belonging to the app stays with the window.
Running an FX Application on a Mac |
This is a Swing app running on a Mac. We get the name of the main class in the top-level menubar. This example also has the app menubar staying with the window.
Running a Swing Application on a Mac |
Mac LAF MenuBar for the same Swing app |
JFXPanel
What this means for the FX developer, is that a small amount of Swing code is needed to best support the Mac. If you're heavily vested in the Application class, like if you're using an FX preloader from JNLP, this may not be the right approach. Technically, I'd prefer the whole thing to be in FX, but the Mac experience is at stake.
This screenshot shows an FX / Swing hybrid. JFXPanel is used to hold the main view of my FX application. In both examples -- before and after involving Swing -- I have an FXML file loaded in a Scene. The Swing version replaces a Stage with a JFXPanel.
Swing Providing Mac MenuBar to JavaFX App |
System.setProperty("apple.laf.useScreenMenuBar", "true");
Next, I create the Swing JFrame and use an FXMLLoader to load the main FX view into a Scene. The Scene is added to a JFXPanel which is then added to the JFrame contentPane.
try { JFXPanel mainFXWindow = createMainFXWindow(); this.getContentPane().add( mainFXWindow ); } catch(Exception exc) { exc.printStackTrace(); System.exit(1); }
This is the code to load the .fxml file.
private JFXPanel createMainFXWindow() throws Exception { JFXPanel jfxPanel = new JFXPanel(); // initializes the toolkit FXMLLoader fxmlLoader = new FXMLLoader( this.getClass().getResource("/macmenu-fxml/MacMenu.fxml") ); fxmlLoader.load(); Parent p = fxmlLoader.getRoot(); Scene scene = new Scene(p); jfxPanel.setScene( scene ); return jfxPanel; }
The FXML code is the same for the pure and hybrid approaches except for a block of code in the @FXML initialize() method of the Controller. This checks the property and will remove the menubar which is being displayed by Swing. If the system property is not set, then we won't have a menubar at the top of the screen, and the VBox can retain what was defined and wired in the FXML.
@FXML VBox vbox; @FXML MenuBar menubar; @FXML public void initialize() { String macMenu = System.getProperty("apple.laf.useScreenMenuBar"); if( macMenu != null && macMenu == "true" ) { vbox.getChildren().remove(menubar); } }
At some point, maybe a pure JavaFX integration with the Mac will be available. However, if you're like me, you can't wait for that. If you're building an FX app, I'd continue doing almost all of it in FX, but you might find a few examples like Mac integration or the System Tray where you may need a little Swing.
This post is based on information from the Apple website.
All source code in this post is available at GitHub under examples-javafx-macmenu.
No comments:
Post a Comment