Hi again! This time a new version that is, as promised, JavaFX 11 compatible.

Keep on reading for details.

JMetro 8.5.7 and 11.5.7 versions

JMetro code has been divided into 2 branches. The master branch holds the Java 8 compatible JMetro version and “11” branch has the Java 11 compatible version. Versions starting with “11.” will be for Java 11 and versions starting with “8.” will for Java 8.

“11” branch is being tested against JavaFX11 but probably should work fine with JavaFX 9 up to the upcoming JavaFX 13.

All the CSS from previous JMetro versions could be used with JavaFX11 the difference here is that now the new skins referenced through CSS also work.

The difficulty here with the migration is that the code has changed significantly from JavaFX8 to JavaFX9 with control skins becoming public and their code changing. Also the introduction of modules and its use in JavaFX9+ framework code, made accessing internal classes used by skins, like “Behavior” classes, impossible (not impossible under some circumstances, but still undesirable).

All skins have been converted. They are:

  • Button Skin

Adds a spring button press animation on Toggle Buttons and regular Buttons: https://pixelduke.com/2018/08/27/fluent-design-button-toggle-button-tooltip/

  • TextField Skin and PasswordField Skin

Adds a button to the right of Textfield that allows you to clear the text, and for PasswordField the button will allow you to see the password in plain text: https://pixelduke.com/2019/01/20/jmetro-version-5-2-released/ , https://pixelduke.com/2018/09/09/fluent-design-style-text-field-and-password-field-for-java-javafx/

  • ProgressBar Skin

Sets an appearance for the indeterminate ProgressBar, equal to the Fluent Design specification indeterminate ProgressBar: https://pixelduke.com/2018/09/30/fluent-design-style-progress-bar-for-java-javafx/

  • Slider Skin

Adds a fill to the Slider. Something that I think is a glaring omission right now in the JavaFX Slider. And adds a popup that shows up to tell the user the exact value that’s being set on the Slider while the thumb is being dragged: https://pixelduke.com/2018/08/19/fluent-design-slider-java-javafx/

  • Toggle Switch Skin

A new Skin for the Toggle Switch control I’ve created for ControlsFX. It adds some features and fixes: https://pixelduke.com/2018/08/12/fluent-design-toggle-switch-java-javafx/

New features in 8.5.7 and 11.5.7

For developers familiar with the previous JMetro versions, the API has changed.

The constructor for the JMetro class now takes in a Scene and a Style or a Parent and a Style.

Then the following properties were added:

  • Style

Either DARK or LIGHT style appearance.

  • Scene

The Scene that will have the JMetro theme applied.

  • Parent

The Parent that will have the JMetro theme applied. If this property is set and a Scene has also been set previously, changes to the JMetro instance will start to apply to the new specified Parent and not the Scene. In case the Scene is set after a Parent has been set the reverse will happen.

  • AutomaticallyColorPanes

If true, all Panes (e.g. BorderPane, AnchorPane, StackPane, Pane, etc) will automatically have their background color set. If the style is DARK the background will be dark (like black), if the style is LIGHT the background will be light (like white). This has the disadvantage that if you have custom controls that have Panes as intermediate children, you’ll usually need to redefine their background to transparent or else you might get whitish/blackish background patches in your custom controls. Alternatively, if this property is set to false (the default), you can add the style class .background to the Panes that are supposed to be in the background of your application. They will then automatically change their background color according to the Style property value.

  • getOverridingStylesheets

This is actually more of an ObservableList that you can add stylesheets to. This stylesheets will be added after the stylesheets that make up the JMetro theme definition. This means that the stylesheets added to this list will override the definitions set by the JMetro theme in case their specificity is the same or higher. As such, this is a good place to redefine and add your own custom styles that override the JMetro ones.

Other changes

Other than the changes mentioned above. A couple more fixes have been made to existing JMetro styles. All JMetro samples have been migrated to JavaFX11 in the “11” branch. Finally the code has been cleaned and things like the CSS code that still existed for JavaFX2 has been removed.

