swt-jface5
2004-11-16 19:16
399 查看
Rich clients with the SWT and JFace
Build a GUI application using Java and the Eclipse GUI APIs
SummaryThe 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
Printer-friendly version |
Mail this to a friend
Page 1 of 3
Advertisement | |
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. |
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 |
Figure 2. SWT packages |
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 |
Window: The
org.eclipse.jface.windowpackage provides window creation and management facilities. Of particular interest is the
ApplicationWindowclass, which provides a higher-level application window and encapsulates the SWT event loop.
Viewers: The
org.eclipse.jface.viewerspackage provides a framework of viewers such as
TreeViewerand
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.dialogspackage provides several commonly used dialog boxes.
Actions: The
org.eclipse.jface.actionspackage 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 packageprovides an advanced framework to create wizards (the familiar dialog boxes that automate repetitive and complex tasks).
Resource: The
org.eclipse.jface.resourcepackage provides support for managing resources, such as SWT fonts and images.
Text: The
org.eclipse.jface.textpackage 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.
Printer-friendly version |
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.jarfile 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.pathparameter 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.pathoptions 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). |
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
Displayclass extends
org.eclipse.swt.graphics.Device(which also has a child class named
Printer).
The next class instantiated is the
Shell. A SWT
Shellis 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
Shellis 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
whileloop at the end of the example:
while (!shell.isDisposed()){ if (!display.readAndDispatch()){ display.sleep(); } } display.dispose();
Actually, the
whileloop 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
Displayclass 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 Displayclass 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.SWTExceptionis thrown. |
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
|
Figure 5. SWT widgets. Click on thumbnail to view full-sized image. |
Widgetclass, which is the top-level class from which all other user interface objects descend. It's analogous to the AWT's
Componentand 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
Widgetclass also provides a
dispose()method that relinquishes any operating-system resources associated with the widget and the widget's children.
Within the
Widgetclass, you have the
Controlclass, which represents a windowed user-interface class, such as
Buttonsand
Labels. Within
Control, you have
Scrollable, and down at the bottom of the hierarchy you have
Compositeand
Canvas. These last two classes form the basis for creating your own widgets.
Canvasis used when the widget is owner-drawn, and
Compositeis 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.SWTclass 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. |
Itemclass. For example, in the case of a Table, the composing parts are
TableColumnsand
TableItems.
Notice that the
Shellclass is just a specialization of a
Composite(a subclass of
Decorations, which provides appearance and behavior for
Shellclasses). In an SWT application, the
Shellclass is the top-level container that can hold widgets.
The last item to point out is the
org.eclipse.swt.custompackage, which provides custom widgets that extend the capabilities of the basic widgets. Also not shown in Figure 5 is the
Dialoghierarchy, 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
Compositehas 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.layoutpackage.
Table 2 lists the available layouts in SWT and their equivalent layout in AWT and Swing.
Table 2. SWT Layouts
|
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
addXXXListenerwhere
XXXis the type of the listener such as
Selection,
Modify, and so on. All SWT events extend the
java.util.EventObjectclass 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
Widgetclass 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
Widgetclass provides a dispose method. When the dispose method of a
Widgetclass 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
Shellclass, 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.javaapplication, which is akin to Swing's
SwingSet2demo. 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. |
Figure 7. Eclipse's Show View dialog box |
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
ApplicationWindowclass 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
ApplicationWindowhas 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. |
javac -classpath .;lib /swt.jar;lib /jface.jar MyApplicationWindow.java
At this point, the running application isn't very exciting. The
ApplicationWindowclass 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
Shellclass
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
Actionclasses. 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
ApplicationWindowclass. 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
ExitActionclass. Notice that you set the constructor of the
ExitActionto take a parameter of type
ApplicationWindow, which is used in the run method to close the given window. Also notice the use of the
ImageDescriptorclass to assign an image to the action. The
ImageDescriptoris 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&xit@Ctrl+X"); setToolTipText("Exit Application"); setImageDescriptor(greenImageDesc); } public void run(){ _window.close(); } }
The
ChangeColorActionuses 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
Compositethat was previously declared. You also set the
ApplicationWindowstatus bar to the string representation of the chosen color. (The
Colorclass's
toString()method returns a string in the form
Color {R, G, B}where
R,
G, and
Bare 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&olor@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
MenuManagerclass is used to add the traditional File menu. Notice that a
MenuManagerinstance is created for each individual submenu. The
_changeColorAction()and the
_exitAction()methods are then added to the
fileMenusubmenu. 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
ToolBarManagerinstance, 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
ApplicationWindowconstructor, 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
Compositethat was previously declared (notice that the method takes a
Compositeas the parent, in this case the parent
Compositerepresents the "client area" of the
ApplicationWindow). You create the composite parented on the client's area. The parameter
SWT.NONEis 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. |
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.