The other day I was taking a look at JavaFX and how it might fit one of my OSGi related projects. As usual things are not as easy as they appear at first sight, so it is time to add JavaFX to osgisnippets.
To use JavaFX with my other project and with osgisnippets, the following goals exist:
- Ability to show Swing components
- Provide a guitool based on JavaFX like the one based on Swing
- Get it to work with OSGi
Goal 1 is covered in this article. The others will be covered in their own articles soon. For the tests I created the class SnipFXSwing. A link to the sources is at the end of the article. In the source archive, it is in the guitool project in package example.guitool.snip.
JFXPanel - embed JavaFX in Swing
This is straight forward, the API doc explains how. (See the Resources section at the end of the article.) Note that JFXPanel is about having a Swing application and starting JavaFX components in it. I need it the other way around.
The class SnipFXSwing has a main method in which different start methods are used depending on the command line argument:
Approach 1 - Just Try It
Lets just fire up a JavaFX UI and try to create a Swing JFrame. To do that, SnipFXSwing is started with the argument fx. The relevant code is this:
This is typical source code for small JavaFX applications. The method createScene creates the JavaFX components. This separate method exists, because this part will be reused later. The Stage is the main window and a handler is registered to shut down the application when the window is closed.
The method doShowSwing is called, when the button in the center of the window is pressed:
The first if statement checks, whether the method has been called from the Event Dispatch Thread (EDT) and calls it from the EDT if not. The remaining code is the usual way to create a Swing JFrame. When running on Mac OS X and pressing the button "Show Swing Window", this error is logged:
So that doesn't work. There are a few issues which require, that AWT is initialized before JavaFX. (Link to bug reports in the Resources section.)
BTW: If the if block is commented out and the code is not run in the EDT, the following error is logged:
On to the next approach.
Approach 2 - Initialize AWT before JavaFX
Some internals require, that AWT is initialized before JavaFX. One approach is this:
To try that, the SnipFXSwing class is started with the argument awtfx. Well, as can be seen, nothing is seen. The application hangs and it must be terminated.
A system property is needed:
Ah! It works :-)
Can we see a Swing window please?
Finally some cannons for the sparrows:
Approach 3 - Embed JavaFX in Swing
If for some reason, the javafx.macosx.embedded property cannot be set, another solution is required. Lets embed JavaFX in Swing so we can embed Swing in JavaFX.
Starting the application:
First Swing is started by creating a JFrame. Such things must be done in the EDT, so it is wrapped in SwingUtilities.invokeLater. Basically a JFrame is created and a JFXPanel is added to the frame's content pane.
Now the JavaFX components are created. Similar to Swing with EDT that must be done in the "JavaFX Application Thread". Hence the Platform.runLater call. A different start method is used now, as there is no Stage instance:
Similar to the Stage related start method, the Scene is created, however, it is set in the JFXPanel instance. Also the code to handle a closed main window must be slightly different. Instead of a Stage, a JFrame must be observed, which is done with a WindowListener. A little tricky is the call to doExit. As this shall be JavaFX code, it must be run in the JavaFX Application Thread, so again Platform.runLater is used.
The "JavaFX in JFXPanel" main window:
Pressing the "Swing" button:
(*Wiping sweat away...*)
JFXPanel API doc:
Related bug report with discussion regarding init order AWT/JavaFX
Related bug report with discussion regarding embedded JavaFX
Source code of OSGi snippets is at the project site: