Introduction

I’ve been using Kotlin for a while now, and I’ve been using it to write JavaFX applications. What I’ve found is that the things that make Kotlin better than Java, make Kotlin way, way better for JavaFX.

If you’re new to Kotlin, you might want to read my Kotlin for Java Programmers article.

Let’s get one thing out of the way first…

Do You Need TornadoFX?

No.

A lot of people are under the impression that TornadoFX is the implementation of JavaFX in Kotlin, and that you need it in order to use JavaFX with Kotlin.

This is simply not true. TornadoFX is an interesting library that does some cool things, but it’s not any kind of replacement for the JavaFX library.

So, what is TornadoFX?

More than anything else, it’s a wrapper for the standard JavaFX library that attempts to make using JavaFX less verbose and more “Kotlin-like”. It tackles the verbosity by supplying generic “builders” that make use of extension functions on Node classes to enable a more declarative style of configuration for layouts. It also incorporates tools to use an MVC framework, also in a manner to make it as less verbose as possible.

And it’s very good at doing this. But this approach has a couple of issues:

Repeated Configurations

If you spend any amount of time programming with JavaFX, you’re going to find that there are certain patterns of configuration and styling that you use over and over and over. And, if you’re using style sheets to handle your styling (as you should), then you’re going to be using most of those styles in the same way, over and over and over.

Want you really want to avoid is repeating that code over and over and over. Apply DRY (Don’t Repeat Yourself), and put that code in a library that you can call. Any such library that you create is going to be highly personalized to yourself or your team.

TornadoFX, is all about making it easier to write that code inside your layout. In many cases your layout code becomes composed of configuring builders that will generate your layout. The builders are nicely designed to streamline the code, but don’t move you towards DRY. You can probably do it, but it moves your task from writing builders for Nodes to writing builders for TornadoFX builders.

Configuration and Styling in the Layout

Let’s take a look at an example from the TornadoFX documentation, this shows a call to one of their builders:

tableview<Person> {
    items = persons
    column("ID", Person::idProperty)
    column("Name", Person::nameProperty)
    column("Birthday", Person::birthdayProperty)
    readonlyColumn("Age", Person::age).cellFormat {
        text = it.toString()
        style {
            if (it < 18) {
                backgroundColor += c("#8b0000")
                textFill = Color.WHITE
            }
        }
    }
}

This would become your layout code with TornadoFX. You can see how this strips away a lot of the boilerplate that you’d need with a pure JavaFX implementation, and the result absolutely does do more with less.

But that “Age” column.

Yes, the customization of the Cell is way more straightforward than in pure JavaFX, but it’s still a clear violation of the “Single Responsibility Principle” - it should be delegated.

But the design of TornadoFX encourages you to put the configuration and styling in with the layout.

So, the question is whether or not the streamlining of the code compensates for the complication of having the configuration and styling mingled in with the layout.

Should You Use TornadoFX?

If you’re following the ideas in this website - building Reactive layouts with MVCI and sticking to the “Single Responsibility Principle” and DRY - there’s probably not a lot of benefit to using TornadoFX.

TornadoFX tries to deal with the complexity by simplifying the integration of the configuration into the layout code. What it doesn’t do is remove the configuration from your layouts, which, in my opinion, is a better approach.

Kotlin JavaFX Techniques

There are a number of cool features in Kotlin that make JavaFX a lot easier to implement, and some features that mean you need to take a look at how you should build things in Kotlin. Let take a look at some of these…

The .apply{} Function

This is the star in Kotlin for JavaFX. When you call Object.apply{} it passes the object to the lambda in the {} as this, and apply{} returns the original object. This is especially useful in the pattern val x = Node().apply{}.

Let’s say you’ve got a Label that you want have display some data the screen and bound to a Property in your model. You have a style class set up for, too.

In Java:

Label label = new Label();
label.getStyleClass().add("data-label");
label.textProperty().bind(model.someDataProperty());
hbox.getChildren().add(label);

But in Kotlin, you can use .apply{} and avoid instantiating label altogether:

hbox.children += Label().apply{
  styleClass += "data-label"
  textProperty().bind(model.someData)
}

(Note that, just like with a class in Java, you can skip the this. when referring to member fields and functions)

Now, that might not seem like much, but it does lead to things like this:

hBox = HBox(10.0,
  Label("Name: ").apply{styleClass += "prompt-label"},
  Label().apply{
    styleClass += "data-label"
    textProperty().bind(model.someData)
  })

This can get a bit garbled (something to watch out for with .apply{}), so you could try:

hBox = HBox(10.0).apply{
  children += Label("Name: ").apply{
    styleClass += "prompt-label"
  }
  children += Label().apply{
    styleClass += "data-label"
    textProperty().bind(model.someData)
  }
}

Perhaps even more useful is that you create functions using the same technique:

hBox = HBox(10.0).apply{
  children += listOf(Label("Name: ").apply{styleClass += "prompt-label"}, dataLabel(model.someData))
  styleClass += "red-border-box"
}

fun dataLabel(boundValue : ObservableStringValue) = Label().apply{
  styleClass += "data-label"
  textProperty().bind(boundValue)
}

JavaFX Property Fields

Kotlin uses the term “property” to refer to what would be called “fields” in Java. Kotlin properties are actually more complicated structures that have integrated getters and setters (which can be customized) which are automatically called when you access the property. This means that you can directly reference the property from outside the class without worrying about tying down your internal implementation.

Of course, in JavaFX a Property is specific kind of ObservableValue, which is quite different from a Kotlin property. Additionally, there is the concept of a Property Bean, which is a particular structure exposing the Property outside its containing class.

A Property Bean has three methods in its containing class. For a Property called fred you would have the following methods:

  1. A getter for the Property value called getFred().
  2. A setter for the Property value called setFred().
  3. A getter for the property itself called fredProperty()

In Kotlin, you can’t just write the getFred() and setFred() methods because they would conflict with the internal getter and setter for fred. So you have to do something a little bit different. I wrote an entire article about this a while back.

Here’s a sample program from the original article, showing how this is done.

And the output looks like this:

Nickname: Shorty
Property: ObjectProperty [value: Shorty]

That gets the job done, and it allows you direct access to the values of the Property via Class.fred.

The JavaFX Property Bean is the accepted way to implement Properties in a Model, but…

Do you really need Property Beans?

This naming convention is primarily designed to facilitate those tools that use reflection in the class PropertyReference to access fields via a String name. In the standard JavaFX library, this class is used in the following places: PropertyValueFactory, TreeViewPropertyValueFactory and variants of Bindings.select(). The first two should probably be deprecated, because you shouldn’t use them any more now that we have lambdas and writing the equivalent Callback is trivial.

The last case is about 14 methods in the Bindings class that, “Create a binding used to get a member, such as a.b.c.”, according to the JavaDocs. Personally, I have never used Bindings.select(), and I suspect that it’s primarily intended when you want to pass around Property names as Strings and then directly use those names for binding.

Of course, if you’re using external JavaFX libraries, they may require that you use the Property Bean format because they are using something that incorporates PropertyReference.

But…if you’re not doing any of those things, you can ditch the Property Bean. Just make your Property property a val you’re done.

Personally, I’ve stopped habitually using the Property Bean pattern because it just doesn’t add any value for me.

Extension Functions, Package Functions and Operator Overloading

The next few features go together produce some really powerful techniques which are the core tools you can use to compress your layout code.

Extension Functions

One of the coolest features of Kotlin is the ability to extend a class without creating a new class. With this technique you can add both methods and data to an existing class without having access to the source of that class.

For instance, you can do something like this:

fun Int.plusThree() : Int = this + 3
   .
   .
   .
   val x: Int = 7
println("The answer: ${x.plusThree()}")   

This can be amazingly valuable with JavaFX, where there are a lot of classes that you use all the time where it would be handy to have methods that do something with a single call that normally take a few steps.

Extension functions can also be used as decorators using .apply{}:

private fun createContent(): Region = BorderPane().apply {
   val model: StringProperty = SimpleStringProperty("Albert")
   padding = Insets(20.0)
   center = HBox(10.0, Label().bound(model), Label("George"))
               .styled("test-border3")
               .padded(10.0)
               .aligned(Pos.CENTER)
}
}

fun <T : Node> T.styled(newStyleClass: String): T = apply { styleClass += newStyleClass }

fun <T : Region> T.padded(padSize: Double): T = apply { padding = Insets(padSize) }

fun <T : Labeled> T.bound(otherProperty: ObservableValue<String>): T = apply { textProperty().bind(otherProperty) }

fun HBox.aligned(pos: Pos): HBox = apply { alignment = pos }

For this, however, it’s important to remember that extension functions are evaluated statically. This means that if you want a decorator to return the original type for any of the subclasses of a type you need to use the <T : Class> T. declaration. In this example, the setAlignment() method is included at the HBox class itself, so you can declare the decorator directly at HBox; getStyleClass() is defined at the Node level, so <T: Node> T is used; and setPadding() is defined at the Region level, so <T: Region> T is used.

Package Functions

Another cool feature in Kotlin is “Package Functions”. These are methods that aren’t part of any specific class, they’re just functions attached to a package. In this way they are very similar to static methods in Java, but you don’t have the baggage of having to call them with ClassName.staticMethod() format. This makes a dramatic difference in how the resulting code looks:

fun labelOf(boundValue : StringProperty) : Label = Label().apply {textProperty().bind(boundValue)}
   .
   .
   .
val hbox = HBox(10.0, Label("Name:"), labelOf(model.nameProperty))

Operator Overloading

Operator Overloading is another nifty idea that Kotlin brings to the table which can simplify your layout code. The idea is that a specific set of operators are automatically associated with specific functions if they are defined in a class as operator. So the “+” operator is associated with class.plus() if it exists.

But here’s the cool part: The operator functions can be extension functions!

This means that you can add operator overloads to any of the JavaFX classes. Let’s see how this works. To me, the “+=” operator seems like a good fit to represent binding on a Property. Here’s how you would do that:

operator fun StringProperty.plusAssign(otherProperty: StringProperty) = this.bind(otherProperty)
   .
   .
   .
val model : StringProperty = SimpleStringProperty("ABC")
val label = Label()
label.textProperty() += model

Or, if you prefer…

operator fun Labeled.plusAssign(otherProperty: StringProperty) = this.textProperty().bind(otherProperty)
   .
   .
   .
val model : StringProperty = SimpleStringProperty("ABC")
val label = Label()
label += model

Infix Functions

If a function of a class has a single parameter, then you can declare it as Infix. This means that you can drop the “.” and the brackets when making the call. The result is something that looks very declarative. This is especially true if you implement decorator functions as infix:


infix fun <T : Labeled> T.styledAs(labelStyle: String) = apply { styleClass += labelStyle }

infix fun <T : Labeled> T.boundTo(value: ObservableStringValue) = apply { textProperty().bind(value) }

fun testIt(): Unit {
   val fred: StringProperty = SimpleStringProperty("abc")
   val label1 = Label() styledAs "label-prompt" boundTo fred
   val label3 = Label().styledAs(LabelStyle.PROMPT).boundTo(fred)
}

The first two methods are declared as infix, but you don’t have to use them that way. These are all also decorator style methods that return the enclosing object. That means that you can string them together. The first calling example shows how this works, and the second shows it without the infix calling style. The two versions are functionally equivalent.

You have to be a little bit careful with the order of operations with infix, but chaining the decorators together like this works nicely.

Putting it All Together

If you’re looking to create a standard toolkit that you can use over a variety of projects, then your best approach is to create Kotlin files in their own package to house your functions. You can call it FxExtentions.kt or ExtensionsFX.kt or WidgetsFX.kt. You don’t need to have a class of the same name, you can just drop all of your package functions and extension functions in there as you like. You can even add some custom classes, Kotlin’s cool with that.

This will get all this stuff right out of your layout code, and out of your mind - where you don’t want it.

My approach is to create a complementary CSS file to go with the code and then write the various utility functions to work with that CSS file.

Naming becomes very important, and you have to think about how the code will read using the names that picked. This could also depend on how you intend to use the utilities. Consider this:

val label = Label() styledAs LabelStyle.DATA boundTo model.someDataProperty

Here the decorators have all been name as past-tense verbs, essentially declaring their effects. I think this reads nicely as a description of the Node; it’s a Label styled as data and bound to a property.

However, you could use this:

val label = Label() styleAs LabelStyle.DATA bindTo model.someProperty

Now the decorators read as commands. This version is more declarative, and not descriptive.

But, if you don’t like the infix usage, then you would have:

val label = Label.styledAs(LabelStyle.DATA).boundTo(model.someDataProperty)

or…

val label = Label.styleAs(LabelStyle.DATA).bindTo(model.someDataProperty)

Since we’re used to traditional function call being named as actions, the second version feels more natural. But both ways could possibly work.

Utility Library Files

With these techniques you can create a toolkit that will make your Kotlin layout code look much more streamlined and declarative than Java code. The trick is to find all the “pain points” in your layout code, the places that you get bogged down in details, and move them into your library of functions.

Here’s what I came up with in just a few minutes. First, WidgetFX.kt:

package ca.pragmaticcoding.widgetsfx

