C H A P T E R   10

JavaFX Languages and Markup

Computer programming is tremendous fun. Like music, it is a skill that derives from an unknown blend of innate talent and constant practice. Like drawing, it can be shaped to a variety of ends—commercial, artistic, and pure entertainment. Programmers have a well-deserved reputation for working long hours, but are rarely credited with being driven by creative fevers. Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination, but because their imagination reveals worlds that others cannot see.

—Larry O'Brien and Bruce Eckel

JavaFX provides a rich set of capabilities for building applications that lets you create immersive user interfaces that go beyond what you can accomplish with traditional UI toolkits. However, it does not stop there, because by sitting on top of the Java language, you can also take full advantage of all the languages and tools that have been developed for the Java platform. Also, JavaFX comes with its own UI declaration language written in XML, which is quite powerful in its own right.

In this chapter we show how you can leverage different languages and markup to create great-looking JavaFX user interfaces with less code. The wonderful thing about all the languages and capabilities discussed in this chapter is that it is your choice. You can continue to build JavaFX applications in pure Java using the imperative or builder style, or you can take advantage of your favorite JVM language. Who knows, you may even become a convert to one of the languages discussed in this chapter based on its usage for JavaFX alone.

A Quick Comparison of Alternative Languages

To give you an idea of the power and expressiveness of using different JVM languages, we start out by taking a simple example and showing it in six different representations. The example is an extension of the Colorful Circles application designed by Jasper Potts from the JavaFX team. It is a great example of shapes, animation, and effects in a very small amount of code, and we have adapted it to show binding and interactivity as well.

The running Vanishing Circles application is shown in Figure 10-1.

images

Figure 10-1. The Vanishing Circles application demonstrating JavaFX effects, animation, and interaction

Vanishing Circles in Java

To start with, let’s show the code in standard Java imperative style. This is the most verbose way of writing JavaFX code, but it is also the most straightforward for anyone familiar with the Java programming language and earlier UI toolkits such as Swing. The full code listing for writing this example is shown in Listing 10-1.

Listing 10-1. Vanishing Circles Application Written in Imperative Java Style

public class VanishingCircles extends Application {

  public static void main(String[] args) {
    Application.launch(args);
  }

  @Override
  public void start(Stage primaryStage) {
    primaryStage.setTitle("Vanishing Circles");
    Group root = new Group();
    Scene scene = new Scene(root, 800, 600, Color.BLACK);
    List<Circle> circles = new ArrayList<Circle>();
    for (int i = 0; i < 50; i++) {
      final Circle circle = new Circle(150);
      circle.setCenterX(Math.random() * 800);
      circle.setCenterY(Math.random() * 600);
      circle.setFill(new Color(Math.random(), Math.random(), Math.random(), .2));
      circle.setEffect(new BoxBlur(10, 10, 3));
      circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
        public void handle(MouseEvent t) {
          KeyValue collapse = new KeyValue(circle.radiusProperty(), 0);
          new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play();
        }
      });
      circle.setStroke(Color.WHITE);
      circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty())
        .then(4)
        .otherwise(0));
      circles.add(circle);
    }
    root.getChildren().addAll(circles);
    primaryStage.setScene(scene);
    primaryStage.show();

    Timeline moveCircles = new Timeline();
    for (Circle circle : circles) {
      KeyValue moveX = new KeyValue(circle.centerXProperty(), Math.random() * 800);
      KeyValue moveY = new KeyValue(circle.centerYProperty(), Math.random() * 600);
      moveCircles.getKeyFrames().add(new KeyFrame(Duration.seconds(40), moveX, moveY));
    }
    moveCircles.play();
  }
}

Although the code is fairly easy to understand, it is also quite verbose at 40 lines and 1,299 characters, excluding imports. The basic functionality can be summarized as follows.

  • Fifty circles of varying colors are overlaid with a transparent fill.
  • Those circles are animated in a semirandom pattern around the window.
  • When the mouse hovers over a circle, the circle gets surrounded by a white border.
  • Upon clicking a circle, it slowly shrinks and vanishes.

In a very short amount of code this lets us demonstrate many different JavaFX features, including shapes, effects, animation, binding, and event listeners. In the next few examples we convert this exact application to several different languages and representations, letting you see how these features vary in each of the choices available to you.

First, let’s convert this code to use the JavaFX builders. This allows for a more fluent API, but also comes with its own boilerplate that is required to do this pattern in Java. As a result, it can sometimes take more code to produce the same result; however, you gain some additional expressiveness in the process. This is also a great segue into other languages, such as Groovy, where we can take advantage of the same builder pattern without the boilerplate required to do this in Java.

The builder version of the Vanishing Circles application is shown in Listing 10-2.

Listing 10-2. Vanishing Circles Application Written in JavaFX Builder Style

public class VanishingCirclesBuilder extends Application {

  public static void main(String[] args) {
    Application.launch(args);
  }

  @Override
  public void start(Stage primaryStage) {
    primaryStage.setTitle("Vanishing Circles");
    List<Circle> circles = new ArrayList<Circle>();
    for (int i = 0; i < 50; i++) {
      Circle circle = CircleBuilder.create()
        .radius(150)
        .centerX(Math.random() * 800)
        .centerY(Math.random() * 600)
        .stroke(Color.WHITE)
        .fill(new Color(Math.random(), Math.random(), Math.random(), .2))
        .effect(new BoxBlur(10, 10, 3))
        .onMouseClicked(new EventHandler<MouseEvent>() {
          public void handle(MouseEvent t) {
            TimelineBuilder.create().keyFrames(
              new KeyFrame(Duration.seconds(3),
                new KeyValue(((Circle) t.getSource()).radiusProperty(), 0))
            ).build().play();
          }
        })
        .build();
      circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty())
        .then(4)
        .otherwise(0));
      circles.add(circle);
    }
    primaryStage.setScene(SceneBuilder.create()
      .width(800)
      .height(600)
      .fill(Color.BLACK)
      .root(GroupBuilder.create()
        .children(
          circles
        ).build()
      ).build());
    primaryStage.show();
    List<KeyFrame> keyFrames = new ArrayList<KeyFrame>();
    for (Circle circle : circles) {
      keyFrames.add(new KeyFrame(Duration.seconds(40),
        new KeyValue(circle.centerXProperty(), Math.random() * 800),
        new KeyValue(circle.centerYProperty(), Math.random() * 600)));
    }
    TimelineBuilder.create()
      .keyFrames(keyFrames)
      .build().play();
  }
}

At 51 lines and 1,285 characters, the character count for the builder pattern is slightly less than that of the earlier imperative style, whereas the line count is actually higher. The main advantage of using the builder pattern is that it allows you to nest the UI elements in a hierarchy matching the UI scene graph, which at a quick glance makes it much easier to understand what the code will produce.

In practice, you will probably find a mix of the imperative and builder styles to be the best choice. Notice that we did not bother using builders for the Color or BlurEffect, because the constructors were much more concise and the number of arguments was reasonable. Also, due to the present lack of closures in Java, it is difficult to construct nested iterating structures, which forced a certain style of construction. Similarly, it was not possible to create the binding in the builder clause; however, most other JVM languages have closures and other advanced language features that get around these limitations in the builder syntax.

Vanishing Circles in Alternative JVM Languages

Now we move on to different JVM languages and show what is possible by using Groovy, Scala, and Visage. For the first two languages we make use of an inner domain-specific language (DSL) written on top of the JavaFX APIs. Visage requires no DSL, because it is a language specifically designed for writing UIs, so it has built-in features that mirror what you would want a DSL to show.

Groovy is a great choice for those getting started with JVM languages, because its syntax most closely matches the Java language. In fact, with a few minor changes all Java programs are also valid Groovy programs! However, to get the full advantage of the language features in Groovy you need to make use of a DSL written for your target use case, such as GroovyFX. GroovyFX is an open source project that lets you write JavaFX code in the Groovy language that looks like the code in Listing 10-3.

Listing 10-3. Vanishing Circles Application Written in Groovy Using the GroovyFX DSL

GroovyFX.start { primaryStage ->
  def sg = new SceneGraphBuilder()
  def rand = new Random().&nextInt
  def circles = []

  sg.stage(title: 'Vanishing Circles', show: true) {
    scene(fill: black, width: 800, height: 600) {
      50.times {
        circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white,
                strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) {
          fill rgb(rand(255), rand(255), rand(255), 0.2)
          effect boxBlur(width: 10, height: 10, iterations: 3)
          onMouseClicked { e ->
            timeline {
              at(3.s) { change e.source.radiusProperty() to 0 }
            }.play()
          }
        }
      }
    }

    timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) {
      circles.each { circle ->
        at (40.s) {
          change circle.centerXProperty() to rand(800)
          change circle.centerYProperty() to rand(600)
        }
      }
    }.play()
  }
}

This GroovyFX code has the same functionality as the earlier JavaFX examples, but is significantly shorter and more expressive. The GroovyFX version of the Vanishing Circles application is only 29 lines and 671 characters, which saves you 639 keystrokes from the initial Java version. Also, as your application grows you will get even more benefit from using a DSL such as this, allowing you to write more complex and feature-rich applications with less code.

Some of the Groovy language features that this code takes advantage of include:

  • Groovy Builder Pattern: Groovy makes it particularly easy to build powerful and concise builder code, as demonstrated with the GroovyFX SceneGraphBuilder.
  • Name Parameters: Remembering the order of arguments to methods and constructors with long argument lists is hard, but named parameters allow you to be explicit and change the order for your convenience.
  • Class Extension: Groovy allows you to add new methods and functionality to existing classes, as demonstrated by the creation of duration objects with syntax 40.s where “s” is a method on integers.
  • Closures: The event handlers are greatly simplified by creating anonymous single method interface extensions via closures.

All of this contributes to a very concise and readable syntax with very little boilerplate code. We dig into the features of how each of the JavaFX features translates to Groovy syntax in the later section entitled, “Making Your JavaFX Groovy.”

The second JVM language we cover is Scala. It provides a lot of the same benefits as Groovy, but has the additional benefit of being fully type safe. This means that the compiler will catch bugs and type errors before you even run your application, which can be a huge boon to productivity, and shorten your development and testing cycle.

Again, we take advantage of an inner DSL written in the Scala language called ScalaFX, which is another open-source project that provides a full wrapper library for the JavaFX 2.0 APIs. The code listing for the Vanishing Circles application written using ScalaFX is shown in Listing 10-4.

Listing 10-4. Vanishing Circles Application Written in Scala Using the ScalaFX DSL

object VanishingCircles extends JFXApp {
  var circles: Seq[Circle] = null
  stage = new Stage {
    title = "Vanishing Circles"
    width = 800
    height = 600
    scene = new Scene {
      fill = BLACK
      circles = for (i <- 0 until 50) yield new Circle {
        centerX = random * 800
        centerY = random * 600
        radius = 150
        fill = color(random, random, random, .2)
        effect = new BoxBlur(10, 10, 3)
        strokeWidth <== when (hover) then 4 otherwise 0
        stroke = WHITE
        onMouseClicked = {
          Timeline(at (3 s) {radius -> 0}).play()
        }
      }
      content = circles
    }
  }

  new Timeline {
    cycleCount = INDEFINITE
    autoReverse = true
    keyFrames = for (circle <- circles) yield at (40 s) {
      Set(
        circle.centerX -> random * stage.width,
        circle.centerY -> random * stage.height
      )
    }
  }.play();
}

The ScalaFX code above is four lines longer than the Groovy code at 33 lines; it is only 591 characters, saving you an additional 76 keystrokes.

The Scala example makes use of an object literal pattern for constructing the scene graph, which gives the same benefits as the Java and Groovy builders. It also benefits from the powerful binding support that is built into the ScalaFX libraries. Some of the Scala language features that this code takes advantage of include:

  • Operator Overloading: Scala lets you overload the existing operators and create entirely new ones. This is used in several places, including the bind and animation syntax.
  • Implicits: Scala’s answer to class extension is via the implicit conversions. This makes it possible to extend the JavaFX API classes with convenience methods for object literal construction, giving you all the power of builders with the core classes.
  • Closures: Scala also supports closures, which makes event handlers much more concise, and allows powerful looping constructs such as the for…yield syntax.
  • DSL-Friendly Syntax: The usual code punctuation, such as dots and parentheses, is optional in most situations, making it possible to build fluent APIs that read like language keywords.

Although the code ends up being fairly short and easy to understand, there is quite a bit of depth to the Scala language. We show how you can take advantage of some of the built-in features of Scala to further improve your applications in the section entitled, “Scala and JavaFX.”

The final language we explore is called Visage. It is the successor to JavaFX Script, which was the language in which all JavaFX applications were written prior to version 2. As a result, its syntax is ideal for expressing UI applications and it integrates seamlessly with many of the JavaFX 2 platform features, such as binding, observable sequences, and animation. The code for the Vanishing Circles application written in the Visage language is shown in Listing 10-5.

Listing 10-5. Vanishing Circles Application Written in the Visage UI Language

var circles:Circle[];
Stage {
  title: "Vanishing Circles"
  Scene {
    width: 800
    height: 600
    fill: BLACK
    Group {
      circles = for (i in [1..50]) {
        def c:Circle = Circle {
          centerX: random() * 800
          centerY: random() * 600
          radius: 150
          fill: color(random(), random(), random(), .2)
          effect: BoxBlur {
            height: 10
            width: 10
            iterations: 3
          }
          stroke: WHITE
          strokeWidth: bind if (c.hover) 5 else 0
          onMouseClicked: function(e) {
            Timeline {at (3s) {c.radius => 0}}.play()
          }
        }
      }
    }
  }
}
Timeline {
  for (circle in circles) at (40s) {
    circle.centerX => random() * 800;
    circle.centerY => random() * 600
  }
}.play()

At 35 lines and 487 characters, this is the shortest of all the DSLs available to write your JavaFX application, which should not be a surprise inasmuch as this was a language written specifically for JavaFX. Also, Visage comes with direct support for accessing the JavaFX APIs as well as a very efficient compiled bind capability, giving you the best performance possible from a JVM language. Some of the Visage language features that we are taking advantage of include the following.

  • Object Literal Syntax: Visage has a native syntax for creating hierarchical user interfaces, including auto-flattening sequences and built-in for comprehensions that make it easy to build complicated UI elements inline.
  • Compile Bind Support: Binding is an API feature in JavaFX 2.0, however, Visage retains the elegance and performance of bind support built into the language.
  • Closures: As do Groovy and Scala, Visage also supports closures, which lets you simplify your event listeners.
  • Animation Syntax: Visage has a custom animation syntax that makes it easy to create timeline-based animations, but sits on top of the JavaFX animation subsystem.

As you have seen, using a domain-specific language to write your JavaFX code can produce significant benefits over what you would have to write in Java using either the imperative or builder styles. Also, because all of these languages sit on top of the same underlying JavaFX APIs, they have the same functionality as applications written in pure Java. Therefore, the choice is yours as to which language you want to use to write your JavaFX applications.

In the next few sections we go into each of these three languages in more detail to help you get started with developing your JavaFX applications using them. Also, it is possible to use virtually any JVM language to write JavaFX applications with, so if you have a favorite language that is not listed here, it is worth a try!

Making Your JavaFX Groovy

According to job trends, Groovy is the most popular language that runs on the JVM other than Java.1 This is helped by the fact that Groovy is extremely easy to get started with; other than a few minor differences,2 any Java program is also a valid Groovy application. This makes it very easy for Java developers to start using it, even before they appreciate all the power and convenience that Groovy brings to the Java ecosystem.

__________

1 Scala, Groovy, Clojure, Jython, JRuby, and Java: Jobs by Language, http://bloodredsun.com/2011/10/04/scala-groovy-clojure-jython-jruby-java-jobs/, October 2011.

2 Differences from Java, http://groovy.codehaus.org/Differences+from+Java, 2011.

Groovy source files compile directly to Java bytecodes and can run anywhere you have a Java Virtual Machine. As a result, you can access any Java libraries directly, including the JavaFX APIs. A well-written Groovy application will almost always be shorter than the equivalent Java version, because of all the conveniences built into the language. Some of the features of the Groovy language that make it attractive as a replacement for Java and JavaFX code include:

  • Closures: The most requested feature for the Java language, closures are particularly important for GUI programming, because they make it much simpler to write code that gets called when an event happens.
  • Operator Overloading: Groovy lets you overload existing operators or define new ones, which is important for writing a readable DSL.
  • Dynamic Typing: Types are optional in Groovy code, which makes it easier to write succinct code.
  • Getter/Setter Access: Groovy will automatically convert direct field access to getters and setters, which shortens this very common pattern for accessing Java APIs.
  • Named Constructor Parameters: When initializing an object with a constructor, you can also set fields on the class by referring to them by name. This pattern is used quite a bit in our GroovyFX code later.
  • Built-In Data Structure Syntax: Many commonly used data structures, such as Lists and Maps, have a built-in syntax in Groovy, which makes it much more convenient to work with them and build them dynamically.

Using JavaFX directly from Groovy is possible; however, you are missing out on a lot of the benefits of Groovy without using a dedicated DSL library, such as GroovyFX. In the next few sections we show you some of the benefits of writing JavaFX code using the Groovy language and GroovyFX library.

Introduction to GroovyFX

GroovyFX is a library for developing JavaFX applications in Groovy that lets you build your application in a more Groovy-like fashion. It was started by Jim Clarke and Dean Iverson, one of the coauthors of this title, and is being developed as an open-source Groovy module. The main landing page for GroovyFX is on GitHub at the following URL:

http://groovyfx-project.github.com/

Some of the benefits of writing code using GroovyFX, rather than simply coding directly against the JavaFX APIs include:

  • Builder Pattern: GroovyFX has Groovy builders for all the major JavaFX classes, making it easy and convenient to declaratively construct a scene graph.
  • Property Generation: The JavaFX property pattern for writing your own properties is quite long-winded, but is replaced with a one-line annotation in GroovyFX.
  • Timeline DSL: GroovyFX has a convenient short-hand syntax for animation.
  • Convenient Bind Syntax: GroovyFX makes creating bindings much more terse and readable than the equivalent Java code.
  • API Improvements: A lot of the JavaFX APIs have been tweaked and enhanced to make them easier to use.

To get you started using GroovyFX, we walk you through setting up a small GroovyFX project from scratch. These directions assume that you already have a current Java and JavaFX 2.0 SDK installed on your system. Also, we have chosen to tailor the instructions for the IntelliJ Community Edition, which is a free, open-source IDE with a long track record of excellent Groovy support. There is also Groovy support for Eclipse, NetBeans, and many other IDEs, so if you have a preference for a different IDE, you can easily adapt these instructions to work with your IDE of choice.

To start with, download and install the latest version of IntelliJ IDEA. The community edition comes with Groovy support, so there is no need to purchase the Ultimate Edition. The IntelliJ web site can be found here:

http://www.jetbrains.com/idea

After installing and launching IntelliJ, you need to create a new Java project with Groovy language support enabled. On the landing page you can click “Create New Project,” or if you are on a different screen you can get to the same wizard by selecting “New Project…” from the “File” menu. This will present you with the new Project Wizard shown in Figure 10-2.

images

Figure 10-2. IntelliJ new project wizard dialog

Name your project “HelloGroovyFX,” make sure that the project type is set to “Java Module,” and then click Next. For the next page of the wizard, you can simply accept the default src folder location and continue, which will take you to the extension screen shown in Figure 10-3.

images

Figure 10-3. The Vanishing Circles application demonstrating JavaFX effects, animation, and interaction

This is where you can select and enable Groovy support for your project. Find Groovy from the list on the left and select the checkbox. You also need to configure a Groovy runtime for it to work with on the right-hand pane. If you don’t already have a current Groovy runtime installation, you can grab the latest from the Groovy site:

http://groovyfx-project.github.com/

The binary zip release should work fine. Extract this to a folder on your hard drive, and then go back to IntelliJ and set up the Groovy library by clicking on the “Create…” button and selecting the folder to where you just finished extracting Groovy. To complete the project creation, click “Finish” and your new project will be opened in the current window.

We are almost done with the project setup, but are missing the dependent libraries for JavaFX and GroovyFX. To add these in, open the “Project Structure…” dialog from the “File” menu, and navigate to the “Modules” section. This lets you configure the project settings in more detail than the new project wizard allows.

Click on the “Dependencies” tab on the right side and the screen shown in Figure 10-4 appears.

images

Figure 10-4. Module configuration screen for the HelloGroovyFX project

In the Dependencies tab you need to configure two additional jar references, both of which are highlighted in the illustration. The first is the GroovyFX jar file, which you can download from the GroovyFX site. Again, the URL for the GroovyFX site is:

http://groovyfx-project.github.com/

The second dependency is the jfxrt.jar, which is in the lib folder of your JavaFX runtime. On the Windows operating system, this is typically located under “Program Files/Oracle/JavaFX/JavaFX 2.0 Runtime/lib” (for 64-bit) or “Program Files (x86)/Oracle/JavaFX/JavaFX 2.0 Runtime/lib” (for 32-bit).

Now to create the HelloGroovyFX application, we need to add a new Groovy script to the project. Right-click on the src directory of your project and choose “New” > “Groovy Script” from the context menu. This pops up a script creation dialog where you can type the name of your script file and click “OK” to create it, as shown in Figure 10-5.

images

Figure 10-5. Groovy Class creation dialog

Now you are finally ready to get started with some coding. The HelloGroovyFX application is very short, so you can grab the code from the source bundle that comes with the book, or just type in the code shown in Listing 10-6 yourself.

Listing 10-6. Hello GroovyFX Code

import groovyx.javafx.GroovyFX
import groovyx.javafx.SceneGraphBuilder

GroovyFX.start {
  new SceneGraphBuilder().stage(visible: true) {
    scene {
      stackPane {
        text("Hello GroovyFX")
      }
    }
  }
}

To run the application, simply right-click on the class and choose ‘Run “HelloGroovyFX”’ from the context menu. This gives you the application shown in Figure 10-6.

images

Figure 10-6. Output from running the Hello GroovyFX application in IntelliJ

Congratulations, you have created and run your very first JavaFX application in Groovy! In the next few sections we go into more detail on the features and capabilities of GroovyFX, but remember that anything you can do in JavaFX is possible in GroovyFX, because it wraps the full JavaFX APIs.

Properties in GroovyFX

One of the features that would most benefit UI development in Java with JavaFX is having a notion of first-class, observable properties in the language. Because this does not exist today, the JavaFX team added properties at an API level, which is sufficient, but much more verbose than a native syntax can provide.

Fortunately, with dynamic languages such as Groovy, it is quite easy to  add in powerful features including a native property syntax. Groovy already has a built-in notion of simplified getter/setter access, so you can retrieve and store JavaFX properties just as if they were normal variables. For example, to set the width of a Rectangle in Groovy, all you need to write is:

rectangle.width = 500

And this will be automatically translated to a setter call, such as the following.

rectangle.setWidth(500);

The other part of the JavaFX property pattern that is even more tedious is the creation of new properties. To define a new property on your class in the same way that the JavaFX APIs define properties, you need a total of one field and three methods per property. The standard boilerplate for creating a name property on a Person object is shown in Listing 10-7.

Listing 10-7. JavaFX Property Pattern to Create New Properties in Java

import javafx.beans.property.SimpleStringProperty
import javafx.beans.property.StringProperty

public class Person {
  private StringProperty name;
  public final void setName(String val) { nameProperty().set(val); }
  public final String getName() { return nameProperty().get(); }
  public StringProperty nameProperty() {
    if (name == null) {
      name = new SimpleStringProperty(this, "name");
    }
    return name;
  }
}

images Note The above code can be further optimized by checking whether the property has been created in the getName method, and returning null if it has not been created (and thus not initializing the name property).

Although this code is only a bit more verbose than the standard Java property bean pattern, multiply it by the number of properties you need to define in your application, and you have quite a bit of code to maintain and debug when there is something that is not working as expected.

GroovyFX has a very elegant solution to this using a compiler hook for AST transformations. Rather than copying and pasting the property boilerplate each time you want to define a new property, you can simply annotate a variable with the @FXBindable annotation, and GroovyFX will take care of the rest. It generates exactly the same optimized code you would write by hand, but does it behind the scenes during the compile phase so that your source code is not cluttered with the additional logic.

Listing 10-8 shows what the name property would look like in Groovy.

Listing 10-8. JavaFX Property Pattern to Create New Properties in Java

import groovyx.javafx.beans.FXBindable

class Person {
  @FXBindable String name
}

The GroovyFX @FXBindable annotation also supports handling the case where a property has a default initialization value:

class Person {
  @FXBindable String name = "Guillaume Laforge"
}

And has a convenient shortcut syntax for converting all the variables in a class to properties:

@FXBindable
class Person {
    String name;
    int age;
    String gender;
    Date dob;
}

images  Caution   There is a bug in Groovy 1.8.5 that will give you a compiler error when using FXBindable with Dates. If this bug has not been fixed by the time this is published, you can roll back to Groovy 1.8.2 to fix this.

GroovyFX Binding

Binding in JavaFX is an extremely powerful feature, but the API-based syntax in JavaFX 2.0 can often get in the way of understanding what the bind code is doing. GroovyFX solves this problem by taking advantage of the operator overloading feature of the Groovy language to provide an infix notation for common bind expressions.

For example, to bind one rectangle’s width to the width of a second rectangle, you can write the following code in GroovyFX.

rect1.widthProperty.bind(rect2.widthProperty)

There is also an alternate version of the same code that you can use instead:

rect1.widthProperty.bind(rect2, 'width')

However, the real power of GroovyFX binding comes into play when you are combining multiple properties in a bind statement. As a second example, let’s say that you want to bind one rectangle’s width to the sum of two others.  In GroovyFX you can write the following code.

rect1.widthProperty.bind(rect2.widthProperty + rect3.widthProperty)

This would translate to the following, much longer, JavaFX Java code.

rect1.getWidthProperty().bind(rect2.getWidthProperty().add(rect3.getWidthProperty()));

The GroovyFX distribution comes with an example of some binding code to animate the hands of an analog clock. This example was written by Jim Clark, and the relevant code showing properties and binding is shown in Listing 10-9.

Listing 10-9. Analog Clock Excerpt from the GroovyFX Demo Package

@FXBindable
class Time {
  Integer hours
  Integer minutes
  Integer seconds

  Double hourAngle
  Double minuteAngle
  Double secondAngle

  public Time() {
    // bind the angle properties to the clock time
    hourAngleProperty.bind((hoursProperty * 30.0) + (minutesProperty * 0.5))
    minuteAngleProperty.bind(minutesProperty * 6.0)
    secondAngleProperty.bind(secondsProperty * 6.0)
  …
}

The combination of automatic properties expansion via AST transforms and infix notation binding lets you express fairly complex logic without much code. The resulting Groovy Analog clock graphic UI that you get when running the example is shown in Figure 10-7.

images

Figure 10-7. Groovy analog clock demo

GroovyFX API Enhancements

In addition to the core language benefits of using Groovy instead of JavaFX, GroovyFX has taken many of the JavaFX APIs and Groovy-ized them to make them easier to use from a dynamic language. We cover three major ones in this section: the GroovyFX custom DSL for animation, a simplified table construction pattern, and streamlined JavaFX layouts. All of these provide significant benefits over the core JavaFX APIs, allowing you to write less code and do more.

Animation

GroovyFX supports building animations using a special DSL that has syntax for creating Durations, KeyFrames, and KeyValues, all with a concise syntax. We showed an example of the Groovy animation syntax earlier in the Vanishing Circles application, which looked like this:

timeline {
  at(3.s) { change e.source.radiusProperty() to 0 }
}.play()

The basic pattern is as follows, where you can have multiple at expressions in a timeline and multiple change expressions within an at.

timeline {
  at(duration) {
    [change property to value]
  }
}

Similar to binding, there is also a second format for referring to the property that makes up the change expression:

timeline {
  at(3.s) { change(e.source, 'radius') to 0 }
}.play()

And the syntax also supports an optional tween that lets you provide a curve for the speed at which the animation proceeds:

timeline {
  at(3.s) { change e.source.radiusProperty() to 0 tween ease_both }
}.play()

With the above change, the animation would start out slow and speed up to its normal rate, and then slow down at the end the same way.

Compared to the full Java code, the Groovy animation syntax is a huge savings in characters and makes it much easier to see what your animation is actually doing.

Tables

Between the extra syntactic sugar for builders or imperative Java, and the need to specify Generic at multiple levels, building simple data tables in Java code can be quite a lot of code. Groovy simplifies this with a fairly intuitive builder format for creating tables, along with some conveniences, such as a built-in type converter that lets you specify a closure to change the output type for a field.

As a result, you can write fairly complex tables with very little code. The following example builds from the Person class that we created earlier to display a list of people in a tabular format. The full code is shown in Listing 10-10.

Listing 10-10. Code Demonstrating a Table in Groovy with Strings, ints, and Dates

import groovyx.javafx.GroovyFX
import groovyx.javafx.SceneGraphBuilder
import binding.Person
import java.text.SimpleDateFormat

def dateFormat = new SimpleDateFormat("MM/dd/yyyy")

def persons = [
  new Person(name: "Ada Lovelace", age: 36, gender: "Female",
             dob: dateFormat.parse("10/10/1815")),
  new Person(name: "Henrietta Swan Leavitt", age: 53, gender: "Female",
             dob: dateFormat.parse("7/4/1868")),
  new Person(name: "Grete Hermann", age: 83, gender: "Female",
             dob: dateFormat.parse("3/2/1901"))
]

GroovyFX.start {
  new SceneGraphBuilder().stage(visible: true) {
    scene {
      tableView(items: persons) {
        tableColumn(property: "name", text: "Name", prefWidth: 160)
        tableColumn(property: "age", text: "Age", prefWidth: 70)
        tableColumn(property: "gender", text: "Gender", prefWidth: 90)
        tableColumn(property: "dob", text: "Birth", prefWidth: 100,
          type: Date,
          converter: { d -> return dateFormat.format(d) })
      }
    }
  }
}

Notice that the code to display the table is almost as short as the code to set up the data. The converter in the last column to format the Date is a one-line operation in Groovy, but requires a CellValueFactory with an implementation of a Callback interface, which is several lines of Java code saved.

Figure 10-8 displays the result of running this table application in Groovy.

images

Figure 10-8. Groovy Table demo with famous women in computers listed

Layouts

Another set of APIs that are relatively challenging to use in a declarative fashion are the JavaFX layouts. They have a powerful constraint system that you can use to give Nodes special layout behavior on a per-layout basis, but this also means that adding a Node to a layout involves two steps: (1) adding it to the container and (2) assigning constraints.

The GroovyFX APIs solve the layout problem with a very clean solution that involves annotating the node object with additional pseudo-properties for layout constraints. This allows you to define the constraints as you construct the scene graph, and then during the layout phase, the JavaFX layout system uses the constraints to control how the Nodes are positioned and sized.

Listing 10-11 shows an example of one of the more complicated layouts, GridPaneLayout, with the entire application written in a declarative style.

Listing 10-11. Example Code of a GridPane Layout in GroovyFX

import groovyx.javafx.GroovyFX
import groovyx.javafx.SceneGraphBuilder
import javafx.scene.layout.GridPane
import javafx.scene.text.Font

GroovyFX.start {
  def sg = new SceneGraphBuilder()

  sg.stage(title: "GridPane Demo", width: 400, height: 500, visible: true) {
    scene {
      stackPane {
        imageView {
          image("puppy.jpg", width: 1100, height: 1100, preserveRatio: true)
          effect colorAdjust(brightness: 0.6, input: gaussianBlur())
        }
        gridPane(hgap: 10, vgap: 10, padding: 20) {
          columnConstraints(minWidth: 60, halignment: "right")
          columnConstraints(prefWidth: 300, hgrow: "always")

          label("Dog Adoption Form", font: new Font(24), margin: [0, 0, 10, 0],
              halignment: "center", columnSpan: GridPane.REMAINING)

          label("Size: ", row: 2)
          textField(promptText: "approximate size in pounds", row: 2, column: 1)

          label("Breed:", row: 3)
          textField(promptText: "pet breed", row: 3, column: 1)

          label("Sex:", row: 4)
          choiceBox(items: ['Male', 'Female', 'Either'], row: 4, column: 1)

          label("Additional Info:", wrapText: true, textAlignment: "right",
              row: 5, valignment: "baseline")
          textArea(prefRowCount: 8, wrapText: true, row: 5, column: 1, vgrow: 'always')
          button("Submit", row: 6, column: 1, halignment: "right")
        }
      }
    }
  }
}

Notice that the code is succinct, clean, and closely models the UI that it is trying to build. The result of running this application looks exactly like what you would expect from a typical UI form, as shown in Figure 10-9.

images

Figure 10-9. Running Dog Adoption Form example with a Cavapoo (cross between Cavalier King Charles Spaniel and Poodle) in the background3

__________

3 Public domain picture from Wikimedia Commons: http://commons.wikimedia.org/wiki/File:Image-Cavapoo_puppy.JPG

Scala and JavaFX

Scala is a powerful JVM language combining the best features of functional and object-oriented programming in a single language. As do the other JVM languages discussed in this chapter, it compiles source files directly to Java bytecodes and all the existing Java language libraries commonly used are compatible with it. However, Scala adds powerful, type-safe collections, an elegant actor model for concurrency, and functional language features including closures, pattern matching, and currying.

Scala was started by Martin Odersky in 2001 at the École Polytechnique Fédérale de Lausanne (EPFL), and has grown in maturity and popularity over the years. Martin has actually been the genius behind the scenes working on Java compilers for many years, including the Pizza language that extended Java and GJ, which is the grandfather of the modern Java compiler after its adoption by Sun in Java 1.3. By developing an entirely new language on the JVM, Martin was able to overcome several of the inherent design limitations of Java.

Scala is used today in many large enterprises such as Twitter, LinkedIn, Foursquare, and Morgan Stanley. There is also commercial support available from the creators of the language via TypeSafe, a Scala language company. Also, Scala has been hailed as the successor to Java by James Gosling, the father of Java, James Strachan, creator of Groovy, and Charles Nutter, JRuby Core Developer, among others. So with all this support behind the Scala language, it makes a great candidate for also providing superior APIs for JavaFX development!

Although you can code in Scala directly against the JavaFX APIs, the end result will look very similar to the Java code we have been writing up to this point and will be unable to take full advantage of the language. The ScalaFX project was started to provide a more Scala-friendly API for doing JavaFX development and is what we use in all the examples throughout this book.

ScalaFX is an open-source project created by Stephen Chin, one of the authors of this book, and has numerous additional contributors who have helped with the design and testing of the library. It is very similar to the GroovyFX library described earlier in this chapter, because it is also an open-source library that constitutes a bridge between a JVM language and the JavaFX APIs. However, ScalaFX is different in that it prioritizes type safety and consistent semantics, which is in spirit with the design goals of the Scala language.

Many of the constructs in the ScalaFX library were inspired by the JavaFX Script language that was used in JavaFX releases prior to 2.0, so for those of you familiar with JavaFX Script, the syntax will feel quite comfortable. It takes advantage of many of the advanced features available in Scala, but does not expose or burden the end user with understanding these to build great-looking UI applications.

Making you an expert Scala developer is beyond the scope of this book, but we do describe the Scala features as we use them in our ScalaFX code, so this should also serve as a gentle introduction to Scala for anyone who is already a proficient Java developer.

Getting Started with ScalaFX

To write your first ScalaFX application you need to download and install Scala as well as the ScalaFX library. Because ScalaFX code is a DSL written in the Scala language, you can use any IDE that supports Scala development, such as IntelliJ, Eclipse, or NetBeans, although you may want to start with the Scala IDE for Eclipse inasmuch as that is the one supported by the Scala language team at TypeSafe. We demonstrate the basic setup of an Eclipse environment for ScalaFX in this chapter, although the concepts apply to other IDEs as well.

To start with, install the latest version of Eclipse and launch it. From the Help menu choose “Install New Software…” and paste the Scala IDE update URL into the “Work with” field. You can get the latest update URL for Scala IDE from their web site:

http://scala-ide.org/download/current.html

This lets you select the Scala IDE for the Eclipse plug-in as shown in Figure 10-10.

images

Figure 10-10. Scala IDE installation in Eclipse

Continue with the wizard, accepting the license agreement and default settings, and after downloading and installing the plug-in and restarting Eclipse, you will be ready to begin Scala development.

To start with, we create a new Scala project. Go to the File menu and choose “New” > “Project…” to open the project creation wizard shown in Figure 10-11. Choose “Scala Wizards”/“Scala Project” from the list of choices and click Next….

images

Figure 10-11. Scala project creation in Eclipse

We name our project “Hello ScalaFX” and use the default project settings. When asked to switch to the Scala Perspective, choose yes and you will be in the proper view for editing Scala code.

In addition to the standard project setup, we also need to add in the ScalaFX and JavaFX libraries to code using this DSL. ScalaFX can be downloaded from the Google Code web site here:

http://code.google.com/p/scalafx/

And you already have the JavaFX libraries installed as part of the JavaFX runtime and SDK. To install ScalaFX, simply download the latest distribution as a jar and add it in to your project as a dependent library. The easiest way to do this is as follows.

  1. Copy the ScalaFX.jar file to a lib folder under your project root.
  2. Right-click on your project and choose “Properties…” from the context menu.
  3. Navigate to the “Java Build Path” entry, select the “Libraries” tab, and click “Add Jars…”.
  4. Select the ScalaFX.jar file you added to your project in Step 1.

Steps 2 through 4 are illustrated in Figure 10-12.

images

Figure 10-12. Adding the ScalaFX jar file to your project

In the same dialog you can also add in the JavaFX runtime library by clicking on “Add External JARs…” and navigating to the JavaFX Runtime or SDK folder. The one jar file you need is called jfxrt.jar and is located in the lib folder under the root of the JavaFX installation.

Now you are ready to create your first ScalaFX application. To start, we create a very simple ScalaFX application that shows a Stage and Scene with a single Label inside it. This is the Hello World of JavaFX applications, and will ensure that all your project settings are correct and you are ready to build larger applications.

To create a new ScalaFX class, choose “New” > “Scala Object” from the “File” menu. This opens a wizard where you can set the name of your class to HelloScalaFX and select the scalafx.application.JFXApp class from which it should extend. Upon completing the wizard, Eclipse will create a stub class for you.

To complete the example, you need to add in a Stage, Scene, and Label to your application. The full code for the Hello ScalaFX application is shown in Listing 10-12.

Listing 10-12. Hello ScalaFX Application to Test Your Newly Created Project

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.stage.Stage
import scalafx.scene.Scene
import scalafx.scene.control.Label

object HelloScalaFX extends JFXApp {
  stage = new Stage {
    scene = new Scene {
      content = new Label {
        text = "Hello ScalaFX"
      }
    }
  }
}

You probably noticed some obvious differences from Java code. In Scala, semicolons are almost always optional where a line break would otherwise indicate that a statement has ended. This is why there is no semicolon on the import statements or the assignment to text. Also, Scala has both classes and objects, either or both of which can be defined in a given file. In this case we are creating an object that subclasses scalafx.application.JFXApp, which serves as a way of both defining our application and launching it in one step.

Creating an Object that extends scalafx.application.JFXApp is the fundamental pattern for building JavaFX applications using ScalaFX. This base class has all the core functionality to instantiate a JavaFX application, freeing you from the usual boilerplate required. All you have to do is take care of whatever initialization you need and override the stage variable with your own ScalaFX stage object.

This same pattern is followed for the Stage, Scene, and Label, all of which are ScalaFX objects that have properties on them for each of the available JavaFX properties of the same class. If you notice from the imports, we are not actually referring to JavaFX classes, but instead working with proxy classes in a parallel set of ScalaFX classes. These proxies are interchangeable with the equivalent JavaFX classes using a feature in Scala called implicits, but have additional functionality that supports this nested object-literal-like syntax.

To run this application, right-click on the file and choose “Run As” > “Scala Application”. Upon execution, this application opens a window with the words Hello ScalaFX as shown in Figure 10-13.

images

Figure 10-13. Hello ScalaFX application launched from Eclipse

Congratulations, you have successfully run your first ScalaFX application! We now dig more into the design and features of ScalaFX, showing you how you can build more complex JavaFX applications in Scala.

ScalaFX Proxies and Implicit Conversions

For almost every JavaFX API class, there is an equivalent ScalaFX proxy class. The ScalaFX proxy classes are in a parallel package structure where javafx is replaced by scalafx and provides additional functionality on top of the JavaFX APIs. The proxy classes each include one or more of the following.

  • Delegate Object: Each proxy contains a reference back to the JavaFX class that it extends and wraps.
  • Property Aliases: For all properties, rather than referring to them as fooProperty, you can instead directly access them as foo.
  • Property Assignment: To support an object-literal-like syntax, the assignment operator is overloaded to allow you to set writeable properties directly.
  • List Access: All JavaFX ObservableLists are wrapped with a property to access the list that lets you treat it as a Scala collection.
  • API Enhancements: Some of the JavaFX APIs were not designed with the Scala language and object-literal construction in mind, so the API authors have taken some liberties in adding strategic enhancements.

For most uses, you can actually ignore the fact that there is a parallel set of classes, because the Scala implicits feature allows you to use them interchangeably. Anywhere you have an API that expects a JavaFX class, you can pass in the ScalaFX proxy and it will automatically get converted back to the JavaFX version. Similarly, if you have an API that expects the ScalaFX version, there is a Scala implicit conversion that will automatically wrap the JavaFX class in a ScalaFX proxy.

For example, you can change the Hello ScalaFX code to use a JavaFX label directly and the code will compile and run fine. The modified version is shown in Listing 10-13.

Listing 10-13. Hello ScalaFX Application to Test Your Newly Created Project

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.stage.Stage
import scalafx.scene.Scene
import javafx.scene.control.Label

object HelloScalaFXImplicits extends JFXApp {
  stage = new Stage {
    scene = new Scene {
      content = new Label("Hello ScalaFX Implicits")
    }
  }
}

Notice that we have changed the import to the normal JavaFX one and used the standard constructor with a String argument. Even though the Scene content is defined as a collection of ScalaFX Nodes, the Scala implicit kicks in and automatically converts the JavaFX object to a ScalaFX proxy.

The ScalaFX implicit conversions require that you import scalafx.Includes._, which has been the first line in all the programs we have shown. This is a special syntax for Scala that is equivalent to a static import in Java, and will automatically include several utility methods and all the JavaFX to ScalaFX implicit conversions. For the purposes of ScalaFX development, you should simply treat it as a preamble and include it on all your ScalaFX source files.

JavaFX Properties in Scala

Although we have been using ScalaFX properties directly, we have not directly shown how they work. In particular, notice the absence of the usual getter/setter pattern found in JavaFX. This is possible because ScalaFX lets us override the behavior of operators and assignment to substitute more efficient versions of the same operations. To understand this, let’s take a look at the JavaFX property pattern again.

When creating properties for JavaFX in Java, each property definition consists of one variable and three different accessor methods as shown again in Listing 10-14.

Listing 10-14. JavaFX Property Pattern to Create New Properties in Java

import javafx.beans.property.SimpleStringProperty
import javafx.beans.property.StringProperty

public class Person {
  private StringProperty name;
  public public void setName(String val) { nameProperty().set(val); }
  public public String getName() { return nameProperty().get(); }
  public StringProperty nameProperty() {
    if (name == null) {
      name = new SimpleStringProperty(this, "name");
    }
    return name;
  }
}

Direct access to the property is restricted in order to allow it to be lazily created upon the first use. The initialization occurs in the nameProperty method if it is null, and then the get and set simply delegate the call to the same named method on the property.

In ScalaFX, you get the same benefits of lazy initialization in a single line as shown in Listing 10-15.

Listing 10-15. Simplified JavaFX Property Pattern Adapted Using ScalaFX APIs

import scalafx.beans.property.StringProperty

class Person {
  lazy val name = new StringProperty(this, "name")

  // this is optional, but supports the object-literal construction pattern:
  def name_=(v: String) {
    name() = v
  }
}

The first line that defines the property is sufficient to do everything that the Java code above does. By declaring “name” as a “val”, this tells Scala that it is a constant variable. The “lazy” keyword that precedes the declaration indicates that it should not be initialized until the first use. As a result, you can directly access this variable from user code and the initialization logic is automatically invoked when needed.

images Tip Scala supports three different types of variable definitions: val, var, and def. The var keyword behaves most closely to what you are familiar with in Java, and defines a variable that can be modified in place. The val keyword, which we used above, declares a constant variable, and is most similar to final variables in Java. The last keyword, def, declares an expression that is re-evaluated each time it is called, so it behaves just like a method in Java.

The second definition of a “name_=” method is special syntax for overloading the assignment operator in Scala. This is how the object-literal-like syntax we used earlier works; however, it is just a shortcut for accessing the property and assigning the value. It also demonstrates the basic pattern for how properties are used in ScalaFX.

Table 10-1 compares all the different combinations of how you access and set properties in Java and ScalaFX.

images

Once you get used to the ScalaFX syntax, it is quite natural. In general, if you refer to a property directly, you will be accessing the full property object, which has all the methods for binding, listeners, and so on. However, if you follow the property name with parentheses, you will get (or set) the property value.

images Tip This use of parentheses is supported via the Scala apply and update syntax, which is commonly used for Arrays and Lists, but in ScalaFX is used to differentiate between the raw properties and their values.

One exception to this rule is in the object literal case (third example of Set Property Value), where you can use the assignment operator directly to set the value of a property (no parentheses needed). An unambiguous case, this is allowed because JavaFX properties references are read-only, and it significantly cleans up the syntax of the user code.

If you ever get confused between properties and values, Scala’s type system will come to the rescue. In general, the ScalaFX APIs have been designed to preserve strong typing and produce type errors anywhere the developer’s intent is ambiguous. Therefore, if you use a property where a type is expected, or vice versa, the compiler will most likely catch the error before you even run the application.

ScalaFX Bind APIs

Binding is arguably one of the most innovative features in the JavaFX library, however, the APIs can be a bit cumbersome to use, especially in cases where you have complicated bind logic. The fluent API provided is quite powerful and expressive given the constraints of the Java language, but lacks a lot of the elegance that made bind so powerful with JavaFX Script.

The ScalaFX Bind APIs sit on top of the JavaFX binding support, but wrap them in a programming language syntax that is natural to write and understand. By taking advantage of operator overloading and infix notation, you can write complex ScalaFX bind expressions without even knowing all of the bind API methods that come with JavaFX.

For example, here is how you bind the height of one Rectangle to the sum of the heights of two others:

rect1.height <== rect2.height + rect3.height

Other than the special bind operator (<==), and lack of parentheses to convert from properties to values, this is the same code that you would write to add up the heights statically. However, once the bind is in place, any updates to rect2 or rect3 will dynamically change the height of rect1.

What this expression actually translates to is the following JavaFX code in Java.

rect1.heightProperty().bind(rect2.heightProperty().add(rect3.heightProperty()));

Even for a simple bind expression such as this, it is easy to get lost in all the parentheses and method calls that Java requires.

You can also do aggregate operators in ScalaFX. Rather than having to use the static methods on the JavaFX Bindings class, the ScalaFX Includes import gives you all these functions as methods you can call directly, such as the following code to bind the width of one rectangle to the max width of three others.

rect1.width <== max(rect2.width, rect3.width, rect4.width)

The other type of bind expression that is extremely common is conditional statements. Creating conditionals with the JavaFX APIs is possible, again by using the static Bindings class, but much simpler using the ScalaFX APIs. The following code changes the strokeWidth based on whether the cursor is hovering (just as we did in the Vanishing Circles application earlier).

strokeWidth <== when (hover) then 4 otherwise 0

The expressions passed in for the conditional value and result clauses can be arbitrarily complicated. The following example combines Boolean logic, String concatenation, and a conditional expression.

text <== when (rect.hover || circle.hover && !disabled) then textField.text + " is enabled"
otherwise "disabled"

In all of the above examples, because you are writing code that sits directly on top of the JavaFX bind APIs, you get all the same benefits, such as lazy evaluation. Also, because Scala is a statically typed language like Java, you get these benefits without giving up type safety as you do with other dynamic languages. In fact, you get better type safety with the ScalaFX APIs, because it also supports type-safe dereferencing of subproperties. For example, in ScalaFX to bind a rectangle to the width of a Scene you would simply write:

rect1.width <== stage.scene.width

This works even if the Scene has not been created yet, such as during initialization of the Stage object. You can also accomplish the same thing in Java, but it requires a non-type-safe property selector:

rect.widthProperty().bind(Bindings.selectDouble(stage, "scene.width");

Underneath the covers, the ScalaFX calls this exact JavaFX API, but it protects the application developer from accessing a property that may be misspelled or of the wrong type.

Finally, a discussion of binding would not be complete without an example of bidirectional binding. This is similarly easy in ScalaFX, and can be accomplished using a slight variation on the bind operator as shown in the following example.

textField.text <==> model.name

This creates a bidirectional binding between the name property in the model and a TextField, such that if the user edits the text field, the model object will automatically be updated too.

Although most of the ScalaFX operators are fairly intuitive, there are a few cases where it was not possible to use the standard operators. These include:

  • if/else: These are Scala language keywords, so as demonstrated earlier, these have been replaced with when/otherwise, just as in the corresponding JavaFX APIs.
  • ==/!=: Directly using the equality and inequality operators produces some unwanted interactions with the same operations on the core Scala object base class. Instead use === and =!=, both of which were carefully chosen to have the same precedence rules as the operators they are replacing.

As an added bonus, you can specify the precision of the === and =!= operators for numeric comparison by using the following syntax.

aboutFiveHundred <== value1 === 500+-.1

This would test that value1 is less than .1 away from 500.

API Enhancements

By this time, you should have a pretty good feel for how the JavaFX applications you have been building translate to equivalent ScalaFX code. In general the ScalaFX APIs mirror the JavaFX APIs, providing equivalent functionality. However, in some cases it was actually possible to provide an improved API or alternative choice that matches the declarative style of programming that ScalaFX encourages. In this section we cover a few different areas in which ScalaFX improves on the JavaFX APIs.

Closures

One of the features that Java developers have been waiting eagerly to see introduced into the language is closures. This simplifies the case where you are creating event listeners or similar callbacks where you need to implement an interface that contains a single method.

Fortunately, most modern JVM languages include closures as a core language feature, as does the Scala language. This means that anywhere you would normally have to implement an event or property listener, you can instead use a closure to simplify your code.

Earlier in the Vanishing Circles application we showed an example of a closure to set a mouse click handler:

onMouseClicked = {
  Timeline(at (3 s) {radius -> 0}).play()
}

The closure part is really that simple; all you have to do is surround your method logic with curly braces and assign it to the property for the event handler. There is another variant of this if you also need access to some of the variables passed in, such as the MouseEvent:

onMouseClicked = { (e: MouseEvent) =>
  Timeline(at (3 s) {radius -> 0}).play()
}
Layout Constraints

The layout constraint mechanism introduced in JavaFX 2.0 is very flexible, because it lets layout authors define their own constraints that get stored on Node, but is also not ideal from an application developer standpoint. It forces your code into an imperative pattern where you add children in one step and then set constraints following that.

Because the set of interesting layout constraints is fairly small, the ScalaFX APIs simply add the common ones onto the Node class directly. This lets you specify constraints such as alignment, margin, and grow declaratively as you are creating your object tree.

For example, to add a margin to the Label in our Hello ScalaFX example, it is as easy as setting the margin property on the Label:

object HelloScalaFXMargin extends JFXApp {
  stage = new Stage {
    scene = new Scene {
      content = new Label {
        text = "Hello ScalaFX Margin"
        margin = new Insets(20)
      }
    }
  }
}

And this adds 20 pixels around the Label to space it out in the window. Without this ScalaFX feature, you would have been required to save a reference to the Label and later call the following.

StackPane.setMargin(new Insets(20));

An added benefit of the ScalaFX Node Layout Constraints is that they apply across all JavaFX layouts regardless of which type of container you are using. With the normal JavaFX Layout Constraints, you need to use the static method from the right layout type, otherwise the constraint will not work.

images Tip For layout authors who want to make use of the ScalaFX Node Layout Constraints, ScalaFX also stores the layout constraint in an unprefixed form that you can directly access. For example, to get the margin constraint, simply call node.getProperties().get(“margin”). For alignment you get your choice of accessing it as “alignment,” “halignment,” or “valignment,” all of which get updated anytime the user sets the alignment property.

Animation

ScalaFX provides a shortcut syntax for expressing timelines that was inspired by the same syntax in JavaFX Script. It lets you specify the duration, keyframes, keyvalues, and tweens in a shortcut syntax that fits on a single line. We used this earlier in the Vanishing Circles example to specify an animation that shrinks the circles when they are clicked:

Timeline(at (3 s) {radius -> 0}).play()

This code is equivalent to the following Java code using the JavaFX animation API.

KeyValue collapse = new KeyValue(circle.radiusProperty(), 0);
new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play();

As you can see, the ScalaFX variant is much more concise and readable even in this simple example. The basic syntax for animations in ScalaFX is:

at (duration) {[property -> value]}

This statement translates to a KeyFrame that can be added to a Timeline directly. You can pass in multiple KeyFrames to a Timeline or use multiple property->value pairs in the above syntax, giving you the flexibility to specify arbitrarily complex animations.

Breaking down this example further, notice that we used a shortcut syntax to create the Duration and the KeyValue. For the former, ScalaFX has an implicit conversion that adds a function to Doubles for ms(), s(), m(), and h(), allowing you to create a new Duration simply by calling the respective function. By using the postfix operator shortcut syntax in Scala, rather than calling “3.s()” or “3.s,” you can further abbreviate it to “3 s” (where the space between 3 and s is required).

For the latter, ScalaFX properties have an overloaded operator of “->” that takes a value and returns a KeyValue object. In fact, not only can you specify the target value, but you can also add in a tween expression as shown here:

radius -> 0 tween EASE_OUT

This addition causes the animation to slow down as it approaches the end, resulting in a smoother transition.

Visage, the JavaFX Language

Most programming languages have their basis in computation and mathematics, and grow towards their most common use case. For some languages, such as LISP, this is artificial intelligence. For others, such as Java, the predominant use case has become backend business applications. However, there are very few languages that are designed and tailored specifically for UI development.

Visage is an exception, inasmuch as it was originally conceived by Christopher Oliver as a UI language called F3 for Form Follows Function, specifically targeted at making it easier to program graphic user interfaces. It was renamed JavaFX Script and served as the UI programming language for all JavaFX applications prior to the release of JavaFX 2.0. Currently it is being developed as an independent UI programming language called Visage, available as open-source, and can be used with JavaFX and other UI toolkits.

Advantages of Using Visage

So what is it about Visage that makes it better suited for UI development than other programming languages? Here are some of the unique features of Visage that make it a superior environment for developing GUIs.

  • Null Safety: Visage applications never throw Null Pointer Exceptions. This is important for UI applications, because getting an NPE in your Java code grinds your application to a halt giving a very poor user experience.
  • Binding: Visage provides powerful compiled bind capabilities. Although you have seen the power of the JavaFX binding library, this goes beyond by allowing you to integrate data and interaction with a natural syntax.
  • Static Typing: Having a language that is specified with strong typing allows better tooling for IDEs and also avoids runtime errors that could blow up on your user unexpectedly.
  • Object Literals: Having a built-in construct for creating object trees allows you to create hierarchical UIs quickly with no external mark-up needed.
  • Built-in Animation: Visage has its own animation syntax for defining quick timeline-based animation with adapters to plug directly into the JavaFX animation library.
  • UI Types: In Visage, Colors, Lengths, Durations, and Angles are built-in types with a shorthand literal syntax. This allows you to have extremely concise UI code.

Some of these concepts are available in other languages or mirrored in APIs and DSLs, but it is the combination of these features when put together that give Visage its power. Visage is also a first-class JVM language with a compiler engineered by the Java compiler team at Sun Microsystems. This means you get the same power and polish that you have come to expect from the Java compiler toolchain, including reliable compiler errors and warnings, efficient byte-code generation, and fast compiler performance for large projects. In the next section we show you how to use these tools to get started with using Visage in your own projects.

Getting Started with Visage

To use Visage you need to download the latest version of the Visage compiler and JavaFX libraries from the Visage open-source web site. The project is currently hosted on Google Code and can be downloaded from the project home page at:

http://code.google.com/p/visage/

The file you need to download is the latest version of the Visage project, version 2.0 or higher. The distribution zip file contains everything you need to start writing JavaFX applications, including the compiler, core libraries, and runtime. We show you how to compile your code and run it from the command line. However, there is work in progress on updating the plug-ins for NetBeans, Eclipse, and IntelliJ to work with the latest release, so by the time you read this you can likely choose your favorite IDE.

Here is a short description of the package contents that come with Visage.

  • bin/visagec[.exe]: This is the compiler that you use to generate Java class files from the Visage source.  It has similar command line switches to javac.
  • bin/visage[w][.exe]: This is an executable that bootstraps your Visage application, doing initialization and setup required by the JavaFX APIs. It is analogous to the java and javaw commands you use to run Java applications.
  • bin/visagedoc[.exe]: Creates HTML API documentation from javadoc-style code comments in your source. This is analogous to the javadoc command for Java source.
  • doc/index.html: The visagedoc documentation for all of the core Visage classes.
  • lib/*: This contains the jar files you need to execute your Visage application, including core libraries as well as JavaFX specific adapters.

Our first Visage application is a single file that is compiled and executed from the command line. You can build on this to create more complicated applications or to integrate Visage with existing projects, but it should be enough to demonstrate the fundamentals.

Start by creating a file called HelloVisage.visage and type the code in Listing 10-16 into the file.

Listing 10-16. Hello Visage Application Demonstrating How to Build a Simple Visage App

import visage.javafx.stage.Stage;
import visage.javafx.scene.Scene;
import visage.javafx.scene.control.Label;

Stage {
  Scene {
    Label {text: "Hello Visage"}
  }
}

Writing a Visage JavaFX application is that simple; this same code would be over seven times as long if written in standard Java, but with Visage can be accomplished in only 39 characters (not including imports).

images Note For readers familiar with JavaFX Script, notice that we have left off the labels for the Stage and Scene’s contents. This is a new feature introduced in Visage called default variables that you can read more about in Appendix A.

To compile the application all you need to do is invoke the visagec compiler on this file. The command to run the compiler is shown in Listing 10-17.

Listing 10-17. Command to Run the visagec Compiler

visagec HelloVisage.visage

images Note The command line examples here assume that you have added %VISAGE_SDK%in (windows) or $VISAGE_SDK/bin (unix/mac) to your path, where VISAGE_SDK is the location to which you unzipped the Visage distribution. You can also simply prefix the commands with the path to the Visage SDK bin folder.

Notice that we did not need to specify the location of Java or the JavaFX Runtime. The Visage wrapper scripts automatically detect the location of these based on your platform and set up the classpath with all the necessary jars. Running your Visage application is similarly easy. To execute the Visage runtime wrapper, simply execute the command shown in Listing 10-18.

Listing 10-18. Command to Execute a Visage Application

visage HelloVisage

Running your application results in the output shown in Figure 10-14.

images

Figure 10-14. Output of the Hello Visage application

Writing and running Visage applications is really that simple. All the examples in this book can easily be rewritten in Visage and compiled and run in the same fashion. As an exercise, try porting some of the GUI examples from earlier chapters into the Visage language.

By using a UI-focused language such as Visage, you will find that your JavaFX code will be easier to write and more maintainable in the future. For more details about the Visage language, see Appendix A, which is a full language reference that includes plenty of examples.

Constructing UIs with FXML Markup

The last language we cover is not actually a language, but instead a type of markup. With the JavaFX 2.0 release, they added in an additional XML-based markup called FXML that provides a declarative way of creating your user interface.

FXML is particularly useful if you want to be able to treat your user interface as data. This may be the case if you need to load portions of the scene graph dynamically from disk or network without restarting. Also, if you are using a UI builder tool, it is highly likely that it will generate an FXML-based UI that you can load in your application to which to bind behavior. To teach the fundamentals of how FXML works, we show you how to handcode the XML format and load it into your application.

images Note In practice you will probably find that one of the alternative languages discussed earlier or even plain old Java is a better language for handwriting user interfaces.

Learning FXML by Example

To build a JavaFX application using FXML, you require at least two files. The first one is a JavaFX application, and the second is the FXML file you want to load into the scene graph. The JavaFX application file is typically written in Java, although any of the JVM languages we covered earlier in this chapter can be used to load an FXML file. The FXML file is always written in XML, and must conform to a specific format used by JavaFX.

To illustrate the use of FXML, we have converted the Dog Adoption Form from the GroovyFX section earlier in this chapter to Java and FXML. The first file in the project is a Java language file that extends the JavaFX Application class and builds the scene graph by loading an FXML file. The full source code is shown in Listing 10-19.

Listing 10-19. Java Source Code for Loading an FXML File

package com.projavafx.fxml;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class FXMLAdoptionForm extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("FXML GridPane Demo");
        Parent root = FXMLLoader.load(getClass().getResource("AdoptionForm.fxml"));
        stage.setScene(new Scene(root));
        stage.show();
    }
}

images Note Due to a NetBeans bug, you may get an NullPointerException caused by a missing resource. If this happens, simply clean and rebuild your project to have the resources copied over properly.

Other than the line that calls the static load method on FXMLLoader, this could be the start of any of the JavaFX examples throughout this book. In this case we are loading the FXML file via a relative classpath reference to a local resource, but you can also load FXML from the network or generate it programmatically.

The second file needed in any FXML application is the XML file that contains the UI markup. FXML is expressive enough to let you build up an entire scene graph, just as you would normally have done via either imperative or builder code in Java. In fact, FXML relies upon method lookups in the Java builder classes, so the property names exactly match what you would normally call when using the builder pattern.

The full source code for the converted Pet Adoption application is shown in Listing 10-20. Just to keep things interesting, we have swapped the test and graphics for a cat-themed UI instead.

Listing 10-20. FXML Markup for the Cat Adoption Form UI

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.collections.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>

<StackPane prefWidth="400" prefHeight="500" xmlns:fx="http://javafx.com/fxml">
    <children>
        <ImageView>
            <image>
                <Image url="@cat.jpg" requestedWidth="800" requestedHeight="800"
                   preserveRatio="true"/>
            </image>
            <effect>
                <ColorAdjust brightness="0.1">
                    <input>
                        <GaussianBlur/>
                    </input>
                </ColorAdjust>
            </effect>
        </ImageView>
        <GridPane hgap="10" vgap="10" style="-fx-padding: 20">
            <columnConstraints>
                <ColumnConstraints minWidth="60" halignment="right"/>
                <ColumnConstraints prefWidth="300" hgrow="always"/>
            </columnConstraints>
            <children>
                <Label text="Cat Adoption Form" style="-fx-font-size: 24"
                  GridPane.halignment="center" GridPane.columnSpan="2147483647"/>

                <Label text="Size: " GridPane.rowIndex="2"/>
                <TextField promptText="approximate size in pounds"
                  GridPane.rowIndex="2" GridPane.columnIndex="1"/>

                <Label text="Breed: " GridPane.rowIndex="3"/>
                <TextField promptText="pet breed"
                  GridPane.rowIndex="3" GridPane.columnIndex="1"/>

                <Label text="Sex: " GridPane.rowIndex="4"/>
                <ChoiceBox GridPane.rowIndex="4" GridPane.columnIndex="1">
                    <items>
                        <FXCollections fx:factory="observableArrayList">
                            <String fx:value="Male"/>
                            <String fx:value="Female"/>
                            <String fx:value="Either"/>
                        </FXCollections>
                    </items>
                </ChoiceBox>

                <Label text="Additional Info: " wrapText="true" textAlignment="right"
                  GridPane.rowIndex="5" GridPane.valignment="baseline"/>
                <TextArea prefRowCount="8" wrapText="true"
                  GridPane.rowIndex="5" GridPane.columnIndex="1" GridPane.vgrow="always"/>

                <Button text="Submit" GridPane.rowIndex="6" GridPane.columnIndex="1"
                  GridPane.halignment="right""/>
            </children>
        </GridPane>
    </children>
</StackPane>

Although the FXML is not nearly as succinct as the earlier GroovyFX code, it retains a lot of the benefits of the declarative style by nesting XML elements. By reading through this code, you should have gotten the hang of the basic FXML file format:

  • All FXML files should start with an XML preamble (<?xml version =" 1.0" encoding="UTF-8"?>
  • Any JavaFX imports you require should also be declared as processing instructions following the preamble.
  • The body of the XML document contains a hierarchical scene definition, the root of which gets returned when the FXML document is loaded.

From here, you should see all the class names (elements) and properties (attributes) that you recognize from writing JavaFX code in Java. There are, however, a few important exceptions that let you declare a complete UI declaratively:

  1. Complex properties (lists, Nodes, or other structures that cannot easily be represented as attribute values) can be declared as nested child elements.
  2. Layout constraints (e.g., margin, alignment, grow) can be declared inline by prefacing them with a static reference to the layout class.
  3. To declare an ObservableList inline, you can use the special factory attribute to refer to a static list constructor, such as those found in FXCollections (see the ChoiceBox list initialization above).
  4. To reference a relative file, you can prefix filenames with an “@” symbol (see the Image loading code above).

Also, for the most part FXML is namespace-free, which makes writing documents much less tedious, but there are a few important tags that require the “http://javafx.com/fxml” namespace, so it is a good idea to declare this on the root element of your document.

Running the completed application will give you very similar output to the earlier Dog Adoption Form example as shown in Figure 10-15, except you have now accomplished this with dynamic XML instead of compiled code.

images

Figure 10-15. Completed Cat Adoption Form application showing a cat with heterochromia in the background4

__________

4 Public domain picture from Wikimedia Commons: http://commons.wikimedia.org/wiki/File:Cat_Eyes.jpg

Controlling FXML Applications

We showed how you can declaratively build applications with FXML, however, this does not let you control and interact with the FXML-generated scene graph from your Java code very easily. To make this interaction more straightforward, FXML also supports a controller class that you can hook up to the generated user interface.

To demonstrate the use of a controller in FXML applications, we augment the previous example to add a controller that shows or hides grid lines when the submit button is clicked. The first step is to write the controller code, which must implement the FXML Initializable interface as shown in Listing 10-21.

Listing 10-21. Java Source Code for an FXML Controller

package com.projavafx.fxml;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.GridPane;

public class AdoptionFormController implements Initializable {
    @FXML
    private GridPane grid;

    @FXML
    private void handleSubmit(ActionEvent event) {
        grid.setGridLinesVisible(!grid.isGridLinesVisible());
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // no init action required
    }
}

Notice that we have made use of the FXML annotation on both the grid variable and the handleSubmit method. This tells the FXML support that these methods correspond to ids and references in the FXML document. To complete the example, let’s add these references.

The first change to the FXML document is to add in a reference to the FXML controller by modifying the root element as shown here:

<StackPane prefWidth="400" prefHeight="500" xmlns:fx="http://javafx.com/fxml"
  fx:controller="com.projavafx.fxml.AdoptionFormController">

The second change is to add in an id reference to the grid that we want to have access to from our Java code:

<GridPane fx:id="grid" hgap="10" vgap="10" style="-fx-padding: 20">

Notice that we have used the namespace prefixed fx:id attribute, rather than just id.

Finally, we have to hook up the event listener to call our handleSubmit method by adding in a reference in the FXML:

<Button text="Submit" GridPane.rowIndex="6" GridPane.columnIndex="1" GridPane.halignment="right" onAction="#handleSubmit"/>

With these three changes to the FXML, plus the controller code we wrote earlier, we have created interaction between the FXML and Java code. If you rerun the application and click the Submit button, you get some helpful gridlines that you can use to debug your layout code in the future as shown in Figure 10-16.

images

Figure 10-15. Modified Cat Adoption Form application that shows grid lines when you click Submit

Summary

As we have shown you in the chapter, you have a lot more options for writing JavaFX code than just using the Java language. You can declare your UI as markup using FXML, there are already several DSLs available written in popular JVM languages such as Groovy and Scala, and you can even use a dedicated UI programming language, such as Visage.

The great thing is that you have the choice to use the language and markup that best suits your project needs. All of these technologies integrate cleanly with JavaFX code written in Java, and have their own benefits that you can take advantage of based on the needs of your project.

If you are interested in learning more about the different JVM languages and markup for JavaFX that we have discussed in this chapter, please refer to the resources section. Also, make sure to check out the final chapter of this book, "Appendix A : The Visage Language Guide," to find out more about this great UI language in detail.

Resources

For more information about Groovy and GroovyFX, consult the following resources.

Additional information on Scala and ScalaFX can be found here:

Here is a resource on the Visage Language:

And finally some FXML resources:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.117.214