Add JavaFX controls to a SWT Eclipse 4 application - Eclipse RCP Cookbook - ...

:

As explained in JavaFX Interoperability with SWT it is possible to embed JavaFX controls in a SWT UI. This is useful for example if you want to softly migrate big applications from SWT to JavaFX or if you need to add animations or special JavaFX controls without completely migrating your application.

The following recipe will show how to integrate JavaFX with an Eclipse 4 application.

Cookware

Ingredients

This recipe uses the Eclipse RCP Cookbook – Basic Recipe. To get started fast with this recipe, we have prepared the basic recipe for you on GitHub.

If you want to use the prepared basic recipe to follow the migration from SWT to JavaFX, import the project by cloning the Git repository:

  • File → Import → Git → Projects from Git
  • Click Next
  • Select Clone URI
  • Enter URI https://github.com/fipro78/e4-cookbook-basic-recipe.git
  • Click Next
  • Select the master branch
  • Click Next
  • Choose a directory where you want to store the checked out sources
  • Click Next
  • Select Import existing projects
  • Click Next
  • Click Finish

Preparation

Step 1: Update the Target Platform

  • Open the target definition de.codecentric.eclipse.tutorial.target.target in the project de.codecentric.eclipse.tutorial.target
  • Add a new Software Site by clicking Add… in the Locations section
    • Select Software Site
    • Software Site for the e(fx)clipse 1.2.0 release build
      http://download.eclipse.org/efxclipse/runtime-released/1.2.0/site
    • Expand FX Target and check RCP e4 Target Platform Feature
    • Click Finish
  • Add a new Software Site by clicking Add… in the Locations section
    • Select Software Site
    • Software Site for Orbit
      http://download.eclipse.org/tools/orbit/downloads/drops/R20150124073747/repository/
    • Expand Orbit Bundles By Name: org.apache.* and check Apache Jakarta log4j Plug-in
      (this is a necessary dependency for the RCP e4 Target Platform Feature)
    • Click Finish 
  • Activate the target platform by clicking Set as Target Platform in the upper right corner of the Target Definition Editor

Note:
With the upcoming e(fx)clipse 2.0 release it is not necessary to add the Apache Jakarta log4j Plug-in to the Target Definition! If you want to play with the current development state of e(fx)clipse 2.0, use the e(fx)clipse Nightly Build Software Site http://download.eclipse.org/efxclipse/runtime-nightly/site. In that case you also need to update the Target Definition to use Eclipse Mars via the Milestone Software Site http://download.eclipse.org/eclipse/updates/4.5milestones.

Step 2: Update the Plug-in project

  • Update the bundle dependencies
    • Open the file MANIFEST.MF in the project de.codecentric.eclipse.tutorial.inverter
    • Switch to the Dependencies tab
    • Add the following packages to the Imported Packages

Note: With the upcoming e(fx)clipse 2.0 release it is not necessary to add the javafx.* packages to the Imported Packages!

  • Open the InverterPart in the project de.codecentric.eclipse.tutorial.inverter
    • Add a javafx.embed.swt.FXCanvas to the parent Composite in InverterPart#postConstruct(Composite)
    • Create an instance of javafx.scene.layout.BorderPane
    • Create a javafx.scene.Scene instance that takes the created BorderPane as root node and sets the background color to be the same as the background color of the parent Shell
    • Set the created javafx.scene.Scene to the FXCanvas
// add FXCanvas for adding JavaFX controls to the UI
FXCanvas canvas = new FXCanvas(parent, SWT.NONE);
GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(canvas);
 
// create the root layout pane
BorderPane layout = new BorderPane();
 
// create a Scene instance
// set the layout container as root
// set the background fill to the background color of the shell
Scene scene = new Scene(layout, Color.rgb(
    parent.getShell().getBackground().getRed(),
    parent.getShell().getBackground().getGreen(),
    parent.getShell().getBackground().getBlue()));
 
// set the Scene to the FXCanvas
canvas.setScene(scene);

// add FXCanvas for adding JavaFX controls to the UI FXCanvas canvas = new FXCanvas(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(canvas);// create the root layout pane BorderPane layout = new BorderPane(); // create a Scene instance // set the layout container as root // set the background fill to the background color of the shell Scene scene = new Scene(layout, Color.rgb( parent.getShell().getBackground().getRed(), parent.getShell().getBackground().getGreen(), parent.getShell().getBackground().getBlue())); // set the Scene to the FXCanvas canvas.setScene(scene);

Now JavaFX controls can be added to the scene graph via the BorderPane instance.

  • Remove the output control of type org.eclipse.swt.widgets.Text
  • Create an output control of type javafx.scene.control.Label
  • Add the created javafx.scene.control.Label to the center of the BorderPane
javafx.scene.control.Label output = new javafx.scene.control.Label();
layout.setCenter(output);

javafx.scene.control.Label output = new javafx.scene.control.Label(); layout.setCenter(output);

Add some animations to see some more JavaFX features.

  • Create a javafx.animation.RotateTransition that rotates the output label.
  • Create a javafx.animation.ScaleTransition that scales the output label.
  • Create a javafx.animation.ParallelTransition that combines the RotateTransition and the ScaleTransition. This way both transitions are executed in parallel.
  • Add starting the animation in the SelectionAdapter and the KeyAdapter that are executed for reverting a String.
RotateTransition rotateTransition = new RotateTransition(Duration.seconds(1), output);
rotateTransition.setByAngle(360);
 
ScaleTransition scaleTransition = new ScaleTransition(Duration.seconds(1), output);
scaleTransition.setFromX(1.0);
scaleTransition.setFromY(1.0);
scaleTransition.setToX(4.0);
scaleTransition.setToY(4.0);
 
ParallelTransition parallelTransition = 
    new ParallelTransition(rotateTransition, scaleTransition);
 
button.addSelectionListener(new SelectionAdapter() {
    @Override
    public void widgetSelected(SelectionEvent e) {
        output.setText(StringInverter.invert(input.getText()));
        parallelTransition.play();
    }
});

RotateTransition rotateTransition = new RotateTransition(Duration.seconds(1), output); rotateTransition.setByAngle(360); ScaleTransition scaleTransition = new ScaleTransition(Duration.seconds(1), output); scaleTransition.setFromX(1.0); scaleTransition.setFromY(1.0); scaleTransition.setToX(4.0); scaleTransition.setToY(4.0); ParallelTransition parallelTransition = new ParallelTransition(rotateTransition, scaleTransition); button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { output.setText(StringInverter.invert(input.getText())); parallelTransition.play(); } });

Step 3: Update the Feature project

  • Open the file feature.xml in the project de.codecentric.eclipse.tutorial.feature
  • Switch to the Plug-ins tab
  • Add the following plug-ins to the list of Plug-ins and Fragments
    • org.apache.commons.lang
    • org.apache.log4j

This step is necessary because these plug-ins are missing in the org.eclipse.fx.target.rcp4.feature in e(fx)clipse 1.2.0.

Note: With the upcoming e(fx)clipse 2.0 release this step is not necessary!

Step 4: Update the Product Configuration

  • Open the file de.codecentric.eclipse.tutorial.app.product in the project de.codecentric.eclipse.tutorial.product
  • Switch to the Dependencies tab
    • Add org.eclipse.fx.target.rcp4.feature
  • Switch to the Launching tab
    • Add -Dosgi.framework.extensions=org.eclipse.fx.osgi to the VM Arguments

Step 5: Taste

  • Start the application from within the IDE
    • Open the Product Configuration in the de.codecentric.eclipse.tutorial.product project
    • Select the Overview tab
    • Click Launch an Eclipse Application in the Testing section

The started application should look similar to the following screenshot.

Eclipse4_basic_with_fx_result

If you want to create a deliverable via the PDE export, you need to configure the jfxswt.jar file as an external compile time library. Otherwise you will see compile errors on export since that library is not part of any classpath because of the dependency to SWT.

In a productive environment you should not rely on such a workaround! The best practice here is to setup a headless build using Maven Tycho for example.

  • Prepare the build classpath for PDE export
    • Create the directory lib in the project de.codecentric.eclipse.tutorial.inverter
    • Copy the file jfxswt.jar from JAVA_HOME/lib on your machine to this directory
    • Open the file build.properties in the project de.codecentric.eclipse.tutorial.inverter
    • Expand the Extra Classpath Entries section at the bottom of the Build Properties Editor
    • Add the entry lib/jfxswt.jar via Add JARs…
  • Export the application and start the deliverable
    • Open the Product Configuration in the de.codecentric.eclipse.tutorial.product project
    • Select the Overview tab
    • Click Eclipse Product export wizard in the Exporting section
    • Select a directory to export to in the Destination section of the export wizard
    • Leave the other options unchanged
    • Click Finish
    • After the export is done, open the directory the application was exported to and start the application via the native executable
      • Note that this might not work correctly on a Mac because of some issues with the PDE export and Mac. In that case uncheck  The product includes native launcher artifacts on the Overview tab of the Product Configuration and start the application by executing the following command on the command line
        java -jar plugins/org.eclipse.equinox.launcher_$VERSION.jar

The complete source code of the example can be found on GitHub.

Feel free to contact me via email or comments section if you have further questions or thoughts about this recipe.