Previous Page
Next Page

4.3. Layout Management

In 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. FillLayout

As 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:

type Determines the orientation of the layout. Valid values are SWT.HORIZONTAL (the default) and SWT.VERTICAL.

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. RowLayout

RowLayout 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:

justify Specifies whether the controls in a row should be fully justified, with any extra space placed between the controls.

marginBottom Specifies the number of pixels of vertical margin that will be placed along the bottom edge of the layout. The default value is 3.

marginLeft Specifies the number of pixels of horizontal margin that will be placed along the left edge of the layout. The default value is 3.

marginRight Specifies the number of pixels of horizontal margin that will be placed along the right edge of the layout. The default value is 3.

marginTop Specifies the number of pixels of vertical margin that will be placed along the top edge of the layout. The default value is 3.

pack Specifies whether all controls in the layout take their preferred size. If pack is false, all controls will have the same size, which is the size required to accommodate the largest preferred height and width of all controls in the layout.

spacing Specifies the number of pixels between the edge of one cell and the edge of its neighboring cell. The default value is 3.

type Determines the orientation of the layout. Valid values are SWT.HORIZONTAL (the default) and SWT.VERTICAL.

wrap Specifies whether a control will be wrapped to the next row if there is insufficient space on the current row.

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:

width Specifies the width of the cell in pixels.

height Specifies the height of the cell in pixels.

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. GridLayout

Most 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.

GridLayout defines the following significant attributes.

horizontalSpacing Specifies the number of pixels between the right edge of one cell and the left edge of its neighboring cell. The default value is 5.

makeColumnsEqualWidth Specifies whether all columns should be forced to the same width. The default is false.

marginWidth Specifies the number of pixels used for the margin on the right and the left edge of the grid. The default value is 5.

marginHeight Specifies the number of pixels used for the margins on the top and bottom edge of the grid. The default value is 5.

numColumns Specifies the number of columns that should be used to make the grid. The default value is 1.

verticalSpacing Specifies the number of pixels between the bottom edge of one cell and the top edge of its neighboring cell. The default value is 5.

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:

grabExcessHorizontalSpace Specifies whether a cell should grow to consume extra horizontal space in the grid. After the cell sizes in the grid are calculated based on the widgets and their grid data, any extra space remaining in the composite will be allocated to those cells that grab excess space.

grabExcessVerticalSpace Specifies whether a cell should grow to consume extra vertical space in the grid.

heightHint Specifies a minimum height for the widget (and therefore for the row that contains it).

horizontalAlignment Specifies the horizontal alignment of the widget within the cell. Valid values are SWT.BEGINNING, SWT.CENTER, SWT.END, and SWT.FILL. SWT.FILL means that the widget will be sized to consume the entire width of its grid cell.

horizontalIndent Specifies the number of pixels between the widget and the left edge of its grid cell. The default value is 0.

horizontalSpan Specifies the number of columns in the grid that the widget should span. By default, a widget consumes one cell in the grid. It can add additional cells horizontally by increasing this value. The default value is 1.

verticalAlignment Specifies the vertical alignment of the widget within the cell. Valid values are SWT.BEGINNING, SWT.CENTER, SWT.END, and SWT.FILL. SWT.FILL means that the widget will be sized to consume the entire height of its grid cell.

verticalSpan Specifies the number of rows in the grid the widget should span. By default, a widget takes up one cell in the grid. It can add additional cells vertically by increasing this value. The default value is 1.

widthHint Specifies a minimum width for the widget (and therefore the column that contains it).

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. FormLayout

Nowhere 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:

marginWidth Specifies the number of pixels of horizontal margin that will be placed along the left and right edges of the layout.

marginHeight Specifies the number of pixels of vertical margin that will be placed along the top and bottom edges of the layout.

FormData specifies several significant attributes:

top Specifies the attachment for the top side of the control.

bottom Specifies the attachment for the bottom side of the control.

left Specifies the attachment for the left side of the control.

right Specifies the attachment for the right side of the control.

width Specifies the preferred width in pixels of the control in the form.

height Specifies the preferred height in pixels of the control in the form.

FormAttachment specifies several significant attributes:

alignment Specifies the alignment of the control side attached to a control. SWT.DEFAULT indicates that the widget should be attached to the adjacent side of the specified control. For top and bottom attachments, SWT.TOP, SWT.BOTTOM, and SWT.CENTER are used to indicate attachment of the specified side of the widget to the specified side of the control. For left and right attachments, SWT.LEFT, SWT.RIGHT, and SWT.CENTER are used to indicate attachment of the specified side of the widget to the specified side of the control. (For example, using SWT.TOP indicates that the top side of the attachment's widget should be attached to the top side of the specified control).

control Specifies the target control to which the attachment's widget is attached.

denominator Specifies the denominator of the "a" term in the equation y = ax + b, which defines the attachment.

numerator Specifies the numerator of the "a" term in the equation y = ax + b, which defines the attachment.

offset Specifies the offset in pixels of the control side from the attachment position; can be positive or negative. This is the "b" term in the equation y = ax + b, which defines the attachment.

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.


Previous Page
Next Page