1. SWT Overview

1.1. What is SWT?

The Standard Widget Toolkit (SWT) is a Java based user interface library for developing desktop application. SWT supports Windows, Linux and Mac OS X.

It provides lots of standard widgets, e.g., buttons and text fields as well as the option to create custom widgets.

SWT uses the native widgets of the platform whenever possible. The native widgets of the OS are accessed by the SWT framework via the Java Native Interface (JNI) framework. In case a widget is not natively available on one platform, SWT emulates this widget on this platform.

1.2. Display and Shell

The Display and Shell classes are key components of SWT applications.

A org.eclipse.swt.widgets.Shell class represents a window.

The org.eclipse.swt.widgets.Display class is responsible for managing event loops, fonts, colors. It also controls the communication between the user interface thread and other threads. Display is therefore the base for all SWT capabilities.

Every SWT application requires at least one Display and one or more Shell instances.

1.3. Event loop

An event loop is needed to transfer user input events from the underlying native operating system widgets to the SWT event system.

SWT does not provide its own event loop. This means that the programmer explicitly starts and checks the event loop to update the user interface. The loop executes the readAndDispatch() method which reads events from the native OS event queue and dispatches them to the SWT event system. The loop is executed until the main shell is closed. If this loop would be left out, the application would terminate immediately.

For example, the following creates a SWT application which creates and executes the event loop.

Display display = new Display();
Shell shell = new Shell(display);
shell.open();

// run the event loop as long as the window is open
while (!shell.isDisposed()) {
    // read the next OS event queue and transfer it to a SWT event
    if (!display.readAndDispatch())
     {
    // if there are currently no other OS event to process
    // sleep until the next OS event is available
        display.sleep();
     }
}

// disposes all associated windows and their components
display.dispose();

If SWT is used for an Eclipse IDE plug-in or an Eclipse RCP application, the event loop is provided by the Eclipse framework.

1.4. Using SWT in a Java project

The SWT library is provided as JAR files and also via Maven central for Maven and Gradle builds. See https://search.maven.org/artifact/org.eclipse.platform/org.eclipse.swt for the Maven artifact.

1.5. Using SWT in an Eclipse plug-in

The JARs contain the OSGi meta-data and therefore can also be used for Eclipse plug-in development. To use SWT in your Eclipse plug-in you have add a dependency to the org.eclipse.swt plug-in in the corresponding manifest file.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: My SWT-based Plug-in
Bundle-SymbolicName: com.vogella.swt
Bundle-Version: 1.0.0.qualifier
Require-Bundle: org.eclipse.swt
Bundle-RequiredExecutionEnvironment: JavaSE-1.6

2. Exercise: Using the SWT library in a Java project

Create a new Java project called com.example.swt.widgets. Create a new lib folder in your project.

Download the SWT library from the Eclipse download page. Navigate to the download page of the latest stable Eclipse release and scroll down to the SWT binaries. Ensure that you download the correct version for your Java and OS version.

Eclipse Download Page - Latest Release
SWT Binaries

Copy the swt.jar library and the src.zip file to the lib folder in your project.

Add the library to the classpath of the project. Attach the src.zip source zip file to the library. Use the Open Type dialog to open the 'Shell' class. If everything has been done correctly you see the source code of the 'Shell' class.

3. SWT Widgets

3.1. Available widgets in the SWT library

SWT widgets are located in the packages org.eclipse.swt.widgets and org.eclipse.swt.custom. Widgets extend either the Widget or the Control class. Several of these widgets are depicted in the following screenshot of the SWT widget homepage.

Overview of the SWT widgets

3.2. Custom widgets and native widgets

SWT provides custom drawn widgets in org.eclipse.swt.custom package for cases in which the native widgets are not sufficient for the needs of SWT. For example, the CCombo class allows to set the height of the widget, which the Combo class does not support. Custom drawn widgets usually start with the additional prefix C to indicate that they are custom widgets, e.g. CCombo.

Another example is StyledText, a class which provides advanced features for displaying text, e.g. drawing a background.

Widgets from the package org.eclipse.swt.custom are implemented in pure Java while widgets from org.eclipse.swt.widgets use native code. Custom widgets are not supposed to use the internal classes of SWT as these classes may be different on the various platforms. Every custom widget must extend the Composite or Canvas class. API compliance is guaranteed only for these base classes.

If the new custom widget is supposed to contain other widgets, it should extend Composite. Otherwise it should extend the Canvas class.

3.3. Memory management

SWT widgets hold a reference to the native widget. Such references are not automatically released by the system. If you want to release an SWT widget, you have to call its dispose() method. If a container is disposed, e.g., a Shell, this container also releases all its children.

The following widgets must by design the explicitly disposed via the program code:

  • Cursor

  • Display

  • Font

  • GC

  • Image

  • Printer

  • Region

JFace provides a way to dispose such objects based on another widget, via the LocalResourceManager class.

3.4. Constructing widgets

SWT widgets, except the Shell object, are always constructed with a parent widget which contains them. This is similar to AWT and different to Swing, where the add() method is used.

The second parameter of the widget constructor contains the stylebits. Depending on the provided stylebits the widget adjusts its look and feel as well as its behavior. Each widget documents the supported stylebits in its Javadoc.

The possible stylebits are predefined in the SWT class. If no special style is required, you can pass SWT.NONE.

For example the following code snippet creates a push button or a checkbox button, dependent on the passed style flags.

new Button(shell, SWT.PUSH);

new Button(shell, SWT.CHECK);

3.5. Basic containers

The Composite class is a container which is capable of containing other widgets.

The Group class is another container which is able to contain other widgets. It additionally draws a border around itself and allows you to set a header for the grouped widgets.

3.6. Event Listener

You can register listeners for specific events on SWT controls. For example, a ModifyListener to listen to changes in a Text widget or a SelectionListener for selection (click) events on a Button widget. The following code demonstrates the implementation. It uses SelectionAdapter which is an implementation of the SelectionLister interface.

Button button =  new Button(shell, SWT.PUSH);
button.setText("Press me");

//register listener for the selection event
button.addSelectionListener(new SelectionAdapter() {
    @Override
    public void widgetSelected(SelectionEvent e) {
        System.out.println("Called!");
    }
});

FocusListener is another examples for such a listener interface.

The *Listener interfaces sometimes contain several methods and you only want to implement one of them. For this purpose Eclipse provides empty default implementations for these interfaces. This allows you to implement only the methods you are interested in. These implementations follow the naming convention:

Name*Listener  Name*Adapter

For example, SelectionListener has the abstract class SelectionAdapter which pre-implements the methods of SelectionListener.

If you want to add a listener to the whole application, you can use the Display class. For example, to add a global mouse listener use Display#addFilter(SWT.MouseMove, listener). If you add filters to the Display you may interfere with existing listeners. Ensure to test your code accordingly.

3.7. Colors in SWT

You can define colors in SWT.

Device device = Display.getCurrent ();
Color red = new Color (device, 255, 0, 0);

// Using system colors

Display display = Display.getCurrent();
Color blue = display.getSystemColor(SWT.COLOR_BLUE);
Color listBackground = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);

If you create non system colors, you do NOT need to call dispose() since the Eclipse 2020-06 release.

4. Optional exercise: Use SWT in a standalone Java application

In this exercise you create an plug-in which allows you to create small standalone Java applications using SWT. This can be useful for example for testing SWT widgets.

4.1. Create new plug-in and add dependencies

Create a new plug-in project called com.example.swt.widgets via File  New  Other…​  Plug-in Development  New Plug-in Project.

Enter the data in the wizard similar to the screenshot below. Make sure you deselect the following options:

  • Select No for Would you like to create a rich client application?

  • Uncheck This plug-in will make contributions to the UI

  • Uncheck Generate an activator, a Java class that controls the plug-in’s life cycle

New Plug-in Projec Wizard Page 1
New Plug-in Projec Wizard Page 2

Press the Finish button on the second page of the wizard.

Open the MANIFEST.MF file in the META-INF folder and select the Dependencies tab.

Press the Add…​ button in the Required Plug-ins part and enter org.eclipse.swt as dependency.

Adding the dependency to SWT in the Plug-in project

4.2. Create a standalone SWT application

Create the following class for a simple SWT application.

package com.example.swt.widgets;

import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class FirstSWTApplication {

    public static void main(String[] args) {
        Display display = new Display();

        Shell shell = new Shell(display);

        // the layout manager handle the layout
        // of the widgets in the container
        shell.setLayout(new FillLayout());

        //TODO add some widgets to the Shell
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}

To start your application, right-click on your Java class, and select Run-As  Java Application. You starts your application and show you an empty window.

Change the TODO in the code to the following.

package com.example.swt.widgets;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class FirstSWTApplication {

    public static void main(String[] args) {
        Display display = new Display();

        Shell shell = new Shell(display);

        // the layout manager handle the layout
        // of the widgets in the container
        shell.setLayout(new FillLayout());


        Label label = new Label(shell, SWT.BORDER);
        label.setText("This is a label:");
        label.setToolTipText("This is the tooltip of this label");

        Text text = new Text(shell, SWT.NONE);
        text.setText("This is the text in the text widget");
        text.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
        text.setForeground(display.getSystemColor(SWT.COLOR_WHITE));

        // set widgets size to their preferred size
        text.pack();
        label.pack();


        (1)
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}
1 This is there the TODO comment was

Make sure you use org.eclipse.swt and not java.awt when adding the missing imports.

If you now start the application you will get a Shell with two Widgets included.

First running SWT application

4.3. Add a SWT Button

Add a Button widget to your application and assign a SelectionListener to it. If the button is pressed, write "Called!" to the console.

Button button =  new Button(shell, SWT.PUSH);
button.setText("Press me");

//register listener for the selection event
button.addSelectionListener(new SelectionAdapter() {
    @Override
    public void widgetSelected(SelectionEvent e) {
        System.out.println("Called!");
    }
});

5. Positioning widgets

5.1. Manually placing widgets on a Composite

Controls must be placed in their container. You can manually define their position and size with the setBounds method. This method allows to define the starting position and the width and height. The coordinates start in the top-left corner of the containing Composite.

SWT widget positioning
package com.vogella.swt.widgets.layout;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

public class SWTLayoutPositionTracker {

    static String newLine = System.getProperty("line.separator");
    private static Label positiongLabel;
    private static Shell shell;
    public static void main(String[] args) {
        Display display = new Display();
        shell = new Shell(display);

        positiongLabel = new Label(shell, SWT.BORDER);

        int x= 60;
        int y=20;
        int width =400;
        int height=200;

        positiongLabel.setBounds(x, y, width, height);
        int toolbarSize = 30;

        shell.setBounds(200, 400, width+2*x , height + 2*y +toolbarSize);
        shell.open();



        shell.addMouseMoveListener(e -> showSize(e));
        positiongLabel.addMouseMoveListener(e -> showSize(e));

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

    public static void showSize(MouseEvent e) {
        int x = e.x;
        int y = e.y;
        String s = "Bounds for Label: " + positiongLabel.getBounds() + newLine;
        s+= "Bounds for Shell: " + shell.getBounds()  + newLine;
        s+= "Mouse pointer: " + x + " " + y;
        positiongLabel.setText(s);

    }
}

setBounds defines both position and size. You can also use setLocation and setSize to define the position.

5.2. The role of a layout manager

A layout manager is responsible for arranging the widgets of a container, e.g., a Composite. SWT offers several standard layout managers. The following table gives an overview of them.

Table 1. Layout Manager
Layout Manager Description

AbsoluteLayout

Allows you to specify the exact position, the width and the height of components. As user interfaces may be used on screens with different sizes this layout manager should be avoided.

FillLayout

Arranges equal-sized widgets in a single row or column.

RowLayout

Puts the widgets in rows or columns and allows you to control the layout with options, e.g., wrap, spacing, fill and so on.

GridLayout

Arranges widgets in a grid.

FormLayout

Arranges the widgets with the help of the associated attachments.

StackLayout

This Layout stacks all the controls one on top of the other and resizes all controls to have the same size and location. The control specified in topControl is visible and all other controls are not visible.

If you assign a layout manager to a control you do not have to call pack anymore. The only case in which you need to call pack, is if a control has no layout.

5.3. Layout Data

Each SWT widget can have a layout specific settings class assigned to it, e.g., GridData for a GridLayout. This allows the developer to control the arrangement of the widgets dwithin the layout.

In the following example you specify that a certain widget should take two columns in a GridLayout.

button = new Button(parent, SWT.PUSH);
GridData gridData = new GridData();
gridData.horizontalSpan = 2;
button.setLayoutData(gridData);
The used layout data must match the layout manager, otherwise an exception is thrown at runtime.

The layout will be automatically calculated when the container is displayed. You can tell a Composite to recalculate its layout with the composite.requestLayout() method.

Layout data objects should not be reused as the layout manager expects that every user interface element has a unique layout data object.

5.4. FillLayout

FillLayout divides the available space provided by the container equally to all widgets. It can arrange the widgets either horizontally (SWT.HORIZONTAL) or vertically (SWT.VERTICAL). It also allows you to set the space between the widgets (attribute spacing ) and the margins of the widgets to the container via the marginWidth and marginHeight attributes.

5.5. RowLayout

RowLayout orders UI components in a row (SWT.HORIZONTAL) or in a column (SWT.VERTICAL). RowLayout supports wrapping of fields (field wrap) by default. Via the pack field, you can define if widgets have their preferred size (default) or if they grab the available space. It is also possible to set margins at the top, bottom, left and right. If you set justify, the widgets will be spread through the available space.

Each element can define its height and width via a RowData element.

5.6. GridLayout

5.6.1. Usage

GridLayout allows you to arrange the user interface components in a Grid with a certain number of columns. It is also possible to specify column and row spanning.

You can use new GridData() and assign properties to the new object. Alternatively, you can use one of its richer constructors to define certain attributes during construction. For example, via the following constructor:

new GridData(horizontalAlignment,
    verticalAlignment,
    grabExcessHorizontalSpace,
    grabExcessVerticalSpace,
    horizontalSpan,
    verticalSpan)

The most important attributes are defined in the following table.

Table 2. GridData
Parameter Description

horizontalAlignment

Defines how the control is positioned horizontally within a cell (one of: SWT.LEFT, SWT.CENTER, SWT.RIGHT, or SWT.FILL ).

verticalAlignment

Defines how the control is positioned vertically within a cell (one of: SWT.TOP, SWT.CENTER, SWT.END, SWT.BOTTOM (treated the same as SWT.END), or SWT.FILL ).

grabExcessHorizontalSpace

Defines whether the control is extended by the layout manager to take all the remaining horizontal space.

grabExcessVerticalSpace

Defines whether the control grabs any remaining vertical space.

horizontalSpan

Defines the number of column cells that the control will take up.

verticalSpan

Defines the number of row cells that the control will take up.

heightHint

Defines the preferred height in pixels.

widthHint

Defines the preferred width in pixels.

If the widget has the grabExcessHorizontalSpace attribute set to true, it will grab available space in its container. SWT.FILL tells the widget to fill the available space. Therefore, grabExcessHorizontalSpace and SWT.FILL are often used together.

5.6.2. Using GridDataFactory

The GridDataFactory class can be used to create GridData objects. This class provides a convenient shorthand for creating and initializing GridData. The following listing demonstrates its usage and compares it with the direct usage of GridData.

// listBox is an SWT widget

// GridDataFactory version
GridDataFactory.fillDefaults().grab(true, true).hint(150, 150).applyTo(listBox);

// Equivalent SWT version
GridData listBoxData = new GridData(GridData.FILL_BOTH);
listBoxData.widthHint = 150;
listBoxData.heightHint = 150;
listBoxData.minimumWidth = 1;
listBoxData.minimumHeight = 1;
listBox.setLayoutData(listBoxData);

The Javadoc of this class contains several examples for it.

Unfortunately the SWT Designer does currently not support GridDataFactory, hence the following examples avoid using them.

5.7. FormLayout

FormLayout works by creating FormAttachments for each side of the widget, and storing them in the layout data. The specific side of the widget is attached either to a position in the parent Composite or to another widget within the layout. This allows you to control on a very detailed level the placement of individual widgets within the layout.

5.8. Adding and removing children from a Composite

You can add children at runtime to a Composite. By default, they are placed after the existing controls but you can reposition them via the moveBefore and moveBelow method calls.

To remove a child from a layout, call its dispose method. To update parent composite after you removed or added children, call its requestLayout method.

The following is an example how to dynamically create new rows in a existing layout and how to remove these rows.

package com.vogella.swt.widgets.layout;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;

public class SWTDynamicLayoutChanges {

    static int number = 0;
    private static List<Color> colors;
    private static int colorIndex;

    public static void main(String[] args) {

        Display display = new Display();
        Shell shell = new Shell(display);
        colorIndex = 0;
        colors = new ArrayList<>();
        colors.add(Display.getDefault().getSystemColor(SWT.COLOR_RED));
        colors.add(Display.getDefault().getSystemColor(SWT.COLOR_GREEN));
        colors.add(Display.getDefault().getSystemColor(SWT.COLOR_YELLOW));
        colors.add(Display.getDefault().getSystemColor(SWT.COLOR_CYAN));
        colors.add(new Color (Display.getDefault(), 122, 122, 122));
        colors.add(new Color (Display.getDefault(), 255, 51, 227));
        colors.add(new Color (Display.getDefault(), 27, 82, 255));
        colors.add(new Color (Display.getDefault(), 240, 201, 27));
        colors.add(new Color (Display.getDefault(), 188, 188, 188));
        colors.add(Display.getDefault().getSystemColor(SWT.COLOR_DARK_MAGENTA));


        GridLayout gridLayout = new GridLayout(1, false);
        gridLayout.marginWidth = 0;
        gridLayout.marginHeight = 0;
        gridLayout.verticalSpacing = 0;
        gridLayout.horizontalSpacing = 0;
        shell.setLayout(gridLayout);

        Composite top = new Composite(shell, SWT.NONE);
        GridData d1 = new GridData(SWT.FILL, SWT.FILL, true, true);
        top.setLayoutData(d1);
        top.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_GREEN));

        createLayer(shell);
        createLayer(shell);
        createLayer(shell);

        shell.setBounds(100, 100, 800, 600);
        shell.open();

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

    private static Composite createLayer(Composite parent) {

        Composite layer = new Composite(parent, SWT.NONE);
        layer.setLayout(new FillLayout());
        for (int i = 0; i < 10; i++) {
            Label label = new Label(layer, SWT.NONE);
            label.setText("I go \u26F7");
            label.addMouseListener(new MouseAdapter() {

                @Override
                public void mouseDown(MouseEvent e) {
                    Shell shell =Display.getDefault().getActiveShell();
                    MessageBox dialog =
                            new MessageBox(shell, SWT.ICON_QUESTION | SWT.OK| SWT.CANCEL);
                    dialog.setText("My info");
                    dialog.setMessage("Do you really want to do this?");
                    dialog.open();

                }

            });
        }
        Button removeButton = new Button(layer, SWT.PUSH);
        removeButton.setText("Remove");
        removeButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                layer.dispose();
                parent.requestLayout();
            }
        });

        Button addButton = new Button(layer, SWT.PUSH);
        addButton.setText("Add");
        addButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Composite composite = createLayer(parent);
                composite.moveAbove(addButton.getParent());
                parent.requestLayout();

            }
        });


        GridData d2 = new GridData(SWT.FILL, SWT.TOP, true, false);
        layer.setLayoutData(d2);
        if (colorIndex > colors.size()-1 ) {
            colorIndex = 0;
        }
        layer.setBackground(colors.get(colorIndex++));
        return layer;
    }
}

