Tuesday, January 1, 2013

JavaFX Loves Xtend

Inspired by a talk by Gerrit Grunwald and the work of Tom Schindl I started to dig a bit deeper into JavaFX. Being one of the committers of the Xtend language, it was a matter of honour to use Xtend in my experiments. The result: JavaFX and Xtend seem to be made for each other.

If you know me a bit, you won't be surprised that I tried implementing a simple graph editor first. I will blog on that particular application in a separate post. Even though JavaFX is not primarily a graphical editing framework, it offers a lot of the required features, e.g.
  • State of the art rendering and CSS styling
  • A life scene-graph with affine transformations in all nodes
  • An easy way to do data-binding and event propagation
  • Easy to use APIs for effects and animations
  • Built-in support for tablets and touch-aware devices
The developers of JavaFX did a pretty good job in creating a clear Java API, but sometimes Java's rigid syntax was a bit in their way. This is where Xtend comes into play. The resulting code is astonishingly short and easy to read.

Lambda Expressions For Event Listeners

Each JavaFX Node has a couple of convenience methods to react on on UI events, such as mouse clicks, key strokes or multi-touch gestures. A mouse click can for example be handled as
Node node = new Rectangle();
node.setOnMouseClicked(new EventHandler<MouseEvent>() {
   @Override
   public void handle(MouseEvent event) {
      System.out.println(event.getButton() + " button clicked");  
  }
});
Xtend's lambda expressions (aka closures) are automatically coerced to interfaces with a single method. Using the implicit parameter it and the shortcut syntax for property access the above becomes
val node = new Rectangle
node.onMouseClicked = [
   println(button + " button clicked")
]

The With Operator => Obsoletes Builders

JavaFX has a couple of objects that need quite a few parameters to be customized. Instead of implementing a new constructor with a huge number of parameters for each use case, the JavaFX developers generated fluent builder APIs, e.g.
Rectangle rectangle = RectangleBuilder.create()
  .width(80)
  .height(30)
  .fill(Color.BLUE)
  .stroke(Color.RED)
  .strokeWidth(1.2)
  .arcWidth(12)
  .arcHeight(12)
  .build();
In Xtend, we have the with operator '=>' that binds the preceding argument to the parameter of a lambda expression and executes the latter. So we can achieve the same as above with an even shorter syntax and without the need for an accompanying builder class:
val rectangle = new Rectangle => [
   width = 80
   height = 30
   fill = Color::BLUE
   stroke = Color::RED
   strokeWidth = 1.2
   arcWidth = 12
   arcHeight = 12
]
The with operator also facilitates the creation of object trees, e.g. subtrees of JavaFX's scenegraph.

Extension Methods For High-Level Property Binding

In JavaFX, properties can be derived from other properties by means of another fluent API for the calculation. The result is the best you can achieve with pure Java, e.g. given two DoubleProperties a and b, you can bind a mean value property which will be automatically updated when a or be change:
mean.bind(a.add(b).divide(2));
If these calculations get more difficult and you do a lot of them it will get quite unreadable. This is the right moment to think about overloaded operator extensions for DoubleProperties. With these, the last line can become as simple as
mean << a + b / 2
Extension methods and operator overloading can also be used to add the missing APIs for geometry calculation, e.g. to apply a Transform to a Point3D or to accumulate Transforms.

No comments: