Introduction to the Standard Widget Toolkit - Events - How to Use Objects: Code and Concepts (2016)

How to Use Objects: Code and Concepts (2016)

Part III: Events

Chapter 7. Introduction to the Standard Widget Toolkit

Object-oriented software at its technical core is constructed from objects that receive method calls, change their internal state, and compute some return value. Part II created a solid and detailed conceptual basis for reasoning about these method calls and about the correctness of software in general. Such a basis is indispensable for developing any substantial software product: If we are not able to capture the expected behavior of its different components precisely, we will never be able to divide the work among a team and still be confident that the parts will fit together seamlessly in the end.

The question of correctness naturally focuses on the caller’s perspective:Image 1.1 Image 4.1 The caller always invokes a method to achieve a particular result. It obeysImage 4.2.3 the method’s pre-condition and expects the method to provide the result described in the post-condition. As a rather subtle consequence, that postcondition also determines the method’s behavior to a large degree: The method will perform only such actions as are necessary to fulfill the postcondition, since any further effort is wasted and indeed unexpected from the caller’s point of view.

In many situations, the contracts cannot be as precise as would be necessary to specify the result completely. We have seen one instance in the case of the OBSERVER pattern: The subject sends out notifications aboutImage 2.1 state changes to many different kinds of observers, by calling the methods of a common interface. Of course, the concrete behavior of the observers can then vary greatly, according to their respective purposes. The subjectImage 12.2.1.8 cannot prescribe a single contract for the method it calls. Similarly, at the system boundary, the network card may signal the arrival of a data packet and the operating system has to deal with it appropriately; when it hands the packet on to some application, it can, again, not prescribe the reaction of that application.

In all of these examples, the focus in reasoning about what is “correct” shifts from the caller to the callee. The observer, the operating system, and the networking application all exhibit some reaction that is appropriate from their own point of view, rather than serving to fulfill any expectation on the caller’s part. Software structured in this way is called event-driven: Something relevant—the event—happens, the software is notified, and it reacts appropriately. Although internally such software will employ contractsImage 7.11 to specify the behavior of its objects, its overall structure is shaped by the view that events are delivered and must be acted upon.

Part III presents and examines the specifics of event-driven software. In this chapter, we look at an important and prototypical example, the construction of graphical user interfaces. It serves to approach the topic from a technical perspective, by just working out the concrete API of the Standard Widget Toolkit (SWT) used by Eclipse. Looking beyond, it analyzes the concept of frameworks and its central feature, inversion of control. Chapter 8 treats the additional complications arising when code is executed concurrently, in several threads. In particular, the communication between threads is best understood as being event-driven. Chapter 9 complements the discussion of user interfaces by examining a practically crucial aspect, that of model-view separation: Professional developers strive to keep as much of an application’s functionality independent of the user interface as possible. Finally, Chapter 10 comes back to the original question: How can we talk about the correctness of event-driven software? It turns out that in many situations, the desired behavior of objects can be captured by finite state machines.

7.1 The Core: Widgets, Layouts, and Events

A graphical user interface is more than just a pixel-based image on the screen. The interface will reflect the user’s mouse gestures, for instance by highlighting a button when the mouse moves in. A window will repaint itself when the user first minimizes and then reopens it. Different parts will interact when the user performs drag-and-drop gestures. In short, the user interface is a highly reactive piece of software.

The technical structure of user interfaces is well understood and thereImage 7.3 are many frameworks that implement the fundamental mechanisms and graphical components. The Standard Widget Toolkit (SWT), which is used in Eclipse, is one prototypical example. Having mastered this specimen, you will easily find your way into the others as well.


Image Image 84The Eclipse web site offers a rich collection of SWT snippets, which show in a minimal environment how particular user interface elements are created and how the SWT API is supposed to be used. In short, they provide a helpful cookbook for creating your own user interfaces, covering basic patterns as well as special knowledge for experienced developers.


To examine the structure and usage of SWT, let us try to implement the simple web browser shown in Fig. 7.1. We need a field to type in the URL, a “Load” button, and a display area for the web page itself. Since SWT already contains a Browser component, as well as all the smaller elements shown in the figure, all we have to do is wire them up appropriately.

Image

Figure 7.1 A Simple Browser


The user interface is represented in the software as a tree of widgets.


At the software level, the overall user interface is constructed as a tree of widgets (Fig. 7.2). Each widget covers a rectangular area of the screen, and it may have children according to the COMPOSITE pattern. In SWT, theImage 2.3.1 top-level window is called a shell and is represented by an object of class Shell. Inside the shell, and below it in the widget tree, we have placed top, browser, and statusBar. The browser is simply a predefined object of class Browser. The other two are Composites, which is SWT’s class that manages child widgets. The top composite contains the url, whichImage 2.3.1 is a Text, the load button of class Button, and a progress bar of class ProgressBar. The lower statusBar contains a single Label called status for displaying a text.

Image

Figure 7.2 Widget Tree of the Browser


Image The class hierarchy of SWT may be somewhat misleading at this point. The visible widgets on the screen derive from Control. The root class Widget, from which ControlImage 7.4.1 also derives, covers all elements of the user interface that may be associated with resources and that may send notifications. Other subclasses of Widget represent, e.g., tool tips, rows in tables, and the system tray. In the following discussion, we will nevertheless continue to speak of “widgets” for anything that is painted on the screen.


Image 2.3.1Let us look at the code that creates the tree in Fig. 7.2. The COMPOSITE pattern suggests introducing methods addChild and removeChild for the purpose. SWT uses a different approach: Each widget is constructed below a specific parent and that parent must be passed to the widget’s constructor. The construction of the tree from Fig. 7.2 then proceeds top-down, starting with the top-level window. The second parameter of each constructor is a generic bit set of flags, which can provide widget-specific customizations needed at creation time. Here, we do not use that feature and pass SWT.NONE.

swt.browser.SimpleBrowser.createContents


shell = new Shell();
top = new Composite(shell, SWT.NONE);
url = new Text(top, SWT.NONE);
load = new Button(top, SWT.NONE);
progress = new ProgressBar(top, SWT.NONE);
browser = new Browser(shell, SWT.NONE);
status = new Composite(shell, SWT.NONE);
statusText = new Label(status, SWT.NONE);


The advantage of the top-down construction is that each widget is properlyImage 2.2.1 owned by its parent, except for the top-level shells, which transitivelyImage 7.4 own all widgets in the tree below them. In this way, the operating system resources invariably associated with an SWT widget cannot be leaked easily.

SWT is very consistent in this approach. Even the single lines in tables and trees, which at the implementation level may allocate icon resources, are represented as Widgets. For instance, to create a table with random people (Fig. 7.3), we add each new line by the following code:

swt.intro.AddRemoveRowDemo.createContents


TableItem item = new TableItem(table, SWT.NONE);
item.setText(new String[] { randomName(), randomDate() });


Image

Figure 7.3 SWT Table

To remove a row, or a widget from the tree in general, we dispose of that widget, rather than asking its parent to remove it as done in COMPOSITE. In the example, the following code removes the currently selected person:

swt.intro.AddRemoveRowDemo.createContents


int sel = table.getSelectionIndex();
if (sel != -1)
table.getItem(sel).dispose();



Layout managers position widgets within their parents.


Once the widget tree is created, the technical backbone of the user interface is in place. However, the visual position of the single widgets is unclear. For instance, how is SWT supposed to know that we expect url, load, and progress to appear in the configuration shown in Fig. 7.1?

The solution consists of making each Composite responsible for positioning its children—that is, for computing a layout for them. However, the positions must be computed in a very flexible, application-specific manner. Composites therefore employ the STRATEGY pattern: They are configuredImage 1.3.4 by a LayoutManager and delegate any positioning requests to that object.

In the example, the top composite uses a GridLayout (lines 1–3 in the next code snippet; the parameters to GridLayout() in line 1 mean two columns, not using equal-width columns). That layout manager arranges the children in a table, but it is very flexible: Children may span multiple columns and rows (below the progress bar spans two columns), they are positioned within their table cell (e.g., left, center, fill), and some table cells may expand to fill the available space (true, false). These parameters are attached to the children as layout constraints of classGridData (lines 5–8).

swt.browser.SimpleBrowser.createContents


1 GridLayout gl_top = new GridLayout(2, false);
2 gl_top.marginWidth = 0;
3 gl_top.marginHeight = 0;
4 top.setLayout(gl_top);
5 url.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
6 false, 1, 1));
7 progress.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false,
8 false, 2, 1));


SWT offers a rich collection of layout managers that can be attached to arbitrary composite widgets. In practice, it is hardly ever necessary to write actual code to lay out children.


Do not specify pixel-based positions and sizes of widgets.


Using layouts requires some experience, since sometimes the widgets do notImage 7.2 come out as expected. The graphical WindowBuilder helps a lot, because you get an immediate preview of the later runtime behavior. However, you may still be tempted to just position widgets explicitly, because that gives you pixel-level control of the outcome:

swt.intro.XYLayoutDemo.createContents


text = new Text(shell, SWT.BORDER);
text.setBounds(53, 10, 387, 25);


Do not yield to this temptation: If the font sizes or the behavior of theImage 7.4 native widgets underlying SWT differ only very slightly on the user’s platform, the display will be ruined. Also, users like resizing windows to fit their current needs, and this is possible only if the inner layout adapts automatically.


Laying out widgets is a recursive top-down process.


In most situations, it is sufficient to configure the local layout managers with the desired constraints for all children. Sometimes, however, the result is not exactly as expected, and then one needs to understand the overall process. Fortunately, that process is straightforward: Each composite widget assigns positions and sizes to its children and lays them out recursively.

The core of the process can be seen in the following snippet from Composite. The composite delegates the positioning of the children to its layout manager (line 2). This assigns all children a position and size via setBounds(). The process then continues recursively to the children (lines 4–7). The parameter all is an optimization for contexts where it is known that the children have not changed internally.

org.eclipse.swt.widgets.Composite


1 void updateLayout (boolean all) {
2 layout.layout (this, changed);
3 if (all) {
4 Control [] children = _getChildren ();
5 for (int i=0; i<children.length; i++) {
6 children [i].updateLayout (all);
7 }
8 }
9 }


At several places, layout managers may wish to take into account the preferred sizes of the children to ensure that the content fits the available screen space. This information is provided by the method computeSize() of each widget. Atomic controls just measure their texts and images and compute the size. Composites again delegate the decision to their layout managers (line 3) but ensure that only the available space, given by the hint parameters, is taken up (lines 4–5). It remains to add any local borders and margins (line 6).

org.eclipse.swt.widgets.Composite


1 public Point computeSize (int wHint, int hHint, boolean changed) {
2 Point size;
3 size = layout.computeSize (this, wHint, hHint, changed);
4 if (wHint != SWT.DEFAULT) size.x = wHint;
5 if (hHint != SWT.DEFAULT) size.y = hHint;
6 Rectangle trim = computeTrim (0, 0, size.x, size.y);
7 return new Point (trim.width, trim.height);
8 }


Since laying out widgets is a top-down process, it is usually triggered on the top-level window. The first choice is to set the size explicitly and then lay out the content again. Essentially the same thing happens when the user resizes the window.

swt.browser.SimpleBrowser


shell.setSize(550, 300);
shell.layout();


The second choice, which is often preferable for dialogs, is to pack theImage 7.6 top-level shell.

swt.browser.SimpleBrowser.open


shell.open();
shell.pack();


Packing is, in fact, available on any widget and means that the widget will be resized to its preferred size:

org.eclipse.swt.widgets.Control


public void pack (boolean changed) {
setSize (computeSize (SWT.DEFAULT, SWT.DEFAULT, changed));
}



Events notify the application about the user’s actions.


In user interfaces, many things can happen almost at any time: The user moves the mouse, clicks a mouse button, presses a key on the keyboard, moves a window, closes the application, and many more. The application can never keep track of all of those things all by itself. Fortunately, this is also not necessary: SWT will notify the application whenever something interesting happens. These “interesting things” are called events in thisImage 10.1 context. To be notified about events, the application has to register an event-listener with the widgets on which they can occur. Between events, the application can “lay back” and “relax”—there is no need to work unlessImage 7.3.2 the user makes a request by doing something to the application’s windows.

In the browser example, the user will type in some URL, but there is no need to load the page until the user clicks the “Load” button. At this point, the entered URL has to be sent to the browser for display. In SWT, the event is called “selection” and is sent to a SelectionListener. The following event-listener contains the action to be taken directly in the method widgetSelected.

swt.browser.SimpleBrowser.createContents


load.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (!url.getText().isEmpty())
browser.setUrl(url.getText());
}
});



Image The generic selection event is used by many different widgets to signal that the user has “clicked” onto something, whether by selecting a table row or by pressing “enter” inside a text field. Simple selection calls method widgetSelected; double-clicks or pressing “enter” inside a text field are usually associated with some default action toImage 3.1.4 be taken, so they are reported by calling widgetDefaultSelected. The base class SelectionAdapter provides empty implementations, so that the application simply has to override the interesting case.



Image Image 2.1Listening to events is very similar to the Observer pattern. However, most of the time events do not refer to changes in the widgets’ state, as in the pattern, but to some external action performed by the user. The message given to the listeners is not so much “something has changed” as “something has happened.” While the change is persistent and can be queried by getter methods, the event is ephemeral and cannot be obtained otherwise. Nevertheless, the technical basis of event notification is the same asImage 2.1.2 Image 2.1.3 for the Observer pattern, so the same design considerations apply.


First-time developers of user interfaces sometimes find the event-based approach a little disconcerting: You add a tiny snippet of code and hope for the best, but can you really rely on being called by SWT? The abstractImage 7.3.2 answer is that SWT is built to guarantee the delivery of the events, so that you can rely on the behavior. It may also help to take a look at the internal processing steps, shown in Fig. 7.4 (much simplified and generalized). When the user clicks, the mouse sends some signal to the computer’s USB port, where it is picked up as a data packet and handed on to the computer’s window system. That part of the operating system determines the top-level window over which the mouse is located and passes the relevant dataImage 7.10.1 about the click to that window, where SWT is ready to receive it. SWT determines the single widget at the mouse location, which in the figureImage 10.1 is a button. The button first goes through the visual feedback. (Observe closely what happens between pressing and releasing the mouse button over a button widget!) Finally, the button notifies the application code in the listener.

Image

Figure 7.4 Overview: Events in a Button Click


Choose the correct event.


Every widget offers several events to which the application can react. The reason is very simple: Listening to events is all that an application can do to implement its functionality, so SWT has to be general and powerful enough to enable all desirable functionality to be implemented.

For instance, the Browser has to access the network and download possibly large amounts of data from possibly slow servers. It is absolutely necessary to provide the user with feedback about progress. The widget therefore offers progress reports as events. The following listener translatesImage 7.10.2 these into a concrete display for the user, by setting the progress bar from Fig. 7.1 appropriately.

