天天看点

swt-jface5Rich clients with the SWT and JFace

Rich clients with the SWT and JFace

Build a GUI application using Java and the Eclipse GUI APIs

Summary

The Eclipse frameworks provide a Java alternative to building robust, responsive, and great-looking desktop applications. In this excerpt from Enterprise Java Development on a Budget, authors Brian Sam-Bodden and Christopher Judd introduce Eclipse's SWT (Simple Widget Toolkit) and JFace frameworks. In their discussion, they illustrate how to build a GUI application using both APIs. ( 5,000 words; April 26, 2004)

By Brian Sam-Bodden and Christopher Judd

swt-jface5Rich clients with the SWT and JFace
swt-jface5Rich clients with the SWT and JFace

Printer-friendly version |

swt-jface5Rich clients with the SWT and JFace

Mail this to a friend

Page 1 of 3

Advertisement
<script language=JavaScript src="http://spinbox.macworld.com/?DC=jw-BigBox&JS=Y&TARGET=_top&BA=1"></script>
swt-jface5Rich clients with the SWT and JFace
swt-jface5Rich clients with the SWT and JFace

he 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.

swt-jface5Rich clients with the SWT and JFace
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.

swt-jface5Rich clients with the SWT and JFace
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.

swt-jface5Rich clients with the SWT and JFace
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.

Next page >

Page 1 Rich clients with the SWT and JFace

Page 2 SWT primer

Page 3 JFace primer

See JavaWorld Talkback on the last page of this article to post your comments and see how fellow readers reacted.

swt-jface5Rich clients with the SWT and JFace

Printer-friendly version |

swt-jface5Rich clients with the SWT and JFace

Mail this to a friend

  • Resources
  • This article is an excerpt from Chapter 10, "Rich Clients with the SWT and JFace" from the book Enterprise Java Development on a Budget, Brian Sam-Bodden and Christopher Judd (APress, March 2004; ISBN: 1590591259):

    http://www.amazon.com/exec/obidos/ASIN/1590591259/javaworld

  • For more information on SWT and JFace:

    http://www.eclipse.org.

  • Obtain SWT in binary or source form:

    http://www.eclipse.org/downloads

  • "SWT: The Standard Widget Toolkit," by Carolyn MacLeod and Steve Northover (Object Technology International, November 2001):

    http://www.eclipse.org/articles/swt-design-2/swt-design-2.html

  • For more articles on Java tools, browse the Development Tools section of JavaWorld's Topical Index:

    http://www.javaworld.com/channel_content/jw-tools-index.shtml

  • Browse the AWT/Swing section of JavaWorld's Topical Index:

    http://www.javaworld.com/channel_content/jw-awt-index.shtml

  • For more articles on GUI development, browse the User Interface Design section of JavaWorld's Topical Index:

    http://www.javaworld.com/channel_content/jw-ui-index.shtml

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.

swt-jface5Rich clients with the SWT and JFace
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

Widget Swing equivalent Description
Tracker None Provides tracking rectangles that provide visual feedback
Menu

JMenu

A container for

MenuItem

s
Button

JButton

A simple button
Label

JLabel

A simpler

JLabel

with no

Image

or

Border

capabilities
ProgressBar

JProgressBar

The traditional progress bar
Sash

JSplitPane

A Sash is actually the Splitter portion, not a container
Scale

JSlider

Selects a value by sliding a knob within a bounded interval
Slider

JSlider

,

JScrollBar

More like a scrollbar than a Slider
List

JList

A list of strings
Text

JTextField

,

JPasswordField

,

JTextArea

A multipurpose text entry field
Combo

JComboBox

A drop-down list for select string values
Group

JPanel

Titled

Border

Tree

JTree

The classical tree view interface
Table

JTable

A table of elements
TabFolder

JTabbedPane

A simpler

JTabbedPane

ToolBar

JToolBar

A simpler

JToolBar

CoolBar

JToolBar

A detachable, more configurable toolbar
AnimatedProgress None Deprecated, instead use ProgressBar with the style SWT.INDETERMINATE
CLabel

JLabel

A simple label
CCombo

JComboBox

A combo box
ViewForm

JPanel

Equivalent to a custom

JPanel

with three subpanels arranged vertically; used in Eclipse to create a view
SashForm

JSplitPane

A

JSplitPane

that allows more than two children
CTabFolder

TabFolder

Like

TabFolder

, but with more style choices
TableTree None A 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.

swt-jface5Rich clients with the SWT and JFace
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

FillLayout

The default layout; arranges components horizontally on a row or vertically on a column

BoxLayout

RowLayout

Similar to

FillLayout

but more flexible, allowing multiple rows, fill, wrapping, and custom spacing

FlowLayout

GridLayout

Lays components on a grid; offers many options for fine-grained control

GridBagLayout

FormLayout

Relative layout that uses attachments to a container edge or a sibling widget's edge None

PageBookLayout

Indirectly used with

org.eclipse.ui.part.PageBook

; not part of SWT or JFace

CardLayout

StackLayout

Stacks components, only top component is visible

CardLayout

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.

swt-jface5Rich clients with the SWT and JFace
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.

swt-jface5Rich clients with the SWT and JFace
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; 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 /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

ImageDescriptor

s, 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("E&[email protected]+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 C&[email protected]+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

MenuManager

s 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.

swt-jface5Rich clients with the SWT and JFace
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.

swt-jface5Rich clients with the SWT and JFace

继续阅读