With SWT, tab folders are a way to integrate in a same container several widgets. However, it appears to be quite limited in terms of design and creativity. I here propose some alternatives for sexier user interfaces.
Tab Folder
This is our witness section.
Here is a snippet (the full code is avilable at the end of the article)…
public void createTabs( Composite parent ) {
final TabFolder tabFolder = new TabFolder( parent, SWT.NONE );
tabFolder.setLayoutData( new GridData( GridData.FILL_BOTH ));
for( int i=0; i<=4; i++) {
TabItem tabItem = new TabItem( tabFolder, SWT.NONE );
tabItem.setText( "Item " + i );
tabItem.setControl( createTabContent( tabFolder ));
}
}
private Composite createTabContent( Composite parent ) {
Composite c = new Composite( parent, SWT.NONE );
c.setLayout( new GridLayout( 2, false ));
String[] properties = { "First Name:", "Last Name:", "Nick Name:" };
for( String property : properties ) {
new Label( c, SWT.NONE ).setText( property );
new Text( c, SWT.SINGLE | SWT.BORDER ).setLayoutData( new GridData( GridData.FILL_HORIZONTAL ));
}
return c;
}
And here is a screenshot.

As you can see, everything is centered, except the tabs.
And that’s not cool at all.
Custom Tab Folder
The CTabFolder class is a little more powerful, but similar limitations appear quickly. It is not possible to center the tabs. And they are either above or below the content, never on side.

public void createTabs( Composite parent ) {
final CTabFolder tabFolder = new CTabFolder( parent, SWT.BORDER );
tabFolder.setLayoutData( new GridData( GridData.FILL_BOTH ));
tabFolder.setSimple( false );
for( int i=0; i<=4; i++ ) {
CTabItem tabItem = new CTabItem( tabFolder, SWT.NONE );
tabItem.setText( " Item " + i + " " );
tabItem.setControl( createTabContent( tabFolder ));
}
}
Note that you can add images on the tab.
Scrolled Page Book
An alternative to these classes is ScrolledPageBook.
It can be used in Eclipse plugins, but also in standalone applications. In addition to SWT bundles, you only have to add org.eclipse.ui.forms to your classpath. No need to be in a form to use it.
Let’s see a first illustration, with navigation labels and not (yet) tabs.
The principle is simple: you have a set of labels and a scrolled page book. When the user clicks on a label, we change the displayed page in the book.
public void createTabs( Composite parent ) {
// Add the container for navigation labels
final int tabCpt = 5;
Composite container = new Composite( parent, SWT.NONE );
container.setLayout( new GridLayout( tabCpt, true ));
container.setLayoutData( new GridData( SWT.CENTER, SWT.DEFAULT, true, false ));
// Add the page book
final ScrolledPageBook pageBook = new ScrolledPageBook( parent );
pageBook.setLayoutData( new GridData( GridData.FILL_BOTH ));
// The listener when the user clicks on a "tab"
Listener listener = new Listener() {
@Override
public void handleEvent( Event event ) {
pageBook.showPage( event.widget.getData());
}
};
// Register the pages and bind it all
for( int i=0; i<=4; i++ ) {
Label l = new Label( container, SWT.BORDER );
l.setText( "Item " + i );
l.setData( i );
pageBook.registerPage( i, createTabContent( pageBook.getContainer(), i ));
l.addListener( SWT.MouseDown, listener );
}
// Force to display the first tab
pageBook.showPage( 0 );
}
And here is a preview.

Ugly, isn’t it?
But we can fix it.
Tabs with a Scrolled Page Book
To have a real tab’s look & feel, we have to reduce the margin between the content and the labels. We also need make our labels look better. Partial borders will be added around the labels thanks to a paint listener.

Here is the code (it’s a little bit longer than before).
/**
* Remember the selected index.
*/
private Integer selectedIndex = 0;
/**
* Creates the tabs.
* @param parent the parent
*/
public void createTabs( Composite parent ) {
// Add the container for navigation labels
final int tabCpt = 5;
Composite container = new Composite( parent, SWT.NONE );
GridLayout layout = new GridLayout( tabCpt, true );
layout.marginHeight = 0;
container.setLayout( layout );
container.setLayoutData( new GridData( SWT.CENTER, SWT.DEFAULT, true, false ));
// Add the page book
final ScrolledPageBook pageBook = new ScrolledPageBook( parent );
pageBook.setLayoutData( new GridData( GridData.FILL_BOTH ));
// The listener when the user clicks on a "tab"
final List<Label> navigationLabels = new ArrayList<Label> ();
Listener listener = new Listener() {
@Override
public void handleEvent( Event event ) {
pageBook.showPage( event.widget.getData());
// Remember the last selected index
TabsWithScrolledPageBookSnippet.this.selectedIndex = (Integer) event.widget.getData();
// Highlight the selected tab
for( Label l : navigationLabels ) {
Color color;
if( l != event.widget && l.getParent() != event.widget )
color = Display.getDefault().getSystemColor( SWT.COLOR_WIDGET_BACKGROUND );
else
color = Display.getDefault().getSystemColor( SWT.COLOR_WHITE );
l.setBackground( color );
l.getParent().setBackground( color );
}
}
};
// The paint listener, to paint partial borders
PaintListener paintListener = new PaintListener() {
@Override
public void paintControl( PaintEvent event ) {
Color color;
if( TabsWithScrolledPageBookSnippet.this.selectedIndex.equals( event.widget.getData()))
color = Display.getDefault().getSystemColor( SWT.COLOR_GRAY );
else
color = Display.getDefault().getSystemColor( SWT.COLOR_WIDGET_DARK_SHADOW );
Rectangle rect = ((Composite) event.widget).getBounds();
event.gc.setForeground( color );
event.gc.setAntialias( SWT.ON );
event.gc.drawLine( 0, 0, rect.width - 1, 0 );
event.gc.drawLine( 0, 0, 0, rect.height - 1 );
event.gc.drawLine( rect.width - 1, 0, rect.width - 1, rect.height - 1 );
}
};
// Register the pages and bind it all
for( int i=0; i<tabCpt; i++ ) {
Label l = createTabLabel( i, container, paintListener, listener );
navigationLabels.add( l );
pageBook.registerPage( i, createTabContent( pageBook.getContainer(), i ));
}
// Force to display the first tab (and force it to be highlighted)
navigationLabels.get( 0 ).notifyListeners( SWT.MouseDown, new Event());
}
/**
* Creates a label for the tab (wrapped in a composite for better display).
* @param index the tab index
* @param parent the container for the label
* @param paintListener the paint listener for the label's container (to paint partial borders)
* @param mouseDownListener the listener for when a tab is selected
* @return the created label
*/
private Label createTabLabel( int index, Composite parent, PaintListener paintListener, Listener mouseDownListener ) {
// Wrap the labels in a composite
Composite c = new Composite( parent, SWT.NONE );
c.setLayout( new GridLayout());
c.setLayoutData( new GridData( 80, 25 ));
c.setData( index );
// To paint partial borders
c.addPaintListener( paintListener );
// Deal with the content
Label l = new Label( c, SWT.NONE );
l.setLayoutData( new GridData( SWT.CENTER, SWT.CENTER, true, true ));
l.setText( "Item " + index );
l.setData( index );
// The click listener
l.addListener( SWT.MouseDown, mouseDownListener );
c.addListener( SWT.MouseDown, mouseDownListener );
return l;
}
Horizontal Tabs
By playing with the previous example, we can also obtain a result similar to the Eclipse tabbed properties. In the following example, the navigation labels are placed on the left of the content. I hardly changed the code (about 10 lines, it is just about the layout).

The code is available in the archive below.
Conclusion
I hope these snippets (and screenshots) will help some of you.
They also illustrate the use of the Scrolled Page Book class. Obviously, this could be used to create a custom and reusable widget (as an example shared in the Nebula project). No need to say that it would be more simple with a HTML and CSS approach. E4 may help in such an approach.
The complete source code for the snippets is available on GitHub.