fun Scene.addWidgetStyles() = apply {
   object {}::class.java.getResource("widgetsfx.css")?.toString()?.let { stylesheets += it }
}

fun <T : Parent> T.addWidgetStyles() = apply {
   object {}::class.java.getResource("widgetsfx.css")?.toString()?.let { stylesheets += it }
}

enum class TestStyle(val selector: String) {
   BLUE("test-blue"), RED("test-red"), GREEN("test-green")
}

infix fun <T : Node> T.testStyleAs(nodeStyle: TestStyle) = apply { styleClass += nodeStyle.selector }

infix fun <T : Region> T.padWith(padSize: Double): T = apply { padding = Insets(padSize) }

infix fun <T : Node> T.addStyle(newStyleClass: String): T = apply { styleClass += newStyleClass }

fun textFieldOf(value: StringProperty) = TextField().apply { textProperty().bind(value) }

infix fun TextField.bindTo(value: StringProperty) = apply { textProperty().bind(value) }

fun buttonOf(text: String, handler: EventHandler<ActionEvent>) = Button(text) addAction handler

operator fun Pane.plusAssign(newChild: Node) {
   children += newChild
}

infix fun HBox.alignTo(pos: Pos): HBox = apply { alignment = pos }

infix fun <T : ButtonBase> T.addAction(eventHandler: EventHandler<ActionEvent>): T = apply { onAction = eventHandler }

This is just a grab-bag of functions and utilities that aren’t necessarily related to each other.

There are decorators to add the widgetsfx.css stylesheet to either a Scene or a Parent. Since these are declared at a package level, there’s no class to provide getResource() to find the stylesheet. Instead the function declares an anonymous Object that will have the correct package which can provide the getResource() method.

Region.padWith() is super useful. It gets absolutely tedious writing setPadding(new Insets(10)) over and over and over. The vast majority of the time, you just want all of the sides to have the same padding so a function that just takes a single Double parameter makes a huge difference.

and then Labels.kt

package ca.pragmaticcoding.widgetsfx

enum class LabelStyle(val selector: String) {
   PROMPT("label-prompt"), HEADING("label-heading")
}

infix fun <T : Labeled> T.styleAs(labelStyle: LabelStyle) = apply { styleClass += labelStyle.selector }

infix fun <T : Labeled> T.bindTo(value: ObservableStringValue) = apply { textProperty().bind(value) }

fun promptOf(value: ObservableStringValue) = Label() styleAs LabelStyle.PROMPT bindTo value
fun promptOf(value: String) = Label(value) styleAs LabelStyle.PROMPT

fun headingOf(value: String) = Label() styleAs LabelStyle.HEADING

operator fun Labeled.plusAssign(otherProperty: StringProperty) = run { textProperty() += otherProperty }

For this file, I’ve grouped together all of the functions that deal with Labels strictly as an organizational tool.

In both of these files, I’ve declared an Enum to abstract the CSS selector classes so that they don’t need to be known outside the utility files.

Obviously, these functions require a complementary style sheet to work with:

.root {
   -theme-colour: #113969;
   -contrast-colour: #C6522F;
   -complementary1: #43AA8B;
   -complementary2: #B4CDED;
   -complementary3: #80A4ED
}


.test-blue {
  -fx-border-color: blue;
  -fx-background-color : lightcyan;
}

.test-red {
  -fx-border-color: firebrick;
  -fx-background-color : lavenderblush;
}

.test-green {
  -fx-border-color: green;
  -fx-background-color : derive(yellowgreen, +50%);
}

.label-prompt {
  -fx-text-fill: -theme-colour;
  -fx-font-weight: bold;
  -fx-font-size: 15px;
}

.label-heading {
  -fx-text-fill: -contrast-colour;
  -fx-font-weight: bold;
  -fx-font-size: 32px;
}

Using It

Good layout code should make it easy for any reader to understand at a glance how a layout works. This means keeping the configuration out of the way, as this is what causes confusion. But how you use these library functions can make a big difference to readability.

For instance, this is hard to read (horrible, actually):

private fun createContent(): Region = BorderPane().apply {
   top = headingOf("Test Screen")
   center = VBox(20.0).apply {
      children += HBox(10.0).apply {
         children += Label("Name") styleAs LabelStyle.PROMPT
         children += TextField() bindTo nameProperty
      } padWith 10.0 alignTo Pos.CENTER_LEFT
      children += Button("Click Me") addAction { buttonAction() }
   }
} testStyleAs TestStyle.BLUE padWith 20.0

