http://www.developer.com/

Back to article

Constructing SWT Layouts


April 15, 2004

"I saw the angel in the marble and carved until I set him free."—Michelangelo

Learning layout managers takes time, but once you get accustomed to using them, you can create good looking user interfaces. You also can use GUI builders to create the GUI easily. I use SWT (Standard Widget Toolkit), a cross platform GUI developed by IBM and part of the Eclipse environment. If you are not familiar with this tool please see my earlier article on programming with SWT.

I prefer to use the GUI builders to create an initial look and I configure the UI manually. If you do not have a good understanding of the layout internals, you will limit yourself to the capabilities of the GUI builder that you are using.

Layouts

A layout automatically controls the position and size of widgets inside a Composite. In SWT, the size of a widget inside a composite is not automatically set. Each composite requires a layout to show its children controls. If the layout is not set, SWT will not be able to set the size and position of the controls inside a composite and our controls will not be visible. We have to set a layout for each composite in order to display the children controls.

Layout Manager classes are inherited from an abstract Layout class. Some Layout Managers allow us to set different properties for each control. Those layout classes have an addition object which can be set separately for each control inside a composite. The Control class provides a standard method, setLayoutData(Object obj), to set this addition parameter. You have to set an appropriate layout data object for each control; otherwise, it throws a classcast exception.

Figure 1. SWT Layout managers.

Layout classes have properties that affect all the components within a composite. Some layout classes support layout data objects to set different properties for each control within a composite. Layout properties can be set by using a layout's member variables. If you are familiar with Swing, you would probably search for methods to set and get these member variables. In SWT, this is different; you read and write to those variables directly.

You can use the following SWT application template to test the code snippets shown in this article.

 Listing 1.  SWT application template
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SWTTemplate 
{
  public static void main(String args[]) 
  {
    Display display = new Display();
    Shell shell = new Shell(display);
    // ------------------------
    // Your code comes to here.
    // ------------------------
    shell.pack();
    shell.open();
    while( !shell.isDisposed())
    {
      if(!display.readAndDispatch()) 
      display.sleep();
    }
    display.dispose();
  }
}

Running SWT applications
If you are new to SWT programming, you can read my previous article "SWT Programming with Eclipse" from http://www.developer.com/java/other/article.php/3330861.

FillLayout

FillLayout is the simplest layout. If there is only one subcomponent, it fills the all available parent area. If there are more than one component, it forces all components to be the same size in a single row or a column. The width and hight of the subcomponents are determined by the widest or the highest widget in a composite. There are no options available to control either the spacing, margins, or wrapping.

The type variable specifies how controls will be positioned within the composite. The type variable can be set to SWT.HORIZONTAL (the default) or SWT.VERTICAL to position the controls either in a single row or a column. This variable is public; you can either directly set the variable or pass the variable to the constructor.

 Listing 2.  FillLayout example
  FillLayout fillLayout = new FillLayout(SWT.HORIZONTAL);
  // or ..
  FillLayout fillLayout = new FillLayout();
  fillLayout.type = SWT.HORIZONTAL;

The FillLayout might be used in a task bar, in a tool bar, or in a group or composite having only one child. Besides that, it might be used to stack the check boxes or radio buttons in a group.

 Listing 3.  FillLayout example
  // ...
  FillLayout fillLayout = new FillLayout(SWT.VERTICAL);
  shell.setLayout(fillLayout);
  for (int i = 0; i < 3; i++) 
  {
    Button button =new Button(shell, SWT.PUSH);
    button.setText("A"+i);
  }
  // ...

The width and height of a control within a composite are determined by the parent composite. Initially, the height of any control is equal to the highest, and the width of a control is equal to the widest.

Table 1. FillLayout examples
Type Before&After Resize
SWT.HORIZONTAL
SWT.VERTICAL

Advantages:

  • Simplest layout
  • Positions the components in rows or columns

RowLayout

RowLayout is very similar to FillLayout. It positions the controls, similar to FillLayout, in rows or columns. In addition to that, RowLayout provides configuration fields to control the position of a control within a composite as seen in Listing 4.

Figure 2 shows a simple use of RowLayout. You can use the preceding code to create the following example. All values are default; nothing is changed. As you can see, it wrapped the row automatically when there is not enough space left in the row.

Figure 2. RowLayout Example with default properties

If you do not want to make any configuration changes, you can use the default values by creating a RowLayout using its default constructor.

The configurable properties of RowLayout are listed in the following table. The variables are member fields in RowLayout. You can access them directly.

Table 2. RowLayout properties
Variable Default Description
justify false Spreads the widgets across the available space within a composite.
marginBottom
marginLeft
marginRight
marginTop
spacing
3 Number of pixels around widgets. Spacing represents number of pixels between widgets.
pack true Forces all components to be the same size within a composite.
type SWT.HORIZONTAL Positions the widgets in rows or columns.
wrap true Wraps the widgets in row/column if there is not enough space on the row/column

Figure 6, shown below, shows the fields that do not depend on the size of the composite. Figures 3, 4, and 5 show the properties that depend on the size of the composite.

Figure 3 shows the effect of the wrap property. The wrap is set to false and shell window is resized. As it is shown, there is no wrapping after resize when the wrap is set to false. In contrast to Figure 2 above, the controls on the composite do not wrap.

Figure 3. RowLayout.wrap=false

The pack property forces all components to be the same size within the composite. This property sets the width of the controls to the widest and height of the components to the highest. Figure 4 shows both cases.

Figure 4. RowLayout.pack=true, false

The justify property spreads the widgets across the available space within a composite. Figure 5 shows the effect of the justify property.

Figure 5. RowLayout.justify=false, true

Figure 6 shows some properties listed in Table 2.

Figure 6. RowLayout properties

RowLayout is similar to FillLayout, but has the following key advantages:

  • If the number of widgets do not fit in a row, it wraps the widgets.
  • It provides configurable margins.
  • It provides configurable spaces.
  • It provides RowData object.

3.1. RowData

Each widget can have different RowData objects in RowLayout. You can specify different a height and width for each widget in the composite having RowLayout.

Table 3. RowData properties
Variable Default Description
height
width
0 Height and width of the widget

RowData only has two properties. There are only the width and height properties that we can use to set the width and height of each widget. If you want to change the size of a widget, you should create a RowLayout for the composite and you should create a RowData object for each widget. You can specify different RowData objects for each widget.

 Listing 5.  RowData Example
  // ...
  RowLayout rowLayout = new RowLayout();
  shell.setLayout(rowLayout);
  for (int i = 1; i < 6; i++) 
  {
    Button button = new Button(shell, SWT.PUSH);
    button.setText("A" + i);
    RowData rowData = new RowData(i*15,i*25); 
    button.setLayoutData(rowData);
  }
  // ...

Listing 5 shows a simple RowData example. This code will generate buttons having different heights and widths.

Figure 7. RowData Example

GridLayout

GridLayout is the most useful and flexible layout.The GridLayout lays out the widgets in grids. Configureable properties of the GridLayout are listed in Table 4.

The properties listed in Table 4 affect all the layout behavior. The GridLayout provides a data object as well. If you need to set a different property for a cell, you need to create a GridData object and set this as the control's layout data object.

The makeColumndEqualWidth property in Table 4 makes all the controls the same size, as shown in Figure 8. This property makes the columns the same size. If you want the widgets also to be the same size, you should need to use GridData object's horizontal/vertical alignment properties, as shown in Figure 12.

Figure 8. GridLayout.makeColumnnsEqualWidth=false,true

The numColumns field must be set. This field specifies the number of columns in the GridLayout. If the number of components added is bigger than the number of the components in a composite, GridLayout adds the component to a new row.

For example, five components added to a GridLayout in Figure 9. The GridLayout in Figure 9 only has four columns. For that reason, button A5 will be added to a the second row.

Figure 9 shows the location of the properties on the UI that are listed in Table 4.

Figure 9. GridLayout Example

Initially, each column can have a different width. If you want to force all columns to be the same size, you must set the makeColumnsEqualWidth variable to true, as shown in Figure 8.

 Listing 6.  GridLayout example
  // ...
  GridLayout gridLayout = new GridLayout();
  shell.setLayout(gridLayout);
  gridLayout.numColumns = 5;
  for (int i = 1; i <= 7; i++)
  {
    Button button = new Button(shell, SWT.PUSH);
    button.setText("A" + i);
  }
  // ...

The preceding code snippet creates a new grid layout and sets the number of columns to 5. Afterwards, seven components are added to the composite. The figure below shows the position of the widgets.

Figure 10. GridLayout Example using default properties

Advantages:

  • It is the most powerful layout
  • It puts widgets in rows and columns
  • It provides configurable margins
  • It provides configurable spaces
  • It provides a GridData object

GridData

Each widget within a composite having GridLayout can have GridData set a different property for each widget. Configureable properties of the GridData object are listed in Table 5.

Table 5. GridData properties
Variable Default Description
grabExcessHorizontalSpace
grabExcessVerticalSpace
false If true, after resize, the widget will grow enough to fit the remaining space.
heightHint
widthHint
SWT.DEFAULT (indicates that no minimum width is specified.) Specifies a minimum width/height for the column.
horizontalAlignment
verticalAlignment
GridData.BEGINNING(possible values are BEGINNING, CENTER, END, FILL) Specifies how controls will be positioned horizontally/vertically within a cell.
horizontalIndent 0 Specifies the number of pixels of indentation that will be placed along the left side of the cell.
horizontalSpan
verticalSpan
1 Specifies the number of column/row cells that the control will take up.

The effect of the first property listed in Table 5 is illustrated below. The grabExcessHorizontalSpace (or grabExcessVerticalSpace) property can be hard to understand at first. For that reason, the area that is affected by this property is highlighted in the picture below. As you can see, if this property is set to true, the width of the grids will be as large as possible. If you want a widget to fill the horizontal space, you need to use this in combination with the alignment property shown in Figure 12.

Figure 11. GridData.grabExcessHorizontalSpace=false,true

The horizontalAlignment (or verticalAlignment) property sets the alignment of the control. You can use this in combination with the grabExcessHorizontalSpace property.

Figure 12. GridData.horizontalAlignment = GridData.BEGINNING, GridData.CENTER, GridData.END, GridData.FILL

The effect of the horizontalSpace is shown below.

Figure 13. GridData.horizontalSpan=1,2

FormLayout

FormLayout is a very flexible layout, like GridLayout, but it works in a completely different way. In GridLayout, you have to plan everything ahead and build your user interface. In contrast to GridLayout, FormLayout is independent from the complete layout. The position and size of the components depend on one Control.

The location of the properties listed in Table 6 can be seen in Figure 9.

FormData and FormAttachment

Each widget within a composite having FormLayout can have FormData to set the different layout properties for a widget.

The FormData object has the properties listed in Table 7.

A FormData object can have 0 or 4 FormAttachment objects. The form attachment can have controls (see Figure 15). The FormAttachment object can have the properties listed in Table 8.

Figure 14. FormLayout Example

The above example shows a form layout created by using the code snippet below.

 Source 7.  FormLayout example
  FormLayout layout= new FormLayout();
  shell.setLayout (layout);

  Button button1 = new Button(shell, SWT.PUSH);
  Button button2 = new Button(shell, SWT.PUSH);
  Button button3 = new Button(shell, SWT.PUSH);
  button1.setText("B1");
  button2.setText("B2");
  button3.setText("B3");

  FormData data1 = new FormData();
  data1 .left    = new FormAttachment(0,5);
  data1 .right   = new FormAttachment(25,0);
  button1.setLayoutData(data1);

  FormData data2 = new FormData();
  data2.left     = new FormAttachment(button1,5);
  data2.right    = new FormAttachment(90,-5);
  button2.setLayoutData(data2);

  FormData data3 = new FormData();
  data3.top      = new FormAttachment(button2,5);
  data3.bottom   = new FormAttachment(100,-5);
  data3.right    = new FormAttachment(100,-5);
  data3.left     = new FormAttachment(25,5);
  button3.setLayoutData(data3);

Figure 15 shows the location of the properties and location of the buttons.

Figure 15. FormLayout Example

StackLayout

StackLayout is different from the other layout classes. StackLayout displays only one Control at a time; however, other layout classes attempt to display many Controls at a time. StackLayout is used in property pages, wizards, and so forth.

The StackLayout has the properties listed in Table 9.

Figure 16. StackLayout Example

Figure 16 shows a simple stack layout example. We click on the Show Next Group object each time to change the top control.

Figure 17. StackLayout Example

Figure 17 shows an example of StackLayout. A stack of controls exists. It is allowed to display only one control at a time. The highlighted picture on the right shows the topControl (active) control.

 Source 8.  StackLayout example
  // ...
  shell.setLayout(new GridLayout());
  
  final Composite parent = new Composite(shell, SWT.NONE);
  parent.setLayoutData(new GridData());
  
  final StackLayout layout = new StackLayout();
  parent.setLayout(layout);
  
final Group[] group = new Group[3];
  for (int k = 0; k < group.length; k++) 
  {
    group[k] = new Group(parent, SWT.NONE);
    group[k].setText("Group " + (k + 1));

    GridLayout gridLayout = new GridLayout();
    gridLayout.numColumns = 4;
    group[k].setLayout(gridLayout);
    
    Character character = new Character((char) ('A' + k));
    for (int i = 10; i < 20; i++)
    {
      Button bArray = new Button(group[k], SWT.PUSH);
      bArray.setText(character + "." + i);
    }
  }
layout.topControl = group[0];
  
  Button b = new Button(shell, SWT.PUSH);
  b.setText("Show Next Group");
  final int[] index = new int[1];
  b.addListener(SWT.Selection, new Listener() 
  {
    public void handleEvent(Event e) 
    {
      index[0]          = (index[0] + 1) % 3;
   layout.topControl = group[index[0]];
   parent.layout();
    }
  });
  // ...

We have three groups and we add button controls to those groups. Afterwards, we need to set the initial topControl. We create an action listener when the button is clicked. The action listener changes the top control and calls the layout() method of the parent control. This step is very important: If you do not call the layout method of the parent control, you won't be able to see any change.

About the Author


Koray Güclü

He is working as a freelance author and software architect. He is currently finishing his book on Software Architectures and Design Patterns. His main interest areas are Software Architectures, Data Warehouses, and Database Modeling.

www.korayguclu.de

Resources

Links

Books

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date