-
Notifications
You must be signed in to change notification settings - Fork 272
Properties
Wiki ▸ Documentation ▸ Properties
TornadoFX features a small set of delegates for Kotlin properties.
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.
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:
- Since it is a mutable
var
, themyButton
property could mistakenly be assigned multiple times - 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