Powering a Live Escape Game with ECF & e(fx)clipse

My talk about “Powering a Live Escape Game with ECF & e(fx)clipse” got selected as an early bird for the EclipseCon Europe 2016! I’m very excited about this.

Actually, this whole thing got started exactly two years ago at the EclipseCon Europe 2014. I was there to learn about new technologies in my area of work as an RCP/JavaEE engineer and I remember that I had some free time in my schedule and stumbled over a talk by Wim Jongman How to cook an egg with the Eclipse Communication Framework and Nebula. That sounded like fun, so I went and had a look. I was immediately and completely hooked and ordered my first Raspberry Pi on my phone the same day. What really caught my interest was that the components could use ECF to remotely talk to each other without needing a server! And even better: With ECF discovery they would be able to find each other dynamically!! And all of that by simply coding OSGi services!!!

As soon I got home I started playing with this stuff but soon got bored because I didn’t have a real world project to work on. Around that time one of my oldest friends, Mike Kleist, built his first Live Escape Game in Interlaken, Switzerland: The Secret of the Captain. If you have never played a live escape game you should definitely check it out. It is a lot of fun! It’s played live (meaning you have to be physically present). You will be locked in a mysterious room full of puzzles, riddles, keys and locks where logic and creativity are the tools you need to escape.

Mike wanted to build another game in Munich and we started to talk about how we could create a technically enhanced version of this concept with lasers, hidden doors and a “bomb disarming simulation” you need to solve by cutting the right cables just like 007 in the movies. Mike had lots of ideas how to use this new technology for his game so we started to get to work. There would be multiple gadgets with different sensors and actors and there would also be an application to show the status of the game to the players.

bombe-simulation
Prototype of the bomb disarming simulation

Now, when you are dealing with any kind of creative process you really need to be agile. You literally need to be able to change the software sitting on the floor with your laptop two hours past midnight while the paint on the walls is still getting applied and the first players are scheduled to arrive in the morning. Using the ECF implementation of OSGi remote services proved to be the perfect technology for this task and during my talk I will demonstrate why.

Mike also wanted to have a display to show the status of the “bomb disarming simulation” to the players. Of course this should look really cool und make use of animations and sound effects. Using JavaFX as the UI technology for this task is pretty much a no-brainer because you get all of this out of the box and you even get a visual designer (Scene Builder) to create the look of the UI together with your artist. Now all I needed to do was to get this to work together with ECF, with proved to be equally easy using e(fx)clipse since they are both based on OSGi.

Bomb_Timer
You need to solve all the riddles before the time is up!

And because everything worked together so beautifully, we also created a e4 application for the operator of the game, so he can see the status of all the sensors and actors and check that everything is working as it should. And of course the operator can remote control all the actors in the event of something not working as it should, for example if a sensor fails or a message gets lost in the network or whatever.

Operator
Game operator application

Et voilà! A new live escape game was born: The Missing Agent.

I want to take this opportunity to thank
Wim Jongman for the inspiration
Scott Lewis (ECF project lead) for the great support on the ecf mailing list
– and Tom Schindl (efxclipse project lead) for his equaly great support on the e(fx)clipse forum.

It’s people like this who make open source development so great and so much fun!
Hope to see you all at the EclipseCon Europe 2016!

Customizing the e4 main window with e(fx)clipse

In e4 the main user interface is constructed (rendered) based on the e4 application model. If you want to change the way that a specific model element is represented in the ui, you can plug in a custom renderer for this type. This approach is very powerful, but can be a technical challenge to get working the way you want it to. If you only want to make minor adjustments to the main window, like adding a search field to the menu bar, there is now an easier way with the e(fx)clipse 2.2.0 default window renderer using FXML.

Starting Point

The starting point for this introduction will be a small demo app I developed to show case this feature. The application model only has one part but uses every possible window trim location to place some buttons.

2015-11-27_133710

Yes, it’s ugly … but don’t worry … that will get even worse! 😉

2015-11-27_120129

The full demo application is available as part of the demos in the e(fx)clipse Git repository. To run it you need to check out all the org.eclipse.fx.demo.customWindow.* projects.

Customize the locations of window ui elements

Setup of the FXML

If we want be able to control the layout of the window, we need to tell the renderer where to put the children of the window. This is done using specific IDs that the renderer will look for after loading (checking the property id, not fx:id).

ID UI Elements Default Location
client-area children (sashes, stacks, part, …) center of the main BorderPane
menu-bar-area main menu bar top of the main BorderPane
top-trim-area top window trim bar (toolbar, tool control) top of the client area
bottom-trim-area bottom window trim bar (toolbar, tool control) bottom of the client area
left-trim-area left window trim bar (toolbar, tool control) left of the client area
right-trim-area right window trim bar (toolbar, tool control) right of the client area

The root element of the FXML has to be a JavaFX BorderPane or a org.eclipse.fx.ui.controls.stage.TrimmedWindow. In most cases using the BorderPane will be what you want to do. You only need to use a TrimmedWindow if you also need control over the window decorations (like the min/max/close buttons on the top).

Hook the FXML to the window

It is very easy to hook an FXML to a window in the application model. All you need to to is set the property efx.window.root.fxml in the persisted state of the window. The value is an URL pointing to the FXML you want to use.

2015-11-27_121821

Examples

Insert a search field into the menu bar

This FXML will insert a TextField in the top row next to the main menu bar. The menu bar will be placed inside the HBox with id="menu-bar-area". The rest of the windows elements (trim bars, client area) will be placed by default. In this example I left out the properties maxHeight and maxWidth which I usually set to Double.MAX_VALUE for all containers, so you can focus on the structure of the window.

<BorderPane fx:controller="org.eclipse.fx.demo.customWindow.app.controller.MainFrameController">
	<top>
		<VBox>
			<children>
				<HBox>
					<children>
						<HBox id="menu-bar-area" />
						<TextField fx:id="searchField" promptText="Search..." />
					</children>
				</HBox>
			</children>
		</VBox>
	</top>
</BorderPane>

As you can see in this screen shot, we now have a text input field next to the menu bar:

2015-11-27_120350

To use the new search field in my application I can inject it into the controller I defined with fx:controller in the FXML. Here I use the e(fx)clipse EventBus to forward a search request, but there are lots of ways on how to proceed from here. Notice that it is possible to use dependency injection to get hold of all the e4 and OSGi services in the controller!

public class MainFrameController implements Initializable {

    @Inject private EventBus eventBus;

    @FXML TextField searchField;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        searchField.textProperty().addListener((observable, oldValue, newValue) -> {
            eventBus.publish(new Topic<String>("app/event/search"), newValue, true);
        });
    }
}

Go crazy

In the full demo I placed every trim bar on the outside of the window so I can insert a “cool” separation bar between the client area and the trim bars (yes … the blue bars with the light blue circles … I warned you it would get ugly!). I also thought it would be totally awesome to place the tool bar on top of the menu bar just to … well … show thats it’s possible, I guess. 😉

2015-11-26_132047

Please have a look at the complete FXML. I also put the “client area” in there, although I could have skipped this, since it would be placed in the center of the main BorderPane by default anyway.

Using SceneBuilder

You might notice that I sometimes used minWidth and minHeight at some places in the FXML. I also gave the root BorderPane a prefWidth and prefHeight. I did this, so that the pane visually resembles the final outcome if you open it with SceneBuilder. This is not really necessary for the correct rendering and can look broken, if you plan to remove trims at runtime, since the container will then leave an empty space where the trim can be. If you omit the minimums the container will shrink to 0 and will therefore become invisible when the trim is removed.

2015-11-27_123325

Extending the JavaFX ComboBox and ChoiceBox

This article is about adding a small feature to the JavaFX ComboBox and ChoiceBox: We want to give the user the possibility to quickly select a specific entry in large lists by using the keyboard.

Introduction

We are migrating a fairly large government application from an in-house ui toolkit based on wxWidgets to JavaFX. One of the first things our users complained about was that it wasn’t possible anymore to select an entry from a choice box or combo box by using the keyboard. Our users know the part of the application the use daily very well and are used to working mostly by keyboard. Every grip to the mouse is annoying. Since this was possible on the native Windows combo box control we need to find a way to make it work with JavaFX.

The user interface is defined in fxml (or at least it will be in the future – at the time of writing we are using an xsl transformation to convert the wxML design to fxml on the fly). We didn’t want to introduce new ComboBox and ChoiceBox classes for this small feature which could very well be part of the framework soon (see JDK-8092270). So we opted to write a customizer which searches the widget tree of a freshly loaded fxml searching for instances of ComboBox and ChoiceBox to install the “prefix selection feature”. (See Walking the JavaFX Scene Graph on how we search the widget tree.)

Prefix Selection

For the rest of the document I will only talk about the “combo box” omitting the “uneditable” and the “choice box” for brevity. Please remember that the combo box needs to be uneditable for this discussion to be applicable and that everything is also applicable to a choice box.

Specification

What we want to do is enable the user to type letters or digits on the keyboard while the combo box has the focus. The combo box will then attempt to select the first item it can find with a matching prefix ignoring case.

Let’s look at an example to clarify this. The combo box offers the items [“Aaaaa”, “Abbbb”, “Abccc”, “Abcdd”, “Abcde”]. The user now types “abc” in quick succession (and then stops typing). The combo box will select a new entry on every key pressed. The first entry it will select is “Aaaaa” since it is the first entry that starts with an “a” (case ignored). It will then select “Abbbb” since this is the first entry that started with “ab” and will finally settle for “Abccc”.

    Keys typed Final element selected
    a Aaaaa
    ab Abbbb
    abc Abccc
    aaa Aaaaa
    abx Abbbb
    xyz

Implementation

The feature is implemented in a separate class that can handle both the ComboBox as well as the ChoiceBox. Let’s call it PrefixSelectionCustomizer and give it static methods to extend already existing combo box instances.

At the time of writing the source is available on my fork of the ControlsFX repository.

To install the feature we add an event handler to the combo box so we get notified on every key the user presses. We then need to check if the user has already typed before so we can add the new letter or digit to the saved prefix selection sequence. Afterwards we can the traverse the item list of the combo box and compare the included items with our sequence. At this stage we need to take care to use the converter installed in the combo box so that we compare to what the user actually sees on the screen. If we find an item we tell the combo box to select it.

Here the part from the implementation that records the prefix selection sequence and tries to find a fitting item:

private <T> T getEntryWithKey(String letter, StringConverter<T> converter, ObservableList<T> items, Control control) {
	T result = null;

	// The converter is null by default for the ChoiceBox. The ComboBox has a default converter
	if (converter == null) {
		converter = new StringConverter<T>() {
			@Override
			public String toString(T t) {
				return t == null ? null : t.toString();
			}

			@Override
			public T fromString(String string) {
				return null;
			}
		};
	}

	String selectionPrefixString = (String) control.getProperties().get(SELECTION_PREFIX_STRING);
	if (selectionPrefixString == null) {
		selectionPrefixString = letter.toUpperCase();
	} else {
		selectionPrefixString += letter.toUpperCase();
	}
	control.getProperties().put(SELECTION_PREFIX_STRING, selectionPrefixString);

	for (T item : items) {
		String string = converter.toString(item);
		if (string != null && string.toUpperCase().startsWith(selectionPrefixString)) {
			result = item;
			break;
		}
	}

	return result;
}

Sometimes the user might not find the item he or she was looking for or wants to start fresh since he or she mistyped. We wanted to implement this so it behaves similar to the Windows combo box. In Windows, if you wait a short amount of time the recorded prefix will be cleared. To achieve this we submit an appropriate task to a scheduled executor. If a new key is pressed we can cancel a possibly present task and create a new one.

ScheduledFuture<?> task = (ScheduledFuture<?>) control.getProperties().get(SELECTION_PREFIX_TASK);
if (task != null) {
    task.cancel(false);
}
// The method `getExecutorService()` returns an instance of `ScheduledExecutorService`
task = getExecutorService().schedule(
        () -> control.getProperties().put(SELECTION_PREFIX_STRING, ""), 500, TimeUnit.MILLISECONDS); 
control.getProperties().put(SELECTION_PREFIX_TASK, task);

After a few tests we settled for half of a second (500 milliseconds) delay before we clear the prefix selection sequence. I am very interested in how this works for you, so please let me know what you think.

Using it

From a developers point of view the usage is very simple. All you need to do is call the appropriate PrefixSelectionCustomizer#customize method and pass your combo box or choice box.

The only thing of note is that the feature should only be installed for a ComboBox when the combo box is not editable.

public static void customize(ComboBox<?> comboBox) {
    if (!comboBox.isEditable()) {
        comboBox.addEventHandler(KeyEvent.KEY_PRESSED, handler);
    }
    comboBox.editableProperty().addListener((o, oV, nV) -> {
        if (!nV) {
            comboBox.addEventHandler(KeyEvent.KEY_PRESSED, handler);
        } else {
            comboBox.removeEventHandler(KeyEvent.KEY_PRESSED, handler);
        }
    });
}

public static void customize(ChoiceBox<?> choiceBox) {
    choiceBox.addEventHandler(KeyEvent.KEY_PRESSED, handler);
}

Or you can use extensions of ComboBox or ChoiceBox which are simply implemented by calling the PrefixSelectionCustomizer.

public class PrefixSelectionChoiceBox<T> extends ChoiceBox<T> {
    public PrefixSelectionChoiceBox() {
        PrefixSelectionCustomizer.customize(this);
    }
}

public class PrefixSelectionComboBox<T> extends ComboBox<T> {
    public PrefixSelectionComboBox() {
        setEditable(false);
        PrefixSelectionCustomizer.customize(this);
    }
}

Conclusion

From a testers point of view this feature seems to work very nicely, but I don’t have any real users playing with this yet, so there might be some problems to sort out in the future when we set it loose.

I would very much like to see this feature in JavaFX. Following a comment on JDK-8092270 by Jonathan I contributed this feature to ControlsFX in pull request #496, which has just been merged. Please try it and join in on the discussion!

Filtering a JavaFX TreeView

In this article I will demonstrate how to filter data in a JavaFX TreeView using a FilteredList.

Introduction

To populate a TreeView with data it is necessary to create a hierarchy of tree items and set the root TreeItem for the TreeView. To filter our data we could directly manipulate TreeItem#getChildren but that would be cumbersome. Especially since JavaFX already provides classes that can help: To filter an OberservableList you can use a FilteredList and set a Predicate on the list to control the filter. So our goal is to use this class together with TreeItem.

We also want to be able to set the predicate only on the root tree item, so that we can perform the filtering in a single place. Usually we also don’t want to hide folders that still have children even if they wouldn’t match the predicate. Ideally we would like the tree item to automatically check whether it has any children and only apply the predicate when it is a leaf.

Implementation

The complete implementation can be viewed here.

The Filterable Tree Item

To start lets marry TreeItem and FilteredList in a new subclass: FilterableTreeItem. In the constructor we will create a FilteredList using a new observable array list as its source. Since overriding getChildren will not work properly in Java 8 (see JDK-8089158), we need to use a hack to set the private field TreeItem#children
to this filtered list instead of the original source. Since a FilteredList is unmodifiable we will provide a new method to access the source list directly: getInternalChildren().

public class FilterableTreeItem<T> extends TreeItem<T> {
    final private ObservableList<TreeItem<T>> sourceList;
    private FilteredList<TreeItem<T>> filteredList;

    public FilterableTreeItem(T value) {
        super(value);
        this.sourceList = FXCollections.observableArrayList();
        this.filteredList = new FilteredList<>(this.sourceList);
        setHiddenFieldChildren(this.filteredList); 
    }

