Rich clients with the SWT and JFace

Build a GUI application using Java and the Eclipse GUI APIs

The rise of the Internet and the Web browser as the universal computing client forced user-interface development and the overall user experience to take a step backwards. Web applications, due to their ease of maintenance in terms of deployment and upgrading, allow you to reach a larger audience. Yet, they deny the user the experience that a full-fledged desktop application can provide. The raw power of today's personal computers is mostly untapped when it comes to browser-based enterprise applications. The browser-based application is to a certain extent a glorified version of the dumb terminal of days gone by. Although Java made its debut with applets, which promised many of the features of rich native applications combined with the ease of maintenance of Web applications, the applets' tumultuous evolution has relegated them to a limited functionality—stock tickers and news feeds. This has led many to argue that browser-side Java is effectively dead. The technology wasn't completely to blame because Java on the browser was a casualty of the browser wars and the early problems faced by VM integration in the two leading browsers, Internet Explorer and Netscape Navigator.

Java's client-side technologies have all had their share of criticisms and never conquered the share of the desktop market that many predicted. As with applets, many believe that the rough transition from the Abstract Window Toolkit (AWT) to the early days of Swing, coupled with the overall complexity and paradigm change in UI development introduced by Java (in comparison to the Model-View-Controller (MVC)-less world of Visual Basic, Delphi, and other RAD (rapid application development) environments) caused Java to lose the battle for the desktop.

In this article, we introduce the open source community's answer to the rich client conundrum in the form of the Eclipse project UI frameworks, namely the Standard Widget Toolkit (SWT) and JFace. The Eclipse frameworks provide a Java alternative to building robust, responsive, and great-looking desktop applications.

The Eclipse user interface frameworks

The Eclipse project is described on its Website as an "IDE for anything and for nothing in particular." The use of the term IDE in the previous sentence might be a bit misleading because, although the composing subsystems of the Eclipse framework have at certain points in their API an IDE-ish flavor to them, the majority of the framework is usable as a general desktop application framework.

The Eclipse project spawned out of the early work of Erich Gamma and the folks at Object Technology International (OTI), which is now an IBM subsidiary. OTI is well known for its work in the areas of development tools (VisualAge) and object languages like Smalltalk and Java.

This article deals with using the underlying frameworks created by OTI and IBM to deliver a fast, responsive Java desktop application. Many pages can be written about the controversies surrounding the Eclipse project, its underlying APIs (particularly the SWT), the design choices, and the impact that open sourcing the codebase has created in the community. Instead, you'll focus on building a robust application using Eclipse.

The following are the two main frameworks that you'll learn about:

  • SWT: A widget set and graphics library that provides a portable graphics API independent of the OS but that relies on the native widgets
  • JFace: A model-based UI toolkit that simplifies common UI programming tasks

Standard Widget Toolkit

SWT is the foundation upon which the Eclipse IDE is built. SWT delivers the richness and responsiveness of an application build using native widgets, yet it manages to do so in an operating system-independent fashion.

The Eclipse team realized early that creating a cross-platform set of widgets is a daunting task, both in the areas of matching the functionality of mature operating-system widgets and in making the application seamlessly blend with the native applications. SWT takes a hybrid approach between those taken by AWT and Swing. Instead of using "fat native peers," SWT uses a procedural pass-through layer to the OS graphics API. This thin Java Native Interface (JNI) layer enables SWT to control the native widgets. This approach minimizes the amount of native code involved, thereby making debugging SWT a lot easier. SWT also avoids the need for a pluggable look and feel because it adopts and immediately reflects any changes to the underlying OS look and feel.

Note
Pluggable look and feel is another hotly debated topic. The Eclipse mentality is one of "uniform is better," and we certainly agree with this when it comes to commercial business software. Many other applications can certainly benefit from a pluggable look and feel in the same way that many applications benefit from the use of skins. If your application needs to support a customizable or personalized look, then Swing is the obvious choice.

The SWT approach not only makes the API simpler, but also provides tight integration with hard-to-integrate features such as drag and drop. Drag-and-drop support is another area in which Swing's implementation was plagued for a long time by bugs and inconsistencies. With SWT, any improvements in the drag-and-drop behavior of the OS are reflected in your Java applications immediately.

To resolve the least common denominator problem, SWT widgets that aren't present in a specific platform are emulated using lightweight techniques in the way that it's done in Swing, yet the components are unencumbered by any built-in patterns. A good example is the Tree widget. In Windows, Tree widgets are native components, but in Motif, they're emulated. The SWT implementation in Motif contains the Java code to provide the Tree functionality, but in Windows, using a Tree widget is simply a matter of calling the correct Windows graphics device interface (GDI) commands. Figure 1 shows the three different approaches.

Figure 1. Rendering approaches of AWT, SWT, and Swing

The SWT API is the same on all different supported platforms. Behind the scenes, SWT uses a factory pattern of sorts to plug the right implementations for a given platform. Not only do SWT applications look like they belong among other native applications, but they also feel like native applications. Figure 2 provides a graphical overview of the SWT architecture.

Figure 2. SWT packages

At the time of this writing, SWT has been ported to the following platforms (operating systems and windowing systems): aix/motif, hpux/motif, linux/gtk, linux/motif, linux/qt, macos/carbon, qnx/photon, solaris/motif, win32/win32, and win32-ce/win32. SWT is also a very lightweight API, which makes it ideal for embedded devices as demonstrated by the Windows CE port.

JFace

From the previous description of SWT, you should have gotten the impression that it provides a raw widget set. But what about all of the advancements implemented in Swing, such as strong MVC microarchitectures for complex, often-used widgets such as Trees and Tables? To provide a more advanced, model-driven interaction with SWT, the Eclipse team created the JFace toolkit. JFace is a higher-level user interface toolkit that uses the raw SWT widgets to provide model-driven widgets, and to some extent some functionality that isn't available in the Swing libraries, such as advanced editors, dialog boxes, and wizards. JFace covers many areas of UI development that developers encounter over and over, and it provides a clean way to accomplish those tasks. JFace depends on SWT, but it doesn't hide SWT widgets. For example, JFace viewers, which are model-based content adapters for SWT widgets, provide methods to access the underlying SWT widgets. This duality provides developers with the separation and ability to choose between model-driven UI development and raw widget manipulation. Figure 3 shows a graphical overview of the JFace API.

Figure 3. JFace packages

Some of the packages shown in Figure 3 and a short explanation of their functionality are shown here:

  • Window: The org.eclipse.jface.window package provides window creation and management facilities. Of particular interest is the ApplicationWindow class, which provides a higher-level application window and encapsulates the SWT event loop.
  • Viewers: The org.eclipse.jface.viewers package provides a framework of viewers such as TreeViewer and TableViewer, which are model-driven components that make use of SWT widgets and adapt content of a model to the widget.
  • Dialogs: The org.eclipse.jface.dialogs package provides several commonly used dialog boxes.
  • Actions: The org.eclipse.jface.actions package provides a UI action framework that's similar to Swing's action framework in order to implement shared behavior between two or more user interface components, such as a menu item and toolbar button.
  • Wizards: The org.eclipse.jface.wizard package provides an advanced framework to create wizards (the familiar dialog boxes that automate repetitive and complex tasks).
  • Resource: The org.eclipse.jface.resource package provides support for managing resources, such as SWT fonts and images.
  • Text: The org.eclipse.jface.text package and its subpackages provide a framework for creating, manipulating, displaying, and editing text documents.

SWT primer

The first step you need to take to start building SWT applications is to get the latest SWT release for your platform. If you've installed the Eclipse IDE on your system, then you already have all the necessary JARs and native libraries. If you don't have Eclipse installed, you can obtain SWT as a separate distribution (since release 2.1).

The downloaded file is swt-2.1.1-win32.zip, which is a drop containing the SWT libraries and source code for standalone SWT application development. The zip file contains a jar file (swt.jar), a Windows DLL (dynamic link library) file (or the native library for your chosen platform), a zip file with the source code, and an about.html file.

For the following simple examples, let's place the contents of the SWT distribution file in a directory named lib and the example Java files in the parent directory of the lib directory. Let's start by looking at the simplest SWT application, which simply shows an empty application window:

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class SimplestSWTExample {
   public static void main(String []args){
      Display display =new Display();
      Shell shell =new Shell(display);
      shell.setText("Simplest SWT Example");
      shell.pack();
      shell.open();
      while (!shell.isDisposed()){
         if (!display.readAndDispatch()){
            display.sleep();
         }
      }
      display.dispose();
   }
}