Wrapping up

As promised a JavaFX9+ version of JMetro is now also available . I was trying to post-pone this as I still have Java8 clients that happen to be using JMetro and maintaining 2 versions can be a pain. As such the Java 8 version in the master branch will still be maintained for the time being.

The API has also been cleaned and I think now it is cleaner and better. New properties have also been added to the JMetro class that allow to further customize JMetro.

4 thoughts on “New JMetro JavaFX 11 compatible version

  1. Hi,
    Using JavaFX on Java 14. Trying to create a TextField:

    java.lang.NullPointerException
    at javafx.controls/javafx.scene.control.skin.TextFieldSkin.lambda$new$5(TextFieldSkin.java:270)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:348)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.base/javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:106)
    at javafx.base/javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:113)
    at javafx.base/javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
    at javafx.graphics/javafx.css.StyleableObjectProperty.set(StyleableObjectProperty.java:82)
    at javafx.controls/javafx.scene.control.TextInputControl$3.set(TextInputControl.java:234)
    at javafx.controls/javafx.scene.control.TextInputControl$3.set(TextInputControl.java:203)
    at javafx.graphics/javafx.css.StyleableObjectProperty.applyStyle(StyleableObjectProperty.java:68)
    at javafx.controls/javafx.scene.control.TextInputControl$3.applyStyle(TextInputControl.java:218)
    at javafx.controls/javafx.scene.control.TextInputControl$3.applyStyle(TextInputControl.java:203)
    at javafx.graphics/javafx.scene.CssStyleHelper.transitionToState(CssStyleHelper.java:787)
    at javafx.graphics/javafx.scene.Node.doProcessCSS(Node.java:9660)
    at javafx.graphics/javafx.scene.Node$1.doProcessCSS(Node.java:472)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSSImpl(NodeHelper.java:192)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.superProcessCSSImpl(ParentHelper.java:93)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.superProcessCSS(ParentHelper.java:63)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1369)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSSImpl(ControlHelper.java:63)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSS(ControlHelper.java:55)
    at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:886)
    at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSSImpl(ControlHelper.java:63)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSS(ControlHelper.java:55)
    at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:886)
    at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSSImpl(ControlHelper.java:63)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSS(ControlHelper.java:55)
    at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:886)
    at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSSImpl(ControlHelper.java:63)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSS(ControlHelper.java:55)
    at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:886)
    at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSSImpl(ControlHelper.java:63)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSS(ControlHelper.java:55)
    at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:886)
    at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
    at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
    at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
    at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
    at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
    at javafx.graphics/javafx.scene.Node.processCSS(Node.java:9542)
    at javafx.graphics/javafx.scene.Scene.doCSSPass(Scene.java:569)
    at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2505)
    at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:412)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:411)
    at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:438)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:563)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:543)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:536)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:342)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Thread.java:832)

    Thanks for the fix4

    • Hi!

      I made a minimal example – bottom line: while using JMetro alone the problem is not reproducable.

      The problem apper if used together with TornadoFX + Kotlin.

      The problem can be worked around, if the application is started the official way, not the TornadoFX way. After this I am not so confident, that the problem is coming from JMetro, however, TornadoFX is a quite popular library, so it may worth it to investigate.

      Should I submit the issue still?

      David

      • Thanks for clearing things up, David.

        I have come up with workarounds before for TornadoFX users who come up with bugs that originated in TornadoFX. However I don’t feel this is the way to go, if TornadoFX is having issues somehow (or any other library) then it should be TornadoFX that fixes it. It is the root of the bug.

        It’s not sustainable to have all the other libraries come up with workarounds for a specific bug in one library. Rather it is much more reasonable to have that library, that has the bug, fix it.

        Having said that, I think the answer would be to file the bug in the TornadoFX issue tracker.

        Thanks,

Leave a Reply to p_duke Cancel reply

Your email address will not be published. Required fields are marked *