First steps with JavaFX

Today I want to write about my first steps with JavaFX and try to go through my first application step by step. I only provide some examples and in this case it’s not a copy-and-run example! You must read the documentation which is mentioned below. In order to see the full code of the application, browse the sources in my GitHub project: https://github.com/seeebiii/PandocGUI

This week I had a small problem: I had to convert some wiki pages from a GitHub project from markdown format to another format like *.docx. So, I remembered a friend told me a few weeks ago about Pandoc which can convert a lot of documentation formats to a lot more of documentation formats. I gave it a try: searched for “Pandoc download” and found an installer for Windows. Unfortunately I’m a person who prefers GUIs, thus I was a little bit disappointed as I realized that I had to use the console for conversion. But after converting I was fascinated that it worked pretty well and fast, I only had to do a few changes, because if you want to convert multiple input files, Pandoc merges all input files together to one file. A few hours later I decided to create a GUI and started a JavaFX project.

After reading this documentation about the basic layout which consists of one or more panes, it was pretty easy to program a working JavaFX application. I decided to describe the layout via FXML (take a look here to get started with an application and read this to know how to create FXML files), because this is straight forward:

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

<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane prefHeight="400.0" prefWidth="550.0" xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1"
            fx:controller="de.sebastianhesse.pandocgui.Controller">

    <center>
         <!-- Here is your main content in the center, like text fields and the list view later -->
    </center>
    
    <bottom>
         <!-- Here is your bottom line -->
    </bottom>
</BorderPane>

This simple layout creates a bordered layout with only a center and a bottom box. If you don’t specify top, right or left, they won’t be displayed. The attribute fx:controller=”…” configurates a controller class which is associated with this FXML file. I will come to that later again.

Next to do is to create some elements like text fields, buttons, text areas and a list view. But before doing so, you need to think how you want to arrange them. I decided to use a GridPane and order everything in a table-like manner with rows and columns:

<center>
    <GridPane>
            <!-- Pandoc executable location -->
            <Label text="Your Pandoc executable location: " GridPane.rowIndex="0" />
            <HBox maxWidth="500" GridPane.rowIndex="1">
                <TextField fx:id="pandocLocation" prefWidth="400" GridPane.columnIndex="0"/>
                <Button onAction="#openPandocLocationFileDialog" text="Select" GridPane.columnIndex="1"/>
            </HBox>

    </GridPane>
</center>

The code listing shows a GridPane containing a label element and an hbox containing a text field and a button. An hbox only structures its elements in a horizontal way and vbox vertically, respectively. To stick the elements to the GridPane, you set an attribute as GridPane.* to your element, e.g. GridPane.rowIndex=”1″. This sticks your element to the first row. You can also define them to a column so that you have you element in one special cell.

You can now fill the other rows and columns, but this is omitted here, you can look up the sources if you want to.

You probably noticed the marked lines. These lines contain two special attributes:

  1. fx:id=”pandocLocation” -> Of course, as the keyword id indicates this identifies the element. But it has something more to offer: In the controller which is linked to the root pane you can create a class attribute with the same name and type and annotate it with @FXML and your dependency will be injected 🙂 See example below.
  2. onAction=”#openPandocLocationFileDialog” -> Also very intuitive, the attribute onAction defines the name of an action to call and this is a method which must be placed into the controller.

Let’s take a look at the example controller:

public class Controller {

    @FXML
    private Stage stage;
    @FXML
    private TextField pandocLocation;


    /**
     * FileChooser for Pandoc executable location
     */
    private FileChooser pandocLocationFileChooser = new FileChooser();

    /**
     * Opens a file dialog for location of Pandoc executable. Stores path in {@link #pandocLocation}.
     */
    @FXML
    public void openPandocLocationFileDialog() {
        File pandocExecutable = this.pandocLocationFileChooser.showOpenDialog(this.stage);
        if (null != pandocExecutable) {
            this.pandocLocation.setText(pandocExecutable.getPath());
        }
    }
    /* .... */
}

There is not a lot to explain: the Stage attribute is something like the root element for JavaFX applications, so you must specify it when you need a file dialog within the application. The FileChooser is a class to get a file dialog from. And @FXML annotates methods and fields to call and inject into your controller – very easy stuff!

This controller would be enough if you only want to see a text field, a button to open a file dialog to select the Pandoc executable and store the file path into the text field. The onAction method is called directly, you don’t have to create any listeners or something like that 🙂