Graphical User Interfaces: Display and Layout

One of the driving forces behind the development of object-oriented programming was the need to create graphical user interfaces (GUIs). It is not surprising, then, that the OO programming model really shines when building GUIs.

In fact, the standard WIMP model of graphical user interfaces (Windows, Icons, Menus, Pointer) was developed at Xerox PARC during the 1970's in and along with one of the first object-oriented programming languages, Smalltalk. GUIs influenced the language design and vice versa. The WIMP model first saw widespread deployment by Apple with the first Macintosh computer and was soon adopted by Microsoft Windows as well.

In this approach to graphical user interface design, the user interface is represented internally as a tree structure, in which the various user interface components (sometimes called widgets) are nodes in the tree, and the parent–child relationship corresponds to containment. In fact, the HTML markup language follows this same approach to defining a user interface.

We will be using JavaFX, a modern object-oriented GUI library. In such a library, there are two interacting hierarchies: first, the containment hierarchy corresponding to the tree of UI components, and second, the class hierarchy of the components themselves. Unlike in HTML, the class hierarchy is extensible, allowing developers to use inheritance to design new components or to customize the behavior of existing components.

Scene Graph

JavaFX manages the user interface as a scene graph, which is actually a tree of nodes. At the root of the scene graph is some node; the node is registered with a Scene object that is in turn registered with a Stage, which corresponds to a top-level window in the application.

Node class hierarchy

JavaFX provides a rich class hierarchy of node types that can appear in the Scene Graph. Some of them provide rendering capability, others are useful for layout, and some are useful user interface devices such as buttons or text editors.

Building a GUI

A scene graph must be constructed. This can be done either by either

All of the non-leaf nodes in the tree are a subclass of Parent (typically a subclass of Pane).

To add a child using code:

Parent p;
Node n;
p.getChildren().add(n);

The getChildren() method returns a collection of children that is tied to the actual children of the parent node. Thus, adding a new node to the collection causes the parent node to acquire a new child. The collection can be used in various other ways, however; for example, it can be used to iterate over the children or to listen to its contents to find out when the set of children changes.

A scene graph can also be created in advance using the JavaFX SceneBuilder application. SceneBuilder saves the scene is saved in XML format in an .fxml file, which can be loaded by the application to create the entire scene graph at once.

Layout

Some JavaFX nodes just display themselves (e.g., Rectangle). Some nodes do something active (e.g., Button, TextField). Other nodes are just there to control the layout of other nodes in the window. Examples of these are the various subclasses of Pane. Pane places all its children in the upper left corner, but its subclasses override this layout behavior in various ways.

For example, the classes HBox and VBox lay out their children in a horizontal row or vertical column, respectively. StackPane stacks all its children on top of each other in the center. GridPane organizes its children into a regular grid, like an HTML table. FlowPane lays out its children like words of text, in a series of top-to-bottom rows in which each row contains children laid out left-to-right. BorderPane expects to have five children; one is placed in the center and the four others are placed at the top, bottom, right, and left.

Appearance

There are several ways to control the appearance of the scene. Some nodes have handles that allow them to be minimized, maximized, or resized. There are also various controls such as sliders or knobs to control shape, size, color, or other aspect of the appearance. Nodes can also be added, removed, moved, or transformed. With JavaFX, when such a change is made and the scene needs to be redrawn, the visible components of the scene redraw themselves in the right order.

A useful feature of JavaFX is that nodes can be styled using style sheets (CSS), similar to how HTML pages are styled (e.g., p.setStyle("-fx-background-color: #ffff00");). The style sheet can also be created in advance and loaded from an external file. Style sheets provide an easy way to rapidly experiment with different looks for an application without recompiling the application.

It is also possible to draw onto a Canvas using a GraphicsContext object. One can draw shapes such as rectangles, circles, ellipses, or curves. Canvases give complete control over rendering anything within their borders, but the program has the responsibility for the rendering. One difference from Swing, JavaFX's predecessor, is that exposed pixels are redrawn automatically.

Event-driven programs

JavaFX applications are typically event driven. This is a somewhat different programming paradigm from the usual imperative style. Its motto is: "Don't call us, we'll call you." When some hardware event takes place such as a mouse click or key press, an Event object is generated. An Event object is simply a Java object like any other Java object. It contains information about the source and type of the event: button click, mouse movement, keystroke, etc. The event handler of any listener that has registered to receive the event is called, and the event object is passed to the handler. In that way an application can receive and respond to events such as mouse clicks and button pushes.

Threads

Unlike simple Java programs, a JavaFX application typically has multiple concurrent threads: a main thread, a application thread, and a rendering thread, as well as various background worker threads, depending on the application. The application thread's sole task is to loop continuously, listening for the occurrence of hardware-generated events such as mouse clicks. For each such occurrence, it constructs an Event object describing the event and the GUI component in which it occurred, then calls the event handler of each registered listener with that Event object. The rendering thread's task is to continuously rerender the display.

Event handlers should not contain long-running computations, because this will tie up the application thread, preventing events from being handled. While the application thread is carrying out a long-running computation, the user interface will freeze and not respond to events. Fortunately, there is a mechanism for scheduling an update of the GUI on the application thread later using the method Platform.runLater(Runnable r). The run() method of the Runnable object r is called at the earliest available opportunity by the application thread.

Programming with threads is challenging because they share access to the same objects. If two threads access the same object, it is challenging to prevent them from interfering. If one thread is in the middle of modifying an object while another thread accesses the object, it is easy for the observing thread to see the object in a state in which its class invariant does not hold. A good programming pattern for preventing interference is to have most objects owned by a single thread. Accesses (either reading or writing) to an object by a thread that does own the object are unsafe and are not permitted by the pattern.

The JavaFX scene graph follows this pattern. The scene graph is owned by the JavaFX application thread. If you create a background thread to do more time-consuming computations, it may be tempting to have the background thread access the nodes in the scene graph. Such accesses are not allowed. JavaFX provides other mechanisms for background threads to communicate with the application thread, notably the method Platform.runLater().