5.9. Triggering a re-layout of a Composite

As of Eclipse 4.7 you should use the requestLayout() method call on the Composite class, as this triggers an asynchronous layout call.

The Composite widget defines also the layout method which triggers a synchronous layout and should be avoided. This method accepts also child widgets and flags. These flags should rarely be used in client code.

Table 3. Layout flags
Flag Description

SWT.ALL

Flag forces all descendents to be marked for layout. AFAIK, there’s no good reason to ever use this flag. If it’s actually doing something, it could be replaced by a series of calls to requestLayout.

SWT.CHANGED

The flag causes subsequent calls to Layout.layout(…​) to receive true for the second argument, which forces the layout to recursively flush all caches. There’s no good reason to ever do this. If something changed, the layout should have been notified via Layout.flushCache(control) (which is what happens when you invoke requestLayout). This is either a brute-force workaround for a stale layout bug or was just unnecessary. I can’t think of any circumstance in which we should keep SWT.CHANGED.

5.10. Example: Using GridLayout manager

The following shows an example for the usage of the GridLayout class in the com.example.swt.widgets project.

package com.example.swt.widgets.layouts;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;

public class GridLayoutSWT {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);

        // create a new GridLayout with two columns
        // of different size
        GridLayout layout = new GridLayout(2, false);

        // set the layout to the shell
        shell.setLayout(layout);

        // create a label and a button
        Label label = new Label(shell, SWT.NONE);
        label.setText("A label");
        Button button = new Button(shell, SWT.PUSH);
        button.setText("Press Me");

        // create a new label that will span two columns
        label = new Label(shell, SWT.BORDER);
        label.setText("This is a label");
        // create new layout data
        GridData data = new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1);
        label.setLayoutData(data);

        // create a new label which is used as a separator
        label = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);

        // create new layout data
        data = new GridData(SWT.FILL, SWT.TOP, true, false);
        data.horizontalSpan = 2;
        label.setLayoutData(data);

        // creates a push button
        Button b = new Button(shell, SWT.PUSH);
        b.setText("New Button");

        data = new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1);
        b.setLayoutData(data);

         // create a spinner with min value 0 and max value 1000
        Spinner spinner = new Spinner(shell, SWT.READ_ONLY);
        spinner.setMinimum(0);
        spinner.setMaximum(1000);
        spinner.setSelection(500);
        spinner.setIncrement(1);
        spinner.setPageIncrement(100);
        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        gridData.widthHint = SWT.DEFAULT;
        gridData.heightHint = SWT.DEFAULT;
        gridData.horizontalSpan = 2;
        spinner.setLayoutData(gridData);

        Composite composite = new Composite(shell, SWT.BORDER);
        gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        gridData.horizontalSpan = 2;
        composite.setLayoutData(gridData);
        composite.setLayout(new GridLayout(1, false));

        Text txtTest = new Text(composite, SWT.NONE);
        txtTest.setText("Testing");
        gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        txtTest.setLayoutData(gridData);

        Text txtMoreTests = new Text(composite, SWT.NONE);
        txtMoreTests.setText("Another test");

        Group group = new Group(shell, SWT.NONE);
        group.setText("This is my group");
        gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        gridData.horizontalSpan = 2;
        group.setLayoutData(gridData);
        group.setLayout(new RowLayout(SWT.VERTICAL));
        Text txtAnotherTest = new Text(group, SWT.NONE);
        txtAnotherTest.setText("Another test");

        // Children of this widget should get a fixed size
        Composite fixedElements = new Composite(shell, SWT.BORDER);
        gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        gridData.horizontalSpan = 2;
        fixedElements.setLayoutData(gridData);
        fixedElements.setLayout(new GridLayout(2, false));
        Label label2 = new Label(fixedElements, SWT.BORDER);
        GridData layoutData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
        layoutData.widthHint=200;
        label2.setLayoutData(layoutData);
        label2.setText("Fixed");
        Label label3 = new Label(fixedElements, SWT.BORDER);
        GridData layoutData2 = new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
        layoutData2.widthHint=20;
        label3.setLayoutData(layoutData2);
        label3.setText("Small but still fixed");



        shell.pack();
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }

}

Start your application, it should look similar to the following screenshot.

gridswtlayout

Resize the window and see how the arrangement of the widgets change.

5.11. Example: Using GridLayout to assign a fixed ratio to its children

The following is an example in which the first children of the grid layout gets 25% of the available space and the other one 75%. As grid layout only supports a fixed size, we have to register a resize listener on its container.

package com.example.swt.widgets.layouts;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class GridLayoutWithFixedColumnDistribution {
    public static void main(String[] args)
    {
        Display display = Display.getDefault();
        Shell shell = new Shell(display);
        shell.setLayout(new GridLayout(2, false));

        Composite left = new Composite(shell, SWT.BORDER);
        Composite right = new Composite(shell, SWT.BORDER);

        GridData leftData = new GridData(SWT.FILL, SWT.FILL, true, true);
        GridData rightData = new GridData(SWT.FILL, SWT.FILL, true, true);

        left.setLayoutData(leftData);
        right.setLayoutData(rightData);

        shell.addListener(SWT.Resize, arg0 -> {
            Point size = shell.getSize();

            leftData.widthHint = (int) (size.x * 0.75);
            rightData.widthHint = size.x - leftData.widthHint;

            System.out.println(leftData.widthHint + " + " + rightData.widthHint + " = " + size.x);
        });

        shell.open();
        shell.requestLayout();

        while (!shell.isDisposed())
        {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }
}

6. Other SWT examples

The following code snippets provide examples on how to use the SWT. It is not expected that you read through them, they are only provided as reference in case you need an code example during coding.

6.1. Tab order of elements

The tab order is the order in which elements are selected when switching between them with Tab. You can specify the tab order of controls via the setTabList() method of a Composite. For this you provide an array of the controls where the order of the controls in the array specify the tab order.

package com.example.swt.widgets


import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class TabExample {
    public static void main(String[] args) {
        Display display = Display.getDefault();
        Shell shell = new Shell(display);
        shell.setLayout(new RowLayout());
        Button b1 = new Button(shell, SWT.PUSH);
        b1.setText("Button1");
        Button b2 = new Button(shell, SWT.PUSH);
        b2.setText("Button2");
        Button b3 = new Button(shell, SWT.PUSH);
        b3.setText("Button3");

        Control[] controls = new Control[] { b2, b1, b3 };
        shell.setTabList(controls);
        shell.pack();
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}
Defining a tab order for your controls is important to increase the accessibility and to allow advanced users a quick navigation.

6.2. DateTime widget

The following code demonstrates the usage of the DateTime widget.

package de.vogella.swt.widgets;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DateTime;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class DateTimeExample {

    public static void main(String[] args) {

        // setup the SWT window
        Display display = new Display();
        final Shell shell = new Shell(display);
        shell.setLayout(new RowLayout());

        // initialize a parent composite with a grid layout manager
        Composite parent = new Composite(shell, SWT.NONE);
        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 1;
        parent.setLayout(gridLayout);
        DateTime calendar = new DateTime(parent, SWT.CALENDAR);
        DateTime date = new DateTime(parent, SWT.DATE);
        DateTime time = new DateTime(parent, SWT.TIME);
        // Date Selection as a drop-down
        DateTime dateD = new DateTime(parent, SWT.DATE | SWT.DROP_DOWN);

        shell.pack();
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        // tear down the SWT window
        display.dispose();
    }
}
Date Time Widget

Please note that the SWT DateTime widget requires a selection, i.e. you cannot use this widget without a date selection.

6.3. Image

Images can be displayed via labels. The following class demonstrates that using some system images.

package de.vogella.swt.widgets;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

public class Photo {

    public static void main(String[] args) {

        // setup the SWT window
        Display display = new Display();
        final Shell shell = new Shell(display);
        shell.setLayout(new RowLayout());
        shell.setText("Photo Application");

        // initialize a parent composite with a grid layout manager
        // with 5x columns
        Composite parent = new Composite(shell, SWT.NONE);
        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 5;
        parent.setLayout(gridLayout);

        // Get the Display default icons
        List<Image> imageList = new ArrayList<Image>();

        imageList.add(Display.getDefault().getSystemImage(SWT.ICON_WARNING));
        imageList.add(Display.getDefault().getSystemImage(SWT.ICON_WORKING));
        imageList.add(Display.getDefault().getSystemImage(SWT.ICON_QUESTION));
        imageList.add(Display.getDefault().getSystemImage(SWT.ICON_INFORMATION));
        imageList.add(Display.getDefault().getSystemImage(SWT.ICON_ERROR));

        for (Image image : imageList) {
            Label label = new Label(parent, SWT.NONE);
            label.setImage(image);
        }
        // show the SWT window

        shell.pack();
        shell.open();

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        // tear down the SWT window
        display.dispose();
        // if you do not use system images you would have to release them
        // not necessary in this example
        // for (Image image : imageList) {
        //    if (image != null) {
        //     image.dispose();
        //    }
        //    }
    }
}

The result should look similar to the following.

swtimages10

You can also load your own images from disk, just remember to dispose them later. The code assumes that you have a file named my_image.png in a sub folder of your current package called images.

new Image(getDisplay(), getClass().getResourceAsStream("images/my_image.png"));

6.4. Table

Create the following class.

package de.vogella.swt.table;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

public class SWTTable {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);

        shell.setLayout(new GridLayout());


        Table table = new Table(shell, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION);
        table.setLinesVisible(true);
        table.setHeaderVisible(true);
        GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
        data.heightHint = 200;
        table.setLayoutData(data);

        String[] titles = { "First Name", "Last Name", "Age" };
        for (int i = 0; i < titles.length; i++) {
            TableColumn column = new TableColumn(table, SWT.NONE);
            column.setText(titles[i]);
            table.getColumn(i).pack();
        }

        for (int i = 0 ; i<= 50 ; i++){
            TableItem item = new TableItem(table, SWT.NONE);
            item.setText (0, "Person " +i );
            item.setText (1, "LastName " +i );
            item.setText (2, String.valueOf(i));
        }

        for (int i=0; i<titles.length; i++) {
            table.getColumn (i).pack ();
        }
        shell.pack ();
        shell.open ();

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }
}

If you run this application a table will be displayed.

6.5. Tree and Menu

Create the following class.

package de.vogella.swt.widgets;

import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

public class TreeMenuTest {
    public static void main(String[] args) {
        Display display = Display.getDefault();
        Shell shell = new Shell(display);
        shell.setLayout(new FillLayout());
        final Tree tree = new Tree(shell, SWT.V_SCROLL);
        for (int i = 0; i < 5; i++) {
            TreeItem item = new TreeItem(tree, SWT.NONE);
            item.setText(String.valueOf(i));
            for (int j = 0; j < 3; j++) {
                TreeItem subItem = new TreeItem(item, SWT.NONE);
                subItem.setText(String.valueOf(i) + " " + String.valueOf(j));
            }
        }
        tree.pack();
        Menu menu = new Menu(tree);
        MenuItem menuItem = new MenuItem(menu, SWT.NONE);
        menuItem.setText("Print Element");

        menuItem.addSelectionListener(widgetSelectedAdapter( e->  System.out.println(tree.getSelection()[0].getText())));

        tree.setMenu(menu);
        shell.setSize(400, 200);
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }
}

If you run this application a tree will be display. Right-click on one of the items to display the context menu.

swt tree menu

If you select the menu the text of the selected item will be printed to the console.

6.6. CTabFolder

CTabFolder and CTabItem allow to create the folders with tabs. Compared with TabFolder and TabItem they allow to set their background color.

package com.vogella.swt.widgets.ctabfolder;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class CTabFolderExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);

        shell.setLayout(new GridLayout());
        // SWT.BOTTOM to show at the bottom
        CTabFolder folder = new CTabFolder(shell, SWT.BOTTOM);
        GridData data = new GridData(SWT.FILL,
                SWT.FILL, true, true,
                2, 1);
        folder.setLayoutData(data);
        folder.setSelectionForeground(display.getSystemColor(SWT.COLOR_RED));
        CTabItem cTabItem1 = new CTabItem(folder, SWT.NONE);
        cTabItem1.setText("Tab1");
        CTabItem cTabItem2 = new CTabItem(folder, SWT.NONE);
        cTabItem2.setText("Tab2");
        CTabItem cTabItem3 = new CTabItem(folder, SWT.NONE);
        cTabItem3.setText("Tab3");

        Text text = new Text(folder, SWT.BORDER);
        text.setText("Hello");
        cTabItem1.setControl(text);

        shell.pack();
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }
}

If you run this application you will see several register cards with tabs on them. The SWT.BOTTOM stylebit on the folder widget, makes the tab appear at the bottom.

ctabfolder10

6.7. Keybindings for SWT widgets

You can add KeyListener to a SWT widgets via the addKeyListener method. This method receives an KeyEvent object. The e object provides for example:

  • e.character: which char was pressed

  • e.stateMask: which modifier keys were pressed (CTRL, ALT, SHIFT)

  • keyCode: integer representation of the key based on constants define in the SWT class.

The following source code demonstrates how to register a KeyListener with an SWT widget and how to evaluate the event.

package com.vogella.swt.widgets;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SWTKeyEvent {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("KeyListener Example");

        shell.setLayout(new GridLayout());

        Button button = new Button(shell, SWT.CENTER);

        button.setText("Type here and use optionally meta keys");

        button.addKeyListener(KeyListener.keyPressedAdapter(e-> handleKeyEvent(e)));

        shell.pack();
        shell.open();

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

    private static void handleKeyEvent(KeyEvent e) {
        String string = "";

        // check click together?
        if ((e.stateMask & SWT.ALT) != 0)
            string += "ALT ";
        if ((e.stateMask & SWT.CTRL) != 0)
            string += "CTRL ";
        if ((e.stateMask & SWT.SHIFT) != 0)
            string += "SHIFT ";

        if (e.keyCode == SWT.BS) {
            string += "BACKSPACE ";
        }

        if (e.keyCode == SWT.ESC) {
            string += "ESCAPE ";
        }

        // check characters (digits are from 48 to 57)
        if ((e.keyCode >= 97 && e.keyCode <= 122) || (e.keyCode >= 48 && e.keyCode <= 57)) {
            string += " " + e.character + " - keyCode = " + e.keyCode;
        }

        if (!string.equals("")) {
            System.out.println(string);
        }
    }

}

If you run this application, move the cursor over the button widget and type you should see the output in the Console view.

swt keylistener

6.8. Using filter on the Display

You can use the 'Display#addFilter' method to filter globally for events as long as your application has focus. The following code demonstrates this.

package de.vogella.swt.listener;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

public class GlobalListener {

    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.open();
        display.addFilter(SWT.KeyDown, new Listener() {
            @Override
            public void handleEvent(Event event) {
                char c = event.character;
                System.out.println(c);
            }
        });

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

6.9. Fonts in SWT

You can change the Font of text in SWT. You do this by creating a Font and then applying it to the desired control.

Label label = new Label(parent, SWT.NONE);
Font font = new Font(label.getDisplay(), new FontData("Mono", 10, SWT.ITALIC));
label.setFont(font);
label.setText("Some text");

To modify the current Font you can retrieve the FontData, change it and create a new Font from it.

Label label = new Label(parent, SWT.NONE);
FontData fontData = label.getFont().getFontData()[0];
// we can change this since getFontData() creates a new FontData instance
fontData.setStyle(SWT.ITALIC);
label.setFont(new Font(label.getDisplay(), fontData));

You can roll out and use custom fonts for your application. To use a custom font you have to load it with the Display.

boolean fontLoaded = Display.getDefault().loadFont("fonts/CustomFont.ttf");
if (fontLoaded) {
    Font font = new Font(Display.getDefault(), "Custom Font", 12, SWT.NORMAL);
    label.setFont(font);
}
Please remember that Fonts don’t get automatically disposed with the widget that uses them.

6.10. Unicode in SWT

SWT supports the display of Unicode characters. Depending on your OS and default font you might have to change the font of your control to a font that supports Unicode characters.

Label label = new Label(parent, SWT.NONE);
label.setFont(new Font(Display.getCurrent(), "Arial Unicode MS", 12, SWT.NORMAL));
label.setText("Follow your \u2764!");

7. SWT builder API via JFace

The JFace library adds builder API to SWT widgets to provide a concise and modern API for building SWT based user interfaces.

The following is an example for using the ButtonFactory to define a new button.

import static org.eclipse.jface.widgets.ButtonFactory.newButton;
....

newButton(SWT.PUSH).text("Press me").onSelect(e-> System.out.print("Testing")).create(shell);

8. SWT snippets and examples

The SWT Snippets are examples for stand-alone SWT applications using different kinds of SWT widgets.

You can copy these snippets and paste them directly into a project with the dependency to SWT inside of the Eclipse IDE. The snippet contains a main method which allows it be started.

You can paste classes from the clipboard onto a project or package. Eclipse automatically creates the Java class based on the content of the clipboard for you.

The SWT Examples page provides useful programs that are written in SWT. These are typically much larger and more comprehensive than the SWT Snippets.

9. The Nebula widgets

The Eclipse Nebula project provides additional widgets for SWT. For example, it provides several visualization widgets as depicted in the following screenshot.

Nebula visualization widgets

Additional widgets can for example also be found here: https://github.com/Haixing-Hu/swt-widgets/

10. Learn more and get support

This tutorial continues on Eclipse RCP online training with lots of video material, additional exercises and much more content.

11. Eclipse SWT resources

SWT Widgets - Webpage showing all SWT widgets with links to examples and Javadoc

SWT Snippets - Short examples demonstrating the usage of SWT API

YARI tool suite - Tools to debug, spy, spider, inspect and navigate Eclipse based application GUIs (Workbench or RCP).

SWT Development Tools - Tools include Sleak (analyze memory leaks in SWT graphic resources) and SWT Spy plug-in which prints out the info about the SWT widget under the cursor.