User Tools

Site Tools


programming:java:swt

SWT

Literature
Info Location
0. SWT Tutorial vogella.com
1. SWT is made up of widgets, which are listed SWT Widget Pics
SWT Code Snippets
Larger examples of SWT Widgets
2. Google Window builder is a tool for building SWT UI. Google Window Builder documentation
3. Using SWT Layouts Using Layouts
4. Creating own SWT Widgets Own SWT Widgets
5. JFace framework provides higher level Widgets vogella.com
6. RCP Widgets like analogue clock http://www.richclientgui.com
7. Forms API like sections and expandables https://eclipse.org/articles/Article-Forms/article.html
Additonal SWT Widget libs
What Location
The Eclipse Nebula Project provides custom SWT widgets Nebula
The google code Opal Project provides many widget too Opal
JFace Widgets extens SWT, simplify the Widgets usage but do not hide the native SWT API (Git and CVS unfortunately offline :( ) JFace Snippets
Tools
What Location
Window Builder is a WYSIWYG GUI Editor Window Builder Tutorials
Snippters
Describtion Link
The Snippet to insert Widgets into the table http://www.java2s.com

Fallpits

  • Layout: when adding children to a composite, controlled by a layout initiate the children first. After that you can add LayoutData. Otherwise the children, which depend on a not yet initialized children - won't show up. When doing a composite layout:
    1.) INITIATE CHILDREN
    2.) SET THE LAYOUT INTERDEPENDENCIES
  • Look out for the right ordering when using the JFace IWorkbenchAdapter Pattern

Eclipse IDE Settings

Configure Exlipse to import SWT-Widgets instead AWT-Widgets on STRG+SHIFT+O
by excluding the awt package from autoimports.

SWT Basics

Glossar

  • org.eclipse.swt.widgets.Shell - represents a Window
  • org.eclipse.swt.widgets.Display - manages event loops. The thread which created the display becomes the UI thread. In most applications there is only one UI thread and so, onlny one Display allowed.
    Display object controls communication between UI thread and other threads. The main Shell gets as a default parameter a Display as a constructor argument.
    Each Shell belongs to a Display. One Display can have multiple Shells.
  • Event loop - SWT does not provide event loop handline. The eventloop has to be implemented manually. Just in RCP the eventloop is provided by the application:
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.open();
    // Create and check the event loop
    while (!shell.isDisposed()) {
    	if (!display.readAndDispatch())
    		display.sleep();
    }
    display.dispose();
    
  • Group - A widget container with a border and a header.
  • *Listener - For every *listener there exists an abstract class *adapter, which preimplements this interface and can be extended to overload the needed methods. FocusListener has FocusAdapter. SelectionListener has SelectionAdapter.
    To listen events for the whole application - listen the add EventListeners to the Display.class
  • Composite - A widget container. Composite is the class, which self implemented widgets will inherit from.
  • ClientArea - the part of widget container, where the content is inserted.
    www.eclipse.org_articles_article-understanding-layouts_images_generalterms.jpg
  • trim - total container height - ClientArea height. So its the rest height, which is not occumied by children (e.g Tab area on Tab Widgets.)
  • preferred widget-size - size of a widget, needed to show it#s content. .pack() method sets the widget to preferredx size. Default size is 0.

Basics

  1. SWT widgets are located in the packages org.eclipse.swt.widgets and org.eclipse.swt.custom.
    Widgets extend as base class either the Widget or the Control class.
  2. Every custom widget must extend the Composite or Canvas class. Only for these base classes, API compliance is guaranteed.
  3. Widgets have to be manually disposed by calling
    dispose()

    . Only after that they will be garbage collected.
    Shell calls dispose() on its widgets automagically

  4. The SWT widgets change their appearance, depending on the stylebit, which is passed during the construction. The stylebits are documented in the JavaDoc.
     
    new Button(shell, SWT.PUSH); //pushbutton
    new Button(shell, SWT.CHECK); //checkbutton
    

Widgets

Shell = Windows

Shell can be created with different parameters:

 new Shell(); new Shell(Shell parent, SWT.NONE|SWT.APLICATION_MODAL...); 

  • Child-Shell is always “on top” of it's parent.
  • Child Shell minimizes together with it's parent
  • APPLICATION_MODAL Shells block their own children too! So when APPLICATION_MODAL shell creates it's own child shell - it is blocked!
StyleDescription
BORDER Adds a border.
CLOSE Adds a close button.
MIN Adds a minimize button. When MIN is visible - CLOSE is visible too
MIN With Parent Shell Dependant Windows do not have their own space in Tray. Thats why they are minimized a little widget on the left.
MAX Adds a maximize button.
NO_TRIM Creates a Shell that has no border and can't be moved, closed, resized, minimized, or maximized. (Useful for splash screens).
RESIZE Adds a resizable border. You need TRIM to resize windows.
TITLE Adds a title bar.
DIALOG_TRIM Convenience style, equivalent to
TITLE | CLOSE | BORDER

.

SHELL_TRIM Convenience style, equivalent to
CLOSE | TITLE | MIN | MAX | RESIZE 
APPLICATION_MODAL Creates a Shell that's modal to the application. Note that you should specify only one of APPLICATION_MODAL, PRIMARY_MODAL, SYSTEM_MODAL, or MODELESS you can specify more, but only one is applied. The order of preference is SYSTEM_MODAL, APPLICATION_MODAL, PRIMARY_MODAL, then MODELESS.

ACHTUNG: APPLICATION_MODAL Shells block their own children too!
The applicaiton window is blocked by red modal shell.
PRIMARY_MODAL Creates a primary modal Shell. Blocks only the parent shell, not the whole application.
SYSTEM_MODAL Creates a Shell that's modal system-wide. It blocks ALL windows of the OS. But this parameter is useless since no OS allows to do that!
MODELESS Creates a modeless Shell. The application is not blocked.

Windows created by shells can behave differently. E.g. depend on other windows - be always on top of others.

new Shell() Ein eigenständiges Window mit der Repräsentation in der Toolbar.
new Shell(Shell parent, …); Gibt man einen Parent an - wird das neue Fenster zum Unterfenster des parents.

ACHTUNG: Child-Shell is always “on top” of it's parent.
Child Shell minimizes together with it's parent
new Shell(Shell parent, SWT.NONE); Erstellt ein Fenster ohne Deko, siehe Bild oben
new Shell(Shell parent, SWT.MODAL_APPLICATION); Erstellt ein Dialogfenster. Es ist immer on Top und lässt sich nicht schließen.

Tables

The Table in SWT

Table to display data
Table to make a user choice

The structure of a table is complicated:

  • Table is a central object
  • TableItems contain Images / Text. They are contained by Table. Each TableItem may contain many columns.
  • TableColumn divides a Table in columns
Tabe
 TableItem
 TableItem
 TableItem
 TableColumn

Possibilities

  • The Tables may be bound by Column to a data source - via IBeanValueProperty or ObservableListContentProvider
  • The Tables may posess different inline editors - EditingSupport

Tables are not suitable to display different cell editors per row.
It is possible, but since the API is not type safe (operates on Objects) it is hard to handle all the special cases.
Editors sometimes operate on indexes and so the modify() methods will receive indexes instead of values.

Styling

Hiding vertical table lines

Is dine via a PaintListener, which overrides the whole cell with the background

		table.addListener(SWT.EraseItem, new Listener() {
			@Override
			public void handleEvent(Event event) {
				// use the bg color here to hide the vertical lines
				event.gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
				event.gc.fillRectangle(event.getBounds());
			}
		});

Adding data

A table cell in SWT may contain

  • Text
  • single Image
  • an editor (dropdown, checkbox etc.)

Data may be added to the table with differnt methods.

Setting data via Listener

Setting the size of Table Items works only via a paint listener:

// resize the row height using a MeasureItem listener
table.addListener(SWT.MeasureItem, new Listener() {
   public void handleEvent(Event event) {
      // height cannot be per row so simply set
      event.height = 100;
      event.width = 300;
   }
});
Setting data via TableItem

Setting the size of Table Items works only via a paint listener:

		table = new Table(this, SWT.BORDER | SWT.FULL_SELECTION);
		
		TableColumn col1 = new TableColumn(table, SWT.NONE);
		col1.setWidth(100);
		col1.setText("Column1");
		
		TableColumn col2 = new TableColumn(table, SWT.RIGHT);
		col2.setWidth(100);
		col2.setText("Column2");

		
		TableItem tableItem = new TableItem(table, SWT.NONE);
		tableItem.setText(0, "InColumn1");
		tableItem.setText(1, "InColumn2");
Table data via TableItems

The most simple way to add data to tables is to use TableItems.

Achtung: The TableItems may be mixed up with TableViewer api. If they are mixed up - the tableitems appear at the bottom of the entries from viewer models.

		TreeItem trtmNewTreeitem_1 = new TreeItem(tree, SWT.NONE);
		trtmNewTreeitem_1.setText(new String[] {"from TableItem1", "from TableItem2"});
		
		TreeItem trtmNewTreeitem = new TreeItem(tree, SWT.NONE);
		trtmNewTreeitem.setText(new String[] {"from TableItem3", "from TableItem4"});

Table data via tableViewer API

Viewer Fascades may be used to set data via models. Read about JFace bindings for more.

Editors in cells

Editors may be integrated in table cells.


import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TreeEditor;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;

public class EditorsInCells extends Shell {

	/**
	 * Launch the application.
	 * @param args
	 */
	public static void main(String args[]) {
		try {
			Display display = Display.getDefault();
			EditorsInCells shell = new EditorsInCells(display);
			shell.open();
			shell.layout();
			while (!shell.isDisposed()) {
				if (!display.readAndDispatch()) {
					display.sleep();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public EditorsInCells(Display display) {
		super(display, SWT.SHELL_TRIM);
		setLayout(new FillLayout(SWT.HORIZONTAL));
		
		Tree tree = new Tree(this, SWT.BORDER);
		
		TreeColumn trclmnNewColumn = new TreeColumn(tree, SWT.NONE);
		trclmnNewColumn.setWidth(200);
		trclmnNewColumn.setText("New Column");
		
		TreeColumn trclmnNewColumn_1 = new TreeColumn(tree, SWT.NONE);
		trclmnNewColumn_1.setWidth(100);
		trclmnNewColumn_1.setText("New Column");
		
		TreeItem trtmNewTreeitem = new TreeItem(tree, SWT.NONE);
		trtmNewTreeitem.setText(new String[] {"New TreeItem", "col2", "col3"});
		
		Link l = new Link(tree, SWT.NONE);
		l.setText("<a>Link</a>");
		
		
		/**
		 * ACHTUNG : Windowsbuilder does not display the widget in cell correctly
		 */
		
		// TreeEditor IS THE IMPORTANT PART
		final TreeEditor treeEditor = new TreeEditor(tree);
		// make it fill the cell of set the width / height
		treeEditor.grabHorizontal=true;
		treeEditor.grabVertical=true;
		// assign the editor to the cell
		treeEditor.setEditor(l, trtmNewTreeitem, 1);
		
	}


	@Override
	protected void checkSubclass() {
		// Disable the check that prevents subclassing of SWT components
	}
}

Translating the hotkeys

The hotkeys may be represented in platform's special way and in platform's language as following:

IKeyFormatter formatter = SWTKeySupport.getKeyFormatterForPlatform();
formatter.format(SWT.CTRL); // Ctrl
Hotkeys in Menu

It is possible to display hotkeys inside of the SWT Menu.
To do so use the \t

String ctrl = SWTKeySupport.getKeyFormatterForPlatform().format(SWT.CTRL);
MenuItem sortMenuItemNo = new MenuItem(sortMenuGroup, SWT.PUSH);
sortMenuItemNo.setText("Search\t"+ctrl+"F");             

More about Menus and mnemonics http://stackoverflow.com/questions/12219537/swt-issues-with-cascaded-menuitem-accelerators

For some reason acceleratos does not work on Windows7 More about Menus ans accelerators: http://book.javanb.com/swt-the-standard-widget-toolkit/ch02lev1sec4.html

Tree

This one shows hierarchical data. In the first column there is a tree, the other columns are expandable

Listen for expansion:

  • execute the listener in an asyncExec block


	private Listener expansionListener2 = new Listener() {
		@Override
		public void handleEvent(Event event) {
		// IMPORTANT: the handler MUST be executed in a asyncExec Block - layout wont be done
			Display.getDefault().asyncExec(new Runnable() {
				
				@Override
				public void run() {
					CompositeCardContainer.this.getShell().layout(true, true);
					CompositeCardContainer.this.getShell().redraw();		
				}
			});
		};
	};

 treeGroupReceipts.addListener(SWT.Expand, expansionListener2);
 treeGroupReceipts.addListener(SWT.Collapse, expansionListener2);

Widget Style

Background

SWT.NO_BACKGROUND sets a single child to inherit the BG from the parent.

Composite composite_1 = new Composite(composite, SWT.NO_BACKGROUND);

SWT.INHERIT_FORCE, SWT.INHERIT_DEFAULT is set on the parent and forces all children to have parent's BG.

Composite parent = new Composite(this, SWT.NONE);
parent.setBackgroundMode(SWT.INHERIT_FORCE);
parent.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));

Composite childWithBg = new Composite(parent, SWT.NONE);
// parent will override this chailds BG, because of INHERIT_FORCE
childWithBg1.setBackground(SWTResourceManager.getColor(SWT.COLOR_CYAN));

Composite childWithoutBg = new Composite(parent, SWT.NONE);

Children may override the BG even if the parent defined the INHERIT_FORCE mode.
So SWT.INHERIT_FORCE, SWT.INHERIT_DEFAULT seem to have the same effect - the parent BG is inherited as default.
Even if the next child in chain defines SWT.INHERIT_NONE the BG of parent is still inherited, but may be overridden by any parent.

Layouts

The FillLayout, RowLayout, GridLayout, FormLayout are described here

RowLayout simple alignement in a row, without expanding, without filling
FormLayout supports most flexible alignement, relative to borders or other widgets
GridLayout suports horizontal space fill by items

The Layouts are controlled though the matching Composites. There are several methods inside of the composites, which control their layout.

  • computeSize() - calculates the size of the children. Returns the size of rectangle, which is needed to wrap all children.
  • getClientArea() - calculates the size of the area, where all children will be put in
  • computeTrim(x,y,width,height) - trim is the composite area, which contains decoration, where the children can not be put in.
  • pack() - sets the widget to prefered size.
    If a Layout exists in Composite - passes the size computing to the layout which USUALLY computes size recursively, measuring the child hierarchy.

    
            Composite parent = new Composite(shell, SWT.DEFAULT);
            Composite child = new Composite(parent, SWT.DEFAULT);
            
            Button button = new Button(child, SWT.DEFAULT);
            button.setText("Hello");
            
            parent.setLayout(new FillLayout());
            
            // passes the measuring to the FillLayout.
            // FIllLayout measures the child, but does not modify it's size.
            parent.pack();
            child.getSize(); // 0,0 since the child was not layed out yet and pack does no changes to the size   
     

  • layout - use current configurations, which my have changed t oupdate the layout.

GridLayout

Can make a row “GONE” by setting an Element to exclude=true.

This Element then will disapear and will not take any space anymore.

Achtung:
A GridLayout is filled row by row, by adding widgets to rows in order: add widgets to row1 until row1 is full, gow on with row2, row3…

excluding a column in multicolumn-layout makes a cell from the next row jump one row higher.
Here the label with y is excluded. Warning jumps 1 row higher.



Drawing Cycle

Every Composite:

  • computeSize(int wHint, int hHint, <fc #FF0000>boolean changed</fc>) - measure the Widget to get the min size. There are computeSize methods with other signatures, but the one with a boolean is used by the system.

    ACHTUNG: if there is a Layoutmanager(RowLayout, GridLayout etc.) then the size is computed by it, not by the widget itself. The size is set by creating new LayoutData then
  • layout(boolean changed) - updates the layout, according to the constraints.
    <fc #FF0000>NOT RECURSIVE</fc> - children are not redrawn.
  • layout(boolean changed, boolean all) - updates the whole hierarchy under the composite
    <fc #FF0000>IS RECURSIVE<\fc> - children are redrawn when all=true.</fc>
  • redraw() - invalidates widget's screen-area and adds a repaint request to the queue. The redraw-Events are queued and collapsed in the queue.
    <fc #008000>IS RECURSIVE</fc> - children are redrawn.
  • redraw(x,y,width,height,all) - can invalidate only a part of the screen, if not the whole widget must be redrawn. IMPORTANT:</fc> the redraw doesn't happen immediately. For immediate redraw call redraw();update();. \\<fc #FF0000>IS RECURSIVE<fc #008000> - children are redrawn when all=true.</fc>
  • update() - forces all outstanding paint requests in the queue to be done immediately!

//redraw whe whole Composite hierarchy immediately:
            composite.getShell().layout(true, true);
            composite.getShell().redraw();
            composite.getShell().update();

Own Widgets

Howt create own widgets is described here

  1. Subclass the Composite or Canvas class
  2. Inside of the new Widget add a DisposeListener to remove all used resources together with the widget. DO NOT OVERRIDE dispose(). WIDGET CAN BE DISPOSED WITHOUT CALLING DISPOSE()
  3. override computeSize(wHint, hHint) to calculate the preferred widget size. wHint, hHint are constrins which can be replaced by SWT.DEFAULT if there are no constraints.

Key Codes

To capture Keypresses add a Filter to the Display. The possible Color Codes are stored in the SWT class

 
// SWT.CR Enter Button
// SWT.KEYPAD_CR Enter Button near numpad

final Listener enterListener = new Listener() {
	@Override
	public void handleEvent(Event event) {
		System.out.println("key down.");
		if (event.keyCode == SWT.CR || event.keyCode == SWT.KEYPAD_CR) {
			pageManagerService.reloadActivePart();
		}
	}
};
composite.getDisplay().addFilter(SWT.Traverse, enterListener );

Disposed Widgets

Die Widgets werden entsorg, wenn deren Parent entsorgt wurde.
Die Widgets sind im Zustand “Dsiposed” wenn sie entsorgt wurden.

Sehr oft bleiben Listener übrig, welche versuchen die Widgets im Disposed Zustand zu nutzen. Das provoziert eine SWTException. Da ein Widget jederzeit “Disposed” werden kann - sollte dieser Fehler innerhalb der Listener mit try-catch abgefangen werden.

Dieser Exceptin ist ein gutes merkmal, um einen Listenr zu deregestrieren.

        final Listener enterListener = new Listener() {
            @Override
            public void handleEvent(Event event) {
                try {
                    System.out.println("key down.");
                } catch (SWTException e) {
                    display.removeFilter(SWT.KeyDown, this);
                }
            }
        };

Animations

There are animations available in SWT.

Update-Site: http://download.eclipse.org/technology/nebula/snapshot/
Plugin:
 org.eclipse.nebula.widgets.gallery
 org.eclipse.nebula.widgets.gallery.example
 org.eclipse.nebula.widgets.gallery.example.source
 org.eclipse.nebula.widgets.gallery.source
 org.eclipse.nebula.widgets.pshelf
Package: org.eclipse.nebula.animation

Details http://blog.richeton.com/swt-animation-toolkit/

Mouse position

The mouse position is retrieved relative to the Shell (Window) There are some Fallpits:

    private Point getDisplayPos(Control control) {
        /*
         * If the control has no parent - the coordinates are already relative to the Display!
         * converting them to display would mean, that the Display realative coords will be treated
         * as composite local coords, which is wrong!
         * 
         * The shell coordinates are relative to the display too!
         **/


        if (control instanceof Shell || control.getParent() == null) {
            Point r = ((Shell) control).getLocation();
            return new Point(r.x, r.y);

            /*
             * If there control has a parent and is not a shell - the coordinates are relative to
             * the parent!
             */
        } else {
            return control.getParent().toDisplay(control.getLocation());
        }
    }

/* The size and position  of the clientarea is indeed always returned relative to the parent.
 * So toDisplay() can be safely used here.  
 */
Rectangle clientArea = parent.getClientArea();
Point pos = parent.toDisplay(clientArea.x, clientArea.y);

JFace

Eclipse JFace is based upon the user interface toolkit SWT. JFace provides classes and frameworks which simplify common SWT use cases. JFace does not hide the SWT API; therefore SWT knowledge is still required.

The JFace Snippets are listed here.

Jface Basics

All the bindings are executed within a context called Realm (Kingdom) as stated here: https://wiki.eclipse.org/JFace_Data_Binding/Realm

So the realm must have been initialized. This is done by eclipse automaticall or may be doneas following, when not using the Eclipse platform:

	public static void main(String[] args) {
		Display display = new Display();
		
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
			@Override
			public void run() {
				// code that uses bindings			
			}
		});
		
	}

Jface Dialogs

TopLevelContainers
TopLevel classes:

The top-level JFace containers are Window, ApplicationWindow and Tooltip. They are described here.

More concrete classes:

MessageDialog


Is able to create simple dialogs. Has static methods which open blocking dialogs:

boolean result = MessageDialog.openConfirm(parent, title, message); 
MessageDialog.openError(parent, title, message); 
MessageDialog.openWarning(parent, title, message)

TrayDialog
ToolTip

/*
 * Tooltip example snippet: create a balloon tooltip for a tray item
 *
 * For a list of all SWT example snippets see
 * http://www.eclipse.org/swt/snippets/
 * 
 * @since 3.2
 */
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolTip;

public class TextFieldTooltip {

    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setLayout(new RowLayout(SWT.VERTICAL));
        final ToolTip tip = new ToolTip(shell, SWT.BALLOON);
        tip.setMessage("Here is a message for the user. When the message is too long it wraps. I should say something cool but nothing comes to my mind.");

