© Stephen Chin, Johan Vos and James Weaver 2019
S. Chin et al.The Definitive Guide to Modern Java Clients with JavaFXhttps://doi.org/10.1007/978-1-4842-4926-0_7

7. Bridging Swing and JavaFX

Stephen Chin1 , Johan Vos2 and James Weaver3
(1)
Belmont, CA, USA
(2)
Leuven, Belgium
(3)
Marion, IN, USA
 

Written by Sven Reimers

One of the major advantages of a new UI toolkit is the possibility to protect your investments in existing applications. This chapter will show you how to integrate legacy Swing components into a modern JavaFX UI and how to integrate modern UI elements into an existing Swing application.

Because moving an existing Swing desktop application to a pure JavaFX application is challenging and not always necessary, this chapter describes the technologies available for integration and offers tips and strategies for the migration process.

Note

To understand a couple of the concepts, a good understanding of Swing technology is helpful. To grasp some of the examples in full detail, refer to other chapters of this book or some good Swing in-depth material.

Integrating JavaFX into Swing

The typical migration path for a Java desktop application is the usage of a new available control from JavaFX, for example, WebView, which finally allows having a real browser embedded in a standard Swing application.

JFXPanel: Swing Component with JavaFX Inside

The way to achieve this is to use a special Swing JComponent – the JFXPanel located in the javafx.embed.swing package in the javafx.swing module. It allows you to embed a JavaFX scene graph into a Swing container hierarchy. The interesting method needed from JFXPanel is
  • public void setScene(final Scene newScene):

    Attaches a Scene object to display in this JFXPanel. This method can be called either on the event dispatch thread or the JavaFX application thread.

Swing coding rules require Swing components to always be created and accessed from the Swing Event Thread. JFXPanel is different in this respect. It can also be managed from the FX Application thread. This can be helpful in case of complex scenes which may demand JavaFX components to be explicitly created on the FX Application thread.

Besides the threading aspect, which will be discussed in more detail as we progress further through the integration, the first major thing to recognize here is that the JavaFX embedding does not work on a Node or Control level, rather on a full Scene level. So, if there is the need to embed a Node, for example, a Chart, you cannot just add the Chart instance to the Swing component hierarchy. Instead, you have to create a full Scene and add this using JFXPanel as a Swing component wrapper around your Scene.

With this, a full-fledged small example for an integrated JavaFX Scene can be seen in Listing 7-1.

Note

All following examples are reduced as much as possible so that the integration handling becomes obvious, not the architecture in terms of object oriented or functional programming. Typically, the only thing needed is a main method into which you can copy and paste the example code. If further special code is needed, this is pointed out in the description for the example.

