Table of Contents
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.
- 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
- 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. - Widgets have to be manually disposed by calling
dispose()
. Only after that they will be garbage collected.
Shell calls dispose() on its widgets automagically - 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!
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
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 } }
Dropdown Menu
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
- Subclass the Composite or Canvas class
- 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()
- 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 |
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:
|
||||||||||||
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:
|
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:
|
Packages:
|
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.
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:
- 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);
- Change the widget on the UI thread
Display.getDefault().asyncExec(new Runnable() { public void run() { someSwtLabel.setText("Complete!"); } });
- 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