To compile the application, use the javac command as usual and include the swt.jar file in the classpath, as follows:

javac -classpath .;lib \swt.jar SimplestSWTExample.java

Let's try to run the example using the javac command as follows:

java -classpath .;lib \swt.jar SimplestSWTExample

The console output should produce the following stack trace:

Exception in thread "main"java.lang.UnsatisfiedLinkError:no swt-win32-
     2135 in java.library.path
   at java.lang.ClassLoader.loadLibrary(Unknown Source)
   at java.lang.Runtime.loadLibrary0(Unknown Source)
   at java.lang.System.loadLibrary(Unknown Source)
   at org.eclipse.swt.internal.Library.loadLibrary(Library.java:108)
   at org.eclipse.swt.internal.win32.OS.<clinit>(OS.java:46)
   at org.eclipse.swt.widgets.Display.internal_new_GC(Display.java:1291)
   at org.eclipse.swt.graphics.Device.init(Device.java:547)
   at org.eclipse.swt.widgets.Display.init(Display.java:1310)
   at org.eclipse.swt.graphics.Device.<init>(Device.java:96)
   at org.eclipse.swt.widgets.Display.<init>(Display.java:291)
   at org.eclipse.swt.widgets.Display.<init>(Display.java:287)
   at SimplestSWTExample.main(SimplestSWTExample.java:10)

The error shown is telling you that in order to run the SWT example, you need the swt-win32 DLL. Notice that as part of the SWT distribution for Windows, you have the swt-win32-VERSION.dll file where VERSION denotes the particular version of the DLL. To make the DLL available to the running JVM use the -Djava.library.path parameter as part of the Java command line as follows (the same applies to other environments such as Linux or Mac OS X):

java -classpath .;lib \swt.jar -Djava.library.path=lib SimplestSWTExample

The output should now resemble what's shown in Figure 4.

Figure 4. A simple SWT example
Tip
If you want to eliminate the need for specifying the classpath and java.library.path options in the Java command line you can integrate the SWT JAR and DLL (for Windows) with your Java Runtime Environment (JRE) by copying the jar files to the JRE's lib/ext directory and the DLL to the JRE's bin directory (the same procedure can be applied to other platforms).

Let's examine the example's code to gain an understanding of how SWT works under the covers. The first object instantiated is of type org.eclipse.swt.widgets.Display, although it's in the SWT widgets package, this class actually isn't a widget but rather a bridge that widgets and other SWT classes use to communicate with the underlying operating system. The Display class extends org.eclipse.swt.graphics.Device (which also has a child class named Printer).

The next class instantiated is the Shell. A SWT Shell is an encapsulation of an operating system's window. Notice that the code sample sets the window title by invoking the setText() method on the Shell. The Shell is then told to pack (force the layout of children components) and to open (show) itself.

The next segment of code in the example is at first rather strange for Java developers. If you work with Swing, you might be asking yourself why there is a while loop at the end of the example:

while (!shell.isDisposed()){
   if (!display.readAndDispatch()){
      display.sleep();
   }
}
display.dispose();

Actually, the while loop is the event loop or message pump of the application. In AWT or Swing, the event loop is actually hidden from the developer. In SWT, the Display class is responsible for the event loop; it forwards all OS events affecting the shell or any of its child widgets to the application until the shell is disposed. If you were to leave that code segment out, your application wouldn't be able to respond to any events. This is another example of how the SWT design doesn't hide any of the raw features of the toolkit. This feature, although strange at first, gives developers greater flexibility in their interaction with the underlying OS.

Caution
In SWT, the UI thread isn't protected or hidden from the developer. In fact, whatever thread creates the Display class becomes the UI thread. This approach facilitates the debugging of threading and timing issues yet it can be confusing to developers accustomed to working with a UI toolkit that hides threading issues from the developer. It's the developers' responsibility to fork a new thread to perform non-UI, computationally-intensive operations in response to an event. Also, all interaction with the UI must originate from the UI thread, otherwise an org.eclipse.swt.SWTException is thrown.

Working with widgets