        Text tfTooltip = new Text(shell, SWT.BORDER);
        tfTooltip.setText("sample text field");
        tfTooltip.addFocusListener(new FocusListener() {

            @Override
            public void focusLost(FocusEvent e) {
                tip.setVisible(false);
            }

            @Override
            public void focusGained(FocusEvent e) {
                Text actionWidget = (Text) e.widget;
                Point loc = actionWidget.toDisplay(actionWidget.getLocation());
                tip.setLocation(loc.x + actionWidget.getSize().x - actionWidget.getBorderWidth(), loc.y);
                tip.setVisible(true);
            }
        });
        Text tfNext = new Text(shell, SWT.BORDER);
        tfNext.setText("TF without tooltip");
        shell.setBounds(50, 50, 300, 200);
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }
}
PopupDialog Will not close itself on mouseout.

The concrete classes: The containers below are lister here: http://sureshkrishna.wordpress.com/

Jface Viewers

Fallpits:
  • When using the TreeViewer - finding the parents for each element inside Adapter is not necessary. To generate a tree - it is enough to find the children inside of each IWorkbenchAdapter
  • For a TreeViewer with a TreeContentProvider, the Input shuld be a root Element with some SubElements. However there should be a root Element, which the TreeContentProvider can find the CHildren from. The root Element is not drawn, it's children become the first level elements.
List

All viewers can:

  • show and hide Table
  • Add Images to columns
  • Add Editors to cells
Viewer Describtion Pic
TableViewer

Can display Data in a table. Is capable to sort columns

TreeViewer

Can display hierarchical data. Is capable to have columns. Some columns can display hierarchical content, some can display non hierarchical content.

Tech

JFace Viewers use the Model-View-Controller metapher.

MVC Class Explanation
View Viewer
The Viewer list is available here , or inside of the Window Builder Pro
The Viewers are instantiated like normal Composites
Model Input
Viewer#setContentProvider()
The Model is an Objectstrucure, which you whant to display inside of your Viewer. E.g. an Array of Persons.
Controler ContentProvider
Viewer#setContentProvider()
The ContenProvider is the class, which will understand the Model and build an Inheritance structure.
Controler2 LabelProvider
Viewer#setLabelProvider()
The ContenProvider is the class, which will understand the Model to set Labels for each Cell.

ContentProvider is supported by the IAdapter Pattern in Eclipse RCP.

PatternConcept Explanation
ContentProvider

is a Controller for a Viewer. A concrete inherent of IContentProvider will be used by the viewer to understand Objects, which should be displayed in the viewer. ContentProvider converts the given Model objects into a Generic Interface IWorkbenchAdapter using a IAdapterFactory and uses this Generic Interface to retrieve strucutral Data (Parents, Children). Example:

TODO

LabelProvider

The same as ContentProvider, but retrieves Labels instead of structural data. Sometimes it may be useful to implement LabelProvider as inner classes, because you only can deside which label should be displayed in a column, depending on the sematic of this column. LabelProvider can be added to:

  • Viewer - this LabelViewer will be used when both are added.
  • ViewerColumn
IAdapterFactory

Inherent of the IAdapterFactory can produce Adapters for every class, which will be understood by ContentProviders and LabelProviders. Factory is given a Class, and it is asked for an Adapter for this class. Factory returns an Adapter, depending on the given class, so that every Object can be handled through the same interface.

TODO

How to use the JFace Viewers in RCP:

Example of instantiating a Viewer

	// 1. create Viewer
        viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
        final Table table = viewer.getTable();
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        // 2. create columns, before setting the input
        createColumns(parent);

        // 3. set content provider which understands IWorkbenchAdapter. Happens after creating content. Inside there will be a request to a Platfortm, to give you a IWorkbenchAdapter for the given Object. The iFacotry is retrieved through a Singleton
        viewer.setContentProvider(new ITreeContentProvider()); // ArrayContentProvider works for

        // 4. Set a LabelProvider to it, which understands IWorkbenchAdapter. Ask an IFactory to give you a IWorkbenchAdapter
        //First way to add a LableProvider. The second is adding it to the viewer's columns. When viewer has a LabelProvider - column's LabelProviders are not triggered.
        viewer.setLabelProvider(new ILabelProvider());
        
        // 5. Set some Input to the Viewer. At the end there should be a factory, which can turn this Input into a IWorkbenchAdapter
        model = getBundles();
        viewer.setInput(model);
        
        // 6. sort, after input
        comparator.setColumn(1);
        viewer.setComparator(comparator);
        viewer.getTable().setSortColumn(sortByColumn);
        viewer.refresh();
        
        //7. Now implement a Factory to convert a Person into a IWorkbenchAdapter and register it through a Singleton:
        Platform.getAdapterManager().registerAdapters(new PersonFactory(), Person.class);
        
        
        
      private void createColumns(final Composite parent) {
        String[] titles = { "Module", "Widget" };
        int[] bounds = { 200, 100 };

        TreeViewerColumn col;

        col = createViewerColumn(titles[0], bounds[0], 0);
        //Second way to add a LableProvider. The first is adding it to the viewer itself. When viewer has a LabelProvider - column's LabelProviders are not triggered.
        col.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                return ((Module) element).getId();

            }

            @Override
            public Image getImage(Object element) {
                return (Module) element).getImage();;
            }
        });

        col = createViewerColumn(titles[1], bounds[1], 1);
        //Second way to add a LableProvider. The first is adding it to the viewer itself. When viewer has a LabelProvider - column's LabelProviders are not triggered.
        col.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
            return ((Module) element).getName();

            }
        });

    }

Example of creating a ContentProvider

/**
 * Can use {@link IWorkbenchAdapter} instances, e.g. to provide data for a @link {@link TreeViewer}.
 */
public class TreeContentProvider4IWorkbenchAdapter implements ITreeContentProvider {

    private IWorkbenchAdapter model;

    @Override
    public void dispose() {

    }

