e(fx)clipse, from BestSolution.at

JavaFX 2.0 Tooling and Runtime for Eclipse and OSGi

fxgraph is a simple DSL to define an (JavaFX 2.0) object graph but instead of generating java or java byte code it generates FXML a special xml-format supported by the JavaFX 2.0 library.

The language supports the following concepts:

  • JSON like object graph notation
  • Static "properties" access (used by mainly to set layout properties)
  • Binding expressions
  • (Java-)Controller binding (used for event-listeners)
  • Translation support
  • Support for CSS-Referencing
  • Live Preview of UI

Like with any other language let's start with an Hello World application to explain the main building blocks

  1. package at.bestsolution.efxclipse.tutorial.fxgraph
  2. import javafx.scene.control.*
  3. import javafx.scene.layout.*
  4. component HelloWorld {
  5.   BorderPane {
  6.     center : Button {
  7.       text : "Hello World"
  8.     }
  9.   }
  10. }

As you'll directly notice the language looks quite similar to JSON to define an object graph but because Java unlike JavaScript has the concept of a package on can import classes from a package so that you don't have to specify the fully qualified name.

Another difference to JSON is that the definition starts with a component keyword which typically matches the filename so you the above definition is stored in a file called HelloWorld.fxgraph

The attribute and its value are seperated by the ":" (colon) and the following simple values are supported directly:

  • String
  • Number (integer and decimal numbers)
  • Object-Definition
More complex values (method references, object references, ...) are described later in the document because they require a special syntax

Normally every UI has to support multiple locales and so instead of putting the UI-Strings directly in to the UI-Definition one puts them into an external resource and loads the correct one based upon the users locale.

A standard way in java to provide such locale informations is that one puts them values in a Properties-File and loads them using java.lang.ResourceBundle at runtime.

Let's suppose you have a message.properties file located next to your HelloWorld.fxgraph-File with the following content:

  1. mybutton.label = Hello World

You can reference the file and the property in your fxgraph-File like this:

  1. package at.bestsolution.efxclipse.tutorial.fxgraph
  2. import javafx.scene.control.*
  3. import javafx.scene.layout.*
  4. component HelloWorld resourcefile "messages.properties" {
  5.   BorderPane {
  6.     center : Button {
  7.       text : rstring "button.label"
  8.     }
  9.   }
  10. }

An UI that does not respond to user interaction is quite meaningless so e.g. if a user clicks a button we'd like to react on the button press. One typically implements such UI callbacks in a programming language like Java, JavaScript, ... .

Let's react on the button press using a controller like this:

  1. package at.bestsolution.efxclipse.tutorial.fxgraph;
  2. import javafx.event.ActionEvent;
  3. import javafx.fxml.FXML;
  4. public class HelloWorldController {
  5. @FXML
  6. public void clicked(ActionEvent event) {
  7. System.out.println("Button clicked");
  8. }
  9. }

Referencing methods like the above is quite easy using the "controllermethod" keyword:

  1. package at.bestsolution.efxclipse.tutorial.fxgraph
  2. import javafx.scene.control.*
  3. import javafx.scene.layout.*
  4. import at.bestsolution.efxclipse.tutorial.fxgraph.*
  5. component HelloWorld controlledby HelloWorldController resourcefile "messages.properties" {
  6.   BorderPane {
  7.     center : Button {
  8.       text : rstring "button.label"
  9.       onAction : controllermethod clicked
  10.     }
  11.   }
  12. }

FXML propagates a clean seperation between UI defined in the FXML-File and business logic implemented in a programming language like Java, JavaScript, ... .

A controller could look like this:

  1. package at.bestsolution.efxclipse.tutorial.fxgraph;
  2. import javafx.event.ActionEvent;
  3. import javafx.fxml.FXML;
  4. public class HelloWorldController {
  5. @FXML
  6. public Button myButton;
  7. @FXML
  8. public void clicked(ActionEvent event) {
  9. System.out.println("Button clicked");
  10. }
  11. }

You can make the FXMLLoader set the myButton-Field by using the id keyword

  1. package at.bestsolution.efxclipse.tutorial.fxgraph
  2. import javafx.scene.control.*
  3. import javafx.scene.layout.*
  4. import at.bestsolution.efxclipse.tutorial.fxgraph.*
  5. component HelloWorld controlledby HelloWorldController resourcefile "messages.properties" {
  6.   BorderPane {
  7.     center : Button id myButton {
  8.       text : rstring "button.label"
  9.       onAction : controllermethod clicked
  10.     }
  11.   }
  12. }

FXGraph files can be previewed while rendering but because most UI-Themeing informations in JavaFX applications are configured through CSS the preview needs to know which css-File it should apply.

  1. package at.bestsolution.efxclipse.tutorial.fxgraph
  2. import javafx.scene.control.*
  3. import javafx.scene.layout.*
  4. import at.bestsolution.efxclipse.tutorial.fxgraph.*
  5. component HelloWorld controlledby HelloWorldController styledwith ["hello.css"] resourcefile "messages.properties" {
  6.   BorderPane {
  7.     center : Button id myButton {
  8.       text : rstring "button.label"
  9.       onAction : controllermethod clicked
  10.     }
  11.   }
  12. }

Inside the square bracket you can provide multiple css-files. The path you are allowed to use are:

  • Relative to the fxgraph-File
    e.g. hello.css
  • Absolute to the project by starting the path using "/"
    e.g. /css/test.css
  • Absolute to the workspace by using "platform:/resource/${projectname}/..."
    e.g. platform:/resource/at.bestsolution.efxclipse.testcases.fxgraph/css/test.css
Having only one CSS-File is a very common usecase so you can omit the square brackets if you only want to reference a single css-File

  1. package at.bestsolution.efxclipse.tutorial.fxgraph
  2. import javafx.scene.control.*
  3. import javafx.scene.layout.*
  4. import at.bestsolution.efxclipse.tutorial.fxgraph.*
  5. component HelloWorld controlledby HelloWorldController styledwith "hello.css" resourcefile "messages.properties" {
  6.   BorderPane {
  7.     center : Button id myButton {
  8.       text : rstring "button.label"
  9.       onAction : controllermethod clicked
  10.     }
  11.   }
  12. }

FXML represents the tree structure of UI but there are some objects living outside this tree. One such an example is a ToggleGroup-instance which is used to link e.g. RadioButtons together. To create an object which is not part of the tree structure and reference it inside the UI later on is done through so called defines.

Defines can be added at the beginning of the component definition:

  1. package at.bestsolution.efxclipse.tutorial.fxgraph
  2. import javafx.scene.control.*
  3. import javafx.scene.layout.*
  4. import at.bestsolution.efxclipse.tutorial.fxgraph.*
  5. component HelloWorld controlledby HelloWorldController styledwith "hello.css" resourcefile "messages.properties" {
  6.   define ToggleGroup id myRadioGroup
  7.   BorderPane {
  8.     center : Button id myButton {
  9.       text : rstring "button.label"
  10.       onAction : controllermethod clicked
  11.     }
  12.   }
  13. }

If you now have to reference such an element you do this using the idref keyword.

  1. package at.bestsolution.efxclipse.tutorial.fxgraph
  2. import javafx.scene.control.*
  3. import javafx.scene.layout.*
  4. import at.bestsolution.efxclipse.tutorial.fxgraph.*
  5. component HelloWorld controlledby HelloWorldController styledwith "hello.css" resourcefile "messages.properties" {
  6.   define ToggleGroup id myRadioGroup
  7.   BorderPane {
  8.     top : HBox {
  9.       children : [
  10.         RadioButton {
  11.           text : "Option 1",
  12.           toggleGroup : idref myRadioGroup
  13.         },
  14.         RadioButton {
  15.           text : "Option 2",
  16.           toggleGroup : idref myRadioGroup
  17.         }
  18.       ]
  19.     },
  20.     center : Button id myButton {
  21.       text : rstring "button.label"
  22.       onAction : controllermethod clicked
  23.     }
  24.   }
  25. }

The useage of the idref is not restricted to elements created through defines but can be used to reference any other element marked with an id