Home > Articles > Programming > Java

Java Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

SWT Layouts

Last updated Mar 14, 2003.

As you have learned thus far, SWT applications are built from a combination of Composites and Controls. Yet, what defines how those Composites and Controls are positioned in the application? The answer is that every Composite must define a Layout that contains the rules by which its Controls and sub-Composites are positioned. Throughout the past two chapters we have used several of the layouts in the examples, but now is time for a formal definition of the subject.

All Layouts are required to extend the org.eclipse.swt.widgets.Layout abstract class. The Layout class declares two abstract methods:

  • protected abstract Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache)

  • This method is responsible for computing and returning the size of the specified composite's client area according to this layout. It computes the minimum size that the client area of the composite must be, in order to position all children at their minimum size inside the composite according to the layout algorithm encoded by this layout.

    When a width or height hint is supplied, it is used to constrain the result. For example, if a width hint is provided that is less than the minimum width of the client area, the layout may choose to wrap and increase height, clip, overlap, or otherwise constrain the children.

  • protected abstract void layout(Composite composite, boolean flushCache)

  • The method is responsible for laying out the children of the specified composite according to this layout. This method positions and sizes the children of a composite using the layout algorithm encoded by this layout. Children of the composite are positioned in the client area of the composite. The position of the composite is not altered by this method. When the flush cache hint is true, the layout is instructed to flush any cached values associated with the children.

    Typically, a layout will cache the preferred sizes of the children to avoid the expense of computing these values each time the widget is layed out. When layout is triggered explicitly by the programmer the flush cache hint is true. When layout is triggered by a resize, either caused by the programmer or by the user, the hint is false.

SWT provides a set of predefined Layouts for you to use in your applications. The standard layouts include:

  • FillLayout

  • RowLayout

  • GridLayout

But when developing the Eclipse IDE, the developers discovered one additional custom layout that they needed, and it is also available to you:

  • StackLayout

Once you construct a Layout, all Composite provide a setLayout() method that you must call to tell the Composite to use this specific layout. When Controls or sub-Composites are created, the first parameter of their constructors is a reference to the Composite that will own them. The Composite then positions the widget according to its layout.

Fill Layout

The Fill Layout is probably the simplest layout. It lays out widgets in a single row or column, forcing them to be the same size. All widgets will be as tall and as wide as the tallest and widest widget. The Fill Layout does not have any support for configurable margins or spacing between its widgets, and it does not wrap components to a second row or column if they are too wide to fit in the Composite.

The Fill Layout has a single default constructor (one that does not take any parameters):

public FillLayout()

It does, however, allow you to position its widgets vertically or horizontally by modifying its public "type" parameter, sending it one of the following two values:

  • SWT.VERTICAL

  • SWT.HORIZONTAL (default value)

Listing 15 shows an example using the FillLayout to position three push buttons.

Listing 15 FillLayoutExample.java

...
public class FillLayoutExample
{
  ...
  public FillLayoutExample()
  {
   // Initialize SWT
   display = new Display();
   shell = new Shell( display );
   shell.setText( "FillLayout Example" );

   // Set the layout
   FillLayout layout = new FillLayout();
   //layout.type = SWT.HORIZONTAL;
   layout.type = SWT.VERTICAL;
   shell.setLayout( layout );

   // Build the buttons
   Button button1 = new Button( shell, SWT.PUSH);
   button1.setText( "Button 1" );
   Button button2 = new Button( shell, SWT.PUSH);
   button2.setText( "Button 2" );
   Button button3 = new Button( shell, SWT.PUSH);
   button3.setText( "Button 3" );
   
   // Display the application window
   shell.setSize( 400, 400 );
   shell.setVisible( true );
   shell.open();

   // Message Pump
   while( !shell.isDisposed() ) 
   {
     if( !display.readAndDispatch() ) display.sleep();
   }
  }
  ...
}

Figure 26 shows the FillLayoutExample running with a vertical layout; figure 27 shows it running horizontally. The only change to the code to layout the components horizontally instead of vertically is to change the following two lines:

   //layout.type = SWT.HORIZONTAL;
   layout.type = SWT.VERTICAL;

To:

   layout.type = SWT.HORIZONTAL;
   //layout.type = SWT.VERTICAL;

Figure 26Figure 26 FillLayoutExample with a vertical layout

Figure 27Figure 27 FillLayoutExample with a horizontal layout

Row Layout

The Row Layout is very similar to a horizontal Fill Layout but with a few key advantages:

  • If the width of the Composite is too small to position all widgets on a single row, then the widgets are "wrapped" to the next line; a second or third or fourth, etc. row is created.

  • It provides configurable margins on each edge of the layout

  • It provides configurable spacing between widgets in the layout

The RowLayout class provides a single default constructor:

public RowLayout()

It is configurable through a set of publicly accessible attributes:

  • boolean wrap: specifies whether a control will be wrapped to the next row if there is insufficient space on the current row. The default value is true.

  • boolean pack: specifies whether all controls in the layout are forced to be the same size. The default value is true.

  • boolean justify: specifies whether the controls in a row should be fully justified, with any extra space placed between the controls. The default value is false.

  • int spacing: specifies the number of pixels between the edge of one cell and the edge of its neighbouring cell. The default value is 3.

  • int 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.

  • int 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.

  • int 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.

  • int 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.

Individual widgets that are added to a Composite with a Row Layout are asked for their default size and sized accordingly. To override the size of a widget, you can use an auxiliary class that specifies the layout information: RowData. The org.eclipse.swt.layout.RowData class manages two values: the width of the widget and the height of the widget. The process of using a RowData is to construct an instance using one of the following three constructors:

RowData()       
RowData(int width, int height)       
RowData(Point point) 

And then calling the widget's setLayoutData() method. The setLayoutData() method is defined in the org.eclipse.swt.widgets.Control class that is the super class of all Controls and Composites. Its signature is as follows:

public void setLayoutData(Object layoutData)

The signature defines the layoutData to be an Object because the type of data contained in this variable is specific to the layout being used; a RowLayout uses a RowData while a GridLayout uses a GridData.

Listing 16 shows an example laying out three buttons in a RowLayout with margins of 10 pixels and specific sizes assigned to the first two buttons (100x100 and 50x50).

Listing 16 RowLayoutExample.java

...
public class RowLayoutExample
{
  ...
  public RowLayoutExample()
  {
   // Initialize SWT
   display = new Display();
   shell = new Shell( display );
   shell.setText( "RowLayout Example" );

   // Set the layout
   RowLayout layout = new RowLayout();
   layout.marginLeft = 10;
   layout.marginRight = 10;
   layout.marginTop = 10;
   layout.marginBottom = 10;
   shell.setLayout( layout );

   // Build the buttons
   Button button1 = new Button( shell, SWT.PUSH);
   button1.setText( "Button 1" );
   RowData rowData1 = new RowData( 100, 100 );
   button1.setLayoutData( rowData1 );
   Button button2 = new Button( shell, SWT.PUSH);
   button2.setText( "Button 2" );
   RowData rowData2 = new RowData( 50, 50 );
   button2.setLayoutData( rowData2 );
   Button button3 = new Button( shell, SWT.PUSH);
   button3.setText( "Button 3" );
   
   // Display the application window
   shell.setSize( 400, 400 );
   shell.setVisible( true );
   shell.open();
   ...
  }
  ...
}

Figure 28 hows a screenshot of the RowLayoutExample running

<insert fig18.jpg>

Figure 28Figure 28 RowLayoutExample screenshot

Listing 16 creates a new instance of the RowLayout, configures its margin parameters, and then sets it as the layout for the shell Composite to use:

   // Set the layout
   RowLayout layout = new RowLayout();
   layout.marginLeft = 10;
   layout.marginRight = 10;
   layout.marginTop = 10;
   layout.marginBottom = 10;
   shell.setLayout( layout );

It then creates three buttons and adds them to the shell; they will be laid out according to the layout just created. For two of the buttons it creates a RowData with a specific width and height for the button and then sets it as the layout data for the button. For example:

   RowData rowData1 = new RowData( 100, 100 );
   button1.setLayoutData( rowData1 );

This tells the RowLayout inside the shell to resize button1 to 100 pixels wide by 100 pixels high.

Grid Layout

The Grid Layout is the most powerful and complex layout. It lays out components in a grid of rows and columns, but the behavior of each cell in the grid is highly configurable. The GridLayout, like the other layouts we have discussed, provides a single default constructor:

public GridLayout()

And its configuration is performed by manipulating its publicly accessible attributes:

int numColumns

Specifies the number of cell columns in the layout

boolean makeColumnsEqualWidth

Specifies whether all columns in the layout will be forced to have the same width

int marginHeight

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

int marginWidth

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

int horizontalSpacing

Specifies the number of pixels between the right edge of one cell and the left edge of its neighboring cell to the right

int verticalSpacing

Specifies the number of pixels between the bottom edge of one cell and the top edge of its neighboring cell underneath


Similar to the Row Layout, the behavior of the individual widgets in their specific grid cells is configurable via an additional layout data component. In the case of a Grid Layout, that component is a GridData. The GridData exposes the following publicly accessible attributes:

int verticalAlignment