    protected void setHiddenFieldChildren(ObservableList<TreeItem<T>> list) {
        try {
            Field childrenField = TreeItem.class.getDeclaredField("children"); //$NON-NLS-1$
            childrenField.setAccessible(true);
            childrenField.set(this, list);

            Field declaredField = TreeItem.class.getDeclaredField("childrenListener"); //$NON-NLS-1$
            declaredField.setAccessible(true);
            list.addListener((ListChangeListener<? super TreeItem<T>>) declaredField.get(this));
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            throw new RuntimeException("Could not set TreeItem.children", e); //$NON-NLS-1$
        }
    }

    public ObservableList<TreeItem<T>> getInternalChildren() {
        return this.sourceList;
    }
}

Edit: The implementation details have changed over time. We don’t use reflection anymore but Bindings.bindContent instead. I recommend you don’t use the code above but to use the current version that is included in efxclipse. The full name of the class is org.eclipse.fx.ui.controls.tree.FilterableTreeItem

The Predicate

Sometimes you might want to adjust the filter depending on the hierarchy you are in. In this case it would be nice to have the current TreeItem available when evaluating Predicate#test. So let’s define a new interface and add a convenience method to convert a classic Predicate:

@FunctionalInterface
public interface TreeItemPredicate<T> {

	boolean test(TreeItem<T> parent, T value);

	static <T> TreeItemPredicate<T> create(Predicate<T> predicate) {
		return (parent, value) -> predicate.test(value);
	}

}

To use this interface in FilterableTreeItem we define an ObjectPropery to set the TreeItemPredicate. We then need to install a binding that sets the predicate property of FilteredList.

public class FilterableTreeItem<T> extends TreeItem<T> {
    private FilteredList<TreeItem<T>> filteredList;

    private ObjectProperty<TreeItemPredicate<T>> predicate = new SimpleObjectProperty<>();

    public FilterableTreeItem(T value) {
        super(value);
        this.sourceList = FXCollections.observableArrayList();
        this.filteredList = new FilteredList<>(this.sourceList);
        this.filteredList.predicateProperty().bind(Bindings.createObjectBinding(() -> {
            return child -> {
                // Set the predicate of child items to force filtering
                if (child instanceof FilterableTreeItem) {
                    FilterableTreeItem<T> filterableChild = (FilterableTreeItem<T>) child;
                    filterableChild.setPredicate(this.predicate.get());
                }
                // If there is no predicate, keep this tree item
                if (this.predicate.get() == null)
                    return true;
                // If there are children, keep this tree item
                if (child.getChildren().size() > 0)
                    return true;
                // Otherwise ask the TreeItemPredicate
                return this.predicate.get().test(this, child.getValue());
            };
        }, this.predicate));
        setHiddenFieldChildren(this.filteredList);
    }

    ...
}

Example

The usage of FilterableTreeItem is pretty straight forward. Let’s assume we are using the data type Actor as a base type for the TreeView.

class Actor {
    public String firstname;
    public String lastname;

    public Actor(String string) {
        this.lastname = string;
    }

    public Actor(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }

    @Override
    public String toString() {
        return firstname == null ? lastname : firstname + " " + lastname;
    }
}

We want to filter the displayed actor using a TextField as input.

FilterableTreeItem<Actor> root = getTreeModel();

root.predicateProperty().bind(Bindings.createObjectBinding(() -> {
    if (filterField.getText() == null || filterField.getText().isEmpty())
        return null;
    return TreeItemPredicate.create(actor -> actor.toString().contains(filterField.getText()));
}, filterField.textProperty()));

TreeView<Actor> treeView = new TreeView<>(root);

The complete sample Application can be viewed here and looks like this:
2015-05-15_132450

Conclusion

A marriage between TreeItem and FilteredList could be implemented fairly nicely if it would be possible to customize the list implementation used in TreeItem without resorting the the evil reflective hack described above (thanks to Jeanette and J. Duke for pointing me to this solution). I hope that a possibility to customize the list implementation will be introduced in the future as I have suggested in RT-40790/JDK-8091687.

These classes were contributed to the e(fx)clipse open source framework in the bundle org.eclipse.fx.ui.controls so you can use this feature, the very similar SortableTreeItem and other cool stuff provided there.

Migrate E4 RCP from SWT to JavaFX using SWTonJavaFX

In this blog I would like to demonstrate how to migrate an Eclipse4 RCP application from SWT to JavaFX using SWTonJavaFX and how to deal with some of the problems that might arise in a real world application.

Introduction

The goal of this investigation is to migrate the project as quickly and simply as possible. Most importantly, we only want to make changes to the code where absolutely necessary. This basically means that we don’t want to change any of our SWT code (parts, wizards, dialogs, etc.)! To achieve this we will integrate SWTonJavaFX, so we can keep on using the SWT API and migrate to JavaFX on demand.

I set up a small test project de.emsw.e4.migration to simulate some of the concepts used in our larger E4-SWT applications. This is what it looks like:
SWT screenshot

You can clone the git repository and have a look at the code changes. The SWT code will be in branch master and the JavaFX code will be in the branch migration.

Dirk Fauth has recently published a migration tutorial about the basic steps, which will serve as a basis for this investigation. Please note however, that Dirk uses efxclipse 1.2.0 whereas I am using efxclipse 2.0.0. There are some small but important differences!

Target Platform

I will not go into the setup a new target platform, because Dirk has already described a good way to get there in his article. Dirk uses the url to efxclipse 1.2.0, so you will need to change that. 2.0.0 has not been released at the time of writing, but you can use the nightly build site http://download.eclipse.org/efxclipse/runtime-nightly/site.

