Add a JavaFX Component Built with Scene Builder and FXML to a NetBeans RCP App

Most of the tutorials and demos I have seen up to this point for integrating JavaFX with NetBeans Rich Client Platform (RCP) applications have created and configured JavaFX components by using the JavaFX Java APIs that are now shipped as part of the JDK as opposed to using the FXML markup language.  This seems logical since if one is already coding a panel or container in Java, it makes sense to instantiate and configure JavaFX components for that container in Java as well.  The disadvantage to this is that you are not able to leverage the benefits of Scene Builder to design your JavaFX components.  Scene Builder is a WYSIWYG design tool for creating components and UIs in JavaFX.  Behind the scenes (pun intended) the application generates an FXML representation of the UI, and does *not* generate Java code.

In the following example I will create a JavaFX component with Scene Builder and add it to a NetBeans RCP TopComponent window alongside a plain vanilla Swing JLabel.


Creating the Application

The first step in the process is to create an ordinary NetBeans  RCP application.  We use maven, so I create a new NetBeans application from the Maven Category

image

In NetBeans IDE there are now 3 projects.

image


Creating a JavaFX Runtime Wrapper Module

The next step is not create a wrapper module for the JavaFX runtime library.  If you are running Java 1.7.0_06 or higher (and you should be if you are developing a JavaFX app), you don’t need to include any of the native libraries in the wrapper module, only the jfxrt.jar itself needs to be included.

I created a new module called jfxrt (JavaFX runtime), and include the jfxrt-2.2.jar in it, which you can find in the JRE’s lib directory.  Below is a screenshot of the jfxrt wrapper module in the IDE.

image

You will then need to update the pom.xml file for the wrapper to include any JavaFX packages that you want to access within your application.  Below is a code snippet showing the JavaFX packages that my app will need in order to run.

<plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>nbm-maven-plugin</artifactId>
   <version>3.7</version>
   <extensions>true</extensions>
   <configuration>
       <useOSGiDependencies>true</useOSGiDependencies>
       <publicPackages>
          <publicPackage>javafx.scene.layout</publicPackage>
          <publicPackage>javafx.application</publicPackage>
          <publicPackage>javafx.collections</publicPackage>
          <publicPackage>javafx.scene.control</publicPackage>
          <publicPackage>javafx.event</publicPackage>
          <publicPackage>javafx.geometry</publicPackage>
          <publicPackage>javafx.beans.property</publicPackage>
          <publicPackage>javafx.beans.value</publicPackage>
          <publicPackage>javafx.concurrent</publicPackage>
          <publicPackage>javafx.scene.web</publicPackage>
          <publicPackage>javafx.fxml</publicPackage>
       </publicPackages>
   </configuration>
</plugin>

Now the fun begins

I create a new NetBeans module called “NumberLabelModule”, which contains a TopComponent to host both the Swing JLabel and JavaFX label.  Unfortunately, it doesn’t appear that the NetBeans IDE WYSIWYG designer, Matisse supports drag-and-drop of JFXPanel, which is the Swing panel that can contain JavaFX components, so the JavaFX portion must be added to the TopComponent by hand. (I attempted to add the JFXPanel to the IDE’s component palette to make use of it in Matisse, but when I attempted to drag the JFXPanel onto the TopComponent the IDE froze and had to be killed with extreme prejudice).

image


Building the JavaFX Label with Scene Builder

For this project the next step is to place a style sheet in the “Other Sources” portion of the project, which the JavaFX component will use for its formatting.

JavaFX Scene Builder is then used to graphically design the new JavaFX label and generate the  FXML markup code, which will be saved right along side the style sheet in the “Other Sources” portion of the project. (NumberLabel.fxml)

image


Adding JavaFX to the TopComponent

Once the component is designed and saved, the .fxml file within the IDE is updated automatically.  The final step is to add the JavaFX component to the TopComponent along with a JLabel.  The TopComponent contains a JPanel that uses a FlowLayout with the JLabel added to it as the first component. (See below).

image

The JavaFX label will be the 2nd component added to the panel, and this is done in the constructor of the TopComponent as follows.

public final class SwingFXDemoTopComponent extends TopComponent {

    JFXPanel jfxPanel = new JFXPanel();

    public SwingFXDemoTopComponent() {
        initComponents();
        putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, Boolean.TRUE);
        setName(Bundle.CTL_SwingFXDemoTopComponent());
        setToolTipText(Bundle.HINT_SwingFXDemoTopComponent());

        Platform.setImplicitExit(false);
        // create JavaFX scene
        Platform.runLater(new Runnable() {
            public void run() {
                Parent root;
                try {
                    root = FXMLLoader.load(getClass().getResource("/com/lynden/fxlabel/NumberLabel.fxml"));
                    Scene scene = new Scene(root);
                    jfxPanel.setScene(scene);
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        });
        jPanel1.add(jfxPanel);
    }

A few more details on the above code:

 Platform.setImplicitExit(false);

This will prevent the JavaFX runtime from exiting when TopComponents are moved around the NetBeans RCP windowing system or when a TopComponent is undocked from the main application frame.  If this is not set, what you will see is that your JavaFX components will disappear when you undock a TopComponent from the main window.

      Platform.runLater(new Runnable() {
            public void run() {
                Parent root;
                try {
                    root = FXMLLoader.load(getClass().getResource("/com/lynden/fxlabel/NumberLabel.fxml"));
                    Scene scene = new Scene(root);
                    jfxPanel.setScene(scene);
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        });
        jPanel1.add(jfxPanel);

 

The code snippet above will create the JavaFX label within the JavaFX event thread (separate from the Swing event thread).  The first step of the process is to load the FXML representation of the JavaFX label that was built in Scene Builder.  Once the root node is loaded it can then be added to a new Scene.  The Scene can then be added to a JFXPanel, which as mentioned earlier, is a Swing component that can contain JavaFX components.   The final step is to then add the JFXPanel to the JPanel that the JLabel is living on.


Final Result

Once this is set up, the application can be run, and displays Swing and JavaFX components (built with FXML) living side by side in perfect harmony.

image

twitter: @RobTerp