swt.browser.SimpleBrowser.createContents


browser.addProgressListener(new ProgressListener() {
public void completed(ProgressEvent event) {
progress.setSelection(0);
}
public void changed(ProgressEvent event) {
progress.setSelection(event.current);
progress.setMaximum(event.total);
}
});



Image In choosing among the different events of a widget, it is sometimes easy to fall into traps. For instance, a Button offers to notify MouseListeners about mouse clicks. At first, this is what you want: When the user clicks, you receive an event. However, there are two problems. First, when the user presses a mouse button, moves the cursor outside the button, and then releases the button, then nothing should happen—the button is “armed” only as long as the cursor is inside. (Observe the visual feedback on your screen!)Image 10.1 Second, a button can be “clicked” by pressing “enter” on the keyboard as well, either by moving the focus to the button or by making the button the default one within the window. In summary, you always have to proceed based on the meaning of events—the precise conditions under which they will be signaled.



User interfaces offer different access paths to a given functionality.


Image 229 One of the core elements of usability is the goal of adapting the user interface to the user’s work flows and expectations. Users will have different preferences on how to access some piece of functionality: Some prefer clicking with the mouse, some use keyboard shortcuts, yet others search through menus.

In the present example, novice users might actually click the “Load” button. More experienced users will expect that pressing “enter” in the URL field will start loading the page. So, let us set up this reaction (note that now we use widgetDefaultSelected):

swt.browser.SimpleBrowser.createContents


url.addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {
if (!url.getText().isEmpty())
browser.setUrl(url.getText());
}
});


Menus are similar to widgets in that they form a tree. The next code snippet shows the construction of a “Quit” entry in a “File” menu. The overall menu bar is attached to the shell (line 2). Menu items, like buttons, notify their selection-listeners when the user clicks on the item (lines 9–13).

swt.browser.SimpleBrowser.createContents


1 Menu menu = new Menu(shell, SWT.BAR);
2 shell.setMenuBar(menu);
3 MenuItem mitemFile = new MenuItem(menu, SWT.CASCADE);
4 mitemFile.setText("File");
5 Menu menuFile = new Menu(mitemFile);
6 mitemFile.setMenu(menuFile);
7 MenuItem mitemQuit = new MenuItem(menuFile, SWT.NONE);
8 mitemQuit.setText("Quit");
9 mitemQuit.addSelectionListener(new SelectionAdapter() {
10 public void widgetSelected(SelectionEvent e) {
11 shell.close();
12 }
13 });


To avoid re-implementing the same reaction in different event-listeners, you should usually factor this reaction out into a protected method of the surrounding class. From a broader perspective, the decisions involved hereImage 2.1.3 are really the same as in the case of implementing the observer interface, where we asked whether the event-listener or the surrounding class should contain the update logic of the observer.


Image Image 9.3.4In most contexts, one does not create menus by hand. JFace wraps the bare functionality in a MenuManager, which keeps track of current “contributions” and reflects them in menus. It also enables navigation by paths for more declarative building of menu structures. The Eclipse platform uses the latter feature to render additions by multipleImage 174 plugins into one consistent menu structure.



The Display is the application’s access to the window system.


A last element of SWT interfaces is the global Display. It represents the application’s access to the underlying window system. In particular, it serves as an anchor point for resources such as Images, Fonts, and Shells. Furthermore, the Display offers global SWT functionality such as timer-basedImage 7.9 execution. Since the display is so important, it is passed to all created elements and can usually be retrieved by getDisplay(), for instance from the surrounding widget from within event-listeners. Globally, a special default display opens the connection to the window system when it is first requested:

swt.browser.SimpleBrowser.open


Display display = Display.getDefault();



Building user interfaces with SWT is technically straightforward.


This finishes the basic setup of any user interface: You create a widget tree, specify the layout of child widgets within their parent, and attach listeners to react to user interactions. The complexity of user interfaces results from keeping track of events and the proper reactions, and from matching theImage 10 user’s expectations.


Do not map data to widgets by hand.


User interfaces usually display some of the data managed by the application: A text field might contain the name of a customer; a table might show the orders placed by that customer, where each order is represented by the product’s name and quantity, and the overall price. Such mappings are both tedious and commonplace. To deal with them, Eclipse’s JFace layer, which resides on top of the SWT widget set, offers two abstractions: Viewers keep the displayed data synchronized with the underlying application’sImage 9.3.1 data structures through changes, and data binding links single inputImage 9.3.3 fields to properties of objects. Most user interface frameworks offer this kindImage 1.3.3 of division, and it must be mastered before attempting any serious project.

7.2 The WindowBuilder: A Graphical Editor for UIs

The introduction in the previous section outlined the standard procedure for building user interfaces: create a widget tree, define the local layouts, and attach event-listeners to the relevant widgets. The process is, in fact, a little tedious and very time-consuming. Although the conceptual basis is fairly obvious, the technical code to be written quickly becomes lengthy and requires a detailed knowledge of the API, in particular for specifying the layout constraints.

Many widget toolkits come with a graphical editor for the user interface. With this tool, you can place widgets by drag-and-drop, specify layout parameters by simple clicks, and attach listeners through the context menu. We present here Eclipse’s WindowBuilder.

From the start, we point out that such tools do not relieve you from knowing details of UI programming yourself. That is, the tools help you with standard tasks, but they will not support you through the detailed adaptations that customers invariably expect from professional user interfaces. In fact, most of the UI code of the Eclipse platform is writtenImage 1.4.5 Image 1.4.8 by hand, and standard code structuring techniques keep it readable and maintainable.

7.2.1 Overview

Fig. 7.5 gives an overview of the WindowBuilder. The right-hand side pane contains a sketch of our simple browser application (Fig. 7.1), with the central browser widget highlighted. The controls at the top represent the layout constraints of the GridLayout, with the current choices depressed. Dragging handles on the selection changes the rows and columns spanned by the widget. To the left of this main area, the palette presents the available elements. The left-hand side gives the overall widget tree at the top andImage 1.3.3 the detailed properties of the currently selected widget.

Image

Figure 7.5 The WindowBuilder

The lower-left corner shows a strength of the WindowBuilder: The tool does not keep a separate description of the user interface, for instance in XML. It extracts the graphical view from the source code instead, probablyImage 9.1 building an internal model for caching the analysis. As a result, you can switch freely between the two perspectives, according to the best fit for your current goals. Modifying the graphical representation generates the corresponding code in the background, while manual modifications of the code are integrated into the graphical view as well. This feature is also highlighted by the ability to reparse the code from scratch (at the top of Fig. 7.5), which is sometimes necessary if the graphical view has gotten out of sync.

Events are handled via the context menu (Fig. 7.6). The WindowBuilder keeps track of the currently defined events for each widget and offers all available events as a simple choice. Selecting any menu item here jumps to the corresponding code section defining the event-listener. The WindowBuilder is even a bit smarter: If the listener calls a single method, then itImage 7.1 jumps to that method instead. This makes it easy to factor out common reactions.

Image

Figure 7.6 Events in the WindowBuilder


You still have to be able to write user interface code by hand.


The WindowBuilder certainly boosts the productivity of professionals, but it can easily lead the novice to make wrong decisions. For instance, theImage 7.1 choice between attaching a selection-listener or a mouse-listener to a button, as mentioned before, must be made by the developer. Also, the abstraction offered by the tool is not perfect. For instance, it sometimes setsImage 7.1 the preferred sizes of widgets in pixels, which later destroys the previews and application behavior. Finally, the tool does not always deal well with legacy code created by hand, so you will have to massage that into a more suitable form.


Image There is one possible trap when combining handwritten widgets with code generated by the WindowBuilder: Constructors must not take custom parameters, such asImage 9.2 the application data that a widget is supposed to display. The WindowBuilder expectsImage 7.5 custom widgets to obey the SWT conventions, where the constructor accepts the parent widget and possibly some flags. For Swing, it expects a default, no-argument constructor.


7.2.2 Creating and Launching SWT Applications

SWT is not part of the regular Java library, but must be linked in from the Eclipse environment. The WindowBuilder offers a tempting shortcut: When you create a new SWT application window in a regular Java project through New/Other/WindowBuilder/SWT Designer/Application Window, the WindowBuilder will find the relevant JAR files from the local Eclipse installation and put them on the project’s class path. Unfortunately, it uses the absolute local paths, so that the project cannot be shared within the development team.


Use SWT only in plugin projects.


Image A.1Eclipse is based on the OSGi module system, where a module is called a bundle. A plugin in Eclipse is nothing but a special bundle. SWT itself is wrapped up as a bundle, to make it accessible to other parts of the Eclipse platform. Fortunately, OSGi is very lightweight and straightforward to use.

Image A.1.2 The way to access SWT is to create a plugin project, rather than a plain Java project (or to convert an existing Java project to a plugin project through the context menu’s Convert entry). Then, in the plugin project’s MANIFEST.MF, you can add the SWT bundle as a dependency. These dependencies are captured by bundle names instead of local paths and can therefore be shared in the team.

Image A.2.5Within Eclipse, this setup enables you to run the SWT application as a simple Java application. The Eclipse launch configuration recognizes the OSGi dependencies and converts them to references to the corresponding JARs from the local Eclipse installation on the fly. What is more, the setup also allows you to export the SWT application as stand-alone software,Image A.2.3 complete with a native launcher similar to the eclipse launcher of Eclipse itself.

Let us summarize this explanation as a simple step-by-step recipe:


Tool: Create and Run an SWT Application

• Create a plugin project through New/Other/Plug-in Project

• Open the project’s MANIFEST.MF and add a dependency on org. eclipse.swt

• Create a package in the project as usual

• Use New/Other in the context menu

• In the dialog, type Application on top

• Select the SWT/Application Window

• Create the window’s content with the WindowBuilder

• Launch the window with Run as/Java Application


7.3 Developing with Frameworks

SWT offers a rich functionality for building user interfaces, but it is not completely trivial to use and to learn. You have to understand conceptsImage 7.1 such as widget trees and layouts, and then you have to learn how to exploit this infrastructure for your own applications. In practice, this often involves looking for hours for the “right way” to do a particular thing. You will encounter this phenomenon with many other reusable software products, such as application servers, extensible editors such as GIMP, and IDEs suchImage 201 Image 117 as Eclipse: You browse tutorials at length to find out what to do and then you do it within 15 minutes with a few lines of code. This experience can be quite frustrating. It is then good to know the conceptual basis of these complexities. This section explains that the approach taken by SWT and other such tools is essentially the only possible way to go.

If you are currently more interested in learning SWT from a technical perspective, feel free to skip this section. But be sure to come back later—knowing the concepts of frameworks is essential for any professional developer, since they help in learning new frameworks more quickly.

7.3.1 The Goals of Frameworks

Many reusable software products come in the form of frameworks thatImage 131,130,97,244 tackle complex tasks. Libraries such as the JDK’s classes for I/O or collections offer small, stand-alone pieces of functionality in the form of individual objects. You can take your pick and understand them one by one. Frameworks, in contrast, can solve their larger tasks only by providing entire networksImage 1.1 Image 11.1 of collaborating objects, which you have to buy complete or not at all. Some are finished but extensible applications, such as the Eclipse IDE. Some are semi-complete applications that just miss any specific functionality,Image 174 such as the Eclipse Rich Client Platform. Some form only theImage 201 backbone of applications, such as SWT or application servers. In any case, the framework you choose determines the overall structure of your application to a large degree.


Image Before we continue, we wish to point out that our subsequent description of “frameworks” follows the classical object-oriented definition from the cited literature. It is not uncommon, however, to find the term “framework” applied in a much broader sense.Image 14,13Often, it is used simply as an alternative for “complex library.” MacOS uses it in the specific sense of a reusable software bundle installed on the system. Despite these different uses of the term, we find that the concepts attached to the classical definition are worth studying in any case, because they help developers to create and to understand professional software structures.



Frameworks provide ready-made structures for specific types of applications.


Frameworks offer complete or semi-complete networks of collaborating objects that support certain types of applications. When the framework starts working, everything falls into place, the large structures of the application are safely fixed, and the software is up and running.

It is useful to distinguish between two types of frameworks, because the type tells you what you can expect. Application frameworks offer infrastructure that is useful for building applications of certain shapes, independentImage 174 of the particular domain that they address. For instance, the Eclipse RichImage 9.3 Image 201,268,9 Client Platform, the SWT and JFace layers, and frameworks for developing web applications do not care about the content of the applications they support. You are free to develop just the application you need. At the same time, there is no support at all for the application’s business logic.

Domain frameworks, in contrast, help in building applications for particular domains. For instance, bioinformatics is a discipline that is quickly maturing and that is developing standard processing steps for genome and other data. New scientific discoveries require building on existing techniques,Image 40,41,25 and many frameworks seek to support this approach. Domain frameworks do include support for the application’s business logic, but at the expense of also constraining the implementation of that logic to fit the framework’s given structures.

7.3.2 Inversion of Control

The approach of building applications on top of existing networks of objects has dramatic consequences for the framework’s API, which are summarized under the term “inversion of control.” We will now examine this central switch of perspective, starting with a brief motivation.


Frameworks define generic mechanisms.


The tasks that frameworks take on are often complex and require an intricate software machinery. For instance, SWT handles layout, repainting,Image 7.1 and event dispatching, among many other things. All of these tasks require collaborations among many objects, both along the structure of the widget tree and outside it.

Frameworks set up such mechanisms, such planned sequences of collaborationsImage 11.1 to relieve the application programmer from tedious standard tasks. The experience from building many applications has told the framework designers which mechanisms recur very often, and now the framework implements them once and for all. The application programmer can rely on the mechanisms, without understanding them in detail.


Frameworks rely on inversion of control.


Since the collaborations within the framework are complex, it is clear that the application cannot determine and prescribe every single step. With libraries, the application calls methods of service provider objects to getImage 1.8.2 things going. With frameworks, things are already going on all the time. The application is notified only at specific points that the framework designers have judged useful based on their experience.

For instance, SWT offers buttons that the user can click. The button itself handles the visual feedback on mouse movements and the keyboard focus. The application is notified when the only really interesting thing happens—that is, when the user has clicked the button.


Image Since some applications require it, there are also low-level notifications about theImage 7.8 mouse movements and the keyboard focus. These are not specific to buttons, but work for almost any widget. They are introduced higher up in the class hierarchy, at Control. Again, the framework designers had to anticipate the need for these events.


At the crucial points, the collaboration between framework and application is thus reversed, compared to the case of libraries: It is the framework that invokes application methods, while previously it was the application that invoked library methods. Since method calls are part of the software’s control flow, one speaks of inversion of control. The framework determines the control flow in general; it passes control to the application only at designated points. A snappy formulation that has stuck is the Hollywood Principle:Image 7.3.3 Image 242 “Don’t call us, we’ll call you.”


Image If you already know SWT in some detail, you may feel at this point that SWT isImage 7.10.1 not a framework at all. From a technical perspective, it is actually the application that drives everything. The main method typically sets up the content of the application window and then keeps dispatching the incoming events by calling SWT methods:

swt.browser.SimpleBrowser.open


Display display = Display.getDefault();
... create shell and window contents
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}


From a conceptual point of view, however, the application does not really control anything: The whole logic of event handling is determined by SWT, after the application calls readAndDispatch(). The application code in event handlers is called back only when and if SWT sees fit.

For development purposes, it is better to understand SWT as a framework with inversion of control. In fact, the boilerplate code for the main method is a peculiarityImage 7.6 of SWT: Other UI frameworks usually incorporate and encapsulate the dispatch loop completely.



Develop a laid back attitude toward frameworks.


We have found that novices often experience inversion of control first as a loss of control. That is, since they cannot see the code that makes things happen, they feel insecure about whether and when they will happen at all.

When working with frameworks, it is better to take the opposite perspective. You trust the framework to do its job properly. You can then conclude that you do not have to do anything until the framework explicitly asks you to do it. At this point, the framework will even hand to you all the information required for the task—you do not have to ask for it. Your software can “lay back” and “relax” in the meantime. As a developer, you can relax because you do not have to provide any code to make things happen, except for the special contributions at the callback points.


Know the framework’s mechanisms.


The biggest hurdle in learning frameworks is to understand which mechanisms exist and which mechanism is supposed to be used for which purpose. Framework designers envision the applications that will be built on top of the framework, and they provide mechanisms for those use cases that are likely to arise. Your job as an application programmer is to take the reverse step: Based on your use case, you check out which mechanism the designer has provided.

The most common way of learning frameworks is by studying examples and tutorials. If they are created by the original designers, these tutorials usually show how to use particular mechanisms. It is important to see a tutorial not as a source for copy-and-paste programming, but as a prototypical case demonstrating a greater idea. Look for the point behind the code, and ask which larger purpose each statement in the example code fulfills.

Sometimes choosing a mechanism requires some thought. As a simpleImage 7.1 example, SWT provides two events that react to mouse clicks on Buttons: mouseClicked() and widgetSelected(). The second one is the correct one for ordinary “button clicks,” because it also covers activations created by pressing the “enter” key and as the default button of a dialog.

Frameworks cannot be coaxed by force to exhibit some behavior you need. Until you have found the mechanism that supports your use case, there is nothing to do but keep looking. Many frameworks have forums on the web, and many discussions in these forums circle around the problem of finding the right mechanism for a particular purpose. The framework users with guru status are usually distinguished by having more mechanisms right at their fingertips.

Very often, finding a mechanism involves understanding it in some detail. For instance, it is simple enough to create application-specific SWTImage 7.8 widgets that paint directly on the screen. For larger ones displaying complex data structures, however, you have to paint only the changed parts.Image 9.4.3 Fig. 7.7 sketches the involved steps, and each step has a particular conceptual justification. (The “model” contains the application’s data and businessImage 9.2.1 logic, the “view” displays the data, and the “graphics context” GC is used for painting on the screen.)

Image

Figure 7.7 Preview: Process of Incremental Screen Updates


Do not look at the framework’s internal implementation.


Even if mechanisms must be understood in some detail, their precise implementation is none of your business. It must be treated as encapsulated, even if it happens to involve protected or even public methods. The framework designers specify the possible points of interaction with the application, butImage 7.3.3 apart from these, the framework code may change without further notice. Only novices use the debugger to examine sequences of method calls and to override arbitrary protected methods that they happen to find. Doing this causes the Fragile Base Class Problem and will almost certainly breakImage 3.1.11 the application within a few revisions of the framework.


Image To be entirely fair, it may be necessary to trace through the details of framework methods to understand the mechanisms. If the framework’s documentation is sketchy and consists of just a few tutorials, looking at the execution is the only chance you get. However, having obtained the information you need to understand the tutorials, you should forget whatever internal details you have learned on the way.


7.3.3 Adaptation Points in Frameworks

Inversion of control demands that the framework code calls application code at specified points. In other words, application code is injected into the control flow established by the framework. In object-oriented frameworks, thisImage 1.4.1 is accomplished through polymorphism and dynamic method dispatch: The application code overrides or implements some method that the framework provides for the purpose. These designated methods are also called the hot spots of the framework. The remainder of the framework is supposed to be treated as a fixed, black-box setup called the frozen spots. The frozen spots constitute the infrastructure that the framework provides. IgnoringImage 3.1.11 the frozen spots leads to the Fragile Base Class Problem: The framework’s classes cannot change without breaking the application. With any framework you learn, it is therefore important to recognize the adaptation points and to understand their intended usage.


White-box frameworks: Applications override methods in framework classes.


The most straightforward way of specifying adaptation points is the directImage 1.4.9 method overriding undertaken in the TEMPLATE METHOD pattern: The framework implements the general mechanisms and algorithms, but at specific points it passes control to designated protected methods. ApplicationsImage 1.4.11 can override these to add new behavior. The documentation of theImage 3.1.2 framework class should then specify the method precisely: Which kind of behavior is expected to fit into it? Which kind of use case is it supposed to cover?

Frameworks that rely on this kind of collaboration are called white-box frameworks, because the application developer has to understand the framework’s class hierarchy and some of the classes’ internal mechanisms.

It is important to note that the application must not override just any protected method that it finds. Many of these methods are used for internalImage 3.1.4 purposes, such as to factor out reusable functionality within the framework’s own class hierarchy.

It can, however, be seen as an advantage of white-box frameworks that an application can in principle override any existing protected methods. It can therefore modify the framework’s behavior even in cases that the framework developers had not foreseen. As a result, it is slightly simpler toImage 7.3.4 develop white-box frameworks than black-box frameworks. Also, the risk that the framework might fall short of the application’s expectations is reduced. Of course, this flexibility comes at the price of increasing the learning curve.


Black-box frameworks: Applications plug in objects with designated interfaces.


Black-box frameworks hide the implementation of their mechanisms completely from applications. They define special interfaces that applications can implement to receive notifications at relevant points in the mechanisms. The API then resembles that of the OBSERVER pattern: The frameworkImage 2.1 defines notification interfaces; the application registers listeners for the notifications.

In SWT (and JFace), all events are delivered through black-box mechanisms. Most widgets in SWT are entirely black-box, because they just wrap native widgets from the window system; the underlying, fixed C-implementationImage 7.4 cannot easily call back protected methods on the Java side. Even the low-level callback for repainting a custom widget is invokedImage 7.8 on a dedicated PaintListener, while Java’s Swing, for instance, uses overriding of the paint() or paintComponent() methods from JComponent.

One advantage of black-box frameworks is that they are easier to learn. The application programmer can understand one adaptation point at a time and does not have to understand the framework’s class hierarchy and its possibly intricate sequences of up-calls and down-calls. The application is not constrained in the choice of super-classes, which enables reuse ofImage 3.1.4 application-specific functionality through common super-classes. Also, the application is shielded from restructurings within the framework and the Fragile Base Class Problem is avoided. Many larger frameworks, such asImage 3.1.11 the Eclipse platform, rely on black-box mechanisms because they are likely to change over time and have large user bases who depend on backward compatibility.

One disadvantage of black-box frameworks is that any use cases not envisaged by the framework designers cannot be implemented at all, not even by low-level tweaks found only through debugging. As a result, developing useful black-box frameworks is more difficult and takes much more experience.

Also, it may be harder to actually implement the objects to plug into the framework, because the framework itself offers no infrastructure for this step. Many black-box frameworks remedy this by providing abstract base classes for their interfaces. For instance, JFace asks the application toImage 9.3.1 provide ILabelProviders to show data on the screen, but it offers a class LabelProvider that implements the obvious bookkeeping of listeners. Be sure to look for such classes: They can also help you understand what the object is supposed to do and which kinds of functionality are envisioned for an adaptation point.


Most frameworks are hybrids between black-box and white-box.


Because white-box and black-box frameworks have complementary advantages and disadvantages, most frameworks are actually hybrids: They offer the most frequently used adaptations in black-box style, and the more advanced (or obscure) adaptations in white-box style. Be sure to understand which kind you are currently dealing with; also, try to find a black-box mechanism first, because it may be more stable through evolutions of the framework.

7.3.4 Liabilities of Frameworks

Frameworks are great tools if they work for your application, but they can bring your project to a grinding halt if they don’t. Once you have decided on one framework, you are usually tied to it for the rest of the project. Be extremely conservative when choosing your frameworks.


Check early on whether the framework covers all of your use cases.


The biggest problem with frameworks is that their web pages look great and promise the world. But when it comes to actually coding the more advanced use cases of your application, you may find that the required adaptation points are missing. This is the most costly scenario for your company: You may have invested 90% of the project’s budget, only to find that you will have to redo the whole thing, because your code is tied to the one framework and will usually not be portable to others.

Two immediate countermeasures suggest themselves. First, before fixing the decision for a framework, build a throwaway prototype of your application; be sure to include the most advanced and most intricate requirements you can think of. The code you produce will probably be horrible, because you are only just learning the framework. But at least you will know that everything can be done. The second idea is related: You should check whether your use cases fall into the broad, well-trodden paths covered in tutorials and examples. That functionality is well tested and stable. Furthermore, if you need something similar, you are likely to find it covered as well.


Check whether the developers are really domain experts.


A good strategy to estimate whether a framework is likely to cover your needs is to look at its design team. If they are experienced professionals who have long worked in the domain that the framework addresses, you can be pretty sure that they have thought of all of your needs even beforeImage 174 you were aware of them. While exploring new corners of the Eclipse andImage 50 Netbeans platforms, I’m constantly surprised by the amount of detailed thought that must have gone into the API design. Anticipating the use cases of a framework is foremost a question of experience in the domain.


Image One reason why Eclipse has come out so well designed is certainly that IBM already had years of experience in building its Visual Age for Java development environment, and related products for other languages, starting with SmallTalk in 1993. The developers already knew the software structures that worked well, and more importantly those that did not work so well.


Opposite examples can be found in the myriad text editors written by hobbyists. Usually, the editors are the first ones that their developers have ever created. The projects start small, which means they do not have sufficient provisions for extensions. The authors have no strong background in software architecture, so they are likely to get the overall structures wrong and miss the important design constraints on APIs. If you build your application as a plugin to such an editor, your project is most certainly doomed to die within a few years (unless you get involved in developing the underlying platform, which will be a heavy investment).


Check whether there are detailed tutorials and examples.


Learning frameworks is best accomplished by studying their API definition and checking out the concrete use cases in their tutorials and examples. Finding the right way to do something in a tutorial saves you the effort of reading through the written documentation. To evaluate a framework, start by gathering the known requirements and checking whether they are covered in official tutorials.


Image Beware of tutorials written in blog style. They may show a possible way of accomplishing something, but this may not be the way that the framework designers had planned for. As a result, the steps in the tutorial may be more complicated than necessary or may use fringe features broken with the next release. Tutorials are more reliable if the authors do not simply list the steps they have taken, but also explain their meaning and purpose in a larger context. If a tutorial does not do that, try to link its proposals to the official documentation.



Check whether the framework has reliable long-term backing.


Another problem with frameworks is that there is usually a flurry of activity when they first appear, but over time the developers have to move on to other projects. For your own company, the effects of this lack of support may be dramatic: You may be stuck with an old version of a software whose bugs will remain unfixed forever. Indications to look out for are rather obvious: the size of the user base, the organization supporting the framework development, and the history of the project.

7.4 SWT and the Native Interface

Java is designed to run anywhere, on top of the JVM installed locally. Accordingly, the Swing and JavaFX user interface frameworks provide a common look-and-feel across platforms. As a result, users familiar with Java applications will be able to work with it regardless of the operating system they are currently using.


SWT lets you build professional applications that users will like.


SWT deviates from this Java point of view and accesses the native widget toolkit of the platform. As a result, an SWT application will look like a Windows, MacOS, or GTK application, depending on the environment. This is, in fact, what most users will prefer: A Swing application on Windows looks out of place to the ordinary Windows user, since everything looks a bit different, the keyboard shortcuts are different, and so on. The native toolkit also has the advantage of speed, since it is usually written in C and integrated tightly with the specific window system.

7.4.1 Influence on the API

Even if the native widgets themselves are encapsulated by the SWT widgets, the decision to target the native widget toolkit will influence the behavior of the SWT widgets at several places. It is therefore necessary to take a brief look at the relationship.

Fig. 7.8 gives an overview. The SWT widget tree is mirrored, although perhaps not one-to-one, in a native widget tree. Each SWT widget is aImage 2.4.3 Image 2.4.4 PROXY for its native counterpart: It holds a handle to the native widget and uses it to invoke native functionality. However, the SWT widget is alsoImage 2.4.1 an ADAPTER, in that it translates an expected, cross-platform API to a platform-specific API.

Image

Figure 7.8 SWT and Native Widget Trees


SWT widgets are not usable without their native counterparts.


Because of the close relationship, SWT widgets can work only if their native counterparts exist. Each constructor therefore creates the correspondingImage 7.1 native widget immediately. Because the constructor receives the parent widget as a parameter, it can add the native widget to the tree andobtain any context information—for instance, about fonts and background colors—that may be necessary at this point.

Furthermore, any access to an SWT widget first checks this fundamentalImage 1.5.4 condition before possibly messing up the native C-level memory.

org.eclipse.swt.widgets.Widget


protected void checkWidget() {
if (display == null)
error(SWT.ERROR_WIDGET_DISPOSED);
...
if ((state & DISPOSED) != 0)
error(SWT.ERROR_WIDGET_DISPOSED);
}


The exception Widget is disposed occurs when you happen to access an SWT widget without a native counterpart.


Most widgets must not be subclassed.


Many user interface frameworks are white-box frameworks: You see a widgetImage 7.3.3 that suits your needs in general, but it would need to be modified in the details—for instance, by painting some fancy overlay when the mouse is moved over it. In such a case, you just subclass and override the methodImage 3.1.7 that paints the widget on the screen.

Since SWT widgets do not really live completely in the Java world, such ad-hoc adaptations by inheritance are not allowed. The interactions with the behavior of the native widgets could hardly be controlled. The method checkSubclass() in SWT widgets checks the restriction at runtime. Only a few widgets, such as Composite and Canvas, are meant to beImage 7.5 Image 7.8 subclassed.


The application must dispose of any SWT widget or resource.


Java objects are managed by the JVM’s garbage collector: When the program cannot access a given object, that object’s memory is reclaimed and reused. This mechanism does not work for the C-level native widgets, because the garbage collector cannot work on the C stack and C dataImage 133structures reliably.

When working with SWT, all widgets, as well as other resources such as images and fonts, must be freed explicitly by calling dispose() on the SWT widget. It is advisable to do so as soon as possible. For instance, if a dialog requires a particular image, then that image should be freed as soon as the dialog is closed.


Image Do not override dispose on widgets such as dialogs to free extra resources allocated. That method is used for the internal disposal of the native widgets. Attach a Dispose Listener instead.


The correct way to proceed is shown in the following snippet from a dialog that displays an image sun.png. Lines 1–2 load the image into memory. This data is an ordinary Java object that is handled by the garbage collector. Line 3 allocates a native image resource, which is not garbage-collected. Lines 4–8 therefore ensure that the image is disposed as soon as the dialog is closed.

swt.resources.DialogWithImage.createContents


1 ImageData imageData = new ImageData(
2 DialogWithImage.class.getResourceAsStream("sun.png"));
3 imageResource = new Image(shell.getDisplay(), imageData);
4 shell.addDisposeListener(new DisposeListener() {
5 public void widgetDisposed(DisposeEvent e) {
6 imageResource.dispose();
7 }
8 });



Image The WindowBuilder allows you to set the images of labels and buttons very conveniently through the widget’s properties. However, it creates a class SWTResource Manager, which owns and caches all images loaded in this manner. This class remains in memory for the whole runtime of the application, and the loaded images will not be freed unless the dispose() method of the class is called. This strategy is acceptable for simple applications, but large images should always be freed as soon as possible. Simple icons and fonts are usually obtained from the platform or are cached in ImageRegistrys associated with plugins.



Test an SWT application on every window system that you support.


Even though SWT provides a pretty good abstraction layer over the native widgets, that abstraction is not perfect. The behavior of widgets and the framework may differ in subtle details that break your application in special situations. Before shipping a product, be sure to test it on each supported target platform.

7.4.2 Influence on Launching Applications

Since SWT widgets internally create and manage native widgets, the SWT implementation is platform-specific and the SWT OSGi bundle contains native libraries accessed through the Java Native Interface (JNI). When anImage A.2 application is launched from within Eclipse, the correct plugin is selected automatically from the Eclipse installation. Similarly, when using SWT within an Eclipse plugin, the SWT implementation from the host Eclipse platform is used.

It is only when creating stand-alone applications that a little care must be taken to provide the right JAR file for the target platform. When that file is included in the class path, the application can be started as usual from the command line.

A more elegant approach is to use the Eclipse Rich Client PlatformImage 174 directly. For this platform, the Eclipse developers provide a delta pack plugin,Image A.3 which contains the SWT plugins for all supported platforms. Also, the Eclipse export wizards for building the product are aware of the different target systems and will create different deliverables automatically.

7.5 Compound Widgets

Object-oriented programming makes it simple to aggregate combinationsImage 1.8.5 Image 2.2 of objects into higher-level components: Just create a new class to hold the necessary helpers, then wire up the helpers internally to create the desired functionality. The same can be done with widgets:


Derive reusable compound widgets from Composite.


What is more, the WindowBuilder actively supports this strategy:Image 7.2


Tool: Create Composite with WindowBuilder

From a package’s context menu, select New/Other and then SWT/Composite in the dialog. (Type “Composite” at the top.)


As a final touch, you can place any such widget easily into a larger context.


Tool: Reference Custom Widgets in WindowBuilder

In the WindowBuilder’s palette, use the System/Choose Component button to select any widget from the class path and drop it into the preview pane. Alternatively, you can create new categories and items in the palette from the palette’s context menu. Finally, you can bundle palette entries with the project using New/Other/WindowBuilder/Project Palette (type “palette” into the filter).



Image Follow the SWT convention of passing only the parent widget and possibly a bit-set of style flags to the constructor. Otherwise, the WindowBuilder might have problems creating the preview.


As an example, we will build a calendar view that enables the user to pick out a date in the expected way (Fig. 7.9, the upper component). That is, first the user switches to the correct month (starting from the current month), and then the user clicks on a day within the month. The widget has a property selectedDate and implements the OBSERVER pattern, soImage 2.1 that the example can track the current selection in the text field.

Image

Figure 7.9 The DatePicker Widget

We will also use this example as a walk-through of user interface development in general. After all, any window or dialog we construct is just a compound widget, one that serves as a root of the widget tree. Although it may not be as stand-alone and reusable as the DatePicker, the general programming guidelines outlined here still apply. The overall setup is this: We subclass Composite and fill in the desired behavior.

swt.compound.DatePicker


public class DatePicker extends Composite {
...
public DatePicker(Composite parent) {
super(parent, SWT.BORDER);
...
}
...
}



Image You might be tempted to derive your widget from something like Text or Label,Image 7.4.1 because its behavior is similar to the one you need to create. However, most widgets in SWT must not be subclassed. Composite is an exception to the rule, as it explicitly allows for subclassing and disabling the corresponding safety check:

org.eclipse.swt.widgets.Composite


protected void checkSubclass() {
/* Do nothing - Subclassing is allowed */
}


If your widget is essentially a variant of an existing one, set the existing one as the Composite’s only child and let it fill the space with a FillLayout.



Make compound widgets into proper objects.


One thing to be aware of is that compound widgets are not just arbitrary collections of sub-widgets or nice visual gadgets. From a development perspective, they are first of all compound objects that function withinImage 2.2 the software machinery. In the best case, they adhere to the fundamentalImage 1.1 characteristics of objects: They provide a consistent API for a small, well-defined piece of functionality; they encapsulate the necessary machinery; and they communicate with their collaborators through well-defined channels. Furthermore, they take the ownership of their child widgets seriously,Image 2.2.1 Image 2.2.3 in creating and disposing of them. Also, they define and maintain invariantsImage 6.2 between their different parts and between their own fields and the parts. Because these aspects look slightly different in an event-driven, visual environment, let us look at them in some more detail for the date picker widget.


Implement a small, well-defined piece of functionality.


The action of “picking a date” is certainly a small enough task, and it appears in many applications. Also, the conventions for picking dates from a calendar are quite obvious, so that we know how the widget is supposed to behave. The idea of objects taking on a single task is thereforeImage 1.1 Image11.2 obeyed.


Give the widgets a proper API.


To work well as a software object, a compound widget must be a convenient collaborator for the software’s other objects. It is therefore crucial to get the API right—just a nice visual appearance does not make for a great widget. As usual, it is a good idea to start from the clients’ perspective.Image 1.1 Image 3.2.2 Image 5.4.3 What is the result of picking a date, from a technical point of view? A date is commonly represented as a Date, so it is natural to provide a getter and setter for a property selectedDay:

swt.compound.DatePicker


public Date getSelectedDay()
public void setSelectedDay(Date date)



Fit compound widgets into the event-driven context.


User interfaces are all about the application reacting to user actions. IfImage 7.1 the new widget is to work well in such an environment, it must offer all necessary events to its clients. For the example widget, it is sufficient toImage 2.1 implement the OBSERVER pattern for its single property. In doing so, we have to follow one more guideline:


Adapt the design to the context.


It is a fundamental design strategy to solve similar problems by similar means, since this keeps the overall software readable and understandable.Image 2.1.2 While the standard implementation of OBSERVER is clear, the context of SWT widgets suggests a different solution: to use SWT’s standard “selection” event and to reuse the notification infrastructure from the Widget class. First, here are the methods for the clients:

swt.compound.DatePicker


public void addSelectionListener(SelectionListener l) {
TypedListener typedListener = new TypedListener(l);
addListener(SWT.Selection, typedListener);
}
public void removeSelectionListener(SelectionListener l) {
removeListener(SWT.Selection, l);
}


Sending the notifications is then straightforward, since the superclass already provides the necessary method:

swt.compound.DatePicker.setSelectedDayInternal


notifyListeners(SWT.Selection, new Event());



Image Image 2.1.2The default construction of new Event() might seem odd, since a subject should at least provide a reference to itself. To simplify this common coding idiom, the notification infrastructure in Widget fills in the basic fields of the event that have not yet been initialized.



Encapsulate internal data structures.


Compound widgets will usually require internal data structures and maintain invariants on them. The DatePicker widget shows how these can be used in the context of event-driven software.


Image Technically, the internal widget structure of compound widgets is accessible viaImage 7.1 getChildren(), because compound widgets are part of the usual widget tree. However, clients are sure to know that such accesses are undesirable.


As a first point, the DatePicker (Fig. 7.9) must somehow maintain the current month, chosen in the upper part of the widget, and display its days in the lower part, aligned correctly with the days of the week. We can create the upper part and place a Composite to maintain the month’s days using the WindowBuilder:

swt.compound.DatePicker


private Label curMonth;
private Composite days;


But how do we represent the current month and the current selection? This question can be answered only by truly internal data structures. At the core of our application, we keep a Calendar, because this makes it simple to do arithmetic for the forward/backward buttons. We maintain the invariant that cal always contains the first day of the current month.Image 4.1 We store the currently selected day separately.

swt.compound.DatePicker


private Calendar cal;
private Calendar selectedDay;


The choice of selectedDay is especially interesting, because it links the internal data structures and the external behavior. Since we already have the current month, we might have used a simple int to store the selected day within that month. However, this choice would entail that when switching to the next month, the selected day would also change, while the user probably perceives the upper part of the widget only as a browser for weeks. With the choice of selectedDay, we can simply highlight in the lower part just the day that is equal to the selectedDay.Image 1.4.13

The next question is how to perform the display of the days. The simplest choice is to set a GridLayout on days and place the different text snippets as labels, as shown in Fig. 7.10. The GridLayout has seven columns, so we can easily place the first row as a list of labels with bold font (addDay places a Label with fixed size into days).

Image

Figure 7.10 Days Area of the DatePicker Widget

swt.compound.DatePicker.updateDisplay


for (String head : DAY_HEADS) {
Label l = addDay(head);
l.setFont(bold);
}


Next, we have to leave a few fields blank (Fig. 7.10; note that fields in Calendar are 1-based).

swt.compound.DatePicker.updateDisplay


int startWeekDay = cal.get(Calendar.DAY_OF_WEEK);
for (int i = 1; i < startWeekDay; i++)
new Label(days, SWT.TRANSPARENT);


Finally, we can fill in the actual days. In general, this is just a loop over the days of the month (line 4). To retrieve the label for a given day easily, we keep the index of the first such label (line 1); the others follow sequentially. For each day, we create a label (line 6); if it is the currently selected day, we highlight it (lines 7–9). Finally, we make the label clickable by registering our own handler (line 10). That handler will have to find out the day that the label represents. We exploit the widget’s generic data slot (line 11), which is meant to contain just such application-specific data associated with a visual element.

swt.compound.DatePicker.updateDisplay


1 firstDayChildIndex = days.getChildren().length;
2 int lastDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
3 Calendar tmp = (Calendar) cal.clone();
4 for (int i = cal.getActualMinimum(Calendar.DAY_OF_MONTH);
5 i <= lastDay; i++) {
6 Label l = addDay(Integer.toString(i));
7 tmp.set(Calendar.DAY_OF_MONTH, i);
8 if (tmp.equals(selectedDay))
9 l.setFont(bold);
10 l.addMouseListener(selectionHandler);
11 l.setData(tmp.clone());
12 }


With this setup, it is straightforward to react to clicks: Just extract the data associated with the label and set it as the currently selected date. The helper method also unhighlights the previously selected label and highlights the new one.

swt.compound.DatePicker


private class SelectionHandler extends MouseAdapter {
public void mouseUp(MouseEvent e) {
Label clicked = (Label) e.getSource();
setSelectedDayInternal((Calendar) clicked.getData());
}
}


The example shows a particular characteristic of custom widgets, andImage 7.11 event-driven programming in general: The event handlers require information that must be set up beforehand, and about which invariants need to be maintained.


Layouts are not automatic.


The process of computing layouts is comparatively expensive. It is thereforeImage 7.1 performed only at special points, such as when a window is first opened. In the current context, we have to trigger the layout of the newly added children of the compound days explicitly:

swt.compound.DatePicker.updateDisplay


days.layout();



Keep track of resources.


The DatePicker also contains an example of using resources: The bold font set on labels must be allocated before it can be used. From a user’s perspective, a font is just given by its name, size, and flags such as “italic” or “bold.” Within the window system, each font is associated with actual resources. Specifically, the relevant font definition (usually in the form of splines) must be loaded from disk, and each character must be rendered into a bitmap for quick rendering according to the font size, the screen resolution, and the color depth.

SWT also differentiates between the two views: FontData objects contain the external description of a desired font, whereas a Font is the actual window system resource associated with the loaded font data and bitmap cache. The next code snippet first creates a bold variant of the currently used default font by overriding the flag in the FontData (lines 1–3) and allocating the corresponding resources (line 4). Lines 5–9 then make sure that the resources are freed once the DatePicker is no longer used.Image 7.1

swt.compound.DatePicker.DatePicker


1 FontData[] fds = getFont().getFontData();
2 for (FontData fd : fds)
3 fd.setStyle(SWT.BOLD);
4 bold = new Font(getDisplay(), fds);
5 addDisposeListener(new DisposeListener() {
6 public void widgetDisposed(DisposeEvent e) {
7 bold.dispose();
8 }
9 });


This kind of behavior is typical of compound objects. First, ownershipImage 2.2.1 dictates that the owner determines the life cycle of its parts; when the DatePicker is disposed, so is its font. Second, there is a consistency conditionImage 2.2.3 Image 6.2.2 or invariant between the parts: The bold font will be allocated as long as it may still be used in any Label created for the days.


Keep widgets independent of their surroundings to enable reuse.


Developing user interfaces can be quite an effort. It is therefore highly desirable to develop elements such as the DatePicker that are reusableImage 12.1 Image 12.4 in different environments. To achieve this, it is necessary to decouple the elements from their context.

Let us briefly review the relevant decisions in the current example. One central element to achieve this is to use the OBSERVER pattern rather than sending specific messages to specific collaborators. Since UIs are event-driven anyway, this choice also fits in well with the general style of programming. Furthermore, the DatePicker’s API offers a generic property selectedDay. In contrast, a widget SelectFlightStartDate with method getSelectedFlightDate, which might occur in a travel booking application, would certainly be not reusable immediately. Also, making the API of the OBSERVER consistent with SWT’s conventions will lower the effort required for reuse because other team members will understand exactly what to do.


Refactor parts of existing widgets into compound widgets.


Image 1.4.8.3Reusable objects are often obtained by extracting reusable parts from larger objects. It is useful to consider compound widgets to be usual objects in this respect, too: Since the WindowBuilder parses the source code, you can actually use the Extract Class tool to create sub-widgets. When you find a UI element in one part of the application that might be reused in a different place, invest the bit of time needed to refactor the part into a stand-alone compound widget, if there is any chance that you can reuse it a third time.

