I'm presenting KotlinFX — a “kotlinskoy” layer on top of JavaFX 8 in the same vein as ScalaFX and GroovyFX are. See https://github.com/eugenkiss/kotlinfx for more information. For the uninitiated, JavaFX 8 is Java's new and *much* improved spiritual Swing successor and is shipped by default with JDK 8. It seems that Oracle takes JavaFX very serious — and it shows as for instance it is even possible to use JavaFX on Android and iOS.
The main benefits that KotlinFX provides over pure Kotlin for JavaFX 8 are UI builders, extension properties and bindings. The builders allow you to define the UI layout in a tree-like manner just like Kotlin’s HTML builder example. The builders also have some common properties exposed as named parameters with default values so that even using the builders as mere constructors without nesting is beneficial. Extension properties make Java’s setters and getters transparent and KotlinFX’s bindings are a more succinct and natural way (i.e. using operators where possible) to define JavaFX bindings between JavaFX properties.
The hello world example from http://docs.oracle.com/javafx/2/get_started/hello_world.htm in KotlinFX looks as follows:
import javafx.application.Application
import javafx.stage.Stage
import kotlinfx.builders.*
import kotlinfx.properties.*
fun main(args: Array<String>) {
Application.launch(javaClass<HelloWorld>())
}
class HelloWorld : Application() {
override fun start(stage: Stage?) {
Stage(stage, title = “Hello World!”) {
scene = Scene(width=300.0, height=250.0) {
root = StackPane {
+ Button(“Say ‘Hello World’”) {
setOnAction { println(“Hello World!”) }
}
}
}
}.show()
}
}
Please note that KotlinFX is currently in the very early “proof-of-concept phase”. That means that I only implemented as much of the library as is needed to make the few examples in the same repository look good. I think the basic structure of the library is sound — I’m very open to suggestions nonetheless — so that implementing most of the rest of the library is primarily an “endurance test”. However, I don’t know if I’ll have the required endurance for the rest :(. At the very least there is now a proof of concept Kotlin integration for JavaFX 8.
I would be glad to hear suggestions for improvements. Kontributers are welcome!
Implementation Remarks
In the following I want to present some remarks and questions with respect to the implementation.
Trait Inheritence
I remember there being a discussion about the usefulness of class inheritance in traits. It turns out that I use this feature beneficially for the UI builders. Namely, I have the following trait K that extends Pane:
trait K : Pane {
fun Node.plus(): Node {
getChildren()!!.add(this)
return this
}
fun Node.plus(node: Node): Node {
getChildren()!!.add(node)
return node
}
}
And then I use this trait for the concrete builders:
public fun HBox(
spacing: Double = 0.0,
padding: JInsets = Insets(0.0),
f: HBoxK.() -> Unit): HBox {
val hbox = HBoxK()
hbox.spacing = spacing
hbox.padding = padding
hbox.f()
return hbox
}
class HBoxK : HBox(), K
Which results in a usage like:
HBox(spacing=10.0, padding=Insets(10.0)) {
+ TextField() + Label("Celsius =") + TextField() + Label("Fahrenheit")
}
By using the trait K this way I only have to define the + operator once in the trait instead of in each concrete Pane subclass like HBoxK.
JDK 8 Annotations
The fact that KAnnotator annotations for JDK 8 seemingly do not exist made interfacing with JavaFX 8 inconvenient due to the !!s. When will Kotlin come with KAnnotator annotations for JDK 8? I tried to use KAnnotator to produce annotations for my copy of the JDK but it chocked with an exception. Maybe I did something wrong or is KAnnotator not yet compatible with Java 8?
Extension Properties Limitation
JavaFX 8 has the concept of properties. For example, you can get and set the value of a TextField t with t.getText() and t.setText(string) but you can also get the text property with t.textProperty(). The cool thing about properties is that you can use them to declare declarative bindings (e.g. connect a text property bidirectionally with another text property with a conversion function) without the need to use callbacks or the observer pattern.
Anyway, I tried to imitate ScalaFX such that t.text is syntactic sugar for t.textProperty(). This way you can update the property with t.text = string and get the (primitive) value of the property with t.text.get. I tried to achieve the same effect with Kotlin’s extension properties. Ideally, I should be allowed to write the following:
public var TextField.text: StringProperty
get() = textProperty()
set(v: String) = textProperty().set(v) // or setText(v) which is the short form
But this is not possible currently. That is why I currently use Kotlin extension properties with the basic setters and getters as follows:
public var TextField.text: String
get() = getText()
set(v) = setText(v)
And at least have an abbreviation for JavaFX's properties:
public val TextField.textP: StringProperty
get() = textProperty()
So my question is if anything speaks against allowing the setter parameter of a Kotlin extension property to be a different type from the returned getter type.
Code Generation
Speaking of extension properties, it seems to me that I could save a lot of work by generating the extension property definitions. Do you have suggestions in terms of tools (and workflows) I could use for that?
Konklusion
All in all though Kotlin's been pretty kool for this task ;).