Getting started with SWT


April 2004

download source

Confessions of the occasional GUI builder

Like many Java programmers, I occasionally get to write GUI code. It's a bit more artistic than server-sided code because you get to watch this intriguing visual world emerge. I've written GUIs in about a half dozen languages over the last 15 years, starting with the old DOS implementation of FoxPro (before Microsoft got a hold of it...), Visual Basic, some unusual attempts in Visual C++, Python/Jython, Java AWT and Java Swing.

Lately, I've taken up SWT, the open-source Software Windowing Toolkit that drives Eclipse and which is creating rave reviews and great claims about the future.

Like the Java AWT, SWT is a relatively thin layer over native operating system windowing APIs. Unlike AWT, it is a much more direct implementation of Java calls to native windowing APIs. The concept of AWT "peer" objects are missing in SWT. As well, it seems quite a bit faster to display and react than typical AWT GUIs. Indeed -- despite some complaints from other OS users -- the Windows implementation behaves almost identical to native Windows applications.

For all its benchmarks to Windows, SWT is intended to be as portable as AWT or Swing. The downside for SWT is that each port requires JNI calls to windowing APIS in each OS graphical toolkit. Since SWT is open-source, sometimes its portability has been called into question. For example, porting SWT to the MAC OS X is in its early stages at the time of this writing.

A noteworthy aspect to SWT's current state is that its widget set is miles ahead of Swing. For example, if you have compared the Tree control to Swing's JTree, you will be pleasantly surprised. It is easy to code and it looks great. SWT version 3 contains an HTML browser that is truly remarkable, something that has been missing from Swing for a long time. The usual widgets like labels, text boxes, combos are much snappier to paint and react than Swing widgets.

How do you download and install it?

Go to the SWT Resources and Links page to find the latest download. For this article, I used Eclipse 3.0 M6 which contained 2 DLL files and the swt.jar.

To compile or run any example from the SWT site, you need to invoke java with a VM argument -Djava.libary.path=DIRECTORY_WHERE_YOU_DROPPED_THE_SWT_DLLs. Of course, swt.jar needs to be in the java classpath.

Running from Eclipse

If you are trying to run the examples from inside Eclipse, I should mention that I ran into problems trying to piece together the DLLs and jars out of the Eclipse plug-in directories. Better you should download the SWT 3.0 separately. Set your project build path to the swt.jar in those directories. If you need to reference jface.jar (another package which I will not discuss here...), however, you may need to find it in your plug-ins directories since it wasn't in the SWT 3.0 download.



When you have compiled your files, you will need to modify a section of the Argument tab in the Run properties of the Java program you are trying to run. Set the VM arguments to something like -Djava.library.path="C:\swt".

The Eclipse download site has some links to snippets that you can compile and run. One of them features the HTML Browser I talked about earlier. Once you see the SWT browser snippet, I think you will be suitably impressed.

Grid Puzzle, history and use

I've coded the Grid Puzzle in a couple of other languages. The games is simple: you start with a set of 16 buttons. One button is empty and the rest contain of a jumble of numbers betweer 1 and 15. The object is to move the Buttons so that the numbers are sequenced correctly.

Grid Puzzle is a good example of what you should be able to do with buttons, captions, some randomizing logic and simple event handlers. For this article, it allows me to compare a SWT GridLayout to a similar Swing GridLayout. As well, the simple button clicks show how very similar the SWT Event model is to AWT's.



a classic puzzle

Some SWT Basics

Before we get started with the GridPuzzle, we need a minimal subset of SWT information. These are:

  • Display. Contained in the org.eclipse.swt.widgets package, Display is the connector between the SWT classes and the underlying operating system. For this reason, it tends to be the first thing to be instantiated and you seldom reference it again, except in the event loop that checks to see that the objects created in a SWT application are "disposed of". It has the appearance of an evil detail that should be abstracted out (and is...somewhat... using JFace). For the purpose of this discussion, you might remember it for fetching all those click events back to your application.
  • Shell. The top-level window in an application. You can have dialog boxes in an application that represent secondary shells, of course. You might want to think of it as a "window" that the OS Window Manager might control. You should also remember that it is a subclass of Widget, Controll, Scrollable, Composite, Canvas and Decorations.
  • Widgets and Controls. Generally, you think of these as gadgets like Text Boxes, Combos and Labels that you want to place on a Shell. However, when you look at the Class hierarchy you notice that Control extends Widget. And the Eclipse documentation suggests that you should think of Controls as those Text Boxes, Combos and so on, while Widgets are more specialized "windows" that exist only with certain controls.
  • GridLayout. If you are familiar with Swing/AWT, you undertand how important Layouts are to the world of Java. SWT keeps that contract. There are several layouts, including FillLayout (which is pretty much a FlowLayout in AWT), a RowLayout which behaves like FillLayout on multiple rows. You need to understand here that a GridLayout allows you to specify the number of Controls you would like to place on a row. Once you do that, the layout area becomes a grid and each controls fits one slot in the grid.
  • Events. SWT events are very, very much like AWT events. You instantiate a listener object and add it to a Control's Event Listener list. When something like a click or a selection or a focus occurs within the application, the listeners registered with the Control are notified. While there are many Events within SWT, for the purpose of this article, you need only worry about the SelectionEvent which happens when a user clicks on one of the buttons in GridPuzzle.

    Basic window population

    The minimum required code to get a main window to appear

    	Display display = new Display();
    
    	Shell shell = new Shell(display);
    	shell.open ();
    	while (!shell.isDisposed ()) {
    		if (!display.readAndDispatch ()) display.sleep (); 
    	} 
    	display.dispose ();
    

    The interplay between the Display and Shell indicate a close relationship between the underlying Operating System and the Application Window in this most basic SWT application. Each Control you wish to place in the application window is passed a reference to the shell in its constructor.

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

    GridPuzzle gets a layout

    While it is easy enough to add Controls to the display, the Layout classes are essential to getting the controls to display in an orderly fashion. For the GridPuzzle the requirement was to display more or less square buttons in a 4 by 4 grid. This code is all that's necessary to achieve the grid look:

    		GridLayout gridLayout = new GridLayout();
    		gridLayout.numColumns = 4;
    		gridLayout.verticalSpacing=0;
    		gridLayout.horizontalSpacing=0;
    		shell.setLayout(gridLayout);
    

    Easy enough... However as you add these buttons to the Shell, you discover that their width and height are defined primarily by their captions. Efforts to call the setSize() method of the Button appear to have no affect and the result is a misshapen hodge podge of buttons.



    GridLayout without GridData

    The answer for the layout is to pass a GridData object to the Button's setLayout() method.

    	button.setLayoutData(new GridData(GridData.FILL_BOTH));
    

    Since there are 16 buttons, I figured for the sake of saving resources, I would reuse a single GridData object for the entire 16. However, this doesn't work! You need to pass a new object to each button.

    for (int i = 0; i < 16; i++){
    	Button button = new Button (shell, SWT.PUSH);
    	button.setFont( font);
    	button.setText( this.getRandomCaption(i) );
    	if (button.getText().equals("0")) button.setText("");
    	button.addSelectionListener( this);
    	button.setLayoutData(new GridData(GridData.FILL_BOTH));
    	button.pack();
    	buttons[i]=button;
    }
    

    The GridData class is designed to allow alignment and fill specifications within the Grid. You can either pass these in the constructor

    new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
    

    or you can define them through public fields:

         	GridData gridData = new GridData();
    	gridData.horizontalAlignment = GridData.END; //right-aligned effect
            gridData.grabExcessHorizontalSpace = true;   //expand on window resize
            button.setLayoutData(gridData);
    

    Some nasty things about fonts

    I really wanted to make the Button captions a little more readable by increasing the size of its font. Delving into the SWT Javadocs (which are, by the way, very well put together... and, with the current state of SWT information, you tend to spend a lot of time in the Javadocs), you discover two things about fonts:

  • Font are OS resources that you must invoke dispose() on when you are finished with them. The key to this pattern of Application vs. OS resource seems to be the constructor:
  • Font font = new Font(display,fd);
    

    Buttons pass a reference to the Shell, but Font uses a Display object in the constructor.

  • The other piece of information in the Font constructor (note the second parameter...) is a FontData object. This data object contain all info about height, name and style. The thing to note is that on different OSes you can get either one FontData object per Font (as in Windows....) or many FontDatas (as with XWindows). Therefore a Font's getFontData() method returns an array.

    To tie the whole thing together, you can start with this code:

    
    	//create a larger font for the buttons
    	FontData[] fd = shell.getFont().getFontData();
    		
    	for (int i = 0; i < fd.length; i++) {
    		fd[i].setHeight(16);
    	}
    
    	Font font = new Font(display,fd);
    

    Then later on, you set it for each Button in the Grid.

    button.setFont( font);
    

    Unlike the GridData class and mercifully for System resources, you can pass the same font object into each of the 16 buttons.

    Event Model...a pleasant surprise

    Sometimes coding in SWT seems like a total departure from either Swing or AWT. But the Event model is surprisingly close to AWT. You define Listeners and you add these listeners to each Control that captures user (or other actor) activities in the form of clicks, keypresses and so on.

    Although most of the SWT examples define Listeners as inner classes, I opted for the old Swing trick of making the Main class implement one of the Listener interfaces. In this case, the Listener I used was SelectionListener. In Swing, the JButton class will add an ActionListener, but the differences between Swing and SWT end there.

    Like Swing, there is an Adapter class -- SelectionAdapter -- that provides a "do nothing" implementation. You can subclass SelectionAdapter and implement any of the two methods it defines. However, since SelectionListener defines only two methods -- and partly because I wasn't sure which of the two to implement -- I decided to implement SelectionListener in the GridPuzzle class.

    It didn't take long to figure out that when I clicked on a button, the event that fired off was widgetSelected()

    public void widgetSelected(SelectionEvent event){
    	if (event.getSource().getClass().equals(Button.class)){
    		Button button = (Button) event.getSource();
    		if (button.getText().equals("") ){
    			Toolkit.getDefaultToolkit().beep();
    		} else {
    			this.swapPlaces(((Integer) button.getData()).intValue());	
    		}
    	}		
    }	
    	
    public void widgetDefaultSelected(SelectionEvent event){
    	System.out.println("Widget was defaultselected: " + event.getSource());	
    }
    

    The mechanics of getting at the cause of the event remains just as in Swing. You make a call to the event's getSource() method, normally casting it to the preferred Control, and you are in business. In the code above, I was trying to beep, if someone clicked on a captionless Button, or swap places with any adjacent, blank-captioned button.

    Concluding comments

    I'm kind of impressed with SWT from the point of view of its speed and relative ease of development. I could remark that after 15 years, GUI programming seems to have gotten more difficult, not easier... but that would be complaining. One thing that really impresses me about SWT is that there may be an opportunity for some of us Java gurus to take a break from coding on the server and migrate to client side. Wouldn't that be a switch?

    Resources

    I'm going to provide some good resources here, but you need to remember that, at the moment, SWT isn't the most widely- documented technology. For all the thousands of pages on Javascript and Jsps, you'd thing it would be easy to find something about something as interesting as SWT. Happy googling!

    The SWT javadocs are a great place to get started.
    The Eclipse manual has a good chapter on SWT and another on JFace.
    The eclipse site has a great SWT section with snippets you should check out, a great FAQ and other resources.
    Migrate your apps to SWT provides a neat tutorial on migrating from Swing to SWT.
    Comparing SWT and Swing is a popular sport. This article from developer.com is an honest comparison.
    Looking for a book? So am I. There are apparently two books coming out soon. Try either Manning or Apress. Both publishing houses will soon feature SWT titles.
    Copyright (c)2004 Gervase Gallant gervasegallant@yahoo.com