Programmers use the Standard Widget Toolkit (SWT) and JFace libraries to develop graphical user interfaces (GUIs) for the Eclipse environment, and to develop stand-alone GUI native applications.
In the first installment of this series, "How to create a simple SWT application," I introduced you to Eclipse, the Eclipse SWT, and the JFace GUI tool kits to construct Eclipse and stand-alone rich GUIs. I also introduced the basic label, text, and button GUI controls, and the composite, group, and shell container types. Finally, I showed you how to combine these controls into a simple working application.
Here, you will learn how to add menus to your application, use some list input controls, and use the more advanced table and tree container controls. I also will demonstrate some best practices by employing service methods to easily build GUIs. Finally, I will show you how to extract reusable function into a base application class.
Except where noted, all the widgets and controls discussed are in the
org.eclipse.swt.widgets
package.Menus
All but the most primitive GUI applications require menus. Menus add to any GUI's usability. Menus are dynamically presented selection lists that correspond to functions available for use (often called commands) or GUI states. As you would expect, you create menus with the menu widget. Menus can contain other menus or menuItems, which can contain menus (that is, a hierarchy of menus). MenuItems represent the commands you can perform or the GUI state you selected. Menus can be associated with the application's (that is, shell) menu bar or they can be pop-up menus that float over the application window.
You must define menus as one of three mutually exclusive styles:
BAR
acts as the menu bar for the shellDROP_DOWN
drops down from the menu bar or a menu itemPOP_UP
pops up from the shell, but is contextual to a specific control
Menus support some additional optional styles:
NO_RADIO_GROUP
does not act as a radio group; use it when the menu containsRADIO
-style itemsLEFT_TO_RIGHT
orRIGHT_TO_LEFT
selects the text direction
You must define menuItems as one of five mutually exclusive styles:
CHECK
can be persistently selected (that is, checked)CASCADE
contains a menu that should drop downPUSH
acts like a button that causes an immediate actionRADIO
acts like aCHECK
where only one of the items with this type can be selectedSEPARATOR
acts as a separator (often a bar) between groups of items; this item has no function
Creating a menu system is fairly complex. Listing 1 shows a code sample that creates an operable menu system.
http://www.ibm.com/developerworks/library/os-jface2/index.html
Listing 1. Creating a menu system and a pop-up menu
import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; : Shell shell = ...; : Label body = ...; : // Create the menu bar system Menu main = createMenu(shell, SWT.BAR | SWT.LEFT_TO_RIGHT); shell.setMenuBar(main); MenuItem fileMenuItem = createMenuItem(main, SWT.CASCADE, "&File", null, -1, true, null); Menu fileMenu = createMenu(shell, SWT.DROP_DOWN, fileMenuItem, true); MenuItem exitMenuItem = createMenuItem(fileMenu, SWT.PUSH, "E&xit\tCtrl+X", null, SWT.CTRL + 'X', true, "doExit"); MenuItem helpMenuItem = createMenuItem(main, SWT.CASCADE, "&Help", null, -1, true, null); Menu helpMenu = createMenu(shell, SWT.DROP_DOWN, helpMenuItem, true); MenuItem aboutMenuItem = createMenuItem(helpMenu, SWT.PUSH, "&About\tCtrl+A", null, SWT.CTRL + 'A', true, "doAbout"); // add popup menu Menu popup = createPopupMenu(shell, body); MenuItem popupMenuItem1 = createMenuItem(popup, SWT.PUSH, "&About", null, -1, true, "doAbout"); MenuItem popupMenuItem2 = createMenuItem(popup, SWT.PUSH, "&Noop", null, -1, true, "doNothing"); |
The code sequence creates the following menu bar, with sub-menus and a pop-up menu (see Figure 1, Figure 2, Figure 3, and Figure 4). The
body
value is a label control with the text "Sample body." The pop-up menu is contextually associated with this control.Figure 1. Menu bar with File and Help menus
Figure 2. File menu drop-down
Figure 3. Help menu drop-down
Figure 4. Pop-up menu
As you can see, menu items can have accelerators (
Ctrl+?
) and mnemonics (underlined characters identified by &
) to help the user select items with the keyboard.
I created these menus with a set of helper methods, shown in Listing 2. Best practice is to create methods like this to use in creating repetitive GUI parts, such as menus. Over time, you can add more support functions to these helper methods and apply them to all use points. These methods also help prompt you for all the required values.
Listing 2. Menu creation helper routines
protected Menu createMenu(Menu parent, boolean enabled) { Menu m = new Menu(parent); m.setEnabled(enabled); return m; } protected Menu createMenu(MenuItem parent, boolean enabled) { Menu m = new Menu(parent); m.setEnabled(enabled); return m; } protected Menu createMenu(Shell parent, int style) { Menu m = new Menu(parent, style); return m; } protected Menu createMenu(Shell parent, int style, MenuItem container, boolean enabled) { Menu m = createMenu(parent, style); m.setEnabled(enabled); container.setMenu(m); return m; } protected Menu createPopupMenu(Shell shell) { Menu m = new Menu(shell, SWT.POP_UP); shell.setMenu(m); return m; } protected Menu createPopupMenu(Shell shell, Control owner) { Menu m = createPopupMenu(shell); owner.setMenu(m); return m; } protected MenuItem createMenuItem(Menu parent, int style, String text, Image icon, int accel, boolean enabled, String callback) { MenuItem mi = new MenuItem(parent, style); if (text != null) { mi.setText(text); } if (icon != null) { mi.setImage(icon); } if (accel != -1) { mi.setAccelerator(accel); } mi.setEnabled(enabled); if (callback != null) { registerCallback(mi, this, callback); } return mi; } |
Listing 3 shows how to use the Java reflection feature to link the menu items with the code that processes them. This feature creates an easy-to-use method where you simply add a
public
method (such as doExit
, doAbout
, or doNothing
) to your application class to process the command.Listing 3.
Callback
routines to process menu commandprotected void registerCallback(final MenuItem mi, final Object handler, final String handlerName) { mi.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { try { Method m = handler.getClass().getMethod(handlerName, null); m.invoke(handler, null); } catch (Exception ex) { ex.printStackTrace(); } } }); } |
I described the details of using a
SelectionListener
in the first installment of this series, "How to create a simple SWT application."
Please note that menu items (and items in the list, combo, table, and tree controls discussed later) support only string values; other types must be converted to string values before you add them.
Combos and lists
Often, you want your GUI's user to select from a predetermined list of values. The list control is the easiest way to do this. A list shows a predetermined set of string values that the user can select from. Lists typically require a large amount of screen real estate. If you want to save space, you can use a combo control that allows the list to drop down when needed. Combos also optionally allow the user to enter the desired value in a text-like field.
You must define combos as one of two mutually exclusive styles:
SIMPLE
shows the list of valuesDROP_DOWN
drops down the list of values
Combos support an optional style:
READ_ONLY
prevents users from editing the text field of this combo
All the controls I discuss (list, combo, table, and tree) support one of two mutually exclusive styles:
SINGLE
users can select only one itemMULTI
users can select multiple items
These controls also support additional styles:
H_SCROLL
shows a horizontal scroll bar when neededV_SCROLL
shows a vertical scroll bar when needed
Creating combos and lists is fairly easy. Create the controls and add the desired string values as shown in Listing 4.
Listing 4. Creating a combo and a list using
FormLayout
import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; import org.eclipse.swt.layout.*; : setLayout(new FormLayout()); String[] data = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 9", "Item 10" }; Combo c = createCombo(this, data); configureLayout(c, new FormAttachment(0, 5), new FormAttachment(0, 5), new FormAttachment(100, -5), null); List l = createList(this, data); configureLayout(l, new FormAttachment(0, 5), new FormAttachment(c, 5), new FormAttachment(100, -5), new FormAttachment(100, -5)); // Create a Combo protected Combo createCombo(Composite parent, String[] data) { Combo combo = new Combo(parent, SWT.DROP_DOWN | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); combo.addSelectionListener(new SelectionListener() { : }); setComboContents(data); return combo; } // Create a List protected List createList(Composite parent, String[] data) { List list = new List(parent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); list.addSelectionListener(new SelectionListener() { : }); setListContents(data); return list; } public void setComboContents(String[] data) { combo.removeAll(); for (int i = 0; i < data.length; i++) { combo.add(data[i]); } } public void setListContents(String[] data) { list.removeAll(); for (int i = 0; i < data.length; i++) { list.add(data[i]); } } |
If you add the
SelectionListener
, it enables the application to take action when the user changes the selected items.
The flow of the main code sequence in Listing 4 assumes it is included in some composite referenced by
this
. It creates the combo and (partially hidden) list shown in Figure 5.Figure 5. Combo and list example
You can use an alternate implementation to the combo control, called CCombo (located in the
org.eclipse.swt.custom
package). CCombo is similar to Combo, except that it supports some additional functions, the most significant being that you can programmatically ask a CCombo to cut, copy, or paste text into or from its embedded Text control. Also, a CCombo is always in the DROP_DOWN
style, so it doesn't support the type styles.
CCombos also support optional styles:
BORDER
shows a border around the text areaREAD_ONLY
prevents users from editing the text field of this combo
The example in Listing 4 uses a
FormLayout
to place the combo and list. FormLayout
is one of the most useful layout managers because it allows you to lay out each control relative to other controls. It allows you to attach any side (left, top, right, or bottom) of a control to the (typically opposing) side of another control or to the side of the container. Unattached sides take the natural corresponding dimension of the control. Use an instance of FormAttachment
to specify the reference control or a percentage of the container size as the attachment point, and to provide a pixel offset from that point. The Listing 4 code uses the helper method from Listing 5.Listing 5.
configureLayout
: FormLayout helper methodprotected static void configureLayout(Control c, FormAttachment left, FormAttachment top, FormAttachment right, FormAttachment bottom) { FormData fd = new FormData(); if (left != null) { fd.left = left; } if (top != null) { fd.top = top; } if (right != null) { fd.right = right; } if (bottom != null) { fd.bottom = bottom; } c.setLayoutData(fd); } |
Tables
Tables are enhanced forms of lists that support TableColumns. These columns align their data into a more readable format. They also support column names and the ability to resize the columns. To create tables, first create the table control, add its table columns, then add the string data wrapped in TableItems.
Tables support optional styles:
CHECK
adds check boxes to the first columnVIRTUAL
support for large tables (platform-specific)FULL_SELECTION
selects all columns (not just the first column)
Listing 6 creates the table shown in Figure 6.
Listing 6. Creating a table using helper methods
// Create the Table and TableColumns protected Table createTable(Composite parent, int mode, Object[] contents) { table = new Table(parent, mode | SWT.SINGLE | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL); table.setHeaderVisible(true); table.setLinesVisible(true); createTableColumn(table, SWT.LEFT, "Column 1", 100); createTableColumn(table, SWT.CENTER, "Column 2", 100); createTableColumn(table, SWT.RIGHT, "Column 3", 100); addTableContents(contents); return table; } protected TableColumn createTableColumn\ (Table table, int style, String title, int width) { TableColumn tc = new TableColumn(table, style); tc.setText(title); tc.setResizable(true); tc.setWidth(width); return tc; } protected void addTableContents(Object[] items) { for (int i = 0; i < items.length; i++) { String[] item = (String[])items[i]; TableItem ti = new TableItem(table, SWT.NONE); ti.setText(item); } } : // sample creation code protected void initGui() { Object[] items = { new String[] {"A", "a", "0"}, new String[] {"B", "b", "1"}, new String[] {"C", "c", "2"}, new String[] {"D", "d", "3"}, new String[] {"E", "e", "4"}, new String[] {"F", "f", "5"}, new String[] {"G", "g", "6"}, new String[] {"H", "h", "7"}, new String[] {"I", "i", "8"}, new String[] {"J", "j", "9"} }; table = createTable(this, SWT.CHECK, items); } |
Figure 6. Table example
The check boxes in the first column are optional. Note the alignment of the columns.
Trees
A tree is a list that can show hierarchical information. Trees support the application's ability to expand and collapse intermediate levels of the hierarchy.
Because trees often display hierarchical structures, you should provide a data model for them to use (I will revisit this model notion later when I talk about JFace). In my example, I use the inner class
Node
, shown in Listing 7, for this purpose.Listing 7. Tree model class node
public class Node { protected java.util.List children; public java.util.List getChildren() { return children; } public void setChildren(java.util.List children) { this.children = children; } public void addChild(Node node) { children.add(node); } protected String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Node(String name) { this(name, new ArrayList()); } public Node(String name, java.util.List children) { setName(name); setChildren(children); } } |
To create trees, first create the tree control, then add the string data wrapped in TreeItems. TreeItems can contain other TreeItems, thus creating the hierarchy of values. Listing 8 creates the tree shown in Figure 7.
Listing 8. Creating a tree using helper methods
// Create the Tree protected Tree createTree(Composite parent, int mode, Node root) { tree = new Tree(parent, mode | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); tree.addSelectionListener(new SelectionListener() { : }); setTreeContents(root); return tree; } protected void setTreeContents(Node root) { tree.removeAll(); TreeItem ti = new TreeItem(tree, SWT.NONE); setTreeItemContents(ti, root); } protected void setTreeItemContents(TreeItem ti, Node root) { ti.setText(root.getName()); java.util.List children = root.getChildren(); if (children != null && children.size() > 0) { for (Iterator i = children.iterator(); i.hasNext();) { Node n = (Node)i.next(); TreeItem tix = new TreeItem(ti, SWT.NONE); setTreeItemContents(tix, n); } } } : // sample creation code protected void addChildren(Node n, int count, int depth, String prefix) { if (depth > 0) { for (int i = 0; i < count; i++) { String name = prefix + '.' + i; Node child = new Node(name); n.addChild(child); addChildren(child, count, depth - 1, name); } } } Node root = new Node(" |
Figure 7. Tree example
The check boxes are optional.
Building a base program
Except for the menu example, all the examples in this article use a base class, called BasicApplication, to simplify their implementation. As another best-practice example, I factored out the common features of an SWT GUI application into this base class (including the helper methods from the menu example) to make them easily available.
BasicApplication is a composite that creates its own shell. This class provides additional services, like an exit verification dialog (see Figure 8) and the ability to dump out the widget tree as a diagnostic aid (see an abridged example in Listing 9). See Download for the code to this class.
Figure 8. Confirmation message dialog
Listing 9. Printout of (partial) control hierarchy
Shell {Tree1App Example} Tree1App {} Tree {} TreeItem { |
Listing 10 shows the
main
method of each subclass (from the combo and list example in Listing 4), and provides the shell's title and size, the application composite's style, and any command-line inputs.Listing 10. Main method for example list application
public static void main(String[] args) { run(List1App.class.getName(), "List1App Example", SWT.NONE, 400, 300, args); } |
Each subclass, which is loaded through Java technology reflection, must define a constructor and the
completeGui
method. Subclasses may optionally provide the initGui
method. Again using the combo and list application in Listing 4 as an example, these methods are shown in Listing 11.Listing 11. Required methods supplied in the application subclass
public List1App(Shell shell, int style) { super(shell, style); // must always supply parent and style } // Allow subclasses to complete the GUI protected void completeGui(String[] args) { // create GUI here : } // Allow subclasses to initialize the GUI protected void initGui() { // finish GUI and add dynamic contents here : } |
MessageBox
Before I close, I will show you how to use the
MessageBox
control to request the user to input choices.
You must define MessageBoxes as one of five mutually exclusive styles:
ICON_ERROR
presents an error messageICON_INFORMATION
presents an information messageICON_QUESTION
presents a question messageICON_WARNING
presents a warning messageICON_WORKING
presents a working message
MessageBoxes support some additional optional styles, all of which present their respective choices on buttons:
OK, OK | CANCEL
YES | NO, YES | NO | CANCEL
RETRY | CANCEL
ABORT | RETRY | IGNORE
Listing 12 shows a typical use of
MessageBox
, which presents the confirmation dialog as seen in Figure 8 when the user closes the application shell.Listing 12. Using
MessageBox
to create an exit confirmation dialogshell.addShellListener(new ShellAdapter() { public void shellClosed(ShellEvent e) { MessageBox mb = new MessageBox(shell, SWT.ICON_QUESTION | SWT.OK | SWT.CANCEL); mb.setText("Confirm Exit"); mb.setMessage("Are you sure you want to exit?"); int rc = mb.open(); e.doit = rc == SWT.OK; } }); |
Conclusion
In this second installment of "A gentle introduction to SWT and JFace", I introduced more SWT controls: combo, list, table, and tree. I also showed you how to create a base class for your SWT applications and how to use helper methods to make it easier to build GUIs.
The next installment will show you how to create more containers and input controls, and how to use the
StackLayout
layout manager.
댓글 없음:
댓글 쓰기