Image 1.4.12 The WindowBuilder itself similarly offers to introduce factory methods that create a chosen composite and can then be invoked from different points of the application as well, since they appear in the palette. Although it requires a bit more effort, creating a separate class gives you better control over encapsulation and makes you independent of the WindowBuilder.

7.6 Dialogs

Most applications have one window and the user interacts with the application mostly through that window. However, the same applications usually do employ additional or auxiliary windows. For instance, these windows may be used for the following purposes:

• To ask for more information or for a decision from within an event handler and wait for the answer before proceeding, such as in confirming the deletion of a file

• To display detailed information or enable editing of some element shown in the main window, such as in the “properties” dialog on files in the package explorer

• To show a small direct editing field for a specific aspect of the dataImage 9.3.1 Image 214 displayed

• To display additional information or choices, such as in Eclipse’s Java auto-completion

The common theme of these situations is that the application’s main window is overlaid by a smaller window and that the new window receives all the input for the application, so that access to the main window is temporarily blocked. We will examine these elements of auxiliary Shellobjects in more detail in this section.

If you wish to experience the different settings discussed, you can use the DialogDemo application from the online supplement (Fig. 7.11). The names of flags and choices in the figure correspond directly to the technical flags at the SWT level that we will discuss.

Image

Figure 7.11 Dialog Demo

In the presentation, we encounter the slight problem that all windows are really “shells.” To simplify the reading, we will speak of the “application window” and the “dialog,” even if strictly speaking the “dialog” is yet another shell that does not differ from the “application window” at the technical level, and “dialogs” can even open “dialogs” themselves.


Image Both SWT and its abstraction layer JFace contain classes named Dialog. These areImage 9.3 not shells themselves, but merely provide a framework for creating shells.



The modality of a shell determines whether it blocks other windows.


Dialogs are usually employed to focus users’ attention on a particular point—for instance, to force them to make a decision or to enable them to edit the properties of a particular element of a data structure. From this perspective, it is sensible to block the remainder of the application as long as the dialog is being shown.

Dialogs that block the application window are called modal; those that do not are called modeless. The window system will silently ignore all input such as mouse clicks that the user may try to send to the application window.


Image SWT provides different levels of modality: The default, primary modal, means that only the window that opens the dialog will be blocked; application modal blocks all windows of the same application; and system modal blocks the remainder of the screen, if this capability is supported by the underlying window system. All of these modality levels should be read as hints given to the window system. For instance, if it does not support primary modal dialogs, it will silently fall back to application modal. It is therefore best for applications not to rely on these fine distinctions.


Blocking dialogs can also greatly simplify the code structure. While the dialog is open, the application’s data structures cannot be modified in some other way, and one can even wait for an answer or decision by the user. To demonstrate the mechanism, the simple application in Fig. 7.12(a)asks the user for a name, as shown in Fig. 7.12(b), and shows the answer.

Image

Figure 7.12 Modal Dialog Demo

The actual code can simply open the dialog. That call blocks until the dialog is closed again, at which point the result is available. (The parameter to the AskMeDialog constructor is the parent shell of the new window, to be discussed later.)

swt.dialogs.ModalDemo.ModalDemo


AskMeDialog dlg = new AskMeDialog(getShell());
String result = dlg.open();
if (result != null)
txtResult.setText(result);
else
txtResult.setText("- no answer -");


The AskMeDialog derives from SWT’s Dialog. It demonstrates two important points in the event-listener attached to the “OK” button. When the user clicks that button, the dialog is closed in line 4. Before that, the user’s answer is read from the input text field in line 3, and is stored in a field of the dialog class.

swt.dialogs.AskMeDialog.createContents


1 btnOk.addSelectionListener(new SelectionAdapter() {
2 public void widgetSelected(SelectionEvent e) {
3 result = txtName.getText();
4 shell.close();
5 }
6 });



Image Always retrieve values from widgets before closing the dialog. Otherwise, the containedImage 7.4.1 widgets are disposed and are no longer accessible.


A final question that needs to be addressed is how the result is actually returned to the caller after the dialog closes. In SWT, the answer can even be traced in the code: The open() method in the following snippet first makes the new shell appear on the screen and then keeps dispatching eventsImage 7.10.1 until the dialog is closed (lines 6–10). At this point, the result has been set by the listener and can be returned to the caller.

swt.dialogs.AskMeDialog.open


1 public String open() {
2 createContents();
3 shell.open();
4 shell.layout();
5 Display display = getParent().getDisplay();
6 while (!shell.isDisposed()) {
7 if (!display.readAndDispatch()) {
8 display.sleep();
9 }
10 }
11 return result;
12 }



The parent element of a shell determines z-order and minimization.


The most important aspect of dialogs is that they appear “above the main window,” where they are expected to catch the user’s attention immediately. In practice, this simple demand has three facets: In a setup with multiple monitors or workspaces, dialogs appear on the same monitor and workspace as the main window. Within that workspace, they usually appear centered over the application window. And, finally, they appear over the application window, in the sense that the application window never partially conceals them. This visual layering of elements on the screen is usually referred to as their z-order. All of those aspects must be taken into account to achieve a satisfactory user experience.

The single most important setting toward that end is to pass the proper parent shell to the constructor of a new shell. That setting links the new window to its parent, so that the new window appears at the expected location on the screen and above its parent in the z-order. The dialog even remains on top if the user reactivates the main window by clicking into that window (which is possible only with modeless dialogs).

A shell without a parent, in contrast, will appear in the middle of the screen when opened. It may also be buried behind the main application window, or the user can place the application window on top of the dialog. However, that behavior is system-dependent and depends on further settings. For instance, a modal dialog may still appear on top of the window that was active before the dialog was opened, even if that window is not its explicit parent shell. Here, the window system has made an informed guess at what was probably the user’s intention in opening the dialog. You should not rely on this behavior, but rather explicitly set a parent shell on each dialog.


Shells always involve interaction with the window manager.


Users appreciate and expect consistency: The windows on their desktop must look and behave the same, regardless of which application happens to open them. Window systems therefore include a window manager component that handles the drawing of and interaction with top-level windows. As a result, the visual appearance of a window is split into two parts (Fig. 7.13). The window’s content is created by the application, possibly using a systemwide library of available widgets. The title bar and border, in contrast, are drawn by the window manager. The window manager also handles moving, resizing, minimizing, and maximizing of windows. SWT calls the elements collectively the trimmings of a window. (Other frameworks call these the window’s decorations.)

Image

Figure 7.13 The Trimmings of a Shell

The trimmings can be specified by many style-bits passed to the constructor of a Shell (see Fig. 7.11). However, many of these should be taken as only hints to the window manager, which is ultimately responsible for creating and handling the visual appearance consistently with the system’s overall look-and-feel. Also, there may be interdependencies. For instance, some window managers will not provide minimize and maximize buttons if the shell is not resizable.


Place shells in global, absolute coordinates.


Several applications mentioned in the introduction require the dialog to appear at specific positions and even to be moved along programmatically. For instance, an auto-completion pop-up in a source code editor may follow the cursor to reflect the current insertion point. We demonstrate here only the basics: We open a list of selections below a button that triggers the appearance of the list (Fig. 7.14). The general style of the dialog is set by SWT.NO_TRIM, meaning that the window manager will not provide additional decorations.

Image

Figure 7.14 Opening a Dialog at a Given Position

The code that follows demonstrates how the special layout requirements are fulfilled. Lines 1–2 first compute the desired size, by determining the preferred width of the list and enlarging the size by any borders that theImage 7.1 window manager may wish to add despite the “no trim” flag. Lines 3–4 compute the lower-left corner of the widget below which the dialog is to appear. Line 3 works in coordinates local to the target widget, and line 4 translates the chosen point to the global coordinate system in which the top-level windows are placed. Lines 5–8 then lay out and display the dialog.

swt.dialogs.DialogDemo.openDialogBelow


1 Point listSize = select.computeSize(-1, -1, true);
2 Rectangle dialogSize = dialog.computeTrim(0, 0, listSize.x, 100);
3 Rectangle targetBounds = target.getBounds();
4 Point anchor = target.toDisplay(0, targetBounds.height);
5 dialog.setBounds(anchor.x, anchor.y, dialogSize.width,
6 dialogSize.height);
7 dialog.layout();
8 dialog.open();



Use standard dialogs where possible.


The discussion presented here has introduced the conceptual and technical points of using dialogs. Since dialogs are such a standard feature, it is not sensible to consider the details over and over again for each instance. Instead, many dialog-style interactions are already supported by standard dialogs. Choosing files, fonts, or printers, as well as requesting confirmation and displaying errors—all of these are hardly new challenges. Indeed,Image 9.3 Image A SWT, JFace, and the Eclipse platform layer already contain standard implementations for them—FileDialog (orDirectoryDialog), FontDialog, PrintDialog, and MessageDialog, respectively.

Using standard dialogs has the additional advantage that they are usually shared across applications, so that the user is familiar with them. Besides simplifying the implementation, they will increase the acceptance of your application.

7.7 Mediator Pattern

Event-driven software comes with one fundamental complexity: Each possible event can be fired at almost any time and events can be fired in almost any order, and by different sources of events. In the case of user interfaces, the user’s mouse gestures and keyboard input, the state changes of widgets, as well as the scheduled timers all cause events that must be processed correctly and reliably. The MEDIATOR pattern helps in organizing the reactionImage 100 to events.


Pattern: Mediator

In a situation where a group of objects sends messages, such as event notifications, to one another, a central mediator object can help to localize the necessary logic and to keep the other participating objects independent of one another.


Image 7.1For an example, let us go back to the introductory simple browser [Fig. 7.1; Fig. 7.15(a)]: The user can target the browser to a new URL either by pressing “enter” in the text field or by pushing a button. The browser then fires notifications about progress and address changes. The “load” button will be enabled only if some URL has been entered. The code implementing these reactions is scattered throughout the various listeners, so that it is hard to trace whether the desired behavior is achieved.

Image

Figure 7.15 Motivation for Mediators

The situation changes dramatically if we introduce a central mediator object [Fig. 7.15(b)]. It receives all events, decides on the proper reaction, and calls the corresponding methods on the involved widgets.

swt.mediator.BrowserWithMediator.createContents


url.addSelectionListener(mediator);
url.addModifyListener(mediator);
load.addSelectionListener(mediator);
browser.addStatusTextListener(mediator);
browser.addProgressListener(mediator);
browser.addTitleListener(mediator);


We make the mediator a nested class within the application window so that it has access to all relevant objects. The mediator is then kept in a field of the browser object:

swt.mediator.BrowserWithMediator


private Mediator mediator = new Mediator();


The Mediator class is set up to receive all occurring events. As a result, it can also contain all the logic that is necessary for handling these events.

swt.mediator.BrowserWithMediator


private class Mediator implements SelectionListener, ModifyListener,
ProgressListener, StatusTextListener, TitleListener {
...
}


Image 7.1The mediator’s methods contain code that is similar to the original event handlers. This is not surprising, since they serve the same purpose and implement the same reactions. However, because in principle events can come in from several widgets, they check the source at the beginning (line 2 in the next code snippet). The example also shows an immediate benefit of the approach: Because the code is centralized within Mediator,Image 7.1 it becomes simpler to extract reactions triggered by different access pathsImage 1.4.5 into helper methods such asretargetBrowser.

swt.mediator.BrowserWithMediator.widgetDefaultSelected


1 public void widgetDefaultSelected(SelectionEvent e) {
2 if (e.getSource() == url) {
3 retargetBrowser();
4 }
5 }
6 public void widgetSelected(SelectionEvent e) {
7 if (e.getSource() == load) {
8 retargetBrowser();
9 }
10 }
11 protected void retargetBrowser() {
12 if (!url.getText().isEmpty())
13 browser.setUrl(url.getText());
14 }


That’s it for the basics. Let us now examine some consequences of using mediators.


Mediators can encapsulate complex logic.


In the example, the interdependencies between the objects were not really complex. However, in dialogs with many widgets that influence one another’s state, the communication between those widgets can itself be substantial. Placing it in a separate mediator object helps keep the code readable.

This result is similar to that of introducing objects as containers forImage 1.8.6 algorithms and the data structures that they work on. There, too, the complexity of a piece of logic suggests that it should be stored away in a separate object, rather than let it clutter its surroundings.


Delegating all events to the host object introduces an implicit mediator.


In the context of the OBSERVER pattern, we have discussed the approachImage 2.1.3 of having observers call protected methods in a surrounding object. The focus there was on handling the state changes induced by the events in one class, rather than throughout the code.

Mediators serve a similar role, but they usually receive the events immediately, rather than providing specialized methods that are called from the listeners attached to the different widgets. However, the resulting structure is similar, and the similarity creates a new perspective on the former approach: the surrounding object becomes an implicit mediator for its parts.


Mediators make the code more complex by an extra indirection.


Finally, it must be said that mediators are not the only possible solution to handling events and that the original structure in Fig. 7.15(a) does, in fact, have its merits: Each widget in the code immediately exhibits the reactions associated with it. One can answer questions like “What happens if...” straightforwardly, because the event-listener is right there on the spot, close to the creation of the widget. In the mediator, in contrast, one has to find the called method and the right branch in the case distinction on the event source.

Introducing MEDIATOR is therefore really a decision. You have to consider whether it is better to have localized reactions at the cost of scattering the overall logic throughout the code, or a centralized logic at the cost of introducing an indirection.

7.8 Custom Painting for Widgets

In some situations, the predefined widgets are not sufficient. For instance, your application data may require special rendering, or you may want toImage 11.3.1 brand your application with special elements to make it stand out.


Avoid creating custom widgets.


Custom widgets imply a lot of work. Besides the actual painting with pixel-based computations of geometry, you have to place their elements manually, compute the optimal size based on the local fonts, and deal with low-level mouse and keyboard input. Also, it is best to check that the widget actually behaves as expected on the different operating systems. Think twice before setting out on that journey.

Very often what seems to be a special widget at first can still be created as a compound widget with special listeners attached. In particular, static graphics can often be simulated by setting icons on Labels without text; the label will render only the given image.


Restrict the custom painting to the smallest possible element.


Since managing a custom widget entails so much work, it is good to keep the work minimal by restricting the new widget to just the things that really need special painting. For example, if you wanted to write a memory game, the largest solution would be a widget MemoryBoard that does everything: places the cards, handles the mouse clicks, implements the rules of the game, and so on. Of course, we need custom painting, because we want to have nice-looking cards with custom borders, and so on. However, the cards are laid out in a grid, so that part is already handled by the GridLayout.

So we decide to implement a custom widget MemoryCard instead. Fig. 7.16 shows two such cards placed side by side. One shows a checkered back-side, and the other shows the card’s image.

Image

Figure 7.16 Memory Cards


Derive custom widgets from Canvas.


Image 7.4.1In SWT, one cannot simply subclass any likely-looking widget and adapt its behavior by method overriding. New widgets requiring custom painting are to be derived from Canvas, which is a raw rectangular space onto which arbitrary graphics can be painted.

swt.custom.MemoryCard


public class MemoryCard extends Canvas {
...
public MemoryCard(Composite parent) {
super(parent, SWT.NONE);
...
}
...
}



Image Canvas, according to its documentation, does not handle children properly, so you can use it only for atomic special elements. Possibly, you will have to stitch together your desired component from a core Canvas, embedded into other elements as before.



Apply the object-oriented structuring guidelines.


As for compound widgets, the new widget should always be considered asImage 7.5 a software artifact: How must its API and behavior be defined to make it a useful collaborator that works well in different contexts? We have discussed the relevant aspects for the compound widgets. For the current example, they translate to the following decisions. We will be brief, because the considerations closely resemble those from the DatePicker.

First, we see that the reaction to a mouse click is context-dependent. While it would usually flip the card, it might also cause two revealed images to be hidden at some other point in the game. We therefore decide to let the MemoryCard provide a generic “selection” mechanism and give it a proper imageName that serves as an identifier for the shown image. Also, the client can explicitly choose whether the image is shown. In this way, different decks of cards can be chosen. Together, these considerations lead to the following API:

swt.custom.MemoryCard


public String getImageName()
public void setImageName(String name)
public boolean isImageShown()
public void setImageShown(boolean show)
public void addSelectionListener(SelectionListener l)
public void removeSelectionListener(SelectionListener l)



Image The imageName is a property rather than a parameter to the constructor because the WindowBuilder does not handle such constructors well.Image 7.5



Painting on the screen is event-driven.


The central question is, of course, how to paint on the Canvas. In other frameworks, such as Swing and the Graphical Editing Framework’s Draw2DImage 214 layer, the solution is to override one or several methods that are responsible for drawing different parts of a figure in an application of the TEMPLATEImage 1.4.9 METHOD pattern. SWT instead lets you register PaintListeners that are called back whenever some part of the Canvas needs re-drawing. The MemoryCard’s constructor sets this up, as shown in the next example. We choose to delegate to the outer class here, because painting is one of theImage 2.1.3 MemoryCard’s tasks and the PaintListener is only a technical necessity.

swt.custom.MemoryCard.MemoryCard


addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
paintWidget(e.gc);
}
});


The actual painting code writes to the display through the given GC (for graphics context) object. Lines 2–5 paint either the image or back-side of the card; lines 6–13 draw a series of rounded rectangles with different shades of gray to create a 3D effect around the card (Fig. 7.16).

swt.custom.MemoryCard


1 protected void paintWidget(GC gc) {
2 if (imageShown)
3 gc.drawImage(image, BORDER_WIDTH, BORDER_WIDTH);
4 else
5 gc.drawImage(checkered, BORDER_WIDTH, BORDER_WIDTH);
6 Rectangle bounds = getBounds();
7 for (int i = 0; i != BORDER_WIDTH; i++) {
8 int val = 150 + 10 * i;
9 gc.setForeground(new Color(null, val, val, val));
10 gc.drawRoundRectangle(i, i, bounds.width - 2 * i,
11 bounds.height - 2 * i, 5, 5);
12
13 }
14 }



Image The GC encapsulates the actual rendering. For instance, it is also possible to create a GC for an Image using new GC(image). Be sure to dispose of the GC after use.


swt.custom.PaintToImage


protected Image createImage() {
Image img = new Image(getDisplay(), 100, 100);
GC gc = new GC(img);
try {
gc.drawOval(30, 30, 60, 40);
} finally {
gc.dispose();
}
return img;
}



Restrict painting to the necessary area.


Drawing operations are comparatively expensive, because they must get back to the operating system and the actual hardware. In larger widgets,Image 9.4.3 it is therefore mandatory to redraw only those parts that are actually no longer up-to-date. The PaintEvent passed to the paint-listener contains the rectangular bounding box of the region that needs redrawing. Use it to skip any drawing code that does not touch the area.


Schedule repainting to cause the widgets to be updated.


Widgets encapsulate and manage internal state and data structures. At some point, they will discover that a state change influences their visual appearance, so that they have to repaint themselves. Since painting is event-driven, at this point they must trigger a suitable event—that is, they must tell SWT that their screen space needs to be updated. The method redraw() does this.

swt.custom.MemoryCard


public void setImageShown(boolean show)
{
imageShown = show;
redraw();
}


We pointed out earlier that a widget should repaint only those parts that are actually touched by the bounding box given in the paint event. That bounding box can be given in a variant of the redraw() request (the parameter all indicates whether children should also be repainted):

org.eclipse.swt.widgets.Control


public void redraw (int x, int y, int width, int height,
boolean all)



Image Do not assume that the bounding box passed to the PaintListener is the same as the bounding box passed to redraw. For instance, the window system might discover that parts of the requested area are actually hidden below another window, in which case it clips the area further to make the painting even more efficient.



Image Do not force immediate repainting. The class GC also offers a constructor for painting onto arbitrary Drawables, and widgets on the screen are also Drawables. (Note that you have to dispose() of the GC obtained in this way.)


org.eclipse.swt.graphics.GC


public GC(Drawable drawable)


In principle, it is possible to repaint a widget directly at the point when the state change occurs. However, this short-circuits and disables all the fine-tuned optimizations implemented by the window system and SWT.

7.9 Timers

So far, we have created interfaces that react to user actions. Without user input, nothing happens. Many situations do, however, require a more dynamic of inactivity. Animations can highlight certain aspects or conditions, such as an urgent error status, and they can make extensive changes in the displayed data, such as recomputing the layout of a graph, more comprehensible by providing intermediate states visually. User interface frameworks provide timers to implement such behaviors that proceed without explicit user interaction.

7.9.1 Timeouts and Delays

A prototypical delayed action is to show a pop-up on auto-completion text fields: As long as the user keeps typing, there is no need for the pop-up; after a period of inactivity, a list of selections is supposed to occur. To explore timers, we implement a small widget that auto-completes country names, as shown in Fig. 7.17: the user keeps typing, and after a delay of 500 ms, the list of possible countries matching the current entry is updated.

Image

Figure 7.17 Auto-Completing Country Names


Image Image 9.3JFace provides a mechanism for auto-completion in its ContentProposalAdapter, which can be attached to text fields. Do not implement auto-completion yourself,Image 7.3 but learn to use the framework instead.



SWT provides timers in the form of callbacks scheduled to occur later on.


Image 7.1Timers in SWT are created through the Display, which provides a simpleImage 1.8.6 method: The given code, which is wrapped up in a Runnable, is executed after a given number of milliseconds. The “timer” in SWT is therefore not really an object, but rather an event, or a callback, that is scheduled to occur at some later point.

org.eclipse.swt.widgets.Display


public void timerExec(int milliseconds, Runnable runnable)



Image The given timeout is not exact, as the callback may occur slightly later than theImage 7.10.1 given delay. The reason is that it will always occur in the event-dispatch thread, and that thread may be busy processing other events at the time.



Image Other toolkits provide timers as proper objects with a proper API. For instance, the Timer class of Swing provides methods to start and stop the timer, and to let it fire events periodically. Watch out for timers in the library that are not related to the user interface: They typically make use of background threads and require the correspondingImage 7.10 precautions.


For the example, we implement a widget AutoCompleteCountries. It contains a text widget input as a single child. The crucial point is that any modification to the text field will result in a later pop-up with completion proposals (line 5 in the next example). As a detail, line 4 serves to disable this reaction for the case where the text replaced by a selected completion proposal. Otherwise, selecting a proposal would immediately pop-up the shell again.

swt.timer.AutoCompleteCountries.AutoCompleteCountries


1 input = new Text(this, SWT.NONE);
2 input.addModifyListener(new ModifyListener() {
3 public void modifyText(ModifyEvent e) {
4 if (!settingSelection)
5 getDisplay().timerExec(DELAY, showPopup);
6 }
7 });


Here showPopup is a Runnable that merely calls the following methodImage 2.1.3 showPopup() in the surrounding class. That method creates and places the pop-up as a new Shell and then fills the list of proposals by searching through a fixed list of country names. Since all of this is not related to timers, we will skip the details here. They can be found in the online supplement.

swt.timer.AutoCompleteCountries


1 protected void showPopup() {
2 if (input.isDisposed())
3 return;
4 createPopup();
5 fillPopup();
6 }



Timers can be rescheduled and canceled.


Existing timers can be rescheduled and canceled by passing the same runnable with a different delay. Line 5 in the modify-listener implicitly makes use of this feature: While the user keeps typing, the pop-up is delayed again and again, until the user stops typing and the delay can elapse.

To cancel a timer, one uses a delay of -1. For instance, when the user presses escape, the following listener unschedules any existing timers and then hides the pop-up. (The listener is named because it is also attached to the pop-up shell to catch the escape when the pop-up has the keyboard focus.)

swt.timer.AutoCompleteCountries.AutoCompleteCountries


closePopupOnEscape = new KeyAdapter() {
public void keyReleased(KeyEvent e) {
if (e.character == SWT.ESC) {
getDisplay().timerExec(-1, showPopup);
hidePopup();
}
}
};
input.addKeyListener(closePopupOnEscape);



Image The example demonstrates several other techniques not related to timers: placing pop-up shells relative to a given widget, managing the focus between the text field and the pop-up, and moving along the pop-up when the user drags the window containing the text field.



Image Image 1.8.8.3As a cross reference, we proposed in Part I to store instances of anonymous nested classes in named fields or variables to improve readability, or to clarify the intention by passing the instance to a method directly. Look again at these two styles in the code here to judge the relative merits.



Check for disposal of widgets in timed callbacks.


Working in an event-driven environment means that you have to expectImage 148 things to happen while you are not looking. Suppose the user closes the dialog containing AutoCompleteCountries widget, but leaves the application running. When a timer is still scheduled, the methodshowPopup()Image 7.4.1 above cannot work properly, because it accesses a disposed widget. Lines 2–3 in the code of that method catch this special case.


Encapsulate local state.


The ModifyListener method given earlier introduces a special flag settingSelection that works around an unintended input programmatically after a user selects a completion, the “modify” event is fired, which would make the pop-up reappear immediately. Fortunately, both the completion and the delayed trigger are contained within the class AutoCompleteCountries, so that we can introduce a workaround that remains hidden from clients. The following code attaches a listener to the selection list in the pop-up. When the user double-clicks or presses “enter,” it sets the current selected text on the input. To suppress the undesired reappearance behavior, lines 6 and 8 set the local flag settingSelection.

swt.timer.AutoCompleteCountries.createPopup


1 list.addSelectionListener(new SelectionAdapter() {
2 public void widgetDefaultSelected(SelectionEvent e) {
3 int selected = list.getSelectionIndex();
4 if (selected != -1) {
5 String country = list.getItem(selected);
6 settingSelection = true;
7 input.setText(country);
8 settingSelection = false;
9 hidePopup();
10 }
11 }
12 });


7.9.2 Animations

Timers can also be used to implement animations. The only necessary addition to the techniques discussed previously is the repeated, periodic scheduling of tasks. Suppose we want to implement a Ticker widget that moves a message around in a Text widget (Fig. 7.18).

Image

Figure 7.18 The Ticker Widget


Periodic behavior is achieved by rescheduling a timer.


The Ticker maintains an internal position pos, which designates the character where the overall text is split to achieve the effect. The central method is tick(), which sets the content of the output text field according to the current position and then advances that position cyclically through the given text. Finally, it schedules a timer ticker, which merely calls, again, the method tick() after the given delay (we use 80 ms).

swt.timer.Ticker


protected void tick() {
if (text.isEmpty() || output.isDisposed())
return;
pos = (pos + 1) % text.length();
String txt = text.substring(pos, text.length()) + " "
+ text.substring(0, pos);
output.setText(txt);
getDisplay().timerExec(DELAY, ticker);
}



Image Image 7.9.1Note again the check for the disposal of the output widget: The user might have closed the surrounding window in the brief time span after the timer has been last scheduled. To illustrate the danger of such intuitions, and to appreciate the necessityImage 4.1 of precise reasoning here, consider the following argument: Processing tick() will take approximately 1 ms, so the chance of the user’s closing the window between ticks is actually relatively high.


The first call to tick() is scheduled in setText():

swt.timer.Ticker.setText


if (!text.isEmpty())
getDisplay().timerExec(DELAY, ticker);



Image Image 7.5The choice of the name text for the property reflects an interesting decision. On the one hand, “message” would be a more appropriate name if one focuses on the API of the compound widget we implement. On the other hand, all widgets in SWT that display some form of text call their property “text.” We have therefore opted to follow the conventions of the user interface context.



Animations keep a current state and advance it through timer events.


The general structure of animations is shown in Fig. 7.19. The animation takes place in short bursts of activity that update the display, and then the application schedules a timer and waits for it to elapse. To keep track of what the current and next display should be, the application maintains an internal state that captures the current progress of the animation abstractly. In each animation step, it updates the state and reflects it on the screen. In the example, the state is only pos, the point where the text was currently split, and moving the animation forward means incrementing poscyclically through the given message.

Image

Figure 7.19 Structure of an Animation with Timers


Image Do not use Thread.sleep() to implement the waiting from Fig. 7.19, even if it is tempting. Maintaining the state in fields requires formulating and maintainingImage 4.1 invariants. Here, we have “pos is the current point where the text gets split. It is always between 0 andtext.length (exclusively).” Often the animation could be expressed more easily in a while loop that updates only local variables, like this:

swt.timer.NoThreadingExample.animate


int pos = 0;
while (!animationFinished){
Thread.sleep(DELAY);
pos = (pos + 1) % text.length();
output.setText(splitText(pos));
}


However, because of the event loop, this code must run in a different thread, which causesImage 7.10.1 all kinds of concerns. Also, in effect, the loop will have invariants that are very similarImage 4.7.2 to those about the fields in the correct solution.


7.10 Background Jobs

User interface toolkits in general make one important trade-off: They assume that event-listeners will run very quickly so as to gain the simplicity of handling the incoming events one after the other, without overlaps in the execution of the listeners. The downside is that listeners that do run longer block the user interface completely, up to the point where highlights under the mouse cursor will no longer show up, menu items do not respond, and it even becomes impossible to close the application window. To demonstrate the effect, you can just attach a “blocking” listener to a button:

swt.intro.BlockUIDemo.BlockUIDemo


btnBlockUI.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Thread.currentThread().sleep(5000);
}
});


The decision of the user interface toolkits works out well most of the time, but sometimes reactions to certain events will take more time. For example, downloading a page from the web or processing nontrivial amounts of data can easily take a several seconds, and even accessing a local file can be delayed if the file happens to be located on a network drive. Users,Image 229 Image 50(§C.1) however, are not prepared to wait for long: They tolerate delays of roughly 200–300 ms before seeing a reaction; otherwise, they get uneasy and assume that the system has crashed. The least the system must do is display a “busy” cursor to indicate that it will take some more time. SWT provides a class BusyIndicator for this purpose.

Sometimes this is not good enough, in which case long-running tasks must be executed in separate threads. Threads are a runtime mechanismImage 8 Image 148 for executing different parts of the program conceptually in parallel, by quickly scheduling these parts on the CPU in a round-robin fashion or by executing them on different CPUs or different cores within one CPU. The result is that the user interface remains responsive and the user remains happy while background threads do the heavy processing.

This section explains the relation to user interfaces to multithreading. The topic of threads in general does, however, fill entire books. Chapter 8Image 148 complements the current chapter by providing a succinct overview that should be sufficient for the purposes of user interface programming.

7.10.1 Threads and the User Interface

The goal of processing one event after the other is achieved by introducing one central event dispatch loop, which gathers events from different sources, decides on their relative order and their destination widgets, and dispatches them to the listeners registered on the widget (Fig. 7.20): The window system sends low-level user input, as well as high-level events such as the signal that the user has clicked the “close” button in a window’s title bar.Image 7.9 Timers may have elapsed, so that their associated handlers must be called. Also, as we shall see shortly, arbitrary Runnables can be injected into the event processing through a method asyncExec(). In concrete systems, further sources of events can be present.

Image

Figure 7.20 Event Dispatching in a Loop

Most user interface toolkits hide the event loop in one “run UI” method that must be called when the application starts up and that finishes only when the application terminates. In SWT, the event loop is actually visible as a very simple coding idiom (which the WindowBuilder generates for top-level windows): While the application window is visible, the loop keeps dispatching events, waiting for the next event if currently there is no work.

swt.browser.SimpleBrowser.open


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



Image Image 148The call to display.sleep() might seem suspicious, since polling by checking for events every few milliseconds is bad coding practice and must be avoided. The method does the right thing, however: It checks for the internal sources of events and otherwise blocks on reading new events from the window system. When timers elapse, they also wake up sleep() to continue processing. An alternative, and possibly more suitable, name for the method would have been waitForEvents.



Widgets must be accessed only in the unique event dispatch thread.


One aspect associated with the event dispatch loop is that dispatching is always executed in a dedicated thread, the event dispatch thread. It is this thread that runs the event loop and calls the registered listeners. Indeed, no other thread must access any widgets, because quasi-parallel accesses to the widgets will easily break invariants, while synchronizing accesses isImage 8.2 Image 148 Image 7.10.2 cumbersome and error-prone. The checkWidget() method, which is calledImage 7.4.1 at the start of any public method in SWT’s widgets, excludes cross-thread accesses:

org.eclipse.swt.widgets.Widget


protected void checkWidget() {
...
if (display.thread != Thread.currentThread())
error(SWT.ERROR_THREAD_INVALID_ACCESS);
...
}



Access the user interface from other threads via asyncExec.


Suppose you decide that you need to perform a long-running operation inImage 7.10.2 a different thread. You will still have to access the user interface to display progress reports and the final result. The technical solution is a method in Display that takes a Runnable and executes that code within the dispatch thread:

org.eclipse.swt.widgets.Display


public void asyncExec(Runnable runnable)



Image The abbreviation async expands, of course, to “asynchronous.” “Asynchronous” in computer science is usually linked to the idea of “running independently” or “not being in lock-step.” One important connotation in the current context is that the Runnable will be executed later on, whenever SWT finds the time for it, without further guarantees.


Of course, this procedure might feel a bit uncomfortable: You have some state change that you wish to report back to the user and now the report is delayed for some arbitrary (but short) time. From the user’s perspective, the delay hardly matters, since it was not clear how long the job would take in the first place.


Tool: Creating Runnables Quickly

In the current context, one needs to create many Runnables. Eclipse offers an easy support: Just type “runnable” and auto-complete (Ctrl-Space) it to a runnable with overridden method run().



Beware of the delay introduced by asyncExec.


From a technical perspective, the consequences of using asyncExec are rather more severe, because one has to consider the possible state changes that occur before the runnable gets executed. The overall situation is shown in Fig. 7.21, where each invocation of asyncExec incurs a brief delayImage 8.2 before the user interface code runs. Within this delay, you should assume that anything can happen, both to the internal data structures and to the user interface—after all, the background thread keeps on working and the user interface keeps dispatching events.

Image

Figure 7.21 AsyncExec from Background Threads

To start from a concrete goal, suppose we simply wish to update a progress bar after a number of steps have been finished by the background job. We correspondingly schedule a runnable (line 2). However, it might be the case that the user has closed the window before the runnable executes,Image 7.4.1 so we need to check that the target widget is still valid in line 4 before performing the update in line 5.

swt.threads.ProgressBarMonitor.worked


1 final int steps = work;
2 display.asyncExec(new Runnable() {
3 public void run() {
4 if (!progressBar.isDisposed()) {
5 progressBar.setSelection(
6 progressBar.getSelection() + steps);
7 }
8 }
9 });



Image Always check for disposed widgets before accessing them in code scheduled via asyncExec(). Otherwise, SWT will throw an exception.



Image Line 2 requires access to the current display. Note that it is not possible to use getDisplay() on the target widget, because that method fails if the widget has been disposed. It is therefore necessary to store the display in a field or local variable beforehand, when the background job is first created, because at that point the target does exist.


A second, slightly less dramatic point is seen in line 1. Suppose work is a field in the background job that is updated whenever the job finds it has made progress. By the time Runnable is executed, this field will have been altered, so that the report is actually wrong and the same work may be reported twice, leading to a wrong setting of the progress bar.

It is therefore useful to make a copy of any data structure or state of the background job that is needed in the display code. This is precisely what line 1 accomplishes. In this way, the report will always be accurate, independent of the time that it is actually delivered.


Think of asyncExec as producer–consumer.


A common pattern in coding with multiple threads, which furthermoreImage 148 keeps the involved complexity at a minimum, is called producer–consumer (Fig. 7.22): One thread keeps producing a sequence of data items that another thread then consumes and processes. Since the threads run in parallel and independently, a queue in the middle buffers data until it is consumed.Image 12.3.4

Image

Figure 7.22 Producer–Consumer Pattern

In the case of asyncExec, one does not pass data, but Runnables, and the role of the queue is played by an internal data structure of Display that buffers the Runnables. The consumer is the event thread, which merely executes the Runnables.

To make this important coding idiom concrete, let us consider a tiny example where a thread computes several results that should be displayed one by one. Also, a progress bar should keep the impatient user informed about the remaining time. As shown in the example code, the overall loop runs in the background thread and the computation in line 2 presumably takes a longish time. Line 3 saves the current counter for the report, becauseImage 1.8.8.3 the counter might change later on. Line 4 schedules the update of the user interface in lines 6–7. The call to asyncExec returns immediately, which enables the background thread to proceed without waiting for the sluggish user interface to finish the display.

swt.threads.ProduceConsume.ProduceConsume


1 for (int i=1; i <= 100; i++) {
2 ... compute next result
3 final int cur = i;
4 display.asyncExec(new Runnable() {
5 public void run() {
6 results.add(Integer.toString(result));
7 progress.setSelection(cur);
8 }
9 });
10 }


The essential point of the producer–consumer pattern is that the data is never shared between the threads, so they never access the data simultaneously. The producer creates and initializes some objects, passes them into the queue, and then relinquishes the references. Of course, this also concerns references that are stored within the data: The object structure that arrives at the consumer must no longer be accessible, or at least not written to, from anywhere within the producer. The pattern, like the technical analysis given earlier, therefore suggests to make copies of any relavant data.


Keep track of which code runs in which thread.


When programming with background threads, it is essential to keep track of which code runs in which thread, because the different threads impose different restrictions:

• Background threads must not access UI widgets under any circumstances.

• Code in the event dispatch thread must run quickly and must not perform long-running operations.

While mistakes in the first point show up immediately in exceptions thrown by SWT, neglecting the latter may not become obvious until some user happens to process an unexpectedly large file or happens to access a file over the network rather than on the local hard disk.

It is useful to think of the event thread and the background threads as two separate worlds where different sets of rules apply. Getting confused between the two worlds is, unfortunately, rather simple. For one thing, Runnables may be nested inside each other, each one signaling a transition between the event dispatch thread and a background thread.

Image 2.1 Image 9.1 A yet more complex case arises when observing objects with the purpose of keeping the user interface up-to-date: If the modification happens to be performed in a background thread, then the notification is also sent in that thread, even if the observer itself is statically contained in a widget.

Suppose we implement a complex task that involves summing up some data. The class Summation contains the current result and notifies any registered PropertyChangeListeners about new results.

swt.threads.ObserveBackground.ObserveBackground


final Summation sum = new Summation();


Then, the background thread creates such a summation and starts working.

swt.threads.ObserveBackground.ObserveBackground


for (int i = 1; i <= 100; i++) {
... compute next delta
sum.add(delta);
}


Since the task takes some time, users would be happy to see the intermediate results on the screen, so that they get a feeling for the progress. This can be done easily enough by registering a change-listener with the Summation. The essential point is that the background thread callssum.add(), so that the change notifications are also sent within that thread, and finally the listener in the next code snippet gets executed in the background thread. As a result, the listener cannot access the widget result immediately, but must do so through asyncExec (line 4).

swt.threads.ObserveBackground.registerObserver


1 final Display display = getDisplay();
2 sum.addPropertyChangeListener(new PropertyChangeListener() {
3 public void propertyChange(final PropertyChangeEvent evt) {
4 display.asyncExec(new Runnable() {
5 public void run() {
6 if (result.isDisposed())
7 return;
8 result.setText(
9 Integer.toString(
10 (Integer)evt.getNewValue()));
11 }
12 });
13
14 }
15 });



Image Note that we need to keep the display in line 1, because a call to getDisplay() in line 4 would fail in case the widget has be disposed.



Treat the event dispatch thread as a scarce resource.


Once one gets used to the idea of scheduling code into the event thread, the asyncExec facility comes in very handy in many situations: Whenever something needs to be done, but cannot be done right now, one packages it up as a Runnable and hands it over to the event dispatch thread. Suppose, for instance, that your application tracks incoming network data. Whenever the receiving object has got hold of a message, it can simply pass a Runnable to the event thread and can immediately go on reading data.

The problem with such a setup is that it swamps the event thread with small pseudo-events. If thousands of them arrive in a second, as may easily be the case, then the processing of the actual user input becomes ever more delayed, with the result that the display appears to be “frozen.” For instance, highlights under the mouse depend on the mouse events to be delivered to the respective widget in a timely fashion.


Image If your application does receive bulk data in a background thread and has to somehow display a digest of the data on the screen, you have no choice but go through asyncExec. In such a case, you should accumulate data in the background thread and send updates to the screen only periodically—for instance, after a few hundred dataImage 7.9 items have been processed or after some timeout, such as 50 ms, has elapsed. This approach relieves the burden on the event thread; the user would not be able to track the faster changes anyway.



Use syncExec (only) if some feedback is required from the user.


In rare situations, the background thread may need to wait until the user interface code has actually run. The class Display provides a variant syncExec of asyncExec for this purpose.

org.eclipse.swt.widgets.Display


public void syncExec(Runnable runnable)


Image 7.6In this way, the background job can, for instance, ask the user a question and proceed according to the answer.

swt.threads.AskFromBackground.startThread


1 final boolean decision[] = new boolean[1];
2 getDisplay().syncExec(new Runnable() {
3 public void run() {
4 decision[0] = MessageDialog.openConfirm(getShell(),
5 "Proceed?", "Do you want to delete the file?");
6 }
7 });



Image Image 1.8.8.3Line 1 must declare decision itself final to make it accessible within the Runnable, but the array still provides a slot that can be filled with the result in line 4.



The first thread accessing a display becomes the event thread.


The question remains as to which thread is singled out as the unique event dispatch thread. SWT’s answer is that a display is associated with the thread that creates the display. Usually, this happens just before the event dispatch loop in the main thread of the application.


Image While SWT reuses some existing thread in this way, many other frameworks create an internal, private thread and dispatch events from that thread. As a result, even the creation of the user interface must be performed explicitly in that thread. In Swing, for instance, the following code schedules the Runnable to be called in the event thread; lines 4–6 then create and display the window. Even though line 6 returns immediately, the application keeps running while the event thread keeps dispatching events.


swt.threads.StartSwingApp


1 public static void main(String[] args) {
2 SwingUtilities.invokeLater(new Runnable() {
3 public void run() {
4 JFrame window = new JFrame("Example");
5 ... create content
6 window.setVisible(true);
7 }
8 });
9 }


7.10.2 Long-Running Tasks

Whenever the user can trigger a task whose execution will potentially take longer than the bearable 200 ms, the task must be executed in a separate thread. Sometimes the actual processing consumes a lot of CPU time, but more often it is the waiting for input data that causes the overall delay. In a separate thread, one has basically all the time in the world.


Prefer timers to threads whenever possible.


Before we delve into the details, we would like to emphasize that the complexities involved in using threads suggest avoiding them if at all possible.Image 8 In particular, most animations are not a case for a background thread:Image 7.9.2 They wait most of the time, then perform some quick rendering, then return to waiting. Such behaviors are better solved by timers.

As an example that does require background processing, we implement an application that merely connects to a web server and downloads a web page, displaying progress as it goes along (Fig. 7.23).

Image

Figure 7.23 Download Job Demo


Image The Eclipse API discussed here is not part of SWT, but resides in the bundle org.eclipse.core.runtime. The parts of that bundle used here are, however, accessibleImage 7.4.2 outside of the Eclipse platform and work in standard Java applications. Since Eclipse provides a global display for progress, normal Java applications have to supply a substitute, which is done by calling the following method once at the beginning of the application.


swt.threads.DownloadDemo.DownloadDemo


Job.getJobManager().setProgressProvider(new ProgressProvider() {
public IProgressMonitor createMonitor(Job job) {
... access suitable UI elements
}
});



Use a task execution framework.


Image 8.1 Image 1.8.6 Starting raw threads is simple: Just wrap code in a Runnable, pass it to a new Thread, and start that thread. However, this has the disadvantage of always working at the lowest possible level. For background jobs, it is usually better to use some execution framework that distributes tasks to a givenImage 148 number of worker threads. These frameworks are also called lightweight executable frameworks, because the single tasks are lightweight, in contrast to threads, which consume actual system resources.

The Java library offers the ThreadPoolExecutor for the purpose. It is designed to be efficient and can be customized to handle large amounts of tasks efficiently. For instance, the Apache Tomcat servlet container uses it to handle incoming requests in parallel. The executor also offers callImage 1.4.9 backs beforeExecute() and afterExecute(), in the form of TEMPLATE METHOD, which allow applications to track single jobs.


Use the infrastructure provided by the user interface toolkit.


User interface toolkits usually offer a task execution framework with even more infrastructure. For instance, this infrastructure often includes observersImage 2.1 for the tasks’ state and progress—something that is usually required in this application area.

The Eclipse API for background processing revolves around the central class Job. One merely has to subclass and override the run() method to employ the framework.

swt.threads.DownloadJob


public class DownloadJob extends Job {
private URL url;
public DownloadJob(URL url) {
super(url.toString());
this.url = url;
}
protected IStatus run(IProgressMonitor monitor) {
... perform the download
}
}


Starting a Job is similarly simple: Just create an instance and schedule it to be run at some convenient time. The framework will choose an available thread from an internal pool to execute the job.

swt.threads.DownloadDemo.DownloadDemo


curDownload = new DownloadJob(parsed);
...
curDownload.schedule(100);


The Job API is usable outside of the overall Eclipse workbench. However, the standard workbench offers more support—for instance, by displaying the progress in the status bar, in the lower-right corner of the window, and enabling the user to cancel running jobs.

When assessing the offered API, one has to be a bit careful. For instance, the ApplicationWindow abstraction offered by JFace also includes a facilityImage 9.3 to run jobs, ostensibly in the background by using an argument fork. The run() method provides a very convenient interface, as seen in the next example. (Its first argument is fork, the second is cancelable.) At second glance, however, these jobs are not properly run in the background, even if technically they execute in a separate thread: The call to run() blocks until the job finishes, so lines 5–6 must wait. The framework dispatches only just enough events to show progress and let the user cancel the job.

swt.threads.ModalOperationsDemo.createContents


1 run(true, true, new IRunnableWithProgress() {
2 public void run(IProgressMonitor monitor) {
3 ... perform the actual work
4 });
5 MessageDialog.openInformation(getShell(), "Done",
6 "The job has finished now");



Keep track of the running jobs.


Jobs are usually not something that one fires and forgets. Users need to cancel jobs, and this may also happen implicitly. When a dialog starts a job, such as to validate some input file, and the user closes the dialog, then the job needs to be stopped as well. For such cases, one always keeps a reference to running jobs. When a job finishes, it must then be cleared from the list:

swt.threads.DownloadDemo.DownloadDemo


curDownload.addJobChangeListener(new JobChangeAdapter() {
public void done(IJobChangeEvent event) {
display.asyncExec(new Runnable() {
public void run() {
curDownload = null;
}
});
}
});



Image Image 7.10.1Note that curDownload is a widget field, so it must be accessed only within the event thread to avoid confusion. For this reason, the actual reaction to the callback done() is scheduled via asyncExec().



Reflect state and results in the user interface.


Users who trigger a job also wish to be informed about its status. In the Eclipse IDE, a global progress monitor is available in the status bar, andImage 2.1.1 that monitor tracks all currently running jobs. In the current example, we also update a label message according to the status. Since the events are fired from a background thread, we have to switch to the event thread (omitted in the second method for brevity).

swt.threads.DownloadDemo.trackJob


curDownload.addJobChangeListener(new JobChangeAdapter() {
public void running(IJobChangeEvent event) {
display.asyncExec(new Runnable() {
public void run() {
message.setText("downloading");
}
});
}
public void done(final IJobChangeEvent event) {
...
if (event.getResult().isOK()) {
message.setText("finished");
} else if (event.getResult().getSeverity()
== IStatus.CANCEL) {
message.setText("canceled");
} else
message.setText(event.getResult()
.getMessage());
...
}
});



Image Image 7.10.1This is a typical instance of the earlier injunction to know which code runs in which thread. In principle, the application merely translates a signal that the job has changed to a message to the user. In more detail, this message involves switching to the event thread.



Image Swing’s SwingWorker anticipates the need to update the user interface. One derives new kinds of jobs from that class and implements the actual task in the method doInBackground(). When that method finishes, the framework calls done() in the event thread. During the processing, doInBackground() may invoke publish() with intermediate results, which get passed on to process(). That latter method is, again, run in the event thread. As a result, the code complexity of switching threads is avoided.


javax.swing.SwingWorker


protected abstract T doInBackground() throws Exception
protected void done()
protected final void publish(V... chunks)
protected void process(List<V> chunks)



Provide timely and accurate progress reports to the user.


Long-running jobs can make for a trying user experience, which can be much improved by the provision of accurate progress reports. The Eclipse API provides the mechanism of IProgressMonitors for the purpose: The job manager supplies each job with such a progress monitor and the job isImage 2.1.1 supposed to invoke the methods of the monitor at suitable points. The IDE will display the information in a global progress view and in the status bar.

The general approach of reporting is as follows: At the start of a job, one determines the overall amount of work and calls beginTask(). When parts of the work have been performed, one reports worked(). Finally, one reports the end via done().

swt.threads.DownloadJob.run


1 monitor.beginTask("download", length);
2 try {
3 ...
4 while ((n = in.read(buf)) != -1) {
5 ... read and write data
6 monitor.worked(n);
7 }
8 } finally {
9 monitor.done();
10 }



Image The API governing the use of progress monitors is rather strict: beginTask() mustImage 197 be invoked only once and done() must always be invoked. When starting delegatingImage 1.3.2 Image 2.4 parts of jobs to collaborators, it is therefore customary to wrap the monitor into aSubProgressMonitor, which calls subTask() only on the original.


Estimating the work up front can, of course, be problematic. An accurate measure that also corresponds linearly to the expected time is the best option. Otherwise, one can also count subtasks to be completed and report each as worked(1). In the worst case, one can passIProgressMonitor. UNKNOWN and hope that the UI does something sensible, such as providing a flashing or cyclic progress bar.


Allow the user to cancel requests.


In the current example (Fig. 7.23), the user cancels the current task via a simple button.

swt.threads.DownloadDemo.DownloadDemo


btnCancel = new Button(this, SWT.NONE);
btnCancel.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (curDownload != null)
curDownload.cancel();
curDownload = null;
}
});



Image Image 10In a real implementation, one should gray out the button if curDownload is null instead of checking this before proceeding.


Image 148 Image 8.3 Canceling a job properly involves two steps: (1) send a signal to the respective job and (2) enable the job to terminate itself. The latter is achieved by providing suitable exit points, which usually occur before the job attempts a new piece of work. In the download example, the loop for reading data is a natural point. In the Eclipse API, the signal for canceling is sent through the monitor also used for creating progress reports.

swt.threads.DownloadJob.run


while ((n = in.read(buf)) != -1) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
... work with data
}



Image Do not forget to clean up after the job. Background jobs usually involve resources such as network connections and file streams, and it is important to close these whenever the job finishes. The standard idiom is using a try-finally block for theImage 1.5.6 purpose to ensure that the cleanup code runs regardless of why the job terminates.


7.10.3 Periodic Jobs

Background jobs can also tackle tasks that run infrequently but periodically. Typical examples would be querying an email inbox for new messages, cleaning up some temporary files, and checking the state of database connections available in a pool.

The Eclipse framework handles periodic jobs as jobs that just reschedule themselves (line 9 in the next example). To keep the runs spaced evenly, it makes sense to adapt the timeout according to the time spent on the actual job (lines 6–8).

swt.threads.PeridodicJob.run


1 protected IStatus run(IProgressMonitor monitor) {
2 long startTime = System.currentTimeMillis();
3 monitor.beginTask("clean up", WORK);
4 try {
5 ... do work
6 long timeTaken = System.currentTimeMillis() - startTime;
7 if (timeTaken > INTERVAL)
8 timeTaken = 0;
9 schedule(INTERVAL - timeTaken);
10 } finally {
11 monitor.done();
12 }
13 return Status.OK_STATUS;
14 }


7.11 Review: Events and Contracts

We started this chapter, and this part of the book, with the observation that events challenge the view that methods are basically service providers for their callers. In an event-driven setting, the proper behavior of an object is not determined by the expectations of the caller, but rather by the role that the object itself plays in the overall software machinery of the application. A listener attached to a button, for instance, will trigger operations that may loosely correspond to the label of the button, but unlike the client of a service provider, the button itself does not depend on this reaction for its future work.

But if methods are no longer essentially service providers, does the rather substantial and somewhat complex framework of contracts remain useful for talking about the correctness of event-driven software? We will now briefly investigate the salient points of the relationship between events and contracts to set the previous framework in a new perspective.


Contracts remain crucial for the machinery employed by event-listeners.


The first and most fundamental argument against dismissing contracts summarily is that an event-listener rarely works on its own. Very often, it willImage 9.1 trigger computations and operations that are independent of the user interface and that are part of the application’s core functionality. The situation is shown in Fig. 7.24: The call (a) from SWT to the registered listener is, indeed, not governed by conventional contracts, since SWT’s widgets do not expect a particular behavior or “service.” However, the event-listenerImage 1.8.2 often relies on some application-specific service provider objects in call (b) to implement the desired overall reaction. These objects, in turn, are usually based on some basic libraries such as for data structures, file I/O, or XML processing. Both calls (b) and (c) are then covered by the framework of contracts, because they clearly take place with a specific expectation on the side of the respective callers.

Image

Figure 7.24 Event Handlers Based on Contracts


SWT’s service methods do have contracts.


The second point where contracts remain important is the use of service methods in widgets and other SWT objects. For instance, when invoking setText() on a text entry field, you rightly expect that getText() will retrieve exactly the same string immediately afterward. The concepts of object state, model fields (here, the text value), and pre- and post-conditions therefore still apply to methods offered by SWT to the application.

In this context, there is only one minor deviation to be noted: The actual painting on the screen that follows the update of the widget’s stateImage 7.8 will be delayed by a few milliseconds, because paint requests are queued and optimized before being carried out. In practice, this hardly matters, because the user is not aware of the delay and the software usually does not examine the screen’s state at all.


The concept of pre- and post-conditions does not fit event-listeners.


The initial observation of this part therefore mainly concerns the specification of event-listeners, for which no proper contract can be given. First, the caller usually supplies a detailed description of the event, which comes with the implicit pre-condition that this event has actually taken place. It should be noted that this pre-condition is not, in fact, a proper assertion,Image 4.1 because it does not concern the current state of the program, but rather some past occurrence. Also, the pre-condition is generic for all interested event-listeners, while conventionally the pre-condition is specific to the single methods, as is made explicit in the advice of using a demanding style.Image 6.1.3

As for the post-condition, specifying one for a generic event-listener in the interface would mean that all possible implementations have to fulfillImage 6.4.1 Image 4.1 the expectation expressed in the post-condition. In summary, it is really the concepts of pre- and post-conditions that do not apply well to event-listeners.


Class invariants become more important in event-driven software.


Class invariants, in contrast, become more important. When reasoningImage 4.7 about the behavior of the code of a method, there are basically two sources of information about the current state at the start of the execution: the pre-condition and the class invariant, which captures the “proper state” ofImage 4.1 the object’s encapsulated data structures. If the pre-condition is missing or generic, the class invariant must carry all the load.

Let us clarify this statement by examining a concrete example, the simple cash register application shown in Fig. 7.25. In this appication, the user enters 4-digit product codes and the application does all the bookkeeping: It looks up the code in the product database, adds the product’s price to the current overall sum, displays the new sum and the new purchased product, and finally clears the entry field to receive the next product code.

Image

Figure 7.25 A Simple Cash Register Application

swt.contracts.TillApp


1 private Map<String, Product> productDatabase =
2 readProductDatabase();
3 private BigDecimal sum = new BigDecimal(0);
4
5 protected void addProduct(String code) {
6 Product p = productDatabase.get(code);
7 if (p != null) {
8 sum = sum.add(p.getPrice());
9 txtSum.setText(sum.toPlainString());
10 TableItem row = new TableItem(bill, SWT.NONE);
11 row.setText(0, p.getName());
12 row.setText(1, p.getPrice().toPlainString());
13 entry.setText("");
14 }
15 }


The heart of the matter is seen in lines 8–9: How do we know that the price we show to the user is the correct one? Why is sum actually the sum of the individual product prices? The answer is, of course, that this is just the class invariant: “The field sum contains the sum of the prices of products shown in the table bill.” This invariant is maintained by the example code, because just as it adds a new product to the bill in lines 10–12, it adds the product’s price to the sum in line 8.

This situation is typical of user interface programming. That is, the incoming events do not carry any particular information about the application’s purpose. That information is always kept in fields, and the class invariant explains how to interpret the fields.


Treat event-listeners as public methods with respect to contracts.


One central question in connection with class invariants is which code will be responsible for maintaining them. In the classical case of serviceImage 4.1 providers, we have used the rule that public methods can assume the invariant at their beginning and must reestablish it at the end. The justification for the rule is that public methods serve as entry points to the class’s code: Before they run, the invariant must hold, and after they finish, it must hold again.

From this perspective, we must now treat the event-listeners as entry points, because they get called from the outside, even if they do occur in private or anonymous classes and are therefore not accessible to the arbitrary clients. The rule to be obeyed is “Each event-listener can assume the class invariant initially and must guarantee it at the end.” Here, “the invariant” refers to the invariant of the outer class surrounding the event-listener, since usually listeners do not have internal state themselvesImage 2.1.3 and often delegate the actual work to protected methods in the outer class.


Cross-object invariants can be broken temporarily.


Event-driven programming is very well suited to maintaining cross-object invariants, because events will signal any change that may violate an invariant so that the event handler can restore the invariant. For instance, different widgets in the same dialog or compound widget are often linked conceptually—for instance, because one displays the result of processing the input from the other. Fig. 7.26 shows a tiny example. The Spinner on top and the Slider at the bottom are linked by the invariant that “spinner and slider always hold the same value.”

Image

Figure 7.26 Linked Widgets Demo

As a result, when either of the widgets changes, the other must follow suit. This can be achieved directly by event handlers, because they are called precisely “whenever the value changes”:

swt.contracts.LinkedWidgets.LinkedWidgets


spinner.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
slider.setSelection(spinner.getSelection());
}
});
slider.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
spinner.setSelection(slider.getSelection());
}
});


There is, however, a small caveat that has already been discussed inImage 6.2.3.5 connection with invariants within compound objects. There, too, the connection between parts could be maintained by observers. However, the invariant was always broken for a brief time, and other observers were able to see this. The same reasoning applies to event handlers.


The user interface belongs to the system boundary.


A final connection to contracts must be made in observing that the user interface belongs, of course, to the system boundary. There, the strict ruleImage 4.6 to be followed is that all input must be checked and no input can ever be trusted. In particular, the non-redundancy principle does not apply. In theImage 4.5 case of the user interface, the software must never assume that the user obeys any rules about the order of entries or the values entered. Instead, every input must be checked and the user must be notified about any invalidImage 229 inputs. Users will greatly appreciate and even expect this resilience of the software toward any errors on their part.