Specifies how controls will be positioned vertically within a cell. The default value is CENTER. Possible values are: BEGINNING: Position the control at the top of the cell CENTER: Position the control in the vertical center of the cell END: Position the control at the bottom of the cell FILL: Resize the control to fill the cell vertically

int horizontalAlignment

Specifies how controls will be positioned horizontally within a cell. The default value is BEGINNING. Possible values are: BEGINNING: Position the control at the left of the cell CENTER: Position the control in the horizontal center of the cell END: Position the control at the right of the cell FILL: Resize the control to fill the cell horizontally

int widthHint

Specifies a minimum width for the column. A value of SWT.DEFAULT indicates that no minimum width is specified. The default value is SWT.DEFAULT.

int heightHint

Specifies a minimum height for the row. A value of SWT.DEFAULT indicates that no minimum height is specified. The default value is SWT.DEFAULT.

int horizontalIndent

Specifies the number of pixels of indentation that will be placed along the left side of the cell. The default value is 0.

int horizontalSpan

Specifies the number of column cells that the control will take up. The default value is 1.

int verticalSpan

Specifies the number of row cells that the control will take up. The default value is 1.

boolean grabExcessHorizontalSpace

Specifies whether the cell will be made wide enough to fit the remaining horizontal space. The default value is false.

boolean grabExcessVerticalSpace

Specifies whether the cell will be made tall enough to fit the remaining vertical space. The default value is false.


Listing 17 demonstrates how to use various aspects of the GridLayout and associated GridDatas.

Listing 17 GridLayoutExample.java

...
public class GridLayoutExample
{
  ...
  public GridLayoutExample()
  {
   // Initialize SWT
   display = new Display();
   shell = new Shell( display );
   shell.setText( "GridLayout Example" );

   // Set the layout
   GridLayout layout = new GridLayout();
   layout.numColumns = 3;
   layout.makeColumnsEqualWidth = true;
   layout.marginWidth = 0;
   layout.marginHeight = 0;
   layout.horizontalSpacing = 0;
   layout.verticalSpacing = 0;
   shell.setLayout( layout );

   // Build the buttons
   Button button1 = new Button( shell, SWT.PUSH);
   button1.setText( "Button 1" );
   Button button2 = new Button( shell, SWT.PUSH);
   button2.setText( "Button 2" );
   GridData gd1 = new GridData();
   gd1.horizontalAlignment = GridData.CENTER;
   button2.setLayoutData( gd1 );
   Button button3 = new Button( shell, SWT.PUSH);
   button3.setText( "Button 3" );
   GridData gd2 = new GridData();
   gd2.horizontalAlignment = GridData.END;
   button3.setLayoutData( gd2 );
   Button button4 = new Button( shell, SWT.PUSH);
   button4.setText( "Button 4" );
   GridData gd3 = new GridData();
   gd3.horizontalIndent = 5;
   button4.setLayoutData( gd3 );
   Button button5 = new Button( shell, SWT.PUSH);
   button5.setText( "Button 5" );
   GridData gd4 = new GridData();
   gd4.horizontalSpan = 2;
   gd4.horizontalAlignment = GridData.FILL;
   gd4.grabExcessHorizontalSpace = true;
   button5.setLayoutData( gd4 );
   Button button6 = new Button( shell, SWT.PUSH);
   button6.setText( "Button 6" );
   GridData gd5 = new GridData();
   gd5.horizontalAlignment = GridData.FILL;
   gd5.grabExcessHorizontalSpace = true;
   button6.setLayoutData( gd5 );
   Button button7 = new Button( shell, SWT.PUSH);
   button7.setText( "Button 7" );
   GridData gd6 = new GridData();
   gd6.horizontalSpan = 2;
   gd6.verticalSpan = 2;
   gd6.horizontalAlignment = GridData.FILL;
   gd6.verticalAlignment = GridData.FILL;
   gd6.grabExcessHorizontalSpace = true;
   gd6.grabExcessVerticalSpace = true;
   button7.setLayoutData( gd6 );
   
   // Display the application window
   shell.setSize( 400, 400 );
   shell.setVisible( true );
   shell.open();
   ...
  }
  ...
}

Figure 29 shows a screenshot of the GridLayoutExample running.

Figure 29Figure 29 GridLayoutExample screenshot

The first observation looking at figure 29 is that there are three equally sized columns and all of the buttons that touch each other do not have any spacing between them. This was accomplished by accessing the attributes of the GridLayout in the following code snippet:

   layout.numColumns = 3;
   layout.makeColumnsEqualWidth = true;
   layout.marginWidth = 0;
   layout.marginHeight = 0;
   layout.horizontalSpacing = 0;
   layout.verticalSpacing = 0;

