JavaFx Integration & Utilities

A JavaFx wrapper to allow using JavaFX in jmonkey - with an appstate to easily control scenes. Support for loading fxml files from the resources directory or coding your own is also added to simplify the whole process. Also included is a PropertyPanel - exactly the same as in Lemur.

just attach a JavaFxGuiState to the state manager and set a scene using the state.

// at some point
stateManager.attach(new JavaFxGuiState());

getState(JavaFxGuiState.class).setScene(myCreatedScene);

An example of a GUI created in code.

public class FxmlCodeLoadTest extends JfxCodeGui {

    public FxmlCodeLoadTest(Application app) {
        super(app);

        Label label = new Label("Label");
        ((Group)getScene().getRoot()).getChildren().add(label);
    }

    @Override protected void initialize(Application app) { }
    @Override protected void cleanup(Application app) { }
    @Override protected void onEnable() { }
    @Override protected void onDisable() { }
    
}

An example of loading an fxml file from the resources folder.

public class FxmlLoadTest extends JfxFxmlGui {

    public FxmlLoadTest(Application app) {
        super(app, "/javafx/test.fxml");
    }

    @Override protected void initialize(Application app) { }
    @Override protected void cleanup(Application app) { }
    @Override protected void onEnable() { }
    @Override protected void onDisable() { }
    
}

https://github.com/jayfella/JavaFxUtils

5 Likes

What kind of resource consumption do you see with a JavaFX UI? I’ve looked into using the JME-JFX integration but always shied away due to: (a) maintainability concerns, and (b) the potential for JavaFX to cause frame stutters as well as various frame rate synchronization issues that I’ve encountered on various experiments in this area.

Right now I’m developing a custom UI widget set on top of Lemur’s lower-level classes (input, focus, events, etc.). It’s pretty far along (and pretty powerful, if I do say so myself) so I’m not really looking to change directions - but I’d love to hear some resounding success stories with JME<->JFX integration. Future options are always great to have, and I’d hate to see the integration work that’s been done fall by the wayside.

I had good experience with javaFX when I workend on my game:

5 Likes

I guess it depends on how complex your gui is for the footprint. I haven’t done any tests on memory if I’m honest.

My workflow is leaning toward JavaFX for most 2D GUIs - almost everything in the GuiNode - almost. I can see use cases where I’d still use lemur or straight up jme objects.

In the 3D node - the root node - I almost exclusively use Lemur or JME-native controls (shader progress bars, etc) because to me it makes sense.

In my opinion both Jfx and lemur have their place, and it’s not a case of one vs the other, but is more of a if and when.

For example lemur doesn’t have a builder app. It can be slow and slightly frustrating to build a gui programmatically. With Jfx I can use scene builder and css and it’s quick to develop. Lemur doesn’t have easy to use scroll capabilities or text wrap - and that can make things difficult in a 2D environment. So for large areas of text it can be challenging to work with. With jfx it’s just regular workflow.

But in a 3D environment lemur really shines. I would definitely gear toward lemur there because those restrictions I mentioned aren’t really there anymore. Everything is jme-based, works just as you’d expect and I get the benefit of game-based effects, shaders, real-time animations and all the goodies.

So it’s not a showdown. It’s just another tool in the shed to make life easier :slight_smile:

1 Like

Well, color me interested. :smiley:

I’m having a shot at trying this out on my project (Java 11). Java 11 is somewhat problematic because it doesn’t include the JavaFX runtime in the JDK and my project is currently non-modular, but I’ve managed to work around that (fortunately, OpenJDK has decent support for this scenario!). However… following the examples you’ve given above (and in the repo), I’m getting a fatal error about the toolkit not being initialized (I am attaching my FX scene in the onEnable() method in another app state). This also occurs in the example provided in the repo on Java 11.

Ok, three things:

  1. I was able to resolve the “Toolkit not initialized” error by creating a JFXPanel immediately before loading an FXML scene:
new JFXPanel();
getStateManager().getState(JavaFxGuiState.class).attachScene(new FxmlLoadTest(app));

It’s inelegant, and worse, it adds an unneeded dependency on the JavaFX Swing module. Is there a way we can work around this, or is there an internal setup call inside @javasabr’s integration that I missed?

  1. The call to attachScene() above crashes with an NPE at: com.jayfella.jfxutils.JavaFxGuiState.attachScene(JavaFxGuiState.java:45) which is container.setScene(attachedScene.getScene((Group)attachedScene.getScene().getRoot());.

I’m not at all sure what to make of that yet - the attachedScene is the the GUI object passed above, so that’s not (directly) the origin of the NPE. That class is a verbatim copy of @jayfella’s FxmlLoadTest above.

My apologies for the sudden dump of rather poorly written trouble reports. I thought JavaFX support had fallen by the wayside, and I’m very happy to see this going somewhere again. I’d be happy to help out however I can (and time allows) with resolving some of these early-adoption issues. It would be great to see this be a bit more of a readily accessible tool for jME users. :smile:

The appstate has to load first. In the example main class I just use a throw-away appstate to let the engine “tick over” once before I attach a scene.

There should be no need to create a jfx panel or anything else. I guess the above information should solve everything. Does running the TestJavaFx main class work for you?

https://github.com/jayfella/JavaFxUtils/blob/master/src/main/java/com/jayfella/jfxutils/TestJavaFx.java#L41

Aha, so that’s what that TickState was for… I thought waiting until after the initialization would be enough, but clearly not.

No, the test main class fails with the following stack trace:

java.lang.IllegalAccessError: class com.jme3.jfx.injme.JmeFxContainerImpl (in unnamed module @0x1a5b6f42) cannot access class com.sun.javafx.application.PlatformImpl (in module javafx.graphics) because module javafx.graphics does not export com.sun.javafx.application to unnamed module @0x1a5b6f42
        at com.jme3.jfx.injme.JmeFxContainerImpl.initFx(JmeFxContainerImpl.java:674)
        at com.jme3.jfx.injme.JmeFxContainerImpl.<init>(JmeFxContainerImpl.java:313)
        at com.jme3.jfx.injme.JmeFxContainerImpl.install(JmeFxContainerImpl.java:85)
        at com.jme3.jfx.injme.JmeFxContainerImpl.install(JmeFxContainerImpl.java:70)
        at com.jme3.jfx.injme.JmeFxContainer.install(JmeFxContainer.java:26)
        at com.jayfella.jfxutils.JavaFxGuiState.initialize(JavaFxGuiState.java:20)
        at com.jme3.app.state.BaseAppState.initialize(BaseAppState.java:124)
        at com.jme3.app.state.AppStateManager.initializePending(AppStateManager.java:267)
        at com.jme3.app.state.AppStateManager.update(AppStateManager.java:297)
        at com.jme3.app.SimpleApplication.update(SimpleApplication.java:253)
        at com.jme3.system.lwjgl.LwjglWindow.runLoop(LwjglWindow.java:506)
        at com.jme3.system.lwjgl.LwjglWindow.run(LwjglWindow.java:589)
        at com.jme3.system.lwjgl.LwjglWindow.create(LwjglWindow.java:427)
        at com.jme3.app.LegacyApplication.start(LegacyApplication.java:463)
        at com.jme3.app.LegacyApplication.start(LegacyApplication.java:424)
        at com.jme3.app.SimpleApplication.start(SimpleApplication.java:125)
        at com.jayfella.jfxutils.TestJavaFx.main(TestJavaFx.java:22)

This is running on Linux on OpenJDK 11.

Hmm. I think that’s because it’s supposed to run on java 10. I think from 11 and onward javafx became a separate module and I guess the implementation changed with it.

I run java 10.0.2 myself. I mean I get what java is doing with all this versioning and phasing but it is a little frustrating for the end user.

Yeah, I like the direction Oracle is headed with all this, but until the dust settles from the Java 9 Jigsaw project this is all going to continue to be a bit annoying.

JavaFX 11 was the first version to be spun off from the JDK into a separate project under independent development, and they seem to have changed quite a bit of the modularization. The issue may very well be in how I have the module paths configured - I had to do a bit of hacking on the grade file to get things running. I’ll take a look at it as soon as I get a chance.

Ok, finally have some time to investigate this further. Both JDK 10 & 11 fail with the stack trace above (package is not exported to unnamed module). Here’s the command I’m running to launch this:

java --add-modules javafx.controls,javafx.fxml,javafx.graphics,javafx.swing
--module-path *path*/javafx-graphics-11-linux.jar:/*path*/javafx-swing-11-linux.jar:/*path*/javafx-fxml-11-linux.jar:/*path*/javafx-controls-11-linux.jar:/*path*/javafx-base-11-linux.jar -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -cp /*path*/JavaFxUtils-1.0-SNAPSHOT.jar:(misc unrelated jars):/*path*/javafx-fxml-11-mac.jar:/*path*/javafx-graphics-11-linux.jar:/*path*/javafx-graphics-11.jar:/*path*/javafx-base-11-mac.jar:/*path*/javafx-swing-11-linux.jar:/*path*/jfx-2.0.0.jar:/*path*/javafx-graphics-11-win.jar:/*path*/lwjgl-openal-3.2.1-natives-macos.jar:/*path*/lwjgl-jemalloc-3.2.1.jar:/*path*/lwjgl-jemalloc-3.2.1-natives-windows.jar:/*path*/javafx-base-11.jar:/*path*/lwjgl-3.2.1-natives-macos.jar:/*path*/javafx-fxml-11-linux.jar:/*path*/javafx-controls-11-mac.jar:/*path*/javafx-swing-11-win.jar:/*path*/javafx-swing-11.jar:/*path*/javafx-base-11-win.jar:/*path*/javafx-fxml-11-win.jar:/*path*/javafx-graphics-11-mac.jar:/*path*/javafx-controls-11-linux.jar:/*path*/javafx-base-11-linux.jar:/*path*/javafx-fxml-11.jar:/*path*/javafx-controls-11.jar:/*path*/javafx-controls-11-win.jar:/*path*/javafx-swing-11-mac.jar 
my.main.class 
--add-opens javafx.graphics/com.sun.javafx.embed=ALL-UNNAMED
--add-opens javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED
--add-opens javafx.graphics/com.sun.javafx.cursor=ALL-UNNAMED
--add-opens javafx.graphics/com.sun.glass.ui=ALL-UNNAMED
--add-opens javafx.graphics/com.sun.javafx.application=ALL-UNNAMED

The --add-opens is the most permissive way to open modules to the unnamed module, and the PlatformImpl class that the access is failing on is in the com.sun.javafx.application package.

Does anyone have any ideas why this might be failing?

Just got a successful run out of TestJavaFX! Thanks to @jayfella & @javasabr for all your hard work on this. Looking forward to experimenting more with this. As I do, I’ll keep notes on any issues I encounter and post them back here for others.

It turns out that Java is very order-sensitive about the way things are specified on the command line (I thought it would collect all arguments and after processing them proceed to launch the program. That’s not the case.). Running the command as I’ve shown above (which is what Gradle’s JavaExec task produced fails. However, manually rewriting it to place the --add-opens parameters immediately after the --module-path parameter resolves the module access issue.

(Sorry for the string of posts - I hope that this will be useful to the next person who’s starting out with this.)

2 Likes

Glad you got it working :slight_smile:

I’ve added support for dialogs and will probably be updating the repository quite a lot over the coming weeks. When it’s slowed down a bit I’ll probably push it on jcenter to make life easier.

// show a regular scene created from fxml or code...
boolean darken = true;
getState(JavaFxGuiState.class).showDialog(new DialogTest(app),  darken );

// and to close
getState(JavaFxGuiState.class).removeDialog();

1 Like