    @Override
    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        model = getAdapter(newInput);
    }

    @Override
    public Object[] getElements(Object inputElement) {
        return model.getChildren(inputElement);
    }

    @Override
    public Object[] getChildren(Object parentElement) {
        IWorkbenchAdapter adapter = getAdapter(parentElement);
        if (adapter != null) {
            return adapter.getChildren(parentElement);
        }
        return new Object[0];
    }

    @Override
    public Object getParent(Object element) {
        return model.getParent(element);
    }

    @Override
    public boolean hasChildren(Object element) {
        return getChildren(element).length > 0;
    }


    /**
     * Returns the implementation of IWorkbenchAdapter for the given
     * object. Returns null if the adapter is not defined or the
     * object is not adaptable.
     * <p>
     * </p>
     * 
     * @param element
     *            the element
     * @return the corresponding workbench adapter object
     */
    protected IWorkbenchAdapter getAdapter(Object element) {
        if (element == null) {
            return null;
        }

        IWorkbenchAdapter adapter = (IWorkbenchAdapter) Platform.getAdapterManager().getAdapter(element,
                IWorkbenchAdapter.class);
        return adapter;
    }

}

Example of creating a LabelProvider

/**
 * Can use {@link IAdapter} instances, e.g. to provide labels for a @link {@link TreeViewer}.
 * If this Label is used - the Label and Image will be provided only for the first column.
 * Use {@link TreeContentProvider4IWorkbenchAdapter} to provide separate labels for all columns.
 */
public class LabelProvider4IWorkbenchAdapter extends LabelProvider {

    @Override
    public Image getImage(Object element) {
        Object adapterObject = Platform.getAdapterManager().getAdapter(element, IWorkbenchAdapter.class);
        IWorkbenchAdapter adapter = (IWorkbenchAdapter) adapterObject;
        return adapter.getImageDescriptor(element).createImage();
    }

    @Override
    public String getText(Object element) {
        Object adapterObject = Platform.getAdapterManager().getAdapter(element, IWorkbenchAdapter.class);
        IWorkbenchAdapter adapter = (IWorkbenchAdapter) adapterObject;
        return adapter.getLabel(element);
    }
}

Example of creating an AdapterFactory

package de.ivu.fare.rcp.gui.parts;

import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.model.IWorkbenchAdapter;

import de.ivu.fare.rcp.spi.Module;
import de.ivu.fare.rcp.spi.Submodule;
import de.ivu.fare.rcp.util.ResourceUtil;

/**
 * A factory, which will be registered to the {@link Platform}, so that JFace Viewer's
 * ContentProviders are able to convert Objects to an Interface {@link IWorkbenchAdapter}, which
 * they can handle.
 * 
 */
public class FactoryModule2IWorkbenchAdapter implements IAdapterFactory {

    private static final String MODULE_TREE_NODE_ICON_PATH = "images/icons/modul.png";

    private IWorkbenchAdapter moduleAdapter = new IWorkbenchAdapter() {

        @Override
        public Object getParent(Object o) {
            return null;
        }

        @Override
        public String getLabel(Object o) {
            Module m = (Module) o;
            return m.getName();
        }

        @Override
        public ImageDescriptor getImageDescriptor(Object o) {
            return ResourceUtil.getImageDescriptor(MODULE_TREE_NODE_ICON_PATH);
        }

        @Override
        public Object[] getChildren(Object o) {
            Module m = (Module) o;
            return m.getSubmodules().toArray();
        }

    };

    private IWorkbenchAdapter submoduleAdapter = new IWorkbenchAdapter() {

        @Override
        public Object getParent(Object o) {
            Submodule m = (Submodule) o;
            return m.getParentModule();
        }

        @Override
        public String getLabel(Object o) {
            Submodule m = (Submodule) o;
            return m.getName();
        }

        @Override
        public ImageDescriptor getImageDescriptor(Object o) {
            return ResourceUtil.getImageDescriptor(MODULE_TREE_NODE_ICON_PATH);
        }

        @Override
        public Object[] getChildren(Object o) {
            Submodule m = (Submodule) o;
            return m.getSubmodules().toArray();
        }

    };

    @Override
    public IWorkbenchAdapter getAdapter(Object adaptableObject, Class adapterType) {
        if (adapterType == IWorkbenchAdapter.class && adaptableObject instanceof Module) {
            return this.moduleAdapter;
        } else if (adapterType == IWorkbenchAdapter.class && adaptableObject instanceof Submodule) {
            return this.submoduleAdapter;
        }
        return null;
    }

    @Override
    public Class[] getAdapterList() {
        return new Class[] { IWorkbenchAdapter.class };
    }
}

System Scenario

The ContentProvider will get a Person.class,
ask the IFactory for an Adapter and retrieve a concrete IWorkbenchAdapter,
use the IWorkbenchAdapter to display retireve Parents and Children.
The LabelProvider will do the same to retrieve Labels.

If we want to pass some other Objects to the Viewer - just implement a new Factory and reegister it, so that it can convert the new Object to a IWorkbenchAdapter - it allready can be handled by the ContentProvider.

Own Model for a TreeViewer

  • The data in the treeViewer will be generated from the model
  • Each column has an own label provider
  • in this example the model changes are not observed
Model

The Model is a list of restauraunt tables. A restauraunt table may have a list of neighbours

package de.example.jface.viewer.mvc;

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

import de.e4.annotations.AbstractBindableModel;

public class Model extends AbstractBindableModel {

	public List<Restauranttable> tables = new ArrayList<Restauranttable>();
	
	{
		tables.add(new Restauranttable(1, true));
		tables.add(new Restauranttable(2, true));
		tables.add(new Restauranttable(3, true));
		tables.add(new Restauranttable(4, false));
		tables.add(new Restauranttable(5, false));
		
		tables.get(1).setNeighbour(tables.get(2));
		tables.get(3).setNeighbour(tables.get(4));
	}
	
	
	public List<Restauranttable> getTables() {
		return tables;
	}


	public void setTables(List<Restauranttable> tables) {
		firePropertyChange("tables", this.tables, this.tables = tables); 
	}




	public static class Restauranttable{
		int number;
		boolean booked;
		List<Restauranttable> neighbours = new ArrayList<Model.Restauranttable>();
		Restauranttable parent;
		
		@Override
		public String toString() {
			return "Table "+number;
		}
		
		public Restauranttable(int num, boolean booked) {
			this.booked = booked;
			this.number = num;
		}
		
		public void setNeighbour(Restauranttable... table){
			for(Restauranttable t : table){
				this.neighbours.add(t);
				t.parent = this;	
			}
		}
		
		public List<Restauranttable> getNeighbours() {
			return neighbours;
		}
		
		public Restauranttable getParent() {
			return parent;
		}
	}
}



TableViewer

The TableViewer displays the list of restauraunt tables in two columns:

Table number booked

package de.example.jface.viewer.mvc;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Table;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.jface.viewers.TreeViewerColumn;

import de.example.jface.viewer.mvc.Model.Restauranttable;

public class MainShell extends Shell {