E(fx)clipse Runtime

Since I wanted to work with the current sources of e(fx)clipse 2.0.0 (and maybe contribute some code while I am into this investigation) I cloned the e(fx)clipse repo and imported the runtime bundles I needed from bundles/runtime/* into the workspace. At our company, we use a manually managed target platform, so we don’t work directly with the provided e(fx)clipse runtime feature anyway.

Note: Since we are using e(fx)clipse 2.0 we don’t need the bundles org.eclipse.fx.osgi and org.eclipse.fx.javafx anymore. For more information why please refer to Tom Schindls post on the forum.

SWTonJavaFX

The bundles for SWTonJavaFX are also in the e(fx)cipse git repository (experimental/swt/*) and can also be imported into the workspace. You need these projects:

  • org.eclipse.fx.runtime.swt
  • org.eclipse.fx.runtime.swt.compat (== org.eclipse.swt)
  • org.eclipse.fx.runtime.swt.e4

The project org.eclipse.fx.runtime.swt contains the actual code implementing the SWT API with JavaFX. The project to watch out for is org.eclipse.fx.runtime.swt.compat, since this is the project that mimics org.eclipse.swt. So you will only want to have either the original bundle org.eclispe.swt or org.eclipse.fx.runtime.swt.compat in your target platform. The bundle org.eclipse.fx.runtime.swt.e4 will register components supporting the migration from SWT.

A side note concerning org.eclipse.fx.runtime.swt.compat: Since I imported it as a project into the workspace, it will automatically be used instead of the original org.eclispe.swt from the target platform definition. If I want to use the original SWT library I only need to close org.eclipse.fx.runtime.swt.compat. This approach can lead to some difficulties though, so if you want to be 100% safe, you should remove org.eclipse.swt from your target platform.

Basic Migration

Now that we have all the libraries we need, we can migrate the actual project. Dirks migration tutorial is a big help, so I will only summarize the effects here. You can also look at the git commit to see all the changes.

Product Definition

The first thing we need to do is change the application entry point in the plugin.xml by changing the attribute application of <product> to:

org.eclipse.fx.ui.workbench.fx.application

(While you are here you can drop the property applicationCSS. This will not be evaluated by efxclipse.)

The application entry point must be changed accordingly in the product configuration (e4migration.product in my test project):

org.eclipse.fx.ui.workbench.fx.application

Bundles

I used a plug-in based product configuration for this demo project, so it is easy to see which bundles need to be replaced.

What we don’t need any more from E4-SWT:

org.eclipse.e4.ui.bindings
org.eclipse.e4.ui.css.core
org.eclipse.e4.ui.css.swt
org.eclipse.e4.ui.css.swt.theme
org.eclipse.e4.ui.widgets
org.eclipse.e4.ui.workbench.addons.swt
org.eclipse.e4.ui.workbench.renderers.swt
org.eclipse.e4.ui.workbench.swt
org.eclipse.e4.ui.workbench3

Additional bundles from e(fx)clipse (including SWTonJavaFX):

org.eclipse.fx.core
org.eclipse.fx.core.databinding
org.eclipse.fx.core.di
org.eclipse.fx.core.di.context
org.eclipse.fx.core.fxml
org.eclipse.fx.osgi.util
org.eclipse.fx.runtime.swt
org.eclipse.fx.runtime.swt.e4
org.eclipse.fx.ui.animation
org.eclipse.fx.ui.controls
org.eclipse.fx.ui.di
org.eclipse.fx.ui.dialogs
org.eclipse.fx.ui.keybindings
org.eclipse.fx.ui.keybindings.e4
org.eclipse.fx.ui.keybindings.generic
org.eclipse.fx.ui.panes
org.eclipse.fx.ui.services
org.eclipse.fx.ui.theme
org.eclipse.fx.ui.workbench.base
org.eclipse.fx.ui.workbench.fx
org.eclipse.fx.ui.workbench.renderers.base
org.eclipse.fx.ui.workbench.renderers.fx
org.eclipse.fx.ui.workbench.services

Because we are using SWTonJavaFX we also don’t need the swt platform fragments, so in my test project for windows I could also get rid of org.eclipse.swt.win32.win32.x86. Remember that we are keeping org.eclipse.swt though, because it is mimicked by org.eclipse.fx.runtime.swt.compat.

At the time of writing, we can also get rid of some dependencies that came with E4-SWT like org.apache.batik.* and org.w3c.*. The only 3rd party dependency that comes with e(fx)clipse at the moment is org.apache.commons.lang.

Launch Configuration

To use JavaFX, you need to tell Equinox to use the extension class loader by adding

-Dorg.osgi.framework.bundle.parent=ext

to the vm arguments. (Note that this differs from efxclipse 1.x where it used to be osgi.framework.extensions=org.eclipse.fx.osgi.)

This is a very important step, otherwise you will see errors about equinox not being able to load javafx.* classes:

java.lang.NoClassDefFoundError: javafx/application/Application
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at org.eclipse.osgi.internal.loader.ModuleClassLoader.defineClass(ModuleClassLoader.java:272)
    ...
Caused by: java.lang.ClassNotFoundException: javafx.application.Application cannot be found by org.eclipse.fx.ui.workbench.fx_2.0.0.qualifier
    at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423)

MANIFEST.MF

In my case I didn’t have to make any changes to the MANIFEST.MF at this stage. This is due to the fact that I have only defined plug-in dependencies that are actually needed by the compiler. Everything else you need is added directly to the feature definition (or product definition, like in the test project).

If you are working on your own migration you need to check this and throw out any of the replaced e4 bundles mentioned above.

Note that, with e(fx)clipse 2.0, you should not add javafx.* to the imported packages, as this will be taken care of by using the extension class loader.

Application Model

To migrate the application model (TestApp.e4xmi in my test project) we follow Dirks migration tutorial and swap the two add-ons

  1. org.eclipse.e4.ui.bindings.BindingServiceAddon
  2. org.eclipse.e4.ui.workbench.swt.util.BindingProcessingAddon

with

  1. org.eclipse.fx.ui.keybindings.e4.BindingServiceAddon
  2. org.eclipse.fx.ui.keybindings.e4.BindingProcessingAddon

Frequently Encountered Problems

In this chapter I will explain some other small changes that you might need to make to your code, depending on whether or not you used certain features.

Injection of the active Shell

When it comes to dependency injection org.eclipse.fx.runtime.swt.e4 is your friend. It registers context functions which will supply a Composite or a Display instance if requested. So most of the DI stuff already works as you would expect.

One exception is IServiceConstants.ACTIVE_SHELL. We use this key in our E4-SWT projects all the time in handlers when we want to open a dialog or wizard. To demonstrate, here is the simplified handler in my test project:

public class DialogHandler {
    @Execute
    public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell shell) {
        SampleTitleAreaDialog dialog = new SampleTitleAreaDialog(shell);
        dialog.open();
    }
}

The problem here is that IServiceConstants.ACTIVE_SHELL is (despite its SWT-like naming) meant to be independent of the ui framework being used. The DI container will inject an object of the type that the ui framework considers to be its equivalent to Shell. Using JavaFX this will be a Stage.

The consequence of this is that our handler will compile correctly, but will never execute, because there is no Shell available to inject, only a Stage. What we want to have is a Shell wrapper around this Stage, so we can pass it as a parent in our constructor. To get this we need to change the key from IServiceConstants.ACTIVE_SHELL to SWTServiceConstants.ACTIVE_SHELL (you will need a dependency on org.eclipse.fx.runtime.swt.e4 to use this). A context function in will take care of supplying a wrapper Shell around the current Stage.

The migrated version of the handler looks almost the same:

public class DialogHandler {
    @Execute
    public void execute(@Named(SWTServiceConstants.ACTIVE_SHELL) Shell shell) {
        SampleTitleAreaDialog dialog = new SampleTitleAreaDialog(shell);
        dialog.open();
    }
}

Modifying a Widget

There might be some cases where you are doing something to an SWT widget after is has been created by the renderer. A simple example of this is the MaximizeAddon in my test project. The add-on registers an EventHandler which waits until the first MWindow has been created and then gets calls getWidget() on it to retrieve the Shell:

MWindow windowModel = (MWindow) objElement;
Shell theShell = (Shell) windowModel.getWidget();
if (theShell == null)
    return;
theShell.setMaximized(true);

Although this will compile, we will get a ClassCastException at runtime. The simple reason being that the renderer doesn’t return a Shell anymore.

Since we are down to the internals of the renderer, we need to change this code. The efxclipse renderer adds another level of abstraction (WWindow), so we need to perform two steps until we reach the Stage:

MWindow windowModel = (MWindow) objElement;
WWindow<?> windowImpl = (WWindow<?>) windowModel.getWidget();
Stage theStage = (Stage) windowImpl.getWidget();
if (theStage == null)
    return;
theStage.setMaximized(true);

You will need to add a dependency on org.eclipse.fx.ui.workbench.renderers.base to get this to compile.

Menu Mnemonics

A small difference between SWT und JavaFX is how you define a mnemonic key for menus and menu items. Where SWT uses the “&” character JavaFX expects you to use the underscore “_” instead. Hence, if you use “&” to set mnemonics with JavaFX, you will see the “&” turn up in the labels of your menu and the mnemonic will not work.

The direct solution here is to change the labels directly in the e4xmi or properties file where your menu labels are defined. If you want to have a fast hook to change the labels at runtime you could supply your own TranslationService on top of the BundleTranslationProvider. I used the life cycle handler to push my custom implementation into the application context:

@ProcessAdditions
public void processAdditions(MApplication application) {
    TranslationService translationProvider = new BundleTranslationProvider() {
        @Override
        public String translate(String key, String contributorURI) {
            String result = super.translate(key, contributorURI);
            return result.replace('&', '_');
        }
    };
    application.getContext().set(TranslationService.class, translationProvider);
}

Toolbars and ToolControls

Toolbars might look “cut off”, especially if you are using a single Toolbar in a Window Trim. The toolbar will be expanded by adding the tag fillspace to the tags of the Toolbar.
2015-03-30_110019

A tool control might also not layout as you would like. This could be because the renderer uses the JavaFX Group as a container by default. You can configure the tool control to use a HBox instead which will behave nicer if resized. This is done by a tag to the ToolControl in the application model: Container:HBox. (Don’t forget to add the fillspace tag too.)

Utilizing e(fx)clipse Features

Now that our application is running, let’s see how we can get more out of e(fx)clipse.

Add a Theme

A common reason to migrate to JavaFX is to have more control over the visual appearance of the application. Efxclipse supports theme based CSS styling. Tom Schindl described the motives for implementing themes with OSGi services in this article.

If you want to view the whole change right away, you can have a look at the commit.

Adding a Theme as a Service

The first thing we need is an implementation of the interface Theme which can be easily done by extending AbstractTheme (you need to add dependencies to the bundles org.eclipse.fx.ui.services and org.eclipse.fx.ui.theme):

public class DefaultTheme extends AbstractTheme {
    public DefaultTheme() {
        super("default", "Default Theme", DefaultTheme.class.getClassLoader().getResource("css/default.css"));
    }
}

This needs to be setup as an OSGi service using a component definition (for example in OSGI-INF/osgi-DefaultTheme-component.xml):

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="de.emsw.e4.migration.theme.default">
   <implementation class="de.emsw.e4.migration.DefaultTheme"/>
   <service>
      <provide interface="org.eclipse.fx.ui.services.theme.Theme"/>
   </service>
   <reference 
     bind="registerStylesheet" 
     cardinality="0..n" 
     interface="org.eclipse.fx.ui.services.theme.Stylesheet" 
     name="Stylesheet" 
     policy="dynamic" 
     unbind="unregisterStylesheet"/>
</scr:component>

Don’t forget to add the new file to the build.properties and to include it in MANIFEST.MF:

Service-Component: OSGI-INF/osgi-DefaultTheme-component.xml

By the way: The applicationCSS property of the product extension point is not evaluated and can be removed, if you havn’t done so already.

Modifying the CSS File

In my case, I already have the file css/default.css (which is loaded by the DefaultTheme we created before) in my project. Until now it contained css for the SWT renderer. Although this is not valid anymore, we can still reuse the file itself, so let’s open it. To edit CSS for JavaFX you should use the e(fx)clipse Css Editor.
2015-04-01_113955
(If you are asked if you want to add the “Xtest nature” to your project: Say “Yes”.)

If there are already contents in your file, you can delete it all and start from scratch. To demonstrate, let’s set up a simple dark theme as described here:

.root {
    -fx-base: rgb(50, 50, 50);
    -fx-background: rgb(50, 50, 50);
    -fx-control-inner-background:  rgb(50, 50, 50);
}

Aborting the Startup

The test application starts up with a login dialog requesting a username and a password. The original SWT code uses `System.exit(0)’ to abort the startup if the user canceled this operation. The code in the life cycle handler basically looks like this:

@PostContextCreate
public void postContextCreate(IEclipseContext context) {
    LoginService loginService = ContextInjectionFactory.make(LoginService.class, context);
    boolean result = loginService.login();
    if (!result) {
        System.exit(0);
    }
}

I have always considered this to be “unkind” to the platform, so I was happy to see that e(fx)clipse offers a “soft” way to abort the startup by returning a value of LifecycleRV from @PostContextCreate:

@PostContextCreate
public LifecycleRV postContextCreate(IEclipseContext context) {
    LoginService loginService = ContextInjectionFactory.make(LoginService.class, context);
    boolean result = loginService.login();
    if (!result) {
        return LifecycleRV.SHUTDOWN;
    }
    return LifecycleRV.CONTINUE;
}

You can also use this to restart the application after an update with LifecycleRV.RESTART or LifecycleRV.RESTART_CLEAR_STATE if you also want to reset the application model. This and some other new features you get with e(fx)clipse are described on the wiki.

Conclusion

So this is what my test application looks like with JavaFX:
JavaFX screenshot

Of course there is still some work to do, but I hope that this information will help you get started with your own migration.

If you run into problems or missing API with SWTonJavaFX please refer to this discussion about the current status of SWTonJavaFX on the e(fx)clipse forum.

If you have any questions please feel free to leave a comment.

Walking the JavaFX Scene Graph

Update

I am keeping this post up for historical reasons. But I warn you: It’s incomplete and might be deceptive!

Why? Because when I tested this, I only created a scene graph with a root node, but I never added the root to a Scene or showed the scene in a Stage. If you do this then everything changes. Most importantly: Parent.getChildrenUnmodifiable will return nodes that are created by the skin!

For example, the MenuBar will not return an emtpy list, but will return the HBox container used in the skin. The same is true for a ToolBar. The TabPane will return a TabPaneSkin$TabContentRegion node for every open Tab. And a TitledPane will give you TitledPaneSkin$1 and TitledPaneSkin$TitleRegion. A regular node like Button (which is also derived from Parent) will return the Text node used when you display a text (and probably the ImageViewif you show an image, but I haven’t tested this).

You can use the practice below if you have just loaded an FXML scene graph and have not yet added the result to a Scene (as is the case in our project).

So … continue at your own risk 😉

Walking the Scene Graph

In this post I want to explore how to walk through a JavaFX scene graph. Usually you will need this kind of processing, when you are searching for a specific node in your scene, i.e. with some userData or with a certain id.

If you look for information on this topic, you will find articles (i.e. Working with the JavaFX Scene Graph) which basically tell you, that:

  • Node ist the abstract base class for all scene graph nodes.
  • Parent is the abstract base class for all branch nodes.

Looking at the API you quickly find Parent.getChildrenUnmodifiable which will return a list of children of this Parent as a read-only list. This is a good starting point to implement the visitor pattern with something like this:

parent.getChildrenUnmodifiable().forEach(node -> visitor.visit(node));

The Tab – a Node or not?

When you look more closely at the members of the scene graph you discover that there is a class that does not follow this paradigm: The Tab. A Tab can be added to a TabPane which itself extends Parent but does not include the tabs in its children. One simple reason being, that the Tab is not derived from Node, but directly inherits from Object. They are related to some extent, as they share the Styleable interface with some common properties like id, style, styleClass and they also both offer getUserData and setUserData methods.

Since the Tab can have a Node as content it makes sense to visit this content when we are looking for some node deep down in an unknown hierarchie.

The MenuItem

Depending on your use case, you might also want to visit menu items when walking through the scene graph. In JavaFX menus can be used in two ways:

  1. A menu can be added to a MenuBar, which is itself an ancestor of Parent and can therefore be added anywhere in the scene
  2. Menu items can be added as a ContextMenu to any Control

The class MenuItem shares the same similarities to Node as the class Tab (see above) and it also directly extends Object, so you won’t find menu items or menus in the children list.

One Adapter to use them all

So let’s implement an adapter to cover these base classes and implement our own version of getChildrenUnmodifiable().

public class NodeAdapter {

    public static NodeAdapter adapt(Object fx) {
        if (fx instanceof Node) {
            return new NodeAdapter((Node)fx);
        } else if (fx instanceof MenuItem) {
            return new NodeAdapter((MenuItem)fx);
        } else if (fx instanceof Tab) {
            return new NodeAdapter((Tab)fx);
        }
        return null;
    }

    private Object fxObject;

    public NodeAdapter(Node node) {
        this.fxObject = node;
    }

    public NodeAdapter(MenuItem menuItem) {
        this.fxObject = menuItem;
    }

    public NodeAdapter(Tab tab) {
        this.fxObject = tab;
    }


    public List<NodeAdapter> getChildrenUnmodifiable() {
        ...
    }

    public void accept(INodeVisitor visitor) {
        boolean result = visitor.visit(this);
        if (!result)
            return;
        getChildrenUnmodifiable().forEach(nodeAdapter -> nodeAdapter.accept(visitor));
    }

}

This adapter can also be used to have a single entry point for things like using the idProperty or calling getUserData(). The source code with some implemented delegation methods can be found here.

Other exceptions to the rule

There are some classes that are ancestors of Parent but don’t expose their subnodes (panes or items) in getChildrenUnmodifiable:

  • Accordion => Accordion.getPanes()
  • SplitPane => SplitPane.getItems()
  • ToolBar => ToolBar.getItems()
  • ButtonBar => ButtonBar.getButtons()

And we also want to cover nodes that can only have one child as content when we walk the graph:

  • TitledPane => TitledPane.getContent()
  • ScrollPane => ScrollPane.getContent()

So the final method looks like this:

public List<NodeAdapter> getChildrenUnmodifiable() {
    List<NodeAdapter> result = new ArrayList<NodeAdapter>();

    // primary parent type derived from the root type
    if (fxObject instanceof Parent) {
        ((Parent)fxObject).getChildrenUnmodifiable().forEach(node -> result.add(new NodeAdapter(node)));
    } else if (fxObject instanceof Menu) {
        ((Menu)fxObject).getItems().forEach(item -> result.add(new NodeAdapter(item)));
    } else if (fxObject instanceof Tab) {
        Node content = ((Tab)fxObject).getContent();
        if (content != null)
            result.add(new NodeAdapter(content));
    } 

    // extended parent types
    if (fxObject instanceof MenuBar) {
        ((MenuBar)fxObject).getMenus().forEach(menu -> result.add(new NodeAdapter(menu)));
    } else if (fxObject instanceof TabPane) {
        ((TabPane)fxObject).getTabs().forEach(tab -> result.add(new NodeAdapter(tab)));
    } else if (fxObject instanceof TitledPane) {
        Node content = ((TitledPane)fxObject).getContent();
        if (content != null)
            result.add(new NodeAdapter(content));
    } else if (fxObject instanceof ScrollPane) {
        Node content = ((ScrollPane)fxObject).getContent();
        if (content != null)
            result.add(new NodeAdapter(content));
    } else if (fxObject instanceof Accordion) {
        ((Accordion)fxObject).getPanes().forEach(pane -> result.add(new NodeAdapter(pane)));
    } else if (fxObject instanceof SplitPane) {
        ((SplitPane)fxObject).getItems().forEach(item -> result.add(new NodeAdapter(item)));
    } else if (fxObject instanceof ToolBar) {
        ((ToolBar)fxObject).getItems().forEach(item -> result.add(new NodeAdapter(item)));
    } else if (fxObject instanceof ButtonBar) {
        ((ButtonBar)fxObject).getButtons().forEach(button -> result.add(new NodeAdapter(button)));
    }

    // context menu
    if (fxObject instanceof Control) {
        ContextMenu contextMenu = ((Control)fxObject).getContextMenu();
        if (contextMenu != null) {
            contextMenu.getItems().forEach(item -> result.add(new NodeAdapter(item)));
        }
    }

    return Collections.unmodifiableList(result);
}

Conclusion

This approach is obviously volatile. It depends on implementation details of JavaFX that might change. It also might need to be updated when new parent types are introduced. I’m not even sure I covered all of the possible angles! So, for most cases, I would not advise to use this in production level code, but to try to find a different approach to solve the problem. But of course, it might come in handy from time to time …

For anyone looking to extend this, here is the JUnit test code used to check the implementation based on 8u40: TestNodeAdapter