All design issues aside, the essence of any UI toolkit is its available components or widgets. Table 1 lists the available SWT widgets and their Swing equivalents.

Table 1. SWT Widgets

WidgetSwing equivalentDescription
TrackerNoneProvides tracking rectangles that provide visual feedback
MenuJMenuA container for MenuItems
ButtonJButtonA simple button
LabelJLabelA simpler JLabel with no Image or Border capabilities
ProgressBarJProgressBarThe traditional progress bar
SashJSplitPaneA Sash is actually the Splitter portion, not a container
ScaleJSliderSelects a value by sliding a knob within a bounded interval
SliderJSlider, JScrollBarMore like a scrollbar than a Slider
ListJListA list of strings
TextJTextField, JPasswordField, JTextAreaA multipurpose text entry field
ComboJComboBoxA drop-down list for select string values
GroupJPanelTitled Border
TreeJTreeThe classical tree view interface
TableJTableA table of elements
TabFolderJTabbedPaneA simpler JTabbedPane
ToolBarJToolBarA simpler JToolBar
CoolBarJToolBarA detachable, more configurable toolbar
AnimatedProgressNoneDeprecated, instead use ProgressBar with the style SWT.INDETERMINATE
CLabelJLabelA simple label
CComboJComboBoxA combo box
ViewFormJPanelEquivalent to a custom JPanel with three subpanels arranged vertically; used in Eclipse to create a view
SashFormJSplitPaneA JSplitPane that allows more than two children
CTabFolderTabFolderLike TabFolder, but with more style choices
TableTreeNoneA combination of a JTree and JTable

In combination with the classes in the JFace packages, the Eclipse UI frameworks provide most of the functionality required to build modern user interfaces. Figure 5 provides a graphical representation of the SWT widget and custom packages.

Figure 5. SWT widgets. Click on thumbnail to view full-sized image.

At the top, you have the Widget class, which is the top-level class from which all other user interface objects descend. It's analogous to the AWT's Component and the Swing's JComponent. One recurring pattern of usage in SWT is that most new components aren't created by subclassing but by using composition. Furthermore, most SWT classes aren't meant to be extended outside the confines of the SWT implementation. The Widget class also provides a dispose() method that relinquishes any operating-system resources associated with the widget and the widget's children.

Within the Widget class, you have the Control class, which represents a windowed user-interface class, such as Buttons and Labels. Within Control, you have Scrollable, and down at the bottom of the hierarchy you have Composite and Canvas. These last two classes form the basis for creating your own widgets. Canvas is used when the widget is owner-drawn, and Composite is used when you're creating a compound widget.

Probably the most radical difference between working with SWT and working with Swing is how the widgets are constructed. SWT has strict parenting rules for the creation of a widget, that is, you cannot create a widget without a having a parent already created. This is just a natural consequence of the widgets being thin veneers to the native widgets, because the OS resources need to be allocated at construction time. A typical widget constructor takes two arguments: the parent widget and the style bits (which can be constructed by OR-ing together individual integer values). Style bits are a hint to the underlying OS of how the widget should be rendered. The org.eclipse.swt.SWT class contains a large collection of constants that are used for setting the style of a particular widget. Because this is a loosely typed way to set a widget's look and feel, it's important to consult the Javadoc on a particular widget to learn which styles are applicable because passing an erroneous style wouldn't cause an exception; the styles are simply ignored.

Caution
For certain widgets, the style is an idempotent property. That is, a widget style cannot be changed after its creation.

Further examination of Figure 5 shows that whole-part widgets such as Trees and Tables contain parts that are descendants of the Item class. For example, in the case of a Table, the composing parts are TableColumns and TableItems.

Notice that the Shell class is just a specialization of a Composite (a subclass of Decorations, which provides appearance and behavior for Shell classes). In an SWT application, the Shell class is the top-level container that can hold widgets.

The last item to point out is the org.eclipse.swt.custom package, which provides custom widgets that extend the capabilities of the basic widgets. Also not shown in Figure 5 is the Dialog hierarchy, which provides commonly used dialog boxes, including ColorDialog, DirectoryDialog, FileDialog, FontDialog, MessageBox, and PrintDialog.

SWT layouts