and this is maybe a bit easier, but still too difficult:

private fun createContent(): Region = (BorderPane() testStyleAs TestStyle.BLUE padWith 20.0).apply {
    top = headingOf("Test Screen")
    center = VBox(20.0,
                  HBox(10.0,
                       Label("Name") styleAs LabelStyle.PROMPT,
                       TextField() bindTo nameProperty) padWith 10.0 alignTo Pos.CENTER_LEFT,
                  Button("Click Me") addAction { buttonAction() })
 }

but specific builders work best, so this is probably clear enough:

private fun createContent(): Region = BorderPane().apply {
   top = headingOf("Test Screen")
   center = VBox(20.0, createNameRow(), buttonOf("Click Me") { buttonAction() })
   this testStyleAs TestStyle.BLUE padWith 20.0
}

private fun createNameRow() =
   HBox(10.0, promptOf("Name"), textFieldOf(nameProperty)) padWith 10.0 alignTo Pos.CENTER_LEFT

But this is probably best of all:

private fun createContent(): Region = BorderPane().apply {
   top = headingOf("Test Screen")
   center = VBox(20.0, createNameRow(), createButton())
} testStyleAs TestStyle.BLUE padWith 20.0

private fun createButton() = buttonOf("Click Me") { buttonAction() }

private fun createNameRow() =
   HBox(10.0, promptOf("Name"), textFieldOf(nameProperty)) padWith 10.0 alignTo Pos.CENTER_LEFT

There’s no doubt that this last version achieves the “at a glance” objective of the layout code. You can see that the BorderPane has the top and centre occupied, and you can see that the centre has two rows in it. If you’re curious about the “Name” row, or the Button you can click through to them in the code. This is really no different than the best approach in Java, but the apply{}, the builders, and the infix functions strip out all of the extraneous boilerplate that would otherwise bloat the layout code.

Coroutines

Coroutines are a neat feature that allow you to run multiple blocking operations on a single thread. When one task blocks, it doesn’t halt the thread, but is “parked” and the thread is freed up to run other tasks. As each task blocks, Kotlin can then “un-park” a previously parked task that has become unblocked, and run it on the thread until it either completes or becomes blocked again.

There is a special library (part of the Kotlin companion libraries for coroutines) that allows you to run coroutines on the FXAT. So that you can do something like this:

private fun createContent(): Region = BorderPane().apply {
    padding = Insets(20.0)
    center = VBox(20.0,
                  HBox(10.0, Label().bound(model))
                     .styled("test-border3")
                     .padded(10.0)
                     .aligned(Pos.CENTER),
                  Button("ClickMe").apply { onAction = EventHandler { _ -> buttonAction() } })
 }

 private fun buttonAction() {
    GlobalScope.launch(Dispatchers.JavaFx) {
       println("Starting the function")
       counter += 1
       model.value = counter.toString()
       delay(10000)
       println("Ending the function")
       counter += 100
       model.value = counter.toString()
    }
 }

The Dispatchers.JavaFX tells it to launch the coroutine on the FXAT. Because of this, you can update model even though it’s bound to a screen element. The call to delay() is used to simulate a blocking operation. While the delay() is running, you can continue to use the GUI, and you can, in fact, click the Button again and launch another coroutine on the FXAT.

As you can see, running the code as a coroutine simply requires wrapping it in a call to GlobalScope.launch with the correct dispatcher. It’s really that trivial.

Potentially, you could do any number of blocking operations that you’d normally use Task for using coroutines and it should work just fine. However you should keep a couple of things in mind:

  1. This is only for blocking operations, like accessing an external API over a network. Running a very long process that simply takes a long time to complete is still going to hang your GUI.

  2. Using this technique introduces the possibility of some of the concurrency issues that the single threaded FXAT is designed to avoid. This particular example is designed to demonstrate that each click of the button messes with the value of counter. If you had code that assumed that counter was equal to 101 after completion, it could fail.

To expand on the “concurrency issues” part: If you ran the same code without using coroutines, then the GUI would definitely hang for 10 seconds after you click the button. No jobs would get processed of the FXAT and no events would get handled. But you could guarantee that the state of the GUI would be exactly the same after the delay as it was before the delay(), and this includes model.value and counter. But with coroutines, when the execution continues after the delay() there’s no reason to believe that these things won’t have changed.

Coroutines are generally considered an advanced technique in Kotlin, and are really included here for the sake of completeness. There are places where they can be of value, but you should probably continue to use Task for most situations.

Categories:

Updated: