4.3. Layout ManagementIn each of the examples presented in the previous section, the widget layouts are very simple. Widgets were either positioned relative to their parents using the setBounds() method (null layout) or they were designed to fill their parent entirely using a FillLayout. Eclipse provides several more powerful layout management algorithms that can be used to aesthetically place widgets under a variety of conditions. Most layout managers in Eclipse trace their heritage to VisualAge for Smalltalk, and in particular, to the layout managers used to construct the wizards and dialogs in VisualAge for Java. As such, they were well thought out and thoroughly tested before ever being converted into Java as part of the Eclipse framework. Interestingly enough, the newest Eclipse layout manager, FormLayout, is based on the oldest and most powerful VisualAge for Smalltalk layout manager. 4.3.1. FillLayoutAs you have seen, FillLayout provides an easy way for a widget (e.g., a list or a table) to completely fill its parent (see Figure 4-5 or 4-6 for an example). FillLayout does more than this, however, because it provides a way to lay out a group of widgets in single row or column such that each widget is the same size as all the other widgets in the group (see Figure 4-8 for an example). The width and height of each widget matches the width and height of the widest and tallest widget in the group, and no options are provided to control the widget spacing, margins, or wrapping. FillLayout defines only this one significant attribute:
FillLayout is ideal for creating a uniform row or column of widgets such as those found in a simple toolbar. The following example creates a row of buttons that are all the same size (see Figure 4-15). import org.eclipse.swt.*; import org.eclipse.swt.events.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; public class FillLayoutExample { public static void main(String[] args) { Button button; Display display = new Display(); Shell shell = new Shell(display); shell.setText("FillLayout Example"); shell.setBounds(100, 100, 400, 75); shell.setLayout(new FillLayout()); for (int i = 1; i <= 8; i++) { button = new Button(shell, SWT.PUSH); button.setText("B" + i); button.addSelectionListener( new SelectionAdapter() { public void widgetSelected( SelectionEvent event) { System.out.println( ((Button)event.widget).getText() + " was clicked!"); } }); } shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } } Figure 4-15. FillLayout example.
By default, FillLayout is oriented horizontally. When buttons are added to the shell, they line up left to right with uniform widths and heights. 4.3.2. RowLayoutRowLayout is very similar to FillLayout in that it lays out widgets in columns or rows and has numerous additional options to control the layout. The spacing between widgets, as well as the margins between the widgets and the parent container, can be controlled. The widgets can be wrapped into multiple rows or columns or packed such that each widget will be the same size. RowLayout defines several significant attributes:
The width and height of each widget in the layout can be controlled by using a RowData object, which can be assigned to widgets with the setLayoutData() method. RowData objects have two significant attributes: The following example creates a row layout with 20 evenly spaced buttons inset from the edge of the window frame. Depending on the size and shape of the parent shell, the line of buttons wraps into one or more rows (see Figure 4-16). import org.eclipse.swt.*; import org.eclipse.swt.events.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; public class RowLayoutExample { public static void main(String[] args) { Button button; Display display = new Display(); Shell shell = new Shell(display); shell.setText("RowLayout Example"); shell.setBounds(100, 100, 400, 100); RowLayout layout = new RowLayout(); layout.marginLeft = 10; layout.marginRight = 10; layout.marginTop = 10; layout.marginBottom = 10; layout.spacing = 10; shell.setLayout(layout); for (int i = 1; i <= 20; i++) { button = new Button(shell, SWT.PUSH); button.setText("B" + i); button.addSelectionListener( new SelectionAdapter() { public void widgetSelected( SelectionEvent event) { System.out.println( ((Button)event.widget).getText() + " was clicked!"); } }); } shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } } Figure 4-16. RowLayout example.
By default, RowLayout is oriented horizontally. The margin spacing between the buttons and the parent shell is set using the four margin attributes: marginLeft, marginRight, marginTop, and marginBottom. The spacing between widgets is set using the spacing attribute. After all the attributes have been set, the layout is assigned to the shell using the setLayout() method. 4.3.3. GridLayoutMost dialogs, wizards, and preference pages are laid out using GridLayout. It is both one of Eclipse's most frequently used layout classes and one of the most complicated. GridLayout arranges its children in a highly configurable grid of rows and columns, where many options are provided to control the sizing behavior of each child element.
The layout characteristics of each widget in the layout can be controlled by using a GridData object, which can be assigned to the widgets with the setLayoutData() method. GridData objects have the following significant attributes:
The example code that follows creates a two-column grid layout containing a two-column spanning label and two sets of labels and fields (see Figure 4-17). import org.eclipse.swt.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; public class GridLayoutExample { public static void main(String[] args) { Label label; Text text; GridData gridData; Display display = new Display(); Shell shell = new Shell(display); shell.setText("GridLayout Example"); shell.setBounds(100, 100, 200, 100); GridLayout layout = new GridLayout(); layout.numColumns = 2; shell.setLayout(layout); label = new Label(shell, SWT.LEFT); label.setText("Enter your first and last name"); gridData = new GridData(); gridData.horizontalSpan = 2; label.setLayoutData(gridData); label = new Label(shell, SWT.LEFT); label.setText("First:"); text = new Text(shell, SWT.SINGLE | SWT.BORDER); gridData = new GridData(); gridData.horizontalAlignment = GridData.FILL; gridData.grabExcessHorizontalSpace = true; text.setLayoutData(gridData); label = new Label(shell, SWT.LEFT); label.setText("Last:"); text = new Text(shell, SWT.SINGLE | SWT.BORDER); gridData = new GridData(); gridData.horizontalAlignment = GridData.FILL; gridData.grabExcessHorizontalSpace = true; text.setLayoutData(gridData); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } } Figure 4-17. GridLayout example.
The numColumn attribute specifies that the GridLayout should have two columns. The horizontalSpan attribute of the GridData object created for the first label specifies that it should span both columns. The GridData objects created have horizontalAlignment attributes that specify that each text field should fill the entire cell and grabExcessHorizontalSpace attributes that specify that each field should grab any horizontal space that is left over. 4.3.4. FormLayoutNowhere does Eclipse show its VisualAge for Smalltalk roots more than in the FormLayout class that implements an attachment-based layout manager. FormLayout is the most powerful Eclipse layout manager and is a close replica of the layout management system first used in VisualAge for Smalltalk more than a decade earlier. With attachment-based layout, you have independent control over the sizing behavior of each of the four sides of a widget. The top, bottom, left, and right sides can be independently attached to the sides of the parent container or the sides of any sibling widget within the same container using either fixed or relative offsets. This proves to be surprisingly powerful and can be used to emulate almost any of the other layout managers. The FormLayout class is very simple and only specifies the margins of the container. The real power is in the FormData object, which holds up to four different FormAttachment objects (one for each side). FormLayout defines two significant attributes:
FormData specifies several significant attributes:
FormAttachment specifies several significant attributes:
The following example creates a simple form layout with two buttons in the lower right corner and a text field that fills the remaining space (see Figure 4-18 for a sketch of the window next to two examples of the running window at different sizes). The Cancel button is attached to the lower right corner while the OK button is attached to the bottom side of the window and to the left side of the Cancel button. The text field is attached to the top, left, and right sides of the window and to the top of the Cancel button. import org.eclipse.swt.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; public class FormLayoutExample { public static void main(String[] args) { FormData formData; Display display = new Display(); final Shell shell = new Shell(display); shell.setText("FormLayout Example"); shell.setBounds(100, 100, 220, 180); shell.setLayout(new FormLayout()); Button cancelButton = new Button(shell, SWT.PUSH); cancelButton.setText("Cancel"); formData = new FormData(); formData.right = new FormAttachment(100,-5); formData.bottom = new FormAttachment(100,-5); cancelButton.setLayoutData(formData); Button okButton = new Button(shell, SWT.PUSH); okButton.setText("OK"); formData = new FormData(); formData.right = new FormAttachment(cancelButton,-5); formData.bottom = new FormAttachment(100,-5); okButton.setLayoutData(formData); Text text = new Text(shell, SWT.MULTI | SWT.BORDER); formData = new FormData(); formData.top = new FormAttachment(0,5); formData.bottom = new FormAttachment( cancelButton,-5); formData.left = new FormAttachment(0,5); formData.right = new FormAttachment(100,-5); text.setLayoutData(formData); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } } Figure 4-18. FormLayout example.The FormData assigned to the Cancel button has a right and bottom attachment to the lower right corner of the shell. The first argument to each FormAttachment object is the percentage of the shell to attach initially (starting in the upper left corner with a 0% value). The value of 100 specifies the right and bottom sides, which are opposite the left and top sides. The second argument represents the fixed offset from the attachment point (with positive values pointing right and down). The value of -5 indicates that the widget should be offset 5 pixels from the bottom and right sides. Note that the left and top attachments are not specified. Leaving them blank will cause the widget to assume its preferred width and height. The OK button is also attached to the bottom of the shell. Its right side is attached to the left side of the Cancel button rather than to the shell itself. This provides a way for the OK button to position itself relative to the preferred size of the Cancel button. This pattern can be particularly effective for internationalized applications where the text of the buttons (and thus their preferred sizes) is not known at design time. Finally, the text field is attached with a fixed offset of 5 pixels from the left, right, and top sides of the shell. The bottom of the text field is attached with a 5-pixel offset to the top of the Cancel button. |