Geertjan is a DZone Zone Leader and has posted 468 posts at DZone. You can read more from them at their website. View Full User Profile

How to Integrate JavaFX into the NetBeans Platform Menubar

04.24.2013
| 7756 views |
  • submit to reddit

Let's say you want to replace the NetBeans Platform menubar, written in Swing, with one written in JavaFX. Why? For fun. Also, CSS stylesheet support in JavaFX is handy for redesigning the menubar's appearance: 

The coloring in the menubar above, which is indeed a JavaFX MenuBar, is as follows:

/*Menu Bar*/
.menu-bar {
    -fx-background-color: orange;
}

/*Menus*/
.menu .label {
    -fx-text-fill: black;
}

/*Menu Items*/
.menu-item:focused {
    -fx-background-color: greenyellow;
}
.menu-item .label {
    -fx-text-fill: black;
}

Here's some references relevant to this task:

And here's the steps, all based on the links above:

  1. Remove the default menubar and replace with your own:
    package org.demo.menu;
    
    import org.openide.modules.OnStart;
    
    @OnStart
    public class Installer implements Runnable {
    
        @Override
        public void run() {
            System.setProperty("netbeans.winsys.menu_bar.path", "LookAndFeel/MenuBar.instance");
        }
    
    }
  2. In the layer, i.e., as referred to in the folder location above, define your Swing menubar:
    <folder name="LookAndFeel">
        <file name="MenuBar.instance">
            <attr name="instanceOf" stringvalue="org.openide.awt.MenuBar"/>
            <attr name="instanceCreate" newvalue="org.demo.menu.MyMenuBar"/>
        </file>
    </folder>
  3. Now create the class that you defined above. Start by putting a JFXPanel into the Swing menubar. Then you're good to go. Just add the MenuBar, Menus, and MenuItems from JavaFX, based on what you find in the relevant folders in the application's filesystem.
    public class MyMenuBar extends JMenuBar {
    
        public MyMenuBar() {
            final JFXPanel menuFxPanel = new JFXPanel();
            add(menuFxPanel);
            Platform.setImplicitExit(false);
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    Scene scene = new Scene(new VBox(), 400, 20);
                    scene.getStylesheets().add(MyMenuBar.class.
                            getResource("myMenuBar.css").toExternalForm());
                    MenuBar menuBar = new MenuBar();
                    FileObject menuFolder = FileUtil.getConfigFile("Menu");
                    FileObject[] menuKids = menuFolder.getChildren();
                    for (FileObject menuKid : FileUtil.getOrder(Arrays.asList(menuKids), true)) {
                        Menu newMenuFromFileObject = new Menu(menuKid.getName());
                        menuBar.getMenus().add(newMenuFromFileObject);
                        builtMenuStructure(menuKid, newMenuFromFileObject);
                    }
                    ((VBox) scene.getRoot()).getChildren().addAll(menuBar);
                    menuFxPanel.setScene(scene);
                }
            });
        }
    
        private void builtMenuStructure(FileObject fo, Menu menu) {
            DataFolder df = DataFolder.findFolder(fo);
            DataObject[] childs = df.getChildren();
            for (DataObject oneChild : childs) {
                if (oneChild.getPrimaryFile().isFolder()) {
                    FileObject childFo = oneChild.getPrimaryFile();
                    Menu newMenu = new Menu(childFo.getName());
                    menu.getItems().add(newMenu);
                    builtMenuStructure(childFo, newMenu);
                } else {
                    Object instanceObj = FileUtil.getConfigObject(
                      oneChild.getPrimaryFile().getPath(), 
                      Object.class);
                    if (instanceObj instanceof JSeparator) {
                        menu.getItems().add(new SeparatorMenuItem());
                    } else if (instanceObj instanceof Action) {
                        final Action a = (Action) instanceObj;
                        String name = (String) a.getValue(Action.NAME);
                        String cutAmpersand = Actions.cutAmpersand(name);
                        MenuItem menuItem = new MenuItem(cutAmpersand);
                        menuItem.setOnAction(new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(final ActionEvent t) {
                                try {
                                    SwingUtilities.invokeAndWait(new Runnable() {
                                        @Override
                                        public void run() {
                                            java.awt.event.ActionEvent event =
                                                    new java.awt.event.ActionEvent(
                                                    t.getSource(),
                                                    t.hashCode(),
                                                    t.toString());
                                            a.actionPerformed(event);
                                        }
                                    });
                                } catch (InterruptedException ex) {
                                    Exceptions.printStackTrace(ex);
                                } catch (InvocationTargetException ex) {
                                    Exceptions.printStackTrace(ex);
                                }
                            }
                        });
                        menu.getItems().add(menuItem);
                    }
                }
            }
        }
    
    }

That's all and you're done. Congratulations, you've replaced the default Swing menubar in the NetBeans Platform with a new one defined in JavaFX.

Maybe handy to know what the import statement in the above class are:

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.layout.VBox;
import javax.swing.Action;
import javax.swing.JMenuBar;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
import org.openide.awt.Actions;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.util.Exceptions;
Published at DZone with permission of its author, Geertjan Wielenga.

Comments

Sean Phillips replied on Thu, 2013/04/25 - 9:02am

 >>FileObject[] menuKids = menuFolder.getChildren();


menuKids... that's funny!  Nice work Geertjan.

Geertjan Wielenga replied on Fri, 2013/04/26 - 4:44am

 Thanks for appreciating the humor. :-)

Cata Nic replied on Tue, 2013/09/03 - 2:29am

 Interesting solution. I will implement into my site very soon if I will test it and it will be stable.

Juan Abreu replied on Fri, 2013/10/11 - 11:01am

Hello!


I have a question: 

The installer and the MyMenuBar needs to be JavaFX projects? and then you imported them into your swing application?

thank you so much for your help

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.