SwingUtilities.invokeLater(() -> {
            var frame = new JFrame("JavaFX 11 integrated in Swing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            var jfxPanel = new JFXPanel();
            var button = new Button("Hello FX");
            var scene = new Scene(button);
            jfxPanel.setScene(scene);
            jfxPanel.setPreferredSize(new Dimension(100,200));
            var panel = new JPanel(new BorderLayout());
            panel.add(new JLabel("Hello Swing North"), BorderLayout.NORTH);
            panel.add(new JLabel("Hello Swing South"), BorderLayout.SOUTH);
            panel.add(jfxPanel, BorderLayout.CENTER);
            frame.setContentPane(panel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
Listing 7-1

Simple JavaFX in Swing embedding

This code will produce a Swing JFrame with three visible parts, one Swing JLabel on top of a JavaFX Button on top of another Swing JLabel as shown in Figure 7-1.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig1_HTML.jpg
Figure 7-1

Simple JavaFX integration

Especially interesting here is the layout of the components. One major aspect is the correct setting of the preferred size of the JFXPanel. Since the JFXPanel has no idea about the size of the Scene, it will have no size set by default. If you resize the Frame in the example, you will see that the Button resizes accordingly. This is due to the fact that resizing starts from the Swing hierarchy (BorderLayout) and trickles down to the Scene and its nodes. If you comment out setting the preferred size, you will see no Button after running the example. It will show up once you start resizing the Frame. The initial view you get should be similar to what is shown in Figure 7-2.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig2_HTML.jpg
Figure 7-2

Simple JavaFX integration without setting preferred size

Having solved this initial integration problem, let’s dive deeper into the possibilities this solution offers.

Because JFXPanel is a Swing component, this opens up the opportunity to create multiple component instances and add them to the Swing component hierarchy. For a simple example, the application from Listing 7-1 is changed to use two JavaFX Labels and one Swing JLabel as shown in Listing 7-2.
SwingUtilities.invokeLater(() -> {
        var frame = new JFrame("JavaFX 11 integrated in Swing (multiple)");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        var northJfxPanel = new JFXPanel();
        var northButton = new Button("Hello FX North");
        var northScene = new Scene(northButton);
        northJfxPanel.setScene(northScene);
        northJfxPanel.setPreferredSize(new Dimension(200,50));
        var southJfxPanel = new JFXPanel();
        var southButton = new Button("Hello FX South");
        var southScene = new Scene(southButton);
        southJfxPanel.setScene(southScene);
        southJfxPanel.setPreferredSize(new Dimension(200,50));
        var panel = new JPanel(new BorderLayout());
        panel.add(northJfxPanel, BorderLayout.NORTH);
        panel.add(southJfxPanel, BorderLayout.SOUTH);
        panel.add(new JLabel("Hello Swing"), BorderLayout.CENTER);
        frame.setContentPane(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
   });
Listing 7-2

Multiple JavaFX Scenes in Swing

If you run this program, the output shown should be similar to Figure 7-3.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig3_HTML.jpg
Figure 7-3

Multiple JavaFX Scenes

So far, all the examples were extremely simplified in comparison to real-world integration scenarios. A typical scenario is the integration of WebView into an existing Swing application. With some small modifications of Listing 7-1, a WebView is integrated instead of the Button of the original application as shown in Listing 7-3.
SwingUtilities.invokeLater(() -> {
            var frame = new JFrame("JavaFX 11 integrated in Swing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            var jfxPanel = new JFXPanel();
            var panel = new JPanel(new BorderLayout());
            panel.add(new JLabel("Hello Swing North"), BorderLayout.NORTH);
            panel.add(new JLabel("Hello Swing South"), BorderLayout.SOUTH);
            Platform.runLater(() -> {
                var webView = new WebView();
                var scene = new Scene(webView);
                webView.getEngine().load("https://openjfx.io/");
                jfxPanel.setScene(scene);
                jfxPanel.setPreferredSize(new Dimension(400,600));
                SwingUtilities.invokeLater(() -> {
                    panel.add(jfxPanel, BorderLayout.CENTER);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                });
            });
            frame.setContentPane(panel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
Listing 7-3

Adding a WebView to a Swing application

Running this example will show a WebView rendering the OpenJFX homepage sitting between two Swing JLabels as shown in Figure 7-4.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig4_HTML.jpg
Figure 7-4

WebView embedded in Swing application

Looking at the code, there is an obvious change in comparison with the original code. The creation of the Scene requires a couple of thread changes to get everything done on the correct UI thread. For a better understanding, let us take a peek under the hood to see the details of threading.

Threading

Threading in an application mixed with JavaFX nodes and Swing components is a complex thing to get right.

As already hinted at in the last section, two major threads have to be considered:
  • JavaFX Application Thread

  • AWT-EventQueue

The first thread is associated with all things JavaFX, for example, adding a new Node to the already rendered (live) scene graph or changing a property of a node belonging to an already rendered scene graph.

The second thread is associated with the Swing UI toolkit (inherited from AWT, hence the name), for example, creation of all Swing components shall occur on this thread. Combining the toolkits will result in a lot of hopping on and off one or the other thread to ensure all things are always triggered and done on the correct thread.

Note

A system property javafx.embed.singleThread is available, which switches both UI toolkits to use the same thread if set to true. This behavior is experimental and may lead to undesired behavior, so use with caution.

One more special thing should be noted, especially since WebView is probably the most wanted JavaFX control to be integrated with Swing. All other JavaFX controls can be created on any thread besides WebView. Due to some initialization problems, WebView has to be created on the JavaFX Application Thread, quoting from JDK-8087718:

In theory, it should be possible to eliminate the restriction by deferring the initialization calls until the first real use of the WebKit code. In practice, such a change is likely to end up being very non-trivial, primarily due to the fact that there is a large number of entry points that may or may not result in the “first real use.”

With this knowledge about the threading, let’s look again at the initialization code of the last example.

The first remarkable part of the code sequence is the necessity to execute some code during the setup of the JFXPanel on the JavaFX Application Thread. Once this code is finished, another code fragment is required to run on the AWT-EventQueue. The nesting of the execution blocks guarantees the correct sequence. So a generic sequence of code can roughly look like the pseudo-code in Listing 7-4.
Platform.runLater(() -> {
    // ensure JavaFX all necessary init is done
    SwingUtilities.invokeLater(() -> {
        // now come back to update Swing component hierarchy accordingly
    });
});
Listing 7-4

Abstract sequence with dedicated thread-sensitive code

Note

There are two utility methods that can be helpful to ensure or detect that code is executed on the correct thread: javax.swing.SwingUtilities.isEventDispatchThread() and javax.application.Platform.isFxApplicationThread(). Used either in asserts to guarantee the thread or as a simple debugging support, they can help make the usage of threads more transparent.

With a better understanding of how to run which code on the correct thread, the next step of integration is providing interaction between JavaFX Nodes and Swing JComponents.

Interaction Between Swing and JavaFX

The next step in an integration story is interaction between components of both UI toolkits. Looking at the threading models of JavaFX and Swing, this will require some extra ceremony. A change from a JavaFX node/control will be notified on the JavaFX Application Thread, and a Swing component will need to be changed on the AWT-EventQueue. Handling events coming from JavaFX toward Swing will require switching threads, that is, execute code blocks (lambdas) on the correct thread. This pattern will look similar to the following code fragment:
NODE.setOnXXX(e ->
        SwingUtilities.invokeLater(() -> JCOMPONENT.setYYY(ZZZZ))).
As an example, the text of the south label shall be changed on press and release of the mouse button. Based on the aforementioned code strategy, the necessary code is
button.setOnMousePressed(e ->
        SwingUtilities.invokeLater(() -> southLabel.setText("FX Button Pressed")));
button.setOnMouseReleased(e ->
        SwingUtilities.invokeLater(() -> southLabel.setText("Hello Swing South")));
The first statement triggers a change of the southLabel text on pressing the mouse button, and the second statement changes the text back to its original value once the button gets released. The full application can be seen in Listing 7-5.
SwingUtilities.invokeLater(() -> {
    var frame = new JFrame("JavaFX 11 integrated in Swing");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    var jfxPanel = new JFXPanel();
    var button = new Button("Hello FX");
    var scene = new Scene(button);
    jfxPanel.setScene(scene);
    jfxPanel.setPreferredSize(new Dimension(200,100));
    jfxPanel.setBorder(new EmptyBorder(5,5,5,5));
    var panel = new JPanel(new BorderLayout());
    panel.add(new JLabel("Hello Swing North"), BorderLayout.NORTH);
    var southLabel = new JLabel("Hello Swing South");
    panel.add(southLabel, BorderLayout.SOUTH);
    button.setOnMousePressed(e ->
    SwingUtilities.invokeLater(() -> southLabel.setText("FX Button Pressed")));
    button.setOnMouseReleased(e ->
    SwingUtilities.invokeLater(() -> southLabel.setText("Hello Swing South")));
    panel.add(jfxPanel, BorderLayout.CENTER);
    frame.setContentPane(panel);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
});
Listing 7-5

Interactive JavaFX in Swing embedding

The interaction works the same way in both directions. To show interaction starting from Swing, let’s change the last example to contain a Swing JButton in the south area and add some listening to it:
           southButton.addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    Platform.runLater(() -> button.setText("Swing Button Pressed"));
                }
                @Override
                public void mouseReleased(MouseEvent e) {
                    Platform.runLater(() -> button.setText("Hello FX"));
                }
            });
As can be seen, interaction will start on the AWT-Event Thread and is then shifted to the JavaFX Application Thread to change the text property of the JavaFX Button. The full example code can be seen in Listing 7-6.
SwingUtilities.invokeLater(() -> {
    var frame = new JFrame("JavaFX 11 bidirectional interaction in Swing");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    var jfxPanel = new JFXPanel();
    var button = new Button("Hello FX");
    var scene = new Scene(button);
    jfxPanel.setScene(scene);
    jfxPanel.setPreferredSize(new Dimension(200,100));
    jfxPanel.setBorder(new EmptyBorder(5,5,5,5));
    var panel = new JPanel(new BorderLayout());
    panel.add(new JLabel("Hello Swing North"), BorderLayout.NORTH);
    var southButton = new JButton("Hello Swing South Button");
    panel.add(southButton, BorderLayout.SOUTH);
    button.setOnMousePressed(e ->
            SwingUtilities.invokeLater(() -> southButton.setText("FX Button Pressed")));
    button.setOnMouseReleased(e ->
            SwingUtilities.invokeLater(() -> southButton.setText("Hello Swing South")));
    southButton.addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            Platform.runLater(() -> button.setText("Swing Button Pressed"));
        }
     @Override
     public void mouseReleased(MouseEvent e) {
        Platform.runLater(() -> button.setText("Hello FX"));
     }
   });
    panel.add(jfxPanel, BorderLayout.CENTER);
    frame.setContentPane(panel);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
});
Listing 7-6

Interactive bidirectional JavaFX in Swing

Running this application shows the following states (see Figures 7-57-7).
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig5_HTML.jpg
Figure 7-5

Start state of interaction demo

../images/468104_1_En_7_Chapter/468104_1_En_7_Fig6_HTML.jpg
Figure 7-6

State after JavaFX button pressed

../images/468104_1_En_7_Chapter/468104_1_En_7_Fig7_HTML.jpg
Figure 7-7

State after Swing button pressed

The next level of interactivity is to dynamically add a JavaFX scene to the Swing application. This is a feature typically required with more complex application frameworks, because they dynamically change the Swing component hierarchy. Modify the preceding example with multiple JavaFX Scenes so that the second JFXPanel will be added as the result of a Swing button click. The major change is the ActionListener necessary:
          swingButton.addActionListener(e -> {
                var southJfxPanel = new JFXPanel();
                var southButton = new Button("Hello FX South");
                var southScene = new Scene(southButton);
                southJfxPanel.setPreferredSize(new Dimension(200,50));
                panel.add(southJfxPanel, BorderLayout.SOUTH);
                Platform.runLater(() -> {
                    southJfxPanel.setScene(southScene);
                    SwingUtilities.invokeLater(frame::pack);
                });
            });

The creation of the JFXPanel itself can be done on the AWT-EventQueue (as described before), but in this case, the setting of the scene has to be done on the JavaFX-Application Thread; and to ensure visibility of the panel, it is necessary to resize the frame again. This has to be done on the AWT-EventQueue, once the scene is set.

Running the example as shown in Listing 7-7 shall display two JFXPanels inside one Swing JFrame as shown in Figure 7-8.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig8_HTML.jpg
Figure 7-8

State after Scene has been added

SwingUtilities.invokeLater(() -> {
    var frame = new JFrame("JavaFX 11 integrated in Swing (multiple, dynamic)");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    var northJfxPanel = new JFXPanel();
    var northButton = new Button("Hello FX North");
    var northScene = new Scene(northButton);
    northJfxPanel.setScene(northScene);
    northJfxPanel.setPreferredSize(new Dimension(200,50));
    var panel = new JPanel(new BorderLayout());
    panel.add(northJfxPanel, BorderLayout.NORTH);
    var swingButton = new JButton("Add FX Scene in South");
    swingButton.addActionListener(e -> {
        var southJfxPanel = new JFXPanel();
        var southButton = new Button("Hello FX South");
        var southScene = new Scene(southButton);
        southJfxPanel.setPreferredSize(new Dimension(200,50));
        panel.add(southJfxPanel, BorderLayout.SOUTH);
        Platform.runLater(() -> {
            southJfxPanel.setScene(southScene);
            SwingUtilities.invokeLater(frame::pack);
        });
    });
    panel.add(swingButton, BorderLayout.CENTER);
    frame.setContentPane(panel);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
});
Listing 7-7

Interactive bidirectional JavaFX in Swing

The next logical step is the interactive removal of a JFXPanel. For demonstration purposes, the last example is enhanced with the additional possibility to remove the north JFXPanel (see Listing 7-8).
SwingUtilities.invokeLater(() -> {
    var frame = new JFrame("JavaFX 11 integrated in Swing (multiple, dynamic)");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    var northJfxPanel = new JFXPanel();
    var northButton = new Button("Hello FX North");
    var northScene = new Scene(northButton);
    northJfxPanel.setScene(northScene);
    northJfxPanel.setPreferredSize(new Dimension(200,50));
    var panel = new JPanel(new BorderLayout());
    panel.add(northJfxPanel, BorderLayout.NORTH);
    var northSwingButton = new JButton("Remove FX Scene in North");
    northSwingButton.addActionListener(e -> {
       panel.remove(northJfxPanel);
       frame.pack();
    });
        var southSwingButton = new JButton("Add FX Scene in South");
        southSwingButton.addActionListener(e -> {
            var southJfxPanel = new JFXPanel();
            var southButton = new Button("Hello FX South");
            var southScene = new Scene(southButton);
            southJfxPanel.setPreferredSize(new Dimension(200,50));
            panel.add(southJfxPanel, BorderLayout.SOUTH);
            Platform.runLater(() -> {
                southJfxPanel.setScene(southScene);
                SwingUtilities.invokeLater(frame::pack);
            });
        });
        var swingInside = new JPanel(new BorderLayout());
        swingInside.add(northSwingButton, BorderLayout.NORTH);
        swingInside.add(southSwingButton, BorderLayout.SOUTH);
        panel.add(swingInside, BorderLayout.CENTER);
        frame.setContentPane(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    });
Listing 7-8

Adding/removing of JFXPanel in Swing

Running this example shows two Swing Buttons – one for removal of the northern JFXPanel and one for the addition of the southern JFXPanel as seen in Figure 7-9.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig9_HTML.jpg
Figure 7-9

Addition/removal of JFXPanels

The result of the application depends on the sequence of the button presses. If the button for adding the JFXPanel to the south is pressed first, the panel will show up, and the press of the removal button removes the northern JFXPanel (result is shown in Figure 7-10).
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig10_HTML.jpg
Figure 7-10

Result of first adding and then removing the JFXPanel

If the buttons are pressed in the inverse order, the northern panel is removed, but the southern panel cannot be added anymore. This is due to the fact that JavaFX has a feature that automagically initiates a shutdown of the JavaFX runtime as soon as the last JavaFX window is closed. This feature is enabled by default, so that the removal of the only JFXPanel triggers the shutdown, and afterward all calls to the runtime, for example, adding the JFXPanel to the south, do not work anymore. This behavior can be changed by disabling the implicitExit feature:
Platform.setImplicitExit(false);

Note

If you try to create some generic integration of JavaFX on top of Swing, it is probably always a good idea to disable this feature, to ensure the JavaFX runtime is not accidentally shut down.

Drag and Drop with JavaFX and Swing

More complex Swing applications will typically have some kind of drag-and-drop support, either inside the application or for dragging stuff from outside the application into it. The second use case is not a special case in the integration, because the drop target is either a Swing JComponent or a JavaFX Node. This allows to use the default drop handling for each of the technologies. The first case is more interesting, since drag source and drop target are based on different UI technologies.

An example application is shown in Figure 7-11.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig11_HTML.jpg
Figure 7-11

Drag and drop with JavaFX and Swing

There are two Swing JTextFields and one JavaFX Label. The drag operation allows for dragging selected text from either the north or the south Swing TextField and dropping it onto the JavaFX Label. Although this sounds like a lot of complex threading, it is not. Most of the complex interaction is done on the toolkit level, invisible to the user.

The first thing that is needed is an interaction for the drag start as shown in Listing 7-9.
private static class MouseDragAdapter extends MouseAdapter {
      @Override
      public void mousePressed(MouseEvent e) {
          var component = (JComponent) e.getSource();
          component.getTransferHandler().
                      exportAsDrag(component, e, TransferHandler.COPY);
      }
  }
Listing 7-9

Swing MouseAdapter for drag start

The shown code fragment defines a MouseListener and just overrides the mousePressed method to ensure that by pressing the mouse button, the content of the component is exported as the drag content. With this, we can now look at the full code in Listing 7-10.
SwingUtilities.invokeLater(() -> {
            var frame = new JFrame("JavaFX 11 DnD in Swing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            var jfxPanel = new JFXPanel();
            var label = new Label("Hello FX");
            var scene = new Scene(button);
            jfxPanel.setScene(scene);
            jfxPanel.setPreferredSize(new Dimension(200, 100));
            label.setOnDragOver(event -> {
                var dragboard = event.getDragboard();
                if (dragboard.getContentTypes().
                    contains( DataFormat.lookupMimeType("application/x-java-serialized-object"))) {
                    event.acceptTransferModes(TransferMode.COPY);
                }
                event.consume();
            });
            label.setOnDragDropped(event -> {
                var dataFormat = DataFormat.
                                lookupMimeType("application/x-java-serialized-object");
                var dragboard = event.getDragboard();
                if (dragboard.hasContent(dataFormat)) {
                    String content = (String) dragboard.getContent(dataFormat);
                    label.setText(content);
                }
                event.setDropCompleted(true);
                event.consume();
            });
            var panel = new JPanel(new BorderLayout());
            var northField = new JTextField("Hello Swing North");
            northField.setDragEnabled(true);
            northField.addMouseListener(new MouseDragAdapter());
            var southField = new JTextField("Hello Swing South");
            southField.setDragEnabled(true);
            southField.addMouseListener(new MouseDragAdapter());
            panel.add(northField, BorderLayout.NORTH);
            panel.add(southField, BorderLayout.SOUTH);
            panel.add(jfxPanel, BorderLayout.CENTER);
            frame.setContentPane(panel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
Listing 7-10

Drag from Swing to JavaFX

There are two distinct pieces ensuring that dropping onto the JavaFX Label works. The first code fragment ensures during detecting the drag is happening over the component that in case of a compatible MimeType in the DragBoard content types, the accept mode for dragging is set. With this done, the only thing missing is the reaction to the real drop. This code fragment ensures the availability of the expected MimeType, retrieves the data from the DragBoard in the correct format, and uses the data to change the displayed text of the Label.

Due to the fact that all those handling methods are called from the UI toolkits, all handling is already on the correct thread, so no thread switching is needed in this example.

Note

Drag and drop from one node in one JFXPanel to another node in another JFXPanel is not different from drag and drop between any two nodes in an ordinary JavaFX scene. Both, source and target of the operation, do know nothing about the embedding in a Swing context. This is an important factor for integrating complex JavaFX nodes/controls into a Swing application.

JavaFX 3D Integrated in Swing

One of the most compelling features of JavaFX is the support for 3D rendering and the mixture of 2D and 3D, which makes the creation of advanced visualization simple. Because JFXPanel just takes any Scene and embeds it into the Swing component hierarchy, this can also be done to 3D-enabled scenes.

Building on top of one of the 3D examples used in this book, Listing 7-11 shows an example of a 3D integration.
    SwingUtilities.invokeLater(() -> {
        var frame = new JFrame("JavaFX 11 3D integrated in Swing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        var jfxPanel = new JFXPanel();
        var camera = createCamera();
        var box = new Box(10, 10, 10);
        var view = new Group(box, camera);
        var scene = new Scene(view, 640, 480);
        scene.setCamera(camera);
        jfxPanel.setScene(scene);
        jfxPanel.setPreferredSize(new Dimension(200,100));
        var panel = new JPanel(new BorderLayout());
        panel.add(new JLabel("Hello Swing North"), BorderLayout.NORTH);
        panel.add(new JLabel("Hello Swing South"), BorderLayout.SOUTH);
        panel.add(jfxPanel, BorderLayout.CENTER);
        frame.setContentPane(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        Platform.runLater(() -> animate());
    });
private static Camera createCamera() {
    Camera answer = new PerspectiveCamera(true);
    answer.getTransforms().addAll(rotateX, rotateY, rotateZ, translateZ);
    return answer;
}
private static void animate() {
    Timeline timeline = new Timeline(
            new KeyFrame(Duration.seconds(0),
                    new KeyValue(translateZ.zProperty(), -20),
                    new KeyValue(rotateX.angleProperty(), 90),
                    new KeyValue(rotateY.angleProperty(), 90),
                    new KeyValue(rotateZ.angleProperty(), 90)),
            new KeyFrame(Duration.seconds(5),
                    new KeyValue(translateZ.zProperty(), -80),
                    new KeyValue(rotateX.angleProperty(), -90),
                    new KeyValue(rotateY.angleProperty(), -90),
                    new KeyValue(rotateZ.angleProperty(), -90))
    );
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.setAutoReverse(true);
    timeline.play();
}
Listing 7-11

3D embedded in Swing

Running the example shows a Swing application with two Swing Labels, one above and one below the 3D animated JavaFX scene as shown in Figure 7-12.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig12_HTML.jpg
Figure 7-12

3D rendering integrated in Swing

Integrating Swing into JavaFX

Having done the integration of new JavaFX controls inside existing Swing applications, this section describes how to use well-known large Swing-based libraries, for example, WorldWind from NASA (https://worldwind.arc.nasa.gov/java), inside the JavaFX scene graph.

The way to achieve this is to use a special JavaFX Node – the SwingNode. It allows you to embed a Swing JComponent inside the scene graph. Where JFXPanel is a JComponent wrapping around a JavaFX Scene, Swing Node is JavaFX Node wrapping a Swing component hierarchy. All things shown and discussed about threading, interaction, and so on are valid for the integration of Swing components in JavaFX applications as well. Because from an interaction standpoint, both elements, the JavaFX Node and the Swing JComponent, do not know how they are integrated, it does not matter if it is JavaFX inside Swing or Swing inside JavaFX. The main difference is that during the construction of the UI tree, either the Swing or the JavaFX rules apply. A simple example is shown Listing 7-12.
@Override
public void start(Stage stage) throws Exception {
    var borderPane = new BorderPane();
    var swingNode = new SwingNode();
    var scene = new Scene(borderPane, 200, 200);
    borderPane.setCenter(swingNode);
    borderPane.setBottom(new Label("JavaFX Bottom"));
    SwingUtilities.invokeLater(() -> {
        var panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(new JLabel("Swing North"), BorderLayout.CENTER);
        swingNode.setContent(panel);
        borderPane.layout();
    });
    stage.setScene(scene);
    stage.show();
}
Listing 7-12

Swing embedded in JavaFX

The result of running this application is shown in Figure 7-13.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig13_HTML.jpg
Figure 7-13

Swing embedded in JavaFX

Migration Strategies

Migrating between UI toolkits is always a tedious and complex process. JavaFX mitigates this by providing quite seamless dual-way integration components – JFXPanel and SwingNode. This allows for arbitrary migration steps from a full Swing-based application to a full JavaFX application.

Typically, the migration path begins with a complex Swing-based application and tries to get rid of as much Swing as possible or tries to integrate better components or controls available from JavaFX. So the first stop is always the JFXPanel-based approach.

Using the strategy “divide et impera,” look for components in your existing Swing component hierarchy that can be easily replaced with their JavaFX counterparts.

While you do this, more and more parts of your application will start to be JavaFX, and you can start to regroup already transformed JFXPanels into larger scene graphs. If there is still some Swing component, which cannot be transformed, there is still the possibility to reuse the original Swing component and wrap it inside a SwingNode and use it as a part of the scene graph.

This approach using both JFXPanel and SwingNode allows for at least theoretically transparent and incremental migration, although the details in doing so may be tricky.

Large-Scale Integrations

The enhanced integration possibilities JavaFX offers in terms of building mash-up applications which are originally built on Swing make it easy to create complex combinations of both technologies.

One very prominent example is a project that tries to embed Scene Builder, a JavaFX rapid application development tool, into Apache NetBeans (https://netbeans.apache.org) – a Swing-based IDE. The actual project can be found at https://github.com/svenreimers/nbscenebuilder.

Figure 7-14 shows an example screenshot of the integration.
../images/468104_1_En_7_Chapter/468104_1_En_7_Fig14_HTML.jpg
Figure 7-14

Scene Builder integration in Apache NetBeans

Conclusion

With two compatibility strategies, allowing for embedding of JavaFX UI parts into existing Swing applications and allowing the reuse of Swing components inside new JavaFX applications, JavaFX is a top choice for building new cross-platform rich client applications. The key points covered in this chapter are as follows:
  • JavaFX offers a Swing component called JFXPanel to integrate JavaFX scene graphs into Swing.

  • JavaFX offers a SwingNode to integrate Swing components into JavaFX.

  • Special attention is required while dealing with two UI toolkits providing their own dedicated UI thread.

  • Interactions between nodes and components from both UI toolkits are fairly easy to implement.

  • Large-scale integrations are certainly possible and can protect your existing investments.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.76.218