Now let us examine each button in turn:

  • Button1 was created without using a GridData and hence is left justified and its default size.

  • Button2 was created using a GridData that centered the button horizontally in its cell:

  gd1.horizontalAlignment = GridData.CENTER;
  • Button3 was right, or end, justified in its cell:

   gd2.horizontalAlignment = GridData.END;
  • Button4 was inset 5 pixels from the left:

   gd3.horizontalIndent = 5;
  • Button5 spans 2 cells, fills its space, and grabs any additional space that it can:

   gd4.horizontalSpan = 2;
   gd4.horizontalAlignment = GridData.FILL;
   gd4.grabExcessHorizontalSpace = true;
  • Button6 occupies a single cell but fills it and grabs any additional space that it can:

   gd5.horizontalAlignment = GridData.FILL;
   gd5.grabExcessHorizontalSpace = true;
  • Button7 spans 2 columns and 2 rows and it fills the area grabbing any additional vertical and horizontal space that it can:

   gd6.horizontalSpan = 2;
   gd6.verticalSpan = 2;
   gd6.horizontalAlignment = GridData.FILL;
   gd6.verticalAlignment = GridData.FILL;
   gd6.grabExcessHorizontalSpace = true;
   gd6.grabExcessVerticalSpace = true;

The GridLayout takes time to master, but once you have it down, you can build very complicated screens.

Stack Layout

The StackLayout can be thought of similar to a stack of cards. Only one is on top of the stack at any given time; that is the only card visible, and all of the rest are hidden. A Composite with a StackLayout can contain an arbitrary number of controls, only one of which is visible at any given time. It contains a single default constructor:

public StackLayout()

And its configuration is controlled by three publicly accessible attributes:

int marginHeight

Specifies the number of pixels of vertical margin that will be placed along the top and bottom edges of the layout; the default is 0

int marginWidth

Specifies the number of pixels of horizontal margin that will be placed along the left and right edges of the layout; the default is 0

Control topControl

This is the Control that is displayed at the top of the stack.


After you add your Controls to your Composite, you must select one of those Controls to be the top control by setting topControl equal to the desired Control. When you want to change the topControl, you simply set the layout's topControl to the new Control and then call the parent Composite's layout() method to re-layout the Composite.

Listing 18 demonstrates how to use StackLayout by creating three buttons. When a button is pressed, it changes the top control to the next button; the third button wraps back around to the first button.

Listing 18 StackLayoutExample.java

...
public class StackLayoutExample implements SelectionListener
{
  ...
  private StackLayout layout;
  private Button button1;
  private Button button2;
  private Button button3;

  public StackLayoutExample()
  {
   // Initialize SWT
   display = new Display();
   shell = new Shell( display );
   shell.setText( "StackLayout Example" );

   // Set the layout
   layout = new StackLayout();
   shell.setLayout( layout );

   // Build the buttons
   button1 = new Button( shell, SWT.PUSH);
   button1.setText( "Button 1" );
   button1.addSelectionListener( this );
   button2 = new Button( shell, SWT.PUSH);
   button2.setText( "Button 2" );
   button2.addSelectionListener( this );
   button3 = new Button( shell, SWT.PUSH);
   button3.setText( "Button 3" );
   button3.addSelectionListener( this );
   layout.topControl = button1;
   
   // Display the application window
   shell.setSize( 400, 400 );
   shell.setVisible( true );
   shell.open();
   ...
  }
  public void widgetSelected(SelectionEvent e) 
  {
    if( e.getSource() == button1 )
    {
     layout.topControl = button2;
    }
    else if( e.getSource() == button2 )
    {
     layout.topControl = button3;
    }
    else if( e.getSource() == button3 )
    {
     layout.topControl = button1;
    }
    shell.layout();
  }
  ...
}

Figure 30 shows a screenshot of the StackLayoutExample running.

Figure 30Figure 30 StackLayoutExample screenshot

Listing 18 creates three buttons in the constructor and then tells the StackLayout to display button1:

   layout.topControl = button1;

The StackLayoutExample class implements the SelectionListener interface and responds to button presses by changing the top control to the next button in the list and then calls the shell's layout() method to ask it to re-layout its components. For example consider button1:

    if( e.getSource() == button1 )
    {
     layout.topControl = button2;
    }
	 ...
    shell.layout();

If you are familiar with AWT programming, the StackLayout is very similar to the AWT card layout.

Summary

SWT provides a Java-based platform independent interface to accessing native GUI elements. It borrowed its philosophy from AWT but extended it to include the advanced controls that today's applications demand. Because it provides a Java interface to native controls, it must include a native library for each supported operating system to do the "work" on that operating system.

This completes the introduction to SWT. I encourage you to explore InformIT.com and www.eclipse.org to learn mode!