2011년 11월 30일 수요일

SWT Part 1 - How to create a simple SWT application


The Standard Widget Toolkit (SWT) and JFace libraries are used to develop graphical user interfaces (GUIs) for the Eclipse environment, and also to develop stand-alone GUI native applications. In this article, I introduce some of the basic SWT (the name for the basic GUI object) types and show how to combine them to create a working application.

About Eclipse, SWT, and JFace

As noted on the Eclipse Web site, Eclipse is a kind of universal tool platform. It's an open, extensible IDE for anything and nothing in particular, and provides tool developers with flexibility and control over their software technologies.
Eclipse provides a base for developers to produce rich GUI-driven tools and applications. Fundamental to this capability are the Eclipse GUI libraries SWT and JFace.
SWT is a library that creates a Java view of the native host operating system GUI controls. It is host implementation-dependent. This means SWT-based applications have several key characteristics:
  1. They look, act, and perform like "native" applications.
  2. The widgets provided reflect the widgets (the components and controls) provided on the host operating system.
  3. Any special behavior of the host GUI libraries is reflected in SWT GUIs.
These goals make SWT different from Java technology's Swing, which was designed to hide operating system differences.
The SWT library reflects the basic widgets of the host operating system. In many circumstances, this approach is too low-level. The JFace library helps by adding many services to SWT applications. JFace does not hide SWT; it just extends it. As you will see later in this series, one of its most important extensions is to isolate the application's data model from the GUI that displays and changes it.



Before we begin, I need to introduce some SWT terminology:
  • Widget ? The basic SWT GUI component (like Component in Java AWT and JComponent in Swing). Widget is an abstract class.
  • Control ? A widget that has an operating system peer (in other words, has an identity in the operating system). Control is an abstract class.
  • Composite ? A control that can contain other controls (like Container in Java AWT and JPanel in Swing).
  • Item ? A widget contained by other controls (that may not be composites), such as a list or a table. Note that controls that contain items rarely also contain controls and vice-versa. Item is an abstract class.
These widgets are arranged in an inheritance hierarchy. See figures 1, 2, and 3 to see how this is done. In Figure 2, theBasic1 class is the class from this article; all the other classes are standard SWT widgets.

Figure 1. SWT Widget Tree
SWT Widget Tree

Figure 2. SWT Composite Tree
SWT Composite Tree

Figure 3. SWT Item List
SWT Item List
Note that although Eclipse is cross-platform (in that it runs on many operating systems), this article is based on the Microsoft? Windows? version of Eclipse. Even so, every example included in this article should work unchanged on other platforms. Also note that this article is based on Eclipse V3.0. Eclipse V3.1 adds a few GUI widget types and features.

Basic controls

Almost all SWT GUIs are created out of a few basic parts. All SWT widgets are found in the org.eclipse.swt.widgets ororg.eclipse.swt.custom packages. (Some Eclipse plug-ins also supply custom widgets in other packages.) The widgets package contains controls based on the operating system controls, while the custom package contains controls that extend beyond the operation system control set. Some custom package controls are similar to controls in the widgets package. To avoid name collision, the custom control names starts with "C" (CLabel vs. Label).
In SWT, all controls (except for top-level controls like shells, to be discussed later) must have a parent control (a composite instance) at creation time. Controls are automatically "added" to the parent upon creation, unlike in AWT/Swing where they must be explicitly added, resulting in a "top-down" approach to GUI construction. Thus, all controls take a composite parent (or a sub-class) as an argument to the constructor.
Most controls have flag options that must be set when created. Thus, most controls also have a second constructor argument, often called style, that provides flags to set these options. All the flag values are static final ints and are defined in the SWT class in the org.eclipse.swt package. If no options are needed, use the SWT.NONE value.

Labels

Perhaps the simplest control, a label is used to display plain (with no color or special fonts or styles) text or a small image called an icon. Labels do not receive focus (in other words, a user cannot move to the label with the Tab key or a mouse) and, thus, do not generate input events.
Listing 1 shows how to create a simple text label.

Listing 1. Create a label with text
import org.eclipse.swt.widgets.*;

 :

Composite parent = ...;

 :

// create a center aligned label

Label label = new Label(parent, SWT.CENTER);  

label.setText("This is the label text");

Notice that the text is set in a separate method from the constructor. This is typical of all SWT controls. Only the parent and style are set in the constructor; all other properties are set on the created object.
Due to platform limitation standard label controls cannot have text and an icon. To support both of these together, use the CLabel control as shown in Listing 2.

Listing 2. Create a label with text and image
import com.eclipse.swt.graphics.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.custom.*;

 :

Composite parent = ...;

Image image = ...;

 :

// create a left aligned label with an icon

CLabel Clabel = new CLabel(parent, SWT.LEFT);  

label.setText("This is the imaged label text"");

label.setImage(image);


Texts

While labels show text, often, you want to allow users to input text. The text control is used to do this. Text can be single-line (a text field) or multiline (a text area). Text can also be read-only. Text fields have no description; thus, they are often proceeded by a label control to identify their purpose. The text control can also have a "tool tip" that can provide information about its use (all controls support this feature).
Listing 3 shows how to create a simple text field with a limit on the number of characters allowed. The default text is selected to allow easy erasure.

Listing 3. Create a text with selected default text and a limit
import org.eclipse.swt.widgets.*;

 :

Composite parent = ...;

 :

// create a text field

Text name = new Text(parent, SWT.SINGLE);  

name.setText("");

name.setTextLimit(50);

name.setToolTipText("Enter your name -- Last, First"); 

name.selectAll();  // enable fast erase


Buttons

Often, you want the user to indicate when an action should occur. The most common way to do this is via the buttoncontrol. There are several styles of buttons:
  • ARROW ? Shows as an arrow in up, down, left, or right directions
  • CHECK ? A labeled check mark
  • FLAT ? A push button without a raised appearance
  • PUSH ? A momentary push button (most common event source)
  • RADIO ? A sticky mark that is exclusive with all other radio buttons in the same group
  • TOGGLE ? A sticky push button
Listing 4 creates a "Clear" push button:

Listing 4. Create a button
import org.eclipse.swt.widgets.*;

 :

Composite parent = ...;

 :

// create a push button

Button clear = new Button(parent, SWT.PUSH);  

clear.setText("Clea&r");

The & in the name causes an accelerator to be created using the next character that allows the button to be pushed by a Ctrl+ sequence (the control sequence is host operating system-dependent).

Event listeners

Often, you want to do some action when a button (especially one of the push types) is selected. You do this by adding aSelectionListener (in the org.eclipse.swt.events package) to the button. When the button state is changed (often by being pushed), the event will be generated. Listing 5 prints a message when the Clear button is clicked.

Listing 5. Button event handler
import org.eclipse.swt.events.*;

 :

// Clear button pressed event handler

clear.addSelectionListener(new SelectionListener() { 

    public void widgetSelected(selectionEvent e) {

        System.out.println("Clear pressed!");   

    }

    public void widgetDefaultSelected(selectionEvent e) {

        widgetSelected(e);

    }

});

This code uses an anonymous inner class, but you may also use named inner classes or independent classes as listeners. Most ...Listener classes that contain two or more methods also have a parallel ...Adapter class that provide empty implementations of the methods and can reduce the amount of code you need to write. For example, there is also aSelectionAdapter class that implements SelectionListener.
Note that the action taken in these methods must happen quickly (generally, sub-second) or the GUI will be unresponsive. Longer actions, such as doing file access, require separate threads, but that is the subject of a future installment.

Composites

So far, we have been talking about individual controls. In most GUIs, multiple controls are grouped together to provide a rich user experience. In SWT, this grouping is implemented by using the Composite class.
Composites can be nested to any level, and can mix and match controls and composites as children. This can greatly reduce GUI development complexity and create opportunities for GUI code reuse (by encapsulating the inner GUI). Composites can have borders and be easily distinguished visually or can be borderless and seamlessly integrate into even larger groups.
Listing 6 creates a bordered composite.

Listing 6. Create a bordered composite
import org.eclipse.swt.widgets.*;

 :

Composite parent = ...;

 :

Composite border = new Composite(parent, SWT.BORDER);

In addition to a border, the Group composite sub-class supports a title. Groups are often used to contain radio-type buttons as they define the set of exclusive buttons.
Listing 7 creates a bordered group.

Listing 7. Create a bordered group
import org.eclipse.swt.widgets.*;

 :

Composite parent = ...;

 :

Group border = new Group(parent, SWT.SHADOW_OUT);

border.setText("Group Description");

Shells

A shell is a top-level composite (frame or window) that may have no parent composite; instead, it has a Display as a parent, often set by default. Shells come in many styles, but the most popular are SWT.SHELL_TRIM or SWT.DIALOG_TRIM. Shells may be modal or modeless. Modal shells, which are most often used for dialogs, prevent the parent GUI (if any) from proceeding until the child shell is closed.
Listing 8 creates a top-level nonmodal shell in frame style.

Listing 8. Create a top-level shell
import org.eclipse.swt.widgets.*;

 :

Shell frame = new Shell(SWT.SHELL_TRIM);

 :

Shells can have child shells. These are independent desktop windows associated with the parent shell (i.e., if the parent is closed, all of its children will also be closed).
Listing 9 creates a child shell in dialog style.

Listing 9. Create a dialog shell
:

Shell dialog = new Shell(frame, SWT.DIALOG_TRIM);

 :

See Figure 4 shell with SWT.SHELL_TRIM and Figure 5 shell with SWT.DIALOG_TRIM to see how these values affect the shell trim.

Figure 4. Shell with SWT.SHELL_TRIM
SWT shell with shell trim 

Figure 5. Shell with SWT.DIALOG_TRIM
SWT shell with dialog trim

Layout managers

Composites often contain more than one control. These controls can be arranged in two ways:
  1. Absolute positioning ? Each control is set to an explicit X and Y position, and a certain width and height by your code.
  2. Managed positioning ? Each control's X, Y, width, and height is set by a LayoutManager.
In most situations, you would choose to use LayoutManagers, as they make adjusting to variable-size GUIs much easier. SWT provides several layout managers for you to use; in this installment, we will talk about two of the more basic ones:FillLayout and GridLayout. In both cases, positioning takes place whenever the containing composite is resized.
Layout managers are uniquely assigned to a composite. Some layout managers are controlled by parameters just on themselves, and others also require additional parameters, LayoutData, specified on each control in the composite they manage.

FillLayout

FillLayout arranges controls in a row or a column. Each control is sized to be as wide or tall as needed to fill the composite, and space is divided evenly among the controls. A special case is when there is only one child control, in which case the control is sized to fill the entire parent composite.
Listing 10 creates a composite using a column FillLayout.

Listing 10. Create a column of controls using FillLayout
import org.eclipse.swt.widgets.*;

import org.eclipse.swt.layouts.*;

 :

Composite composite = ...;

FillLayout fillLayout = new FillLayout(SWT.VERTICAL);

composite.setLayout(fillLayout);

GridLayout

GridLayout offers a more powerful layout approach that resembles using HTML tables. It creates a 2-D grid of cells. Controls can be placed in one or more cells (called cell spanning). Cells can be made equal size or be given varying percentage of the grid's width or height. Controls are added to the next available column in a row; if no more columns exist in the row, the control moves to the first column of the next row.
Listing 11 creates a composite of two rows and two columns containing two labeled text fields. The columns can have different widths.

Listing 11. Create a table of controls
import org.eclipse.swt.widgets.*;

import org.eclipse.swt.layouts.*;

 :

Composite composite = ...;

GridLayout gridLayout = new GridLayout(2, false);

composite.setLayout(gridLayout);

Label l1 = new Label(composite, SWT.LEFT);

l1.settext("First Name: ");

Text first = new Text(composite, SWT.SINGLE);

Label l1 = new Label(composite, SWT.LEFT);

l2.setText("Last Name: ");

Text last = new Text(composite, SWT.SINGLE);

GridData

Consider the case where you need to specify how each control uses excess space within its cell. To provide this type of refined control for each cell, the controls added to a GridLayout managed composite can have instances of GridData (a LayoutData sub-class).
Listing 12 sets the text fields to take any excess available space (based on the prior listing).

Listing 12. Configure a layout that expands to all available space
first.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

last.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));


Building a running program

Now it is time to see all of the SWT controls we have discussed come together in a simple executable example: Basic1. See Download for the complete source of this application.
SWT GUIs require a configured environment to run in. This environment is provided by a display instance, which provides access to the host operating system display device. The display allows you to process each user input (mouse or keyboard) to drive your GUI.
Listing 13 creates the environment, creates a GUI, then displays it.

Listing 13. Create and launch a GUI application
import org.eclipse.swt.widgets.*;

 :

Display display = new Display();

Shell shell = new Shell(display);

shell.setText("Shell Title");



// *** construct Shell children here ***



shell.open();       // open shell for user access



// process all user input events

while(!shell.isDisposed()) {

   // process the next event, wait when none available

   if(!display.readAndDispatch()) {

       display.sleep();

   }

}

display.dispose();  // must always clean up

This code creates a window similar to Figure 6.

Figure 6. Example Application
SWT application

Conclusion

In this first installment of A gentle introduction to SWT and JFace, I have introduced the most basic SWT widget controls: label, text, button, composite, and shell. These controls, combined with the display class, allow fully functional GUIs to be created.
The second installment, "How to use combo, list, table, and tree controls," will show you how to create menus, and how to use additional input controls and information display controls like list, tree, and table.

댓글 없음:

댓글 쓰기