From 0e05c01187605c86107d23918147b00743a66618 Mon Sep 17 00:00:00 2001 From: Dirk Fauth Date: Fri, 31 May 2024 10:04:58 +0200 Subject: [PATCH] Add recipe #6 about JavaFX --- README.md | 8 + .../Application.e4xmi | 2 +- .../META-INF/MANIFEST.MF | 8 +- .../fragment.e4xmi | 5 +- .../inverter/part/InverterFXPart.java | 151 ++++++ .../fragment.e4xmi | 2 +- .../org.fipro.eclipse.tutorial.app.product | 7 + ...g.fipro.eclipse.tutorial.app.product.fxrcp | 58 +++ .../org.fipro.eclipse.tutorial.target.target | 40 ++ pom.xml | 30 +- tutorials/Eclipse_RCP_Cookbook_JavaFX.md | 447 ++++++++++++++++++ tutorials/Eclipse_RCP_Cookbook_Preferences.md | 4 +- tutorials/application_fx.png | Bin 0 -> 17034 bytes tutorials/efxclipse_preferences.png | Bin 0 -> 33978 bytes 14 files changed, 749 insertions(+), 13 deletions(-) create mode 100644 org.fipro.eclipse.tutorial.inverter/src/org/fipro/eclipse/tutorial/inverter/part/InverterFXPart.java create mode 100644 org.fipro.eclipse.tutorial.product/org.fipro.eclipse.tutorial.app.product.fxrcp create mode 100644 tutorials/Eclipse_RCP_Cookbook_JavaFX.md create mode 100644 tutorials/application_fx.png create mode 100644 tutorials/efxclipse_preferences.png diff --git a/README.md b/README.md index 34165b1..241e6f7 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,11 @@ The updated version is contained in this repository in [Topping Recipe](/tutoria This is the _Decoration Recipe_, which means that we add the functionality that a user can change the behavior or the look&feel of an application by changing preferences. This is done by using Eclipse Preferences with a custom plugin that adapts a plain E4 preferences mechanism. The tutorial is contained in this repository in [Decoration Recipe](/tutorials/Eclipse_RCP_Cookbook_Preferences.md) + +## Recipe #6 - The Veggie Recipe (Add JavaFX controls to an SWT Eclipse 4 application) + +This is the _Veggie Recipe_, which means that we use an *alternative* UI technology with our Eclipse application. So it is like an alternative to meat (SWT). But instead of doing a full SWT to JavaFX migration, we integrate JavaFX controls to the SWT based Eclipse application. + +The original blog post can be found here: [Add JavaFX controls to a SWT Eclipse 4 application – Eclipse RCP Cookbook](https://www.codecentric.de/wissens-hub/blog/add-javafx-controls-swt-eclipse-4-application-eclipse-rcp-cookbook) +An updated was already published here: [Add JavaFX controls to a SWT Eclipse 4 application – Eclipse RCP Cookbook UPDATE](https://vogella.com/blog/add-javafx-controls-to-a-swt-eclipse-4-application-eclipse-rcp-cookbook-update/) +The most current updated version is contained in this repository in [Veggie Recipe](/tutorials/Eclipse_RCP_Cookbook_JavaFX.md) diff --git a/org.fipro.eclipse.tutorial.app/Application.e4xmi b/org.fipro.eclipse.tutorial.app/Application.e4xmi index 4c15459..f174f23 100644 --- a/org.fipro.eclipse.tutorial.app/Application.e4xmi +++ b/org.fipro.eclipse.tutorial.app/Application.e4xmi @@ -1,6 +1,6 @@ - + diff --git a/org.fipro.eclipse.tutorial.inverter/META-INF/MANIFEST.MF b/org.fipro.eclipse.tutorial.inverter/META-INF/MANIFEST.MF index e848c51..ab4e9b6 100644 --- a/org.fipro.eclipse.tutorial.inverter/META-INF/MANIFEST.MF +++ b/org.fipro.eclipse.tutorial.inverter/META-INF/MANIFEST.MF @@ -7,14 +7,14 @@ Require-Bundle: org.eclipse.swt;bundle-version="3.125.0", org.eclipse.jface;bundle-version="3.33.0", org.eclipse.e4.ui.model.workbench;bundle-version="2.4.200.v20240109-1025" Bundle-RequiredExecutionEnvironment: JavaSE-17 -Import-Package: jakarta.annotation;version="[2.1.0,3.0.0]", - jakarta.inject;version="[2.0.0,3.0.0]", +Automatic-Module-Name: org.fipro.eclipse.tutorial.inverter +Model-Fragment: fragment.e4xmi +Import-Package: jakarta.annotation;version="[2.1.0,3.0.0)";resolution:=optional, + jakarta.inject;version="[2.0.0,3.0.0)", org.eclipse.e4.core.di.annotations;version="[1.6.0,2.0.0]", org.eclipse.e4.core.di.extensions;version="[0.16.0,1.0.0]", org.eclipse.e4.core.services.events, org.fipro.e4.service.preferences;version="[0.4.0,1.0.0]", org.fipro.eclipse.tutorial.service.inverter;version="[1.0.0,2.0.0]" -Model-Fragment: fragment.e4xmi -Automatic-Module-Name: org.fipro.eclipse.tutorial.inverter Bundle-ActivationPolicy: lazy Service-Component: OSGI-INF/org.fipro.eclipse.tutorial.inverter.preferences.InverterPreferencesContribution.xml diff --git a/org.fipro.eclipse.tutorial.inverter/fragment.e4xmi b/org.fipro.eclipse.tutorial.inverter/fragment.e4xmi index 2f08efa..910b0f1 100644 --- a/org.fipro.eclipse.tutorial.inverter/fragment.e4xmi +++ b/org.fipro.eclipse.tutorial.inverter/fragment.e4xmi @@ -2,6 +2,9 @@ - + + + + diff --git a/org.fipro.eclipse.tutorial.inverter/src/org/fipro/eclipse/tutorial/inverter/part/InverterFXPart.java b/org.fipro.eclipse.tutorial.inverter/src/org/fipro/eclipse/tutorial/inverter/part/InverterFXPart.java new file mode 100644 index 0000000..ac3dc87 --- /dev/null +++ b/org.fipro.eclipse.tutorial.inverter/src/org/fipro/eclipse/tutorial/inverter/part/InverterFXPart.java @@ -0,0 +1,151 @@ +package org.fipro.eclipse.tutorial.inverter.part; + +import org.eclipse.e4.core.di.annotations.Optional; +import org.eclipse.e4.core.di.extensions.Preference; +import org.eclipse.e4.core.di.extensions.Service; +import org.eclipse.e4.core.services.events.IEventBroker; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.fipro.eclipse.tutorial.service.inverter.InverterService; + +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import javafx.animation.ParallelTransition; +import javafx.animation.RotateTransition; +import javafx.animation.ScaleTransition; +import javafx.embed.swt.FXCanvas; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.paint.Color; +import javafx.util.Duration; + +public class InverterFXPart { + + @Inject + @Service + private InverterService inverter; + + @Inject + IEventBroker broker; + + TextField input; + Label output; + + Color textColor = Color.BLACK; + + @PostConstruct + public void postConstruct(Composite parent) { + GridLayoutFactory.fillDefaults().applyTo(parent); + + // 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 + GridPane layout = new GridPane(); + + // 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); + + // create the controls + Label inputLabel = new Label(); + inputLabel.setText("String to revert:"); + GridPane.setConstraints(inputLabel, 0, 0); + GridPane.setMargin(inputLabel, new Insets(5.0)); + + input = new TextField(); + input.setStyle("-fx-text-fill: " + (textColor == Color.BLUE ? "blue" : "black") + ";"); + GridPane.setConstraints(input, 1, 0); + GridPane.setHgrow(input, Priority.ALWAYS); + GridPane.setMargin(input, new Insets(5.0)); + + Button button = new Button(); + button.setText("Revert"); + GridPane.setConstraints(button, 2, 0); + GridPane.setMargin(button, new Insets(5.0)); + + Label outputLabel = new Label(); + outputLabel.setText("Inverted String:"); + GridPane.setConstraints(outputLabel, 0, 1); + GridPane.setMargin(outputLabel, new Insets(5.0)); + + output = new Label(); + output.setTextFill(textColor); + GridPane.setConstraints(output, 0, 2); + GridPane.setColumnSpan(output, 3); + GridPane.setHgrow(output, Priority.ALWAYS); + GridPane.setHalignment(output, HPos.CENTER); + + // don't forget to add children to gridpane + layout.getChildren().addAll( + inputLabel, input, button, outputLabel, output); + + // add an animation for the output + 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); + + // add the action listener + button.setOnAction(event -> { + output.setText(inverter.invert(input.getText())); + broker.post("TOPIC_LOGGING", "triggered via button (FX)"); + parallelTransition.play(); + }); + + input.setOnAction(event -> { + output.setText(inverter.invert(input.getText())); + broker.post("TOPIC_LOGGING", "triggered via field (FX)"); + parallelTransition.play(); + }); + + } + + @Inject + @Optional + public void setTextColor( + @Preference(nodePath = "org.fipro.eclipse.tutorial.inverter", value = "inverter_color") String color) { + + textColor = "blue".equals(color) + ? Color.BLUE + : Color.BLACK; + + if (input != null) { + input.setStyle("-fx-text-fill: " + color + ";"); + } + + if (output != null) { + output.setTextFill(textColor); + } + } + +} \ No newline at end of file diff --git a/org.fipro.eclipse.tutorial.logview/fragment.e4xmi b/org.fipro.eclipse.tutorial.logview/fragment.e4xmi index 33af540..4b4dfc2 100644 --- a/org.fipro.eclipse.tutorial.logview/fragment.e4xmi +++ b/org.fipro.eclipse.tutorial.logview/fragment.e4xmi @@ -1,7 +1,7 @@ - + diff --git a/org.fipro.eclipse.tutorial.product/org.fipro.eclipse.tutorial.app.product b/org.fipro.eclipse.tutorial.product/org.fipro.eclipse.tutorial.app.product index 66fe4ee..66c1974 100644 --- a/org.fipro.eclipse.tutorial.product/org.fipro.eclipse.tutorial.app.product +++ b/org.fipro.eclipse.tutorial.product/org.fipro.eclipse.tutorial.app.product @@ -9,6 +9,8 @@ -clearPersistedState + -Dosgi.framework.extensions=org.eclipse.fx.osgi + -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts @@ -38,6 +40,11 @@ + + + + + diff --git a/org.fipro.eclipse.tutorial.product/org.fipro.eclipse.tutorial.app.product.fxrcp b/org.fipro.eclipse.tutorial.product/org.fipro.eclipse.tutorial.app.product.fxrcp new file mode 100644 index 0000000..4f8e9dc --- /dev/null +++ b/org.fipro.eclipse.tutorial.product/org.fipro.eclipse.tutorial.app.product.fxrcp @@ -0,0 +1,58 @@ + + + + + + + + + + -clearPersistedState + + -Dosgi.framework.extensions=org.eclipse.fx.osgi + + -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.fipro.eclipse.tutorial.target/org.fipro.eclipse.tutorial.target.target b/org.fipro.eclipse.tutorial.target/org.fipro.eclipse.tutorial.target.target index 47714b9..ebfed15 100644 --- a/org.fipro.eclipse.tutorial.target/org.fipro.eclipse.tutorial.target.target +++ b/org.fipro.eclipse.tutorial.target/org.fipro.eclipse.tutorial.target.target @@ -40,5 +40,45 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6a42089..0adfd4a 100644 --- a/pom.xml +++ b/pom.xml @@ -82,17 +82,39 @@ - + org.eclipse.tycho tycho-p2-director-plugin ${tycho.version} + + org.eclipse.tycho + tycho-compiler-plugin + ${tycho.version} + + UTF-8 + + + org.openjfx + javafx-controls + 17.0.11 + + + org.openjfx + javafx-swt + 17.0.11 + ${JAVAFX_HOME}/lib/javafx-swt.jar + system + + + + diff --git a/tutorials/Eclipse_RCP_Cookbook_JavaFX.md b/tutorials/Eclipse_RCP_Cookbook_JavaFX.md new file mode 100644 index 0000000..757c658 --- /dev/null +++ b/tutorials/Eclipse_RCP_Cookbook_JavaFX.md @@ -0,0 +1,447 @@ +# Eclipse RCP Cookbook – The Veggie Recipe (Add JavaFX controls to an SWT Eclipse 4 application) + +As explained in [JavaFX Interoperability with SWT](https://docs.oracle.com/javafx/2/swt_interoperability/jfxpub-swt_interoperability.htm "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. It will cover the usage of Java 17 and JavaFX 17. + +**_Note:_** +If you are interested in the usage of Java 8 with integrated JavaFX, have a look at the older versions of this tutorial, e.g. the one published at [vogella Blog](https://vogella.com/blog/add-javafx-controls-to-a-swt-eclipse-4-application-eclipse-rcp-cookbook-update/). + +## Cookware +- JDK >= 17 + - e.g. [Eclipse Temurin](https://adoptium.net/temurin/releases/) + - Simply run the executable and follow the installation instructions +- Eclipse IDE >= 2024-03 + - [Eclipse IDE Downloads](https://www.eclipse.org/downloads/) + - Choose the package that fits your needs the best, e.g. _Eclipse for RCP and RAP Developers_ + - After starting the IDE and choosing a workspace, update the IDE to ensure the latest service release is installed. This is necessary to get the latest bugfixes and security patches. + - Main Menu → Help → Check for Updates +- e(fx)clipse 3.9.0 + - Update your Eclipse installation + - _Main Menu → Help → Install New Software..._ + - Use the e(fx)clipse 3.9.0 Update Site _https://download.eclipse.org/efxclipse/updates-released/3.9.0/site_ + - Select _**e(fx)clipse - install / e(fx)clipse - IDE**_ to get the all IDE features installed needed to develop JavaFX within Eclipse ([https://www.eclipse.org/efxclipse/index.html](https://www.eclipse.org/efxclipse/index.html)) + - Restart the IDE and choose a workspace +- JavaFX >= 17 + - [https://openjfx.io/](https://openjfx.io/) + - Download the JavaFX archive for your operating system + - Extract it +- e(fx)clipse + - Configure the JavaFX SDK **_lib_** folder + - _Main Menu → Window → Preferences → JavaFX_ + + + +## Ingredients + +This recipe is based on the [Eclipse RCP Cookbook – The Decoration Recipe](Eclipse_RCP_Cookbook_Preferences.md). To get started fast with this recipe, the recipe is prepared for you on GitHub. + +To use the prepared recipe, 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 **preferences** branch +- Click _Next_ +- Choose a directory where you want to store the checked out sources +- Select _Import existing Eclipse projects_ +- Click _Finish_ + +## Preparation + +### Step 1: Update the Target Platform + +- Open the target definition _org.fipro.eclipse.tutorial.target.target_ in the project _org.fipro.eclipse.tutorial.target_ +- Update the Software Sites in the opened _Target Definition Editor_ + - Alternative A + - Switch to the _Source_ tab and add the following snippet to the editor + ```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ``` + + - Alternative B + - Add the e(fx)clipse Runtime extension to add JavaFX support + - Select _Add..._ + - Select _Software Site_ + - Click _Next_ + - Enter _http://download.eclipse.org/efxclipse/runtime-released/3.9.0/site_ in _Work with:_ for the e(fx)clipse 3.9.0 release build + - Expand **_FX Target_** + - Check **_Minimal JavaFX OSGi integration bundles_** + - Check **_RCP e4 Target Platform Feature_** + _(in case additional features provided by e(fx)clipse should be used)_ + - Click _Finish_ + - Add the JavaFX libraries as OSGi bundles + _Further information [here](https://github.com/eclipse-efx/efxclipse-eclipse/wiki/Eclipse4-application-on-OpenJFX-11)_ + - Select _Add..._ + - Select _Software Site_ + - Click _Next_ + - Enter _https://downloads.efxclipse.bestsolution.at/p2-repos/openjfx-17.0.2/_ in _Work with:_ + - Disable _Group by Category_ as the items are not categorized and check all available items + - _**openjfx.media.feature**_ + - _**openjfx.standard.feature**_ + - _**openjfx.swt.feature**_ + - _**openjfx.web.feature**_ + - Click _Finish_ +- Switch to the _Definition_ tab + - Wait until the Target Definition is completely resolved (check the progress at the bottom right) + - Reload and activate the target platform by clicking _Reload Target Platform_ in the upper right corner of the Target Definition Editor + +### Step 2: Update the Plug-in project + +Instead of changing the `InverterPart` to use a single JavaFX control instead of a SWT control for the output, we add a new `InverterFXPart` that completely uses JavaFX controls. This is a difference to the original blog post. + +- Update the application model fragments + - Open the file _fragment.e4xmi_ in the project _org.fipro.eclipse.tutorial.inverter_ + - Add a _PartStack_ + - Select _Model Fragment Definition → Model Fragments → Model Fragment for PartSashContainer_ + - On the details side select _PartStack_ in the dropdown and click _Add_ + - In the tree view drag the existing _Part_ to the created _PartStack_ + - Select the _Part_ and set _Label_ to _Inverter (SWT)_ + - Select the _PartStack_ in the tree view + - Select _Part_ in the combo + - Click on the _Add_ button + - Set _Label_ to _Inverter (JavaFX)_ + + - Create the part implementation + - Click the _Class URI_ link in the part detail view + - Set the values in the opened dialog + + | Property | Value | + | --- | --- | + | Package | org.fipro.eclipse.tutorial.inverter.part | + | Name | InverterFXPart | + | PostConstruct Method | check | + + - Click _Finish_ + - Save the _fragment.e4xmi_ file + - Open the file _fragment.e4xmi_ in the project _org.fipro.eclipse.tutorial.logview_ + - Select _Model Fragment Definition → Model Fragments → Model Fragment for PartSashContainer_ + - Change the value in _Position in list:_ to `after:org.fipro.eclipse.tutorial.inverter.partstack.0` + + - Implement the `InverterFXPart` + - Create the content in the method annotated with `@PostConstruct` + - Ensure that a `org.eclipse.swt.layout.GridLayout` is set on the parent `Composite` + - Add a `javafx.embed.swt.FXCanvas` to the parent `Composite` in `InverterFXPart#postConstruct(Composite)` + - Create an instance of `javafx.scene.layout.GridPane` + - Create a `javafx.scene.Scene` instance that takes the created `GridPane` 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` + +``` java +GridLayoutFactory.fillDefaults().applyTo(parent); + +// 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 +GridPane layout = new GridPane(); + +// 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 `GridPane` instance. + +- Create an instance field for the `input` control of type `javafx.scene.control.TextField` +- Create an instance field for the `output` control of type `javafx.scene.control.Label` +- Create an instance field for the `Color` to be used with the controls (needed for the preference support on startup) + +```java +TextField input; +Label output; +Color textColor; +``` + +- Create the user interface with labels and buttons and configure the layout via `javafx.scene.layout.GridPane` +- Add the created controls to the `GridPane` + +``` java +// create the controls +Label inputLabel = new Label(); +inputLabel.setText("String to revert:"); +GridPane.setConstraints(inputLabel, 0, 0); +GridPane.setMargin(inputLabel, new Insets(5.0)); + +input = new TextField(); +input.setStyle("-fx-text-fill: " + (textColor == Color.BLUE ? "blue" : "black") + ";"); +GridPane.setConstraints(input, 1, 0); +GridPane.setHgrow(input, Priority.ALWAYS); +GridPane.setMargin(input, new Insets(5.0)); + +Button button = new Button(); +button.setText("Revert"); +GridPane.setConstraints(button, 2, 0); +GridPane.setMargin(button, new Insets(5.0)); + +Label outputLabel = new Label(); +outputLabel.setText("Inverted String:"); +GridPane.setConstraints(outputLabel, 0, 1); +GridPane.setMargin(outputLabel, new Insets(5.0)); + +output = new Label(); +output.setTextFill(textColor); +GridPane.setConstraints(output, 0, 2); +GridPane.setColumnSpan(output, 3); +GridPane.setHgrow(output, Priority.ALWAYS); +GridPane.setHalignment(output, HPos.CENTER); + +// don't forget to add children to gridpane +layout.getChildren().addAll( + inputLabel, input, button, outputLabel, 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. + + ``` java + // add an animation for the output + 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); + ``` + +Align the functionality with the `InverterPart`: +- Get the `InverterService` and the `IEventBroker` injected + + ```java + @Inject + @Service + private InverterService inverter; + + @Inject + IEventBroker broker; + ``` + +- Add the action listener for the button and the input field + ```java + // add the action listener + button.setOnAction(event -> { + output.setText(inverter.invert(input.getText())); + broker.post("TOPIC_LOGGING", "triggered via button (FX)"); + parallelTransition.play(); + }); + + input.setOnAction(event -> { + output.setText(inverter.invert(input.getText())); + broker.post("TOPIC_LOGGING", "triggered via field (FX)"); + parallelTransition.play(); + }); + ``` + +- Add the following method to add the preference support like in the SWT `InverterPart`: + ```java + @Inject + @Optional + public void setTextColor( + @Preference(nodePath = "org.fipro.eclipse.tutorial.inverter", value = "inverter_color") String color) { + + textColor = "blue".equals(color) + ? Color.BLUE + : Color.BLACK; + + if (input != null) { + input.setStyle("-fx-text-fill: " + color + ";"); + } + + if (output != null) { + output.setTextFill(textColor); + } + } + ``` + +### Step 3: Update the Product Configuration + +- Open the file _org.fipro.eclipse.tutorial.app.product_ in the project _org.fipro.eclipse.tutorial.product_ +- Switch to the _Contents_ tab and add additional features + - **Option A:** Use the **_Minimal JavaFX OSGi integration bundles_** + - _org.eclipse.fx.runtime.min.feature_ + - **Option B:** Use the **_RCP e4 Target Platform Feature_** + __*Note:*__ As _Include required Features and Plug-ins automatically_ is active, the additional required features and plug-ins will be added automatically. To have more control over the included Features and Plug-ins, disable this option and ensure manually that everything that is required is included. + - _org.eclipse.fx.target.rcp4.feature_ + - JavaFX OSGi bundles + - _openjfx.media.feature_ + - _openjfx.standard.feature_ + - _openjfx.swt.feature_ + - _openjfx.web.feature_ +- Switch to the _Launching_ tab + - Add _\-Dosgi.framework.extensions=org.eclipse.fx.osgi_ to the _VM Arguments_ (adapter hook to get JavaFX-SWT integration on the classpath) + +## Taste + +- Start the application from within the IDE + - Open the Product Configuration in the _org.fipro.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. + + + +## Thermomix Update - Maven Tycho build + +To build a deliverable product it is recommended to use Maven Tycho. The project was already prepared to build via pomless Tycho in the [Eclipse RCP Cookbook – The Thermomix Recipe](Eclipse_RCP_Cookbook_Tycho.md). + +JavaFX is not on the default classpath, therefore the location of the JavaFX libraries need to be configured in the Tycho build for compile time resolution. The OpenJFX libraries are available via Maven Central and can be added as extra classpath elements via Maven. But the `javafx-swt` module is not available via Maven Central as reported [here](https://github.com/javafxports/openjdk-jfx/issues/268). + + +That means for OpenJFX 17 following section needs to be added in the `pluginManagement` section, where the **JAVAFX\_HOME** environment variable points to your OpenJFX installation: +```xml + + org.eclipse.tycho + tycho-compiler-plugin + ${tycho.version} + + UTF-8 + + + org.openjfx + javafx-controls + 17.0.11 + + + org.openjfx + javafx-swt + 17.0.11 + ${JAVAFX_HOME}/lib/javafx-swt.jar + system + + + + +``` +Start the build + +``` console +mvn clean verify +``` + +The resulting product variants for each platform is located under _e4-cookbook-basic-recipe/org.fipro.eclipse.tutorial.product/target/products_ + +The currently available re-bundled OpenJFX versions can be found in [this download area](https://downloads.efxclipse.bestsolution.at/p2-repos/). If you are interested about newer OpenJFX versions you can have a look at the [openjfx-osgi](https://github.com/BestSolution-at/openjfx-osgi) repository on GitHub or get in contact with [BestSolution.at](https://www.bestsolution.at/) who created and provide the bundles. + +**Note:** +As an alternative to bundling JavaFX with your application, you can also configure to use externally located JavaFX libraries. For this add **_\-Defxclipse.java-modules.dir=_** to the VM Arguments of the Product Configuration. As this approach makes the installation dependent on external states, I did not cover it here. But it is worth to mention as it might be interesting in some cases. There is also a [blog post by Tom Schindl](https://tomsondev.bestsolution.at/2020/01/28/setting-up-efxclipse-rcp-development-for-java11-and-pde/) about JavaFX, Java 11, RCP and PDE. + +## Interlude: UI Freeze issue + +There is an UI freeze issue related to JavaFX in Eclipse. It is an incompatibility of JavaFX and the _glass.dll_ it tries to load when there are multiple Java versions available, e.g. if you start your application with a Java version that is different to the Java version configured in your PATH system environment variable. + +When Eclipse launches it automatically generates an entry for `java.library.path` that contains the path to the Java installation that is used to start Eclipse, and the PATH system environment variable. If another or additional Java installations are on the PATH, the `java.library.path` environment variable contains paths to multiple Java versions. It can then happen that JavaFX loads the _glass.dll_ from the not matching Java installation, which then leads to a `NoSuchMethodError` or similar that cause a crash. + +This issue is already reported [here](https://bugs.eclipse.org/bugs/show_bug.cgi?id=540247). + +`java.library.path` is a Java environment variable that is used to add additional native libraries to the runtime. It is only used in case native libraries should be added to an application that are loaded inside Java via `System.loadLibrary()`. In Eclipse/OSGi development, native libraries are typically included inside a plug-in project and do not reside external, as this would make the installation dependent on locally installed native libraries on the consumer side. Therefore setting `java.library.path` to an empty value (or an appropriate value in case it is needed) should not have any effect on other functionalities. + +- Open the file _org.fipro.eclipse.tutorial.app.product_ in the project _org.fipro.eclipse.tutorial.product_ +- Switch to the _Launching_ tab + - Add _\-Djava.library.path=_ to the _VM Arguments_ (java.library.path will be empty and JavaFX will load the _glass.dll_ from the Java installation that was used to start the application) diff --git a/tutorials/Eclipse_RCP_Cookbook_Preferences.md b/tutorials/Eclipse_RCP_Cookbook_Preferences.md index af41df2..8746bb6 100644 --- a/tutorials/Eclipse_RCP_Cookbook_Preferences.md +++ b/tutorials/Eclipse_RCP_Cookbook_Preferences.md @@ -8,7 +8,7 @@ This recipe will explain and show how to add a preference page to an Eclipse 4 a ## Ingredients -This recipe is based on the [Eclipse RCP Cookbook – The Topping Recipe](Eclipse_RCP_Cookbook_p2.md). To get started fast with this recipe, the recipe is prepared for you on GitHub . +This recipe is based on the [Eclipse RCP Cookbook – The Topping Recipe](Eclipse_RCP_Cookbook_p2.md). To get started fast with this recipe, the recipe is prepared for you on GitHub. To use the prepared recipe, import the project by cloning the Git repository: @@ -31,7 +31,7 @@ In a plain Eclipse 4 application, you don't want to use the _Compatibility Layer - Open the target definition _org.fipro.eclipse.tutorial.target.target_ in the project _org.fipro.eclipse.tutorial.target_ -- Update the Software Site in the opened _Target Definition Editor_ +- Update the Software Sites in the opened _Target Definition Editor_ - Alternative A - Switch to the _Source_ tab and add the following snippet to the editor ```xml diff --git a/tutorials/application_fx.png b/tutorials/application_fx.png new file mode 100644 index 0000000000000000000000000000000000000000..38f97926f0da25fad4d6919ca7f620c584b787e6 GIT binary patch literal 17034 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 ap8R8P*0(_`7Z4-`E?+Xfn1BA(!~X}5&Qsg~ literal 0 HcmV?d00001 diff --git a/tutorials/efxclipse_preferences.png b/tutorials/efxclipse_preferences.png new file mode 100644 index 0000000000000000000000000000000000000000..6bd91adfdd7508e11ee58805959af741568a093f GIT binary patch literal 33978 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