Like AWT and Swing, SWT uses the concept of layouts (or layout managers) to determine the position and size of widgets in a container. An SWT Composite has an associated layout, and child widgets have associated layout data that enables a layout to make decisions about the size and positioning of a widget. You can find SWT layout classes in the org.eclipse.swt.layout package.

Table 2 lists the available layouts in SWT and their equivalent layout in AWT and Swing.

Table 2. SWT Layouts

Layout Description Swing equivalent
FillLayoutThe default layout; arranges components horizontally on a row or vertically on a columnBoxLayout
RowLayoutSimilar to FillLayout but more flexible, allowing multiple rows, fill, wrapping, and custom spacingFlowLayout
GridLayoutLays components on a grid; offers many options for fine-grained controlGridBagLayout
FormLayoutRelative layout that uses attachments to a container edge or a sibling widget's edgeNone
PageBookLayoutIndirectly used with org.eclipse.ui.part.PageBook; not part of SWT or JFaceCardLayout
StackLayoutStacks components, only top component is visibleCardLayout

SWT events

The SWT event model is similar to the AWT or Swing event models in that there are listener interfaces for different types of events. Events are handled by implementing one of the listener interfaces and registering the listener implementation with the widget that's producing the event.

The listener register methods follow the naming convention addXXXListener where XXX is the type of the listener such as Selection, Modify, and so on. All SWT events extend the java.util.EventObject class with information specific to the event.

Untyped Events and Event Handling

Besides having typed methods for all supported types of events, all classes descending from the Widget class have a generic way to add a listener using the method void addListener(int eventType, Listener listener), which adds the listener to the collection of listeners who will be notified when an event of the given type occurs. Also the complement method void removeListener(int eventType, Listener listener) can remove the given listener for a given type of event.

This facility comes in handy when testing event-handling code or if you need to manipulate different types of listeners as a group. For example the following three snippets of code are all equivalent ways to add a selection listener to a Tree widget:

//Use addListener to add a Listener
tree.addListener(SWT.Selection,new Listener(){
   public void handleEvent(Event arg0){
      System.out.println("SWT.Selection Event!");
   }
});
//Use addSelectionListener and implement the SelectionListener Interface
tree.addSelectionListener(new SelectionListener(){
   public void widgetSelected(SelectionEvent arg0){
      System.out.println("SWT.Selection Event!");
   }
   public void widgetDefaultSelected(SelectionEvent arg0){
      System.out.println("SWT.Selection Event!");
   }
});
//Use addSelectionListener and override the widgetSelectedMethod of SelectionAdapter
tree.addSelectionListener(new SelectionAdapter(){
   public void widgetSelected(SelectionEvent arg0){
      System.out.println("SWT.Selection Event!");
   }
});

Equally, for testing purposes only, you could implement a generic event handler by using a case style construct using the integer value of the event type, as the following snippet shows:

Listener listener =new Listener(){
   public void handleEvent(Event e){
      switch (e.type){
         case SWT.Selection:
            if (e.widget instanceof Table){
               //Handle Selection event on a Table
            }
            else if (e.widget instanceof Tree){
               //Handle Selection event on a Tree
            }
            break;
         case SWT.Expand :
            //Handle Expand event
            break;
      }
   }
};

You can see that for a large number of widgets and event types, this solution can result in a large procedural-looking piece of code. Again, common sense and good programming practices will tell you that a natural progression would be to first try anonymous inner classes by extending an adaptor and, alternatively (based on the complexity of the event-handling code), creating a standalone event-handling class.

SWT resources

The next piece of SWT theory you need to explore before moving to the higher-level world of JFace is the management of SWT resources. This is an area of great controversy in UI development circles and some clarifications are in order.

In AWT or Swing, resource deallocation is handled by the garbage collector, although you can null a resource so that it isn't reachable, which makes it eligible for garbage collection. This is merely a way to provide a hint to the garbage collector. Garbage collection is a good thing when it doesn't get in the way of your user's experience. Garbage-collected languages such as Java tend to foster productivity by removing the burden of programmatically tracking resources. Although garbage-collection algorithms have made great strides, most of them aren't fine-tuned for the needs of user interfaces. SWT instead places the burden of deallocating operating-system resources on the programmer. Because SWT objects allocate operating system resources at construction time, a general guideline is that the code that created the resource must dispose of it. This translates to SWT's first rule for resource management from "SWT: The Standard Widget Toolkit":

