Skip to content

Properties

Edvin Syse edited this page Jul 14, 2016 · 15 revisions

WikiDocumentationProperties

JavaFX Properties enhancements

TornadoFX features a small set of delegates for Kotlin properties.

Property delegate

Generating JavaFX properties is a bit verbose, even in Kotlin. The conventional way looks like this:

private val fooProperty by lazy { SimpleObjectProperty<T>() }
fun fooProperty() = fooProperty
var foo: T
    get() = fooProperty.get()
    set(value) = fooProperty.set(value)

A private property called foo with a method exposes the property and getters and setters for the value

TornadoFX improves upon this structure in several ways to reduce boiler plate code. The first alternative looks like this:

var foo by property<String>()
fun fooProperty() = getProperty(ClassName::foo)

A delegate that wraps the generated property to create getters and setters and a method that extracts the property from the delegate.

If you don't care about exposing the property in a function (as JavaFX properties commonly are) you can write it like this:

val fooProperty = SimpleStringProperty()  // Or any other Property
var foo by fooProperty

The alternative version exposes the property as a field member instead of a function. If you like the above syntax but want to keep the function, you can make the property private and add the function like this:

private val fooProperty = SimpleStringProperty()
fun fooProperty() = fooProperty
var foo by fooProperty

These options are all a matter of taste, you can use whatever version looks best to you.

Single Assign Property

It is not uncommon to want to save Nodes to properties in a given View declaration, especially when you are using Type Safe Builders. This way you have access to the Nodes later to manipulate them, such as turning them into RxJava Observables or ReactFX EventStreams.

The problem is you typically declare the Node structure in the init { } block, which exists outside the constructor. You cannot save the Node to a val property. You have to do it later with a var. If you want the property to not be nullable, you will have to use the lateinit modifier.

class MyView: View() {
        override val root = VBox()

        lateinit var myButton: Button

        init {
            with(root) {
                    myButton = button("New Entry")
            }
       }

       //Using RxJavaFX to turn button events into Observable
       fun getEvents(): Observable<ActionEvent> =
            myButton.toNodeEvents(ActionEvent.Action)
}

There are still two problems:

  1. Since it is a mutable var, the myButton property could mistakenly be assigned multiple times
  2. Access to the myButton property is not necessarily threadsafe

You can remedy these two problems by using the singleAssign() property delegate.

class MyView: View() {
        override val root = VBox()

        var myButton: Button by singleAssign()

        init {
            with(root) {
                    myButton = button("New Entry")
            }
       }

       //Using RxJavaFX to turn button events into Observable
       fun getEvents(): Observable<ActionEvent> =
            myButton.toNodeEvents(ActionEvent.Action)
}

Now myButton can only be assigned a value once. Any subsequent assignment attempts will result in an exception. If the property is accessed before it is assigned, that will also throw an exception.

It is also threadsafe with the highest concurrency achievable. If you are concerned about synchronization overhead, you can explicitly pass an argument to specify no synchronization.

var myButton: Button by singleAssign(SingleAssignThreadSafetyMode.NONE)

If your application is at all multithreaded, it is recommended to not disable the synchronization.

Next: Binding

Clone this wiki locally