Categories: Java
Introduction
There are many different ways for a Java application to create a (non-web) graphical interface; the primary ones are:
- AWT
- Swing
- SWT/JFace
- JavaFX
- GTK+ and Qt bindings
This article looks briefly at the differences between these approaches.
Low-level Graphics
Operating systems generally support graphics in one of the following ways:
- the kernel provides the absolute minimum needed for user-space to perform graphics;
- the kernel provides moderate hooks; or
- the kernel provides basic graphical rendering primitives directly;
The Linux kernel originally supported category (1) only; it exposes raw kernel memory and machine registers via file /dev/mem
which an X server implementation running in userspace as root can use to directly read/write the control registers of whichever graphics card the machine has.
The Linux DRM (Direct Rendering Manager) framework falls into category (2); the DRM API is a moderately sophisticated way of communicating with graphics devices. However it stops short of providing specific “draw a line” type operations; the kernel just manages modesetting and the transfer of buffers of GPU-specific instructions from userspace to the graphics driver. Card-specific userspace code is still required to generate the appropriate card-specific drawing operations. The DRM infrastructure can be used for rendering both 2D and 3D graphics.
Linux framebuffer drivers fall at least partially into category (3). They run within the kernel and provide an API to userspace for operations including rendering filled rectangles and performing “bitblits”. However graphics on Linux systems is generally not rendered via framebuffer drivers, as they are very limited and have insufficient performance - and are limited to 2D operations only.
Microsoft Windows NT and later fall into category (3); the GDI API which provides primitive rendering operations is implemented within the kernel itself.
No operating system (as far as I know) has ever supported user interface widgets as part of the operating system kernel; such code is always in userspace libraries which generate lower-level drawing commands (lines, etc) for execution either by the kernel (MS-Windows), or by additional userspace code.
Even the X display server does not offer “widgets”, just drawing commands. X client applications
Native Widget Toolkits and Wrapper Toolkits
Most applications are not willing to “draw” their interfaces directly with low-level graphics operations (draw line, draw text, etc). There are various libraries of widgets (aka widget toolkits) that can be used to draw things such as menubars, labels, buttons, combo-boxes - and to simplify the rendering of application content into these widgets and to simplify the handling of user interaction with such widgets.
Examples of such libraries are:
- the original MS-Windows user interface libraries (comctl32.dll, etc).
- Windows Presentation Foundation
- Motif - the original widget library for X11 (and the open-source reimplementation, Lesstif)
- GTK+ - best known as the widget library used by the Gnome desktop, but also used by other software (Gimp, Cinnamon, etc)
- Qt - best known as the widget library used by the KDE desktop
- FLTK - a “light-weight and portable” widget library
- Elementary - part of the Enlightenment Foundation Libraries which are used by the Enlightenment desktop.
- Tk - most commonly used from the Tcl language (Tcl/Tk)
- Swing - a pure-java widget library (see later)
- JavaFX - another pure-java widget library (see later)
On MS-Windows operating systems, there are two official widget libraries from Microsoft, and 99% of MS-Windows software uses one of them. The GTK+ and Qt libraries can be run on MS-Windows.
On Linux and other open-source unix-like systems, GTK+ and Qt are both in widespread use. Elementary is not widely used at the moment, but may become more significant in the future (under active development). Tk and FLTK are rarely used. Motif/Lesstif are almost dead.
There are also libraries which wrap one or more of the above libraries, trying to either provide a “cross-platform API” that maps to whatever native widget library is available locally, or simply because they think they can improve on the API provided by the native library. Examples are:
- MFC (Microsoft Foundation Classes) - a c++ wrapper around the base MS-Windows user interface APIs
- Windows Forms - a .NET wrapper around the base MS-Windows user interface APIs
- WxWidgets - wraps MS-Windows and GTK+
- gtkmm - a c++ wrapper around the GTK+ library
- AWT - a pure-java library that wraps multiple underlying widget libraries
- SWT - a pure-java library that wraps multiple underlying widget libraries
Mixing Multiple Widget Libraries on One Platform
As widget libraries sit “on top of” an operating system, it is possible to have multiple active at once, eg on MS-Windows to have some applications using the default MS-Windows API library while other applications are using GTK+ or Qt. In this case, the applications can potentially look somewhat different, which is rather confusing to the user. Most widget toolkits support the same core models: menus, buttons, scrollbars, text-input-fields, labels, etc. However the way they are drawn and the way they behave when interacted with can be somewhat different.
Many toolkits support the concept of “themes” which change their appearance and behaviour. Ensuring all widget libraries on a single system use themes which make them behave similarly helps a good deal - but seldom lets them work 100% identically.
Cross-platform widget libraries which wrap (delegate to) one of several native implementations do not suffer from this “inconsistency” problem; an app using the wrapping library is still (indirectly) using the components as other apps on the same system which use the system’s native library directly and so will behave identically. However such libraries either need to restrict themselves to offering a subset of functionality which is available on all supported (wrapped) platforms or need to do complicated emulation/transformation which can go wrong, sometimes leading to strange behaviour.
AWT
AWT (Abstract Widget Toolkit) was the first attempt to build a widget toolkit for Java. The general approach is to delegate to whatever native widget library is available on the local platform, ie to build a java wrapper class for each of the native graphical “widgets”. An instance of a wrapper creates the corresponding native control and delegates to it.
The API is cross-platform, ie identical on all platforms. In some cases, the “subset of functionality” approach has been taken, ie some functionality supported by platform X is not available in AWT because some other supported platform does not support it. In other cases, emulation is used on the platforms that do not support the behaviour natively.
For a long time, AWT had a reputation for unreliability. The wrap/delegate approach led to many odd problems such as wrong “depth” of widgets (widgets being obscured by other widgets). Most of these were eventually fixed.
A more significant problem in AWT is that it uses the then-trendy “deep class-based hierarchy” pattern. To handle events in an AWT widget (eg handle clicks), the programmer subclasses a concrete AWT class and overrides methods. Subclassing of concrete classes was eventually found to be a bad way to build OO programs; using interfaces rather than classes produces much cleaner and more maintainable code; see composition over inheritance.
AWT does require some supporting native code to bridge from the Java world into the local native code APIs, but this native code does very little.
Swing
The second attempt to build a widget toolkit for Java is Swing. This solved the problem of AWT’s deep class hierarchies by using the listener pattern extensively, where code registers implementations of some listener interface on a widget rather than subclassing it. This is a definite step forward.
Swing also has a somewhat higher-level API between applications and widgets: a model-view-controller design.
And Swing made a radical change to another aspect of AWT: rather than delegate to a native toolkit, it draws the widgets itself. This solves all of the nasty interactions that AWT was prone to, where widgets would obscure other widgets, or would not receive focus at the expected time, etc. It also fixes the problem that AWT sometimes breaks when the underlying operating-system is updated (changing the behaviour of the native widgets in subtle ways). And it means that cross-platform portability is far more reliable; an application that works on one platform is much more likely to work correctly on other platforms.
However drawing widgets directly has another effect: the applications no longer look “native”. Swing-based applications look and feel different than the standard applications on the same operating system. Themes have been produced for Swing that attempt to mimic the look-and-feel of MS-Windows, GTK+, etc. The first attempts were pretty poor; later attempts are somewhat better but Swing-based applications still usually look out-of-place.
The other drawback of drawing widgets directly is speed - Swing applications (particularly for early version of Swing) feel far less responsive than ones using native widgets. This also eventually improved; the developers of the NetBeans Java IDE claim that their UI code runs close to native speed, due to careful and efficient use of Swing. Nevertheless, many Swing-based apps still feel less than “snappy”.
SWT and JFace
Due to the problems with Swing, a development team at IBM created the SWT library - basically a design half-way between AWT and Swing. This library was used as the basis for the Eclipse IDE, and eventually open-sourced.
SWT delegates to a native toolkit (as AWT does) for most functionality. Behaviour/widgets not provided natively by the library are emulated. However calling into native widgets requires a layer of native code to bridge between Java and the (non-Java) target widget toolkit. The SWT jarfile has the relevant native code embedded into it, meaning that different jarfiles exist for each supportred CPU architecture, operating system, and target widget library. The standard AWT implementation also includes CPU/OS-specific native code but as it is part of the JRE download which is already CPU/OS-specific this isn’t a significant problem.
SWT is less ambitious in the “portable” aspect than Swing or even AWT; its APIs are fairly close to Win32 APIs (with GTK+ also an influence). This makes development easier for users familiar with those APIs, and simplifies implementation of the native SWT libraries for those primary platforms. However SWT may not be implementable at all on some platforms.
Because SWT delegates to native widgets, it is not independently themable and always has the look-and-feel of the underlying native library.
Like the Win32/GTK+ APIs it is modelled on, SWT uses “composition over inheritance”, ie SWT classes are generally customised by registering handler-objects which implement appropriate interfaces, rather than the AWT approach of subclassing widget types. The only significant exceptions (ie SWT classes designed to support subclassing) are:
-
org.eclipse.swt.widgets.Dialog
which is the base class for “popup” windows such as the file-selection dialog. -
org.eclipse.swt.widgets.Composite
which is a concrete class that holds multiple “child widgets”, and is sometimes subclassed to customise the way in which the child widgets interact. -
org.eclipse.swt.widgets.Canvas
which is an area on which arbitrary drawing operations can be performed
JFace was created later as an “extension library” for SWT. It has a few widgets whose functionality replaces equivalent widgets in SWT but mostly it offers APIs dealing with higher-level tasks that may internally build multiple SWT widgets. The JFace API has a somewhat different flavour than SWT, with its own terminology and design patterns. In particular, SWT’s Win32-like API is not a “model-view-controller” style API, while JFace is. JFace does not attempt to hide its use of underlying SWT; it deliberately exposes SWT APIs itself. JFace and SWT widgets can be readily mixed together.
The most significant users of SWT and JFace are still the Eclipse IDE - and any application built on the Eclipse Rich Client Platform (RCP).
The SWT library can be downloaded from eclipse here. Alternately, search the Maven repository for groupId “org.eclipse.swt”, then download the latest jarfile for the appropriate platform (eg artifactId=”org.eclipse.swt.gtk.linux.x86_64”). The swt jarfile includes the necessary native libraries embedded within it.
One significant difference between the Swing and SWT APIs is that in Swing it is traditional to create a child widget, then add it to its parent. In SWT, the parent must be created first. The constructors for widgets normally take a “parent widget” parameter, and automatically add themselves to that parent.
See:
JavaFX (aka JFX)
The name JavaFX is presumably a pun on “Java Effects”. This section discusses JavaFX 2.2 and later only (1.x had a significantly different architecture).
JavaFX is intended to be a replacement for Swing, providing similar functionality in a more efficient way.
JavaFX was originally an optional package for Java; it is included by default in Java8 for some platforms (including x86). On some platforms (in particular ARM), JavaFX remains an optional add-on library; Oracle state that the reason for not providing JavaFX by default on ARM is that graphics varies wildly from system to system and they don’t have resources to guarantee it works in all cases - but will work with platform developers to provide it if possible.
JavaFX is internally implemented like Swing: it renders all components directly using low-level rendering APIs. However while Swing uses the 2D drawing APIs, JavaFX uses 3D drawing APIs - thus getting access to the 3D acceleration features of a modern graphics chip when one is available. On the negative side, when no graphics chip is available then 3D rendering is emulated in software.
JavaFX supports defining a UI in an xml-formatted file which is loaded at runtime (see “GUI Builders” later). Alternately, a JavaFX-based UI can be built using code that is roughly similar to equivalent code in Swing, with Layout objects containing instances of Button, Label, etc. A JavaFX UI can be themed via a cascading stylesheet (CSS).
A sophisticated nested HTML browser widget is provided, and this can be manipulated from Java code (including the DOM of the content it is rendering) to provide all sorts of effects.
Due to the use of 3D rendering, interesting effects are available such as the ability to scale, shear, and rotate nodes (trees of widgets).
It is interesting that Microsoft’s original widget toolkit draws its supported widgets using a 2D API, while its newer WPF toolkit draws using Direct3D. This is similar to the way Swing (using the Java 2D graphics API) is being replaced by JavaFX (using the Java 3D graphics API to draw 2D components). WPF also introduced an XML-based format for defining interfaces (XAML), and JavaFX does something similar.
See Also:
GTK+ and Qt Bindings
There are some open-source projects which provide bindings for GTK+ (eg java-gnome) and Qt (eg QtJambi). Sadly both the GTK and Qt bindings appear to be well behind the latest releases of the libraries they wrap.
The GTK+ project does maintain a page that points to bindings to the GTK+ library for many languages, including Java. Sadly (as noted in that page), Java is not one of the “official” language bindings which is part of the normal release cycle. There are also Wikipedia pages tracking bindings to GTK+, Qt4 and Qt5.
A Java program can use the provided API to be an entirely native GTK+ or Qt application.
Interestingly, as GTK+ and Qt can run on MS-Windows, a Java application using these bindings is technically (indirectly) cross-platform.
This basic example demonstrates how Java/GTK layouts and event-handling work - which is not too different from Swing or SWT.
Layouts
AWT, Swing and SWT all use the concepts of “layouts”, where a “compound widget” that has child widgets will also have a “layout” component that specifies where those child widgets will be located in relation to each other.
As an example, SWT supports FillLayout, RowLayout, GridLayout, FormLayout, PageBookLayout (via PageBook), StackLayout.
In the case of SWT, the child component can tweak the way it is layed out by its parent by having an associated LayoutData object of the corresponding type. If it has no LayoutData then the parent will lay it out using default settings.
The Event Loop
Most (all?) native widget libraries are single-threaded. This saves a lot of complications in the code; widgets and event-handlers do not need to be multi-threaded, and the fact that a click-handler (or similar) runs completely before the next UI action begins processing avoids lots of race-conditions. This leads to the common design where a single thread-safe queue holds UI events waiting to be processed, and that a single thread is responsible for taking events from this queue and invoking the appropriate handler code. The consequences of this design are:
- event-handlers must not run for long periods of time, as this leads to “unresponsive” applications;
- any code running in the non-ui threads must not update the UI widgets but instead request the UI thread to do so.
The normal way to “request the UI thread” to do something is to place an event on the UI event-queue which the UI thread then executes. The event can wrap any arbitrary piece of code, eg an object implementing java.lang.Runnable or a Java8 closure (lamba expression).
Data Binding
Widgets display application-specific data, and many (eg text-fields, or checkboxes) allow users to modify that data. There must therefore be some kind of two-way communication between the widgets and the application itself.
Most native widget toolkits take a very simple approach: widgets have setter methods that the application calls to set the data to display (eg textField.setText("...")
or checkbox.setEnabled(true)
) and to obtain the data again later (eg textField.getText()
or checkbox.isEnabled()
). The application is then responsible for “pushing” its data into the corresponding widgets before displaying a screen and for “pulling” data from the widgets on some action such as the user clicking the “ok” button. However this approach leaves many complicated decisions up to the application, including how to validate data before copying it from the widgets into the application’s datastructures (model), how to display errors, and how to handle the case where the same application data is visible in multiple places at the same time.
The AWT and SWT libraries take this approach (as does the native MS-Windows user interface library).
Libraries such as Swing, Jface and JavaFX support the model-view-controller pattern where a binding is established between a widget and some field belonging to the application’s datamodel. Widgets still have their own current value stored internally, but the value from the application is now “pushed into” the widget automatically when the widget is first rendered. There is usually also some kind of validator support where an object that can verify the correctness of an entry is attached to each field; a single call from the application executes all validators on all fields and only if all report “ok” are the current values from the widgets pushed back (via the binding) into the matching fields in the application model. Such bindings/validators are part of the controller concept in the MVC pattern.
In the case of JFace, the data binding features it provides can be applied not only to JFace widgets, but also to SWT widgets.
See:
GUI Builders
There are different possible ways to create a user interface at runtime:
- a bunch of calls to the library API
- loading a “UI description” from a text-file
- loading a serialized object tree from a binary file
A graphical UI builder can optionally be used to drag-and-drop components together and then either generate code, generate a “ui description” file, or serialise the result. Tools that generate code sometimes support round-tripping where the code can be manually edited, and still used within the UI builder tool.
The first two options are common in many widget libraries (not just Java). For example, GTK+ and Qt both supports the first two; the Glade UI designer is the primary way to create GTK+ “description files”. Qt’s QML is a kind of “UI description” with embedded scripting.
Java’s default serialization implementation is not stable over software updates; serialized UI descriptions often become unloadable in the next release of an application. A project attempted to tweak Java’s serialization to make it more stable in respect to code changes; see long term persistence. The primary motivation for this project was to be able to design UIs in a tool, serialize the result, and load it later. However this approach fell out of favour, and most UIs are now built via either plain Java code or loading a UI description in a format specifically designed for that purpose (rather than a general-purpose serialization format).
The Eclipse4 “Application Model” is a kind of implementation of the “UI description in textfile” approach; an Eclipse RCP application can built its UI at runtime based on a “.xmi” XML file (at least down to the “part” level; widgets within a part are defined in code).