"If you create it, you dispose it."

As a side effect of SWT's use of the platform's native widgets, SWT resources are bound by the memory allocation and deallocation rules of the operating system. Previously, we mentioned that all SWT widgets are required to have a parent widget at construction time and that the Widget class provides a dispose method. When the dispose method of a Widget class is invoked, the disposed methods of all of its children are also invoked, which leads to the second rule of SWT resource management (also from "SWT: The Standard Widget Toolkit"):

"Disposing the parent disposes of the children."

From the two rules, you can see that resources are either disposed explicitly by using the dispose method, which is the case typically with resources like fonts and colors. The disposal of resources like fonts and colors depends on whether they were acquired from the system with the getSystemXXXXX() methods. Even though these resources are usually parented by the display and by rule number two, they will be disposed of at the time when the display is disposed.

In non-garbage-collected languages, when you ask the OS for a resource, you eventually have to give it back. Like Java, some of these languages will have an operator similar to the "new" operator in Java, and they also provide a way to "free" the allocated memory for the resource. You know that OS resources are limited, which is why the dispose method in SWT gives you the control to decide when a resource is no longer needed, instead of allowing the garbage collector to decide. Visual controls are typically parented to a Shell class, so, for example, if you have a shell with a label, a text field, and a button when the shell is disposed of, all of its contained children are, too. These two simple rules will guarantee that your applications are resource conscious, which will translate to a better user experience.

A more elaborate SWT example

The essence of using any UI toolkit boils down to knowing how to create UI elements, how to arrange them, and how to wire them together to accomplish a meaningful task in response to a user action. A great source of SWT examples is the SWT Controls application, which is part of the Example Plug-ins distribution, which is available from the Eclipse downloads page. Figure 6 shows the ControlExample.java application, which is akin to Swing's SwingSet2 demo. This application provides a good introduction to the available SWT widgets, and it will help you get familiarized with the different style bits available and the events produced by the different widgets.

Figure 6. SWT controls example. Click on thumbnail to view full-sized image.

There are two ways to launch the different example applications. As with any other Eclipse plug-in, you can simply unzip the contents of the eclipse-examples- 2.1.1.zip file to the location of your Eclipse installation. Once you restart the Eclipse Work Bench from the menu you can select Window, then Show View, then Other, at which point the Show View dialog box will appear, as shown in Figure 7.

Figure 7. Eclipse's Show View dialog box

From the dialog box, select the SWT Example Launcher view, which launches an Eclipse view from which you can choose several example applications, including the SWT controls example that was shown in Figure 6.

Of course you don't need Eclipse to run an SWT application, as shown earlier in the article. The Eclipse plug-in just makes it easier for you to execute the applications. To launch the controls example application directly from the command line use the following:

java -classpath .;swtexamples.jar;c:\swt \swt.jar
   -Djava.library.path=c:\swt
   org.eclipse.swt.examples.controlexample.ControlExample

The previous command assumes that the swt.jar and the SWT DLL are in a directory named swt on the drive c:\. Modify accordingly for your platform and the location of the SWT files.

JFace primer

As mentioned earlier, JFace is a collection of helper classes for developing user interface features. One of these helper classes is the ApplicationWindow, which is a high-level window. The ApplicationWindow class provides support for commonly needed items, such as a menu, toolbar, and a status line. Internally, it uses a custom layout to set the menu, toolbar, and the status-line positioning.

Simple JFace application

Let's start with an empty shell and build on it. The following code snippet will produce the simplest JFace application:

import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
 *JFace example
 */
public class MyApplicationWindow extends ApplicationWindow {
   public MyApplicationWindow(){
      this(null);
   }
   public MyApplicationWindow(Shell shell){
      super(shell);
   }
   public static void main(String []args){
      MyApplicationWindow window =new MyApplicationWindow();
      window.setBlockOnOpen(true);
      window.open();
      Display.getCurrent().dispose();
   }
} 

ApplicationWindow