	/**
	 * Launch the application.
	 * @param args
	 */
	public static void main(String args[]) {
		try {
			Display display = Display.getDefault();
			MainShell shell = new MainShell(display);
			shell.open();
			shell.layout();
			while (!shell.isDisposed()) {
				if (!display.readAndDispatch()) {
					display.sleep();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Create the shell.
	 * @param display
	 */
	public MainShell(Display display) {
		super(display, SWT.SHELL_TRIM);
		setLayout(new FillLayout(SWT.HORIZONTAL));
		
		TreeViewer treeViewer = new TreeViewer(this, SWT.BORDER);
		Tree tree = treeViewer.getTree();
		
		TreeViewerColumn treeViewerColumn1 = new TreeViewerColumn(treeViewer, SWT.NONE);
		TreeColumn treeColumn1 = treeViewerColumn1.getColumn();
		treeColumn1.setWidth(100);
		treeColumn1.setText("New Column");
		
		TreeViewerColumn treeViewerColumn2 = new TreeViewerColumn(treeViewer, SWT.NONE);
		TreeColumn treeColumn2 = treeViewerColumn2.getColumn();
		treeColumn2.setWidth(100);
		treeColumn2.setText("New Column");
		createContents();
		
		setupMvc(treeViewer, treeViewerColumn1, treeViewerColumn2);
	}
	
	public void setupMvc(TreeViewer treeViewer, TreeViewerColumn treeViewerColumn1, TreeViewerColumn treeViewerColumn2){
		// Model
		Model model = new Model();
		
		// tell the viewer how to read the model
		treeViewer.setContentProvider(new ITreeContentProvider() {
			
			@Override
			public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
				// ?
				// this method would be used, if the model would be observable.
				// here we would register to track changes on model

			}
			
			@Override
			public void dispose() {
				
			}
			
			@Override
			public boolean hasChildren(Object element) {
				Restauranttable r = (Restauranttable) element;
				return r.getNeighbours() != null && !r.getNeighbours().isEmpty();
			}
			
			@Override
			public Object getParent(Object element) {
				// single model element here
				Restauranttable r = (Restauranttable) element;
				return r.getParent();
			}
			
			@Override
			public Object[] getElements(Object inputElement) {
				// retrieve the element from the Model (input = Model)
				Model model = (Model) inputElement;
				return model.tables.toArray();
			}
			
			@Override
			public Object[] getChildren(Object parentElement) {
				Restauranttable r = (Restauranttable) parentElement;
				return r.neighbours.toArray();
			}
		});
		
		// setup Label provider on columns. FOr each column an own Label provider
		// col 1
		treeViewerColumn1.setLabelProvider(new CellLabelProvider() {
			@Override
			public void update(ViewerCell cell) {
				// retrieve the element in the cell, Must be the element in my model
				Restauranttable table = (Restauranttable) cell.getElement();
				
				// set the text in the cell
				cell.setText(table.toString());
//				cell.setImage(image)
//				cell.setForeground(color)
//				cell.setBackground(background)
			}
		});
		// col 2		
		treeViewerColumn2.setLabelProvider(new CellLabelProvider() {
			@Override
			public void update(ViewerCell cell) {
				// retrieve the element in the cell, Must be the element in my model
				Restauranttable table = (Restauranttable) cell.getElement();
				
				// set the text in the cell
				cell.setText(""+table.booked);
//				cell.setImage(image)
			}
		});
		
		// add the data to the viewer
		treeViewer.setInput(model);
	}

	/**
	 * Create contents of the shell.
	 */
	protected void createContents() {
		setText("SWT Application");
		setSize(450, 300);

	}

	@Override
	protected void checkSubclass() {
		// Disable the check that prevents subclassing of SWT components
	}

}

JFace Bindings:

ACHTUNG:

  • The programmatical selection does not trigger the selection bindings. \\To trigger the RadioButton-select-Binding programmatically the selection listeners have to be notified manually:
    Button buttonRadio = new Button(parent, SWT.Radio);
    buttonRadion.setSelection(true);
    buttonRadio.notifyListeners(SWT.Selection, new Event());

A good tutorial: http://www.vogella.com/articles/EclipseDataBinding/article.html Use WindowBuilder to establish bindings.

Summary

The Widgets are bound to the model:

// A Model may be any POJO
public class MyPojoModel{
	private String label = "label";
	private boolean isEnabled = true;
	public boolean isEnabled() {
		return isEnabled;
	}
	public void setEnabled(boolean isEnabled) {
		this.isEnabled = isEnabled;
	}
	public String getLabel() {
		return label;
	}
	public void setLabel(String label) {
		this.label = label;
	}
}


public static void main{ ...

	protected DataBindingContext initDataBindings() {
		DataBindingContext bindingContext = new DataBindingContext();
		
		// JFace bindings can observe properties in POJOs. Here obseving property "enabled" in Object model
		// Factory PojoProperties observes Pojos
		IObservableValue enabledModelObserveValue = PojoProperties.value("enabled").observe(model);

		// on change different filters and converters may be applied. They are combined to a stragegy
		UpdateValueStrategy strategy = new UpdateValueStrategy();
		strategy.setConverter(new MyBoolInverterConverter());
		
		// a Factory WidgetProperties observers widgets
		IObservableValue observeEnabledBtnNewButton = WidgetProperties.enabled().observe(button);
		
		// bind POJO and Widget together
		bindingContext.bindValue(observeEnabledBtnNewButton, enabledModelObserveValue, null, strategy);
		return bindingContext;
	}

// when changing the Widgets - changes are directly propagated to the Model
// when changing the Model - the changes are NOT directly propagated to the bound Widgets. call binding to do so
SelectionAdapter sa = new SelectionAdapter() {
	@Override
	public void widgetSelected(SelectionEvent e) {
		model.setEnabled(!model.isEnabled());
		//model changed. propagate changes.
		initDataBindings();
	}
};
button.addSelectionListener(sa);


Usage of available ContentProviders to bind Beans properties "name", "familiyName" to a viewer

			// Properties of Person "name" and "firstName"
			IBeanValueProperty propName = BeanProperties.value(Person.class, "name");
			IBeanValueProperty propFirstname = BeanProperties.value( Person.class, "firstName");
			
			// IObservable Set of Persons
			WritableSet writableSet = new WritableSet(this.viewModel.getPeople(), Person.class);
			
			/* Splits the Beans in two maps, by properties
			 * Set of Beans Person:

				Dave Orme
				Gili Mendel
				Joe Winchester
				Boris Bokowski
				Brad Reynolds
				Matthew Hall 
				
				into 
				
				[ Winchester, Mendel, Hall, Bokowski, Reynolds, Orme ] and
				[ Joe, Gili,  Matthew, Boris, Brad,  Dave]
			 */
			IObservableMap[] result = Properties.observeEach(writableSet, new IBeanValueProperty[] { propName, propFirstname });
			
			// make the column understand it's data source by setting a Label provider
			columnName.setLabelProvider(new ObservableMapCellLabelProvider( result[0]));
			columnFirstName.setLabelProvider(new ObservableMapCellLabelProvider(result[1]));
			
			
			// feed the list with Persons to the viewer as datasource
			peopleViewer.setInput(new WritableList(viewModel.getPeople(), Person.class));

API:

IObservableList

may be observed by the JFace bindings

	IObservableList sourceObservableList = new WritableList(); 

bindingContext

Main class which stores all the bindings.

		bindingContext.bindValue(
		SWTObservables.observeText(selectedCommitterName), 
		BeansObservables.observeDetailValue(selection, "name", String.class));

SWTObservables

creates a bindable ISWTObservableValue from widget properties

	SWTObservables.observeTooltipText(label)

	observeDelayedValue(int, ISWTObservableValue)
	observeEnabled(Widget)
	observeEnabled(Control)
	observeVisible(Control)
	observeTooltipText(Widget)
	observeTooltipText(Control)
	observeSelection(Widget)
	observeSelection(Control)
	observeMin(Control)
	observeMax(Control)
	observeText(Control, int[])
	observeText(Control, int)
	observeText(Widget)
	observeText(Control)
	observeMessage(Widget)
	observeImage(Widget)
	observeItems(Control)
	observeSingleSelectionIndex(Control)
	observeForeground(Control)
	observeBackground(Control)
	observeFont(Control)
	observeSize(Control)
	observeLocation(Control)
	observeFocus(Control)
	observeBounds(Control)
	observeEditable(Control) 

WidgetProperties

Used to observe Widget's properties.

 
	WidgetProperties.items().observe(combo);
	
	background()
	bounds()
	editable()
	enabled()
	focused()
	font()
	foreground()
	image()
	items()
	location()
	maximum()
	message()
	minimum()
	selection()
	singleSelectionIndex()
	size()
	text()
	text(int)
	text(int[])
	tooltipText()
	visible()

BeansObservables

Factory to observe Beans' properties

 

	observeValue(Object, String)
	observeValue(Realm, Object, String)
	observeMap(IObservableSet, String)
	observeMap(IObservableSet, Class, String)
	observeMap(Realm, Object, String)
	observeMap(Realm, Object, String, Class, Class)
	observeMap(Object, String)
	observeMap(Object, String, Class, Class)
	observeMaps(IObservableSet, String[])
	observeMaps(IObservableSet, Class, String[])
	observeList(Realm, Object, String)
	observeList(Object, String)
	observeList(Realm, Object, String, Class)
	observeList(Object, String, Class)
	observeSet(Realm, Object, String)
	observeSet(Object, String)
	valueFactory(Realm, String)
	valueFactory(String)
	listFactory(Realm, String, Class)
	listFactory(String, Class)
	setFactory(Realm, String)
	setFactory(String)
	observeDetailValue(Realm, IObservableValue, String, Class)
	observeDetailValue(IObservableValue, String, Class)
	observeDetailValue(Realm, IObservableValue, Class, String, Class)
	observeDetailValue(IObservableValue, Class, String, Class)
	observeDetailList(Realm, IObservableValue, String, Class)
	observeDetailList(IObservableValue, String, Class)
	observeDetailSet(Realm, IObservableValue, String, Class)
	observeDetailSet(IObservableValue, String, Class)
	observeDetailMap(Realm, IObservableValue, String)
	observeDetailMap(IObservableValue, String)
	observeSet(Realm, Object, String, Class)
	observeSet(Object, String, Class)
	setFactory(Realm, String, Class)
	setFactory(String, Class)
	setToMapFactory(Class, String)
	mapPropertyFactory(Realm, String)
	mapPropertyFactory(String)

BeanProperties

ceates bindable IBeanValueProperty

 

IBeanValueProperty propName = BeanProperties.value(Person.class, "name");


	value(String)
	value(String, Class)
	value(Class, String)
	value(Class, String, Class)
	values(Class, String[])
	values(String[])
	set(String)
	set(String, Class)
	set(Class, String)
	set(Class, String, Class)
	list(String)
	list(String, Class)
	list(Class, String)
	list(Class, String, Class)
	map(String)
	map(String, Class, Class)
	map(Class, String)
	map(Class, String, Class, Class)

JFace ControlDecorators

It is possible to decorate the Controls like Forms or Labels using SWT.

Here is an example with FieldDecorationRegistry.DEC_WARNING

        ControlDecoration controlDecoration = new ControlDecoration(lblNewLabel, SWT.LEFT | SWT.TOP);
        controlDecoration.setDescriptionText("HoverText");
        FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_WARNING);
        controlDecoration.setImage(fieldDecoration.getImage());
        controlDecoration.show();

JFace Wizards

Dynamically choose next Page

TO e.g. Skip a Page depending on model:

Override the IWizard.getNextPage(), IWizard.getPreviousPage(),

   @Override
    public IWizardPage getNextPage(IWizardPage page) {
        IWizardPage nextPage = super.getNextPage(page);
        if (modelUsercardWizard.isCreatingAnonymousCustomerCard() && nextPage == wizardPageUsercardcreationFoto) {
            return nextPage.getNextPage();
        }
        return nextPage;
    }

    @Override
    public IWizardPage getPreviousPage(IWizardPage page) {
        IWizardPage prevPage = super.getPreviousPage(page);
        if (modelUsercardWizard.isCreatingAnonymousCustomerCard() && prevPage == wizardPageUsercardcreationFoto) {
            return prevPage.getPreviousPage();
        }
        return prevPage;
    }

Forms API

Update-Site: http://download.eclipse.org/eclipse/updates/4.5
Feature-Name: Eclipse-Platform x.y

Plugin:

  • org.eclipse.ui.forms

Packages:

  • org.eclipse.ui.forms.*
  • org.eclipse.ui.forms.widgets.*

The Forms API is described here https://eclipse.org/articles/Article-Forms/article.html

It brings some layouts and expandable secitons and forms. Indeed it implements the forms, which are used within eclipse to display plugins, etc.

article.html

Expandable Composite
  • The expandable composite looks different, than arrows in TreeViewers widgets.
  • The margins of the expandable composite are different than e.g. within of other widgets.
  • The row distances are different than e.g. between the Expandable Composite Headers

Animation

Rules:

  1. Use timer for animation:
    int delay = 5000;   // delay for 5 sec.
    int period = 1000;  // repeat every sec.
    Timer timer = new Timer();
    
    timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                // Task here ...
            }
        }, delay, period);
    
  2. Change the widget on the UI thread
    Display.getDefault().asyncExec(new Runnable() {
        public void run() {
            someSwtLabel.setText("Complete!");
        }
    });
    
  3. Catch the errors, which will occur, when the widget is disposed by other threads, e.g. when the shell is closed
    try{
    	composite.width ++;
    	
    	//layout of the composite doesn't work
    //	composite.layout(true, true);
    
    	//layout of parent works
    	composite.getParent().layout(true, true);
    		
    	composite.redraw(); //marks the composite's screen are as invalidates, which will force a redraw on next paint request 
    	composite.update(); //tells the application to do all outstanding paint requests immediately
    	
    }catch(org.eclipse.swt.SWTException e){
    	// Widget is allready disposed. Probably the application was closed.
    	timer.cancel();
    }
    
Run on UI Thread

The code can be executed on UI thread as following:

Display.getDefault().asyncExec(new Runnable() {
    public void run() {
        someSwtLabel.setText("Complete!");
    }
});

Fine Usability Tuning

Tab Order

The tab order of the controls on a form determines the sequence in which the focus will change when the user strikes the tab key. You can specify the tab order of controls via the setTabList() method of a Composite.

setTabList() 

2D Drawing


color_bgcolor           = getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
color_foreground        = getDisplay().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND);
color_border            = getDisplay().getSystemColor(SWT.COLOR_WIDGET_BORDER);
color_dark_shadow       = getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
color_highlight_shadow  = getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
color_light_shadow      = getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
color_normal_shadow     = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
color_text              = color_dark_shadow;
	
	//constants
	final int shadow_margin_left = WIDTH-10;
	

//drawing will happen here
this.addPaintListener(new PaintListener() { 
	public void paintControl(PaintEvent e) { 
		
		//draw in this area. Must be inside of the Paint Listener, because the clientarea changes its size
		Rectangle clientArea = canvas.getClientArea();
		
		final int text_margin_left = shadow_margin_left-textsize.y;
		final int text_margin_top = (clientArea.height)/2;
			
		//Background
			//0. default background
			e.gc.setBackground(color_light_shadow);
			e.gc.fillRectangle(0,0,clientArea.width, clientArea.height);
			
			
			//1. draw a gradient shadow
			e.gc.setForeground(color_light_shadow);
			e.gc.setBackground(color_dark_shadow);
			e.gc.fillGradientRectangle(shadow_margin_left, 1, clientArea.width, clientArea.height-1, false);
			
			//2. draw a light frame 
			if(accordion.styleItemBorder){
				e.gc.setForeground(color_highlight_shadow);
				e.gc.drawRectangle(1, 1, clientArea.width, clientArea.height);
			}
			
			//3. draw a dark frame   
			e.gc.setForeground(color_normal_shadow);
			e.gc.drawRectangle(0, 0, clientArea.width, clientArea.height-1);

		//Text
			//rotate the screen
			Transform tr = new Transform(display);
			tr.rotate(-90);
			e.gc.setTransform(tr);

			e.gc.setFont(display.getSystemFont());
			e.gc.setForeground(color_text);
			e.gc.drawString(text, -text_margin_top, text_margin_left, true);
			
			//rotate back
			tr.identity();
			e.gc.setTransform(tr);
			
		
		
		
		//FOR DEBUGGING
		if(HAccordion.DEBUG){
			
			//DEMO colors
			e.gc.setBackground(color_bgcolor);
			e.gc.fillRectangle(0, 10, 10, 10);
			
			e.gc.setBackground(color_dark_shadow);
			e.gc.fillRectangle(10, 10, 10, 10);     	

			e.gc.setBackground(color_foreground);
			e.gc.fillRectangle(10, 20, 10, 10);     	
			
			e.gc.setBackground(color_border);
			e.gc.fillRectangle(10, 30, 10, 10);     	
			
			e.gc.setBackground(color_dark_shadow);
			e.gc.fillRectangle(10, 40, 10, 10);     	
			
			e.gc.setBackground(color_highlight_shadow);
			e.gc.fillRectangle(10, 50, 10, 10);     	
			
			e.gc.setBackground(color_light_shadow);
			e.gc.fillRectangle(10, 60, 10, 10);     	
			
			e.gc.setBackground(color_normal_shadow);
			e.gc.fillRectangle(10, 70, 10, 10);   
			
			
			
			//write down the index in the top left corner
			int index = accordionItem.getParent().getItemsIterator().getIndexOf(accordionItem);
			e.gc.drawText(String.valueOf(index), 1, 1);
			
			//random color
			int red = (int)(Math.random()*256);  
			int green = (int)(Math.random()*256);  
			int blue = (int)(Math.random()*256);  
			Color random = new Color(getDisplay(),red,green,blue);
			
			//use colors random color for filling
			e.gc.setBackground(random);
			e.gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_BLACK));

			//draw
			e.gc.fillOval(0,0,clientArea.width,clientArea.height); 
			e.gc.drawRectangle(0, 0, clientArea.width-1, clientArea.height-1);
		}
	} 
});

// draws an image. White is told to be the transparent color
class FactoryImages{
    private static final Color NUM_DECORATOR_transparentColor = SWTResourceManager.getColor(SWT.COLOR_WHITE);
    private static final Color NUM_DECORATOR_textColor = SWTResourceManager.getColor(SWT.COLOR_BLACK);


    private static Image createTransparentImage(Display d, int width, int height) {
        // create a cursor with a transparent image
        // allows the red, green and blue color masks to be specified.
        PaletteData palette = new PaletteData(0xFF, 0xFF00, 0xFF0000);
        ImageData sourceData = new ImageData(width, height, 24, palette);

        // tell the platform which color should be made transparent
        int transparentPixel = sourceData.palette.getPixel(NUM_DECORATOR_transparentColor.getRGB());
        sourceData.transparentPixel = transparentPixel;

        Image image = new Image(d, sourceData);
        GC gc = new GC(image);

        // bg
        gc.setBackground(NUM_DECORATOR_transparentColor);
        gc.fillRectangle(0, 0, width, height);

        //text 
        gc.setBackground(NUM_DECORATOR_textColor);
        gc.drawText("Test", 0, 0, true);
        
        gc.dispose();

        return image;
    }
}

Generating a colored rectangle in memory

        lblFoto.setImage( createImage(new RGB(100, 100, 100)) );
    }

    // generate the image
    public static Image createImage(RGB... rgb) {
        PaletteData paletteData = new PaletteData(rgb);
        ImageData imageData = new ImageData(14, 14, 1, paletteData);
        for (int x = 4; x < 8; x++) {
            for (int y = 4; y < 8; y++) {
                imageData.setPixel(x, y, 1);
            }
        }
        return new Image(Display.getCurrent(), imageData);
    }

Custom SWT Widgets

When implementing custom SWT Widgets think about:

  • Right-To-Left behaviour - how will the widget look like, when switched to the right to left view
  • flexible stylin possibilities. E.g. use CSS if relying on Eclipse4
  • internationalization - can you translate the widget?

Right To Left

An RCP Applicaiton may be switched to the RTL View by using the Eclipse Run Parameter

-dir rtl

How do concrete layouts behave when switched to the Right To Left view?

RowLayout
GridLayout
FormLayout

Windowbuilder

The WindowBuilder should be used to create composites in SWT. It has a preview function which is quite fragile.

Rules

Following should be considered when creating widgets with Windowbuilder:

  • when using object's variables - move the initialization of variables into the constructor. Otherwise the WindwBuilder will display a NullPointerException
    class LayoutA{
        public Composite column1;
        public LayoutA(Composite parent) {
            // OK - create in constructor
            this.column1 = parent;
        }
    }
    
    class LayoutB{
        public Composite column1;
        public layout(Composite parent) {
            // WRONG - you Will not be able to use LayoutB.column1 in WindowBuilder preview
            this.column1 = parent;
        }
    }
    
  • Anonymous Objects are forbidden
programming/java/swt.txt · Last modified: 2023/11/01 07:31 by skipidar