Notice that ApplicationWindow has a constructor that takes a shell (that is the parent shell). If you pass null to this constructor, you're simply stating that the shell that contains this window has no parent shell. To run the application, you will need the jar file jface.jar in addition to the SWT files. JFace isn't available as an individual download. If you install Eclipse, the jface.jar file is located under <eclipse-installation>\plugins\org.eclipse.jface_2.1.1.

Note
Eclipse is able to keep multiple versions of a plug-in in its plug-in repository. In the case of our install, we had versions 2.1.0 and 2.1.1 of the JFace plug-in.

To compile the simple JFace application use the following command line:

javac -classpath .;lib \swt.jar;lib \jface.jar MyApplicationWindow.java

At this point, the running application isn't very exciting. The ApplicationWindow class was designed to be subclassed, and as such, there are a number of protected methods that can be overridden to provide specific functionality to the window. These methods include the following:

  • initializeBounds(): Sets the location and size of the window
  • configureShell(): Customizes the window's Shell class
  • createContents(): Returns the contents of the effective client area of the window
  • createMenuManager(): Returns a new menu manager for the window
  • createToolBarManager(): Returns a new toolbar manager for the window
  • createStatusLineManager(): Returns a new status-line manager for the window

The menu, toolbar, and status line-related methods only configure their respective widgets. In order for them to appear in the window, the class provides corresponding add methods. For example, once you configure the menu using createMenuManager(), you can add it on the window by using the addMenuBar() method.

Let's modify the simple JFace example to experiment with some of the features of ApplicationWindow. Because the menu and toolbar can share actions, let's create a couple of Action classes. The first will set the background color of a Composite widget. Let's place it in the client area of the window, and the other will handle the closing of the window. Let's use the status line to signal that the widget's color has been changed.

First, let's add the imports necessary to the sample application, as follows:

import java.util.Random;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

Next, you should add a declaration for Composite—it should be placed in the client area of the ApplicationWindow class. Also, you're creating two ImageDescriptors, which are lightweight descriptions of an image that you can use to create an image on demand. These will be used by the actions (the images used are 16-by-16). For the purpose of the example, you can use any two 16-by-16 images:

private Composite _composite;
ImageDescriptor greenImageDesc =ImageDescriptor.createFromFile(
      MyApplicationWindow.class,
      "green.gif"
   );
ImageDescriptor redImageDesc =ImageDescriptor.createFromFile(
      MyApplicationWindow.class,
      "red.gif"
   );

JFace actions

Now you can implement the actions. One possible way to do so is by using static inner classes. First, there's the ExitAction class. Notice that you set the constructor of the ExitAction to take a parameter of type ApplicationWindow, which is used in the run method to close the given window. Also notice the use of the ImageDescriptor class to assign an image to the action. The ImageDescriptor is a lightweight class that can create an image on demand:

private ExitAction _exitAction =new ExitAction(this);
private class ExitAction extends Action {
   ApplicationWindow _window;
public ExitAction(ApplicationWindow window){
   _window =window;
   setText("Exit Ctrl+X");
   setToolTipText("Exit Application");
   setImageDescriptor(greenImageDesc);
}
   public void run(){
      _window.close();
   }
}

The ChangeColorAction uses an array of five colors (obtained using the display method getSystemColor() and the appropriate SWT integer constant). In the run method of the action, you generate a random number in the range 0 to 4 and use it to assign one of the five colors to the background of the Composite that was previously declared. You also set the ApplicationWindow status bar to the string representation of the chosen color. (The Color class's toString() method returns a string in the form Color {R, G, B} where R, G, and B are the red, green, and blue components of the color.)

private ChangeColorAction _changeColorAction =new ChangeColorAction();
private class ChangeColorAction extends Action {
   private Color [] colors;;
   public ChangeColorAction(){
      Display d =Display.getDefault();
      setImageDescriptor(redImageDesc);
      setText("Change Color Alt+C");
      setToolTipText("Change Color");
      colors =new Color [ ] {
      d.getSystemColor(SWT.COLOR_BLACK),
      d.getSystemColor(SWT.COLOR_BLUE),
      d.getSystemColor(SWT.COLOR_RED),
      d.getSystemColor(SWT.COLOR_YELLOW),
      d.getSystemColor(SWT.COLOR_GREEN)};
   } 
   public void run(){
      Random generator =new Random();
      int index =generator.nextInt(4);
      Color color =colors [index ];
      _composite.setBackground(color);
      setStatus(color.toString());
   }
}

Configuring the shell

You can override the configureShell() method to modify the appearance of the shell; in this case, you'll set the application window title like you did with the first SWT example, as follows:

protected void configureShell(Shell shell){
   super.configureShell(shell);
   shell.setText("JFace Example");
}

You can override the initializeBounds() methods to set the initial size and location of the window. Notice that to access the shell, you make use of the ApplicationWindow's utility method getShell():

protected void initializeBounds(){
   getShell().setSize(640,480);
   getShell().setLocation(0,0);
}

Configuring the application's menu

To create a menu, you override the createMenuManager() method. The MenuManager class is used to add the traditional File menu. Notice that a MenuManager instance is created for each individual submenu. The _changeColorAction() and the _exitAction() methods are then added to the fileMenu submenu. The submenu MenuManagers are then added to the main MenuManager, which is the return value of the method.

protected MenuManager createMenuManager(){
   MenuManager menuManager =new MenuManager();
   MenuManager fileMenu =new MenuManager("&File");
   fileMenu.add(_changeColorAction);
   fileMenu.add(_exitAction);
   menuManager.add(fileMenu);
   return menuManager;
}

Configuring the application's toolbar

Similar to the menu construction, the toolbar method is created by instantiating a ToolBarManager. The action methods are then added to the ToolBarManager instance, as follows:

protected ToolBarManager createToolBarManager(int style){
   ToolBarManager toolBarManager =new ToolBarManager(style);
   toolBarManager.add(_changeColorAction);
   toolBarManager.add(_exitAction);
   return toolBarManager;
}

Enabling UI elements

To enable the menu, toolbar, and status-line methods, you need to invoke the addMenuBar(), addToolBar(), and addStatusLine() methods, and add them to the ApplicationWindow constructor, as follows:

public MyApplicationWindow(Shell shell){
   super(shell);
   addMenuBar();
   addToolBar(SWT.FLAT |SWT.WRAP);
   addStatusLine();
} 

Creating the application's contents

Finally, you override the createContents() method, instantiate the Composite that was previously declared (notice that the method takes a Composite as the parent, in this case the parent Composite represents the "client area" of the ApplicationWindow). You create the composite parented on the client's area. The parameter SWT.NONE is one of many integer constants used in the context of appearance-related aspects of widgets, as follows:

protected Control createContents(Composite parent){
   _composite =new Composite(parent,SWT.NONE);
   return _composite;
}

The running application should resemble Figure 8.

Figure 8. JFace application. Click on thumbnail to view full-sized image.

Conclusions

In this article you learned how to build a commercial-quality GUI application using Java and the Eclipse GUI APIs. SWT and JFace provide a viable alternative to AWT and Swing for certain types of applications. Although SWT and JFace applications are slightly less portable than Swing applications, most modern platforms are currently available.

For experienced Swing developers, SWT and JFace require a fairly flat learning curve. Although, feature-by-feature, Swing is a much more complete toolkit, SWT and JFace provide for most of the needs of modern UI applications. Swing is improving with every new version of J2SE, and we don't expect any one toolkit to reign supreme; they're all just options. Remember in the end, it's all about the user. So use whatever will bring you the most return on investment.

Brian Sam-Bodden has been working with object technologies for the last nine years with a strong emphasis on the Java platform. He holds dual bachelor degrees from Ohio Wesleyan University in computer science and physics. He currently serves as president and chief software architect for Integrallis Software. As an independent consultant, Brian has promoted the use of open source in the industry by educating his clients on the cost benefit and productivity gains achieved by it. He is a Sun Certified Java programmer, developer, and architect. Brian is a frequent speaker at Java user groups and at conferences throughout the country. Christopher Judd is the president and primary consultant for Judd Solutions, and is an international speaker, open source evangelist, Central Ohio Java Users Group board member and JBuilder-certified developer and instructor. He has spent the last nine years developing software in the insurance, retail, government, manufacturing, service, and transportation industries. His current focus is consulting, mentoring, and training with Java, J2EE, J2ME, Web services, and related technologies. He also holds a bachelor's degree from Ashland University in computer information systems with minors in accounting and finance.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies