====== SWT ======
==Literature==
^Info ^Location ^
|0. SWT Tutorial | [[http://www.vogella.com/articles/SWT/article.html|vogella.com]] |
|1. SWT is made up of widgets, which are listed | [[http://www.eclipse.org/swt/widgets/ | SWT Widget Pics]] \\ [[http://www.eclipse.org/swt/snippets/ | SWT Code Snippets]] \\ [[http://www.eclipse.org/swt/examples.php | Larger examples of SWT Widgets]] |
|2. Google Window builder is a tool for building SWT UI. | [[https://developers.google.com/java-dev-tools/wbpro/ | Google Window Builder documentation]] |
|3. Using SWT Layouts | [[http://www.eclipse.org/articles/article.php?file=Article-Understanding-Layouts/index.html|Using Layouts]] |
|4. Creating own SWT Widgets | [[http://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm|Own SWT Widgets]] |
|5. JFace framework provides higher level Widgets | [[http://www.vogella.com/articles/EclipseJFace/article.html|vogella.com]] |
|6. RCP Widgets like analogue clock | [[http://www.richclientgui.com/detail.php?product_id=1|http://www.richclientgui.com]] |
|7. Forms API like sections and expandables | [[https://eclipse.org/articles/Article-Forms/article.html|https://eclipse.org/articles/Article-Forms/article.html]] |
== Additonal SWT Widget libs ==
^What ^Location ^
| The Eclipse **Nebula Project** provides custom SWT widgets | [[http://eclipse.org/nebula/|Nebula]] |
| The google code Opal Project provides many widget too | [[http://code.google.com/a/eclipselabs.org/p/opal/|Opal]] |
| JFace Widgets extens SWT, simplify the Widgets usage but do not hide the native SWT API (Git and CVS unfortunately offline :( ) | [[http://wiki.eclipse.org/index.php/JFaceSnippets|JFace Snippets]] |
== Tools ==
^What ^Location ^
| Window Builder is a WYSIWYG GUI Editor | [[https://developers.google.com/java-dev-tools/wbpro/wizards/swt/?hl=en-US|Window Builder Tutorials]] |
== Snippters ==
^Describtion ^ Link^
| The Snippet to insert Widgets into the table | [[http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/PlacearbitrarycontrolsinaSWTtable.htm|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.
{{http://i.imgur.com/2RoZJ.png}}
===== SWT Basics =====
=== Glossar ===
* **[[http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Shell.html|org.eclipse.swt.widgets.Shell]]** - represents a Window
* **[[http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Display.html|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. \\ {{http://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 ===
- 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.
- Every custom widget must extend the Composite or Canvas class. Only for these base classes, API compliance is guaranteed. \\ {{http://i45.tinypic.com/2vt5xkp.png?500}}
- 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...);
{{http://i520.photobucket.com/albums/w327/schajtan/2013-11-26_16-15-32_zpsbda4496c.png?400}}
* 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!
^Style^Description^
|BORDER |Adds a border. | {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-01_18-50-11_zps226845f1.png}} |
|CLOSE |Adds a close button. | {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-01_18-51-51_zps52a6cd42.png}} |
|MIN |Adds a minimize button. When MIN is visible - CLOSE is visible too | {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-01_18-52-44_zps6c944231.png}} |
|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. | {{http://i520.photobucket.com/albums/w327/schajtan/2015-03-17_17-06-58_zps1b807006.png}} |
|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).| {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-01_18-54-35_zps3aa318ce.png}} |
|RESIZE |Adds a resizable border. You need TRIM to resize windows.| {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-01_18-56-00_zps98345bd2.png}} |
|TITLE |Adds a title bar.| {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-01_18-58-08_zps30b4445d.png}} |
|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!| {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-01_19-30-42_zpsb906fd8a.png}} 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.| {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-01_19-37-45_zps981cfaed.png}} 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| {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-27_13-59-18_zps7a19fce5.png?600}} |
|Table to make a user choice | {{http://i520.photobucket.com/albums/w327/schajtan/2014-11-27_14-02-53_zpse465626c.png?600}} |
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.
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-13_01-13-15_zps68f18522.png}}
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-13_01-18-59_zpsac83202f.png}}
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-13_01-21-17_zpsf9db80f5.png}}
=== Styling ===
== Hiding vertical table lines ==
| {{http://i520.photobucket.com/albums/w327/schajtan/2015-02-01_11-38-38_zps83312260.png}} | {{http://i520.photobucket.com/albums/w327/schajtan/2015-02-01_11-36-41_zps0a64b1db.png}} |
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.
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-14_14-42-08_zpsef6ef782.png}}
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.
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-13_13-38-23_zpsdd76964d.png}}
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("Link");
/**
* 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
{{http://i520.photobucket.com/albums/w327/schajtan/2015-01-26_10-19-45_zps0baaaa4a.png}}
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);
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-04_09-17-29_zpsf5e9b551.png}}
**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);
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-04_09-34-42_zps31c8366a.png}}
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.
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-04_09-57-26_zps69027c73.png}}
===== Layouts =====
The **FillLayout, RowLayout, GridLayout, FormLayout** are described [[http://www.eclipse.org/articles/article.php?file=Article-Understanding-Layouts/index.html|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.
{{http://i520.photobucket.com/albums/w327/schajtan/2014-06-18_12-26-05_zps868437b6.png?800}}
{{http://i520.photobucket.com/albums/w327/schajtan/2014-06-18_12-28-02_zps9df14c6e.png?800}}
**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...
{{http://i520.photobucket.com/albums/w327/schajtan/2014-08-20_13-44-24_zps5ce41690.png}}
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.
{{http://i520.photobucket.com/albums/w327/schajtan/2014-08-20_13-48-15_zpsf87f30e7.png}} \\
{{http://i520.photobucket.com/albums/w327/schajtan/2014-08-20_13-49-39_zps738fef76.png}} \\
===== Drawing Cycle =====
**Every Composite:**
* **computeSize(int wHint, int hHint, boolean changed)** - 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. \\ NOT RECURSIVE - children are not redrawn.
* **layout(boolean changed, boolean all)** - updates the whole hierarchy under the composite \\ IS RECURSIVE<\fc> - children are redrawn when all=true.
* **redraw()** - invalidates widget's screen-area and adds a repaint request to the queue. The redraw-Events are queued and collapsed in the queue. \\ IS RECURSIVE - 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: the redraw doesn't happen immediately. For immediate redraw call redraw();update();. \\IS RECURSIVE - children are redrawn when all=true.
* **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 [[http://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm|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 [[http://help.eclipse.org/kepler/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Findex.html|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/
{{youtube>NyYqXsavqp4?medium}}
===== 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 [[http://wiki.eclipse.org/index.php/JFaceSnippets|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 [[http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjface%2Fwindow%2Fpackage-summary.html|here]]. |
|**More concrete classes:**
| [[http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjface%2Fdialogs%2FMessageDialog.html|MessageDialog]] | {{http://i.imgur.com/tmJnf.png?200}} \\ 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)
|
| [[http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjface%2Fdialogs%2FTrayDialog.html|TrayDialog]]| {{http://i.imgur.com/wMe0Z.png?400}} |
| [[http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjface%2Fwindow%2FToolTip.html|ToolTip]] | {{http://archive.eclipse.org/eclipse/downloads/drops/R-3.3-200706251500/whatsnew/images/tooltip.png}} \\ {{http://i.imgur.com/5jFJ3.png?400}} \\
/*
* 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();
}
}
|
| [[http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/dialogs/PopupDialog.html|PopupDialog]] |{{http://i.imgur.com/Rzzyz.png?100}} Will not close itself on mouseout.| |
|
|**The concrete classes:**
The containers below are lister here: [[http://sureshkrishna.wordpress.com/2008/03/15/jface-dialogs-which-one-is-right-for-you/|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^
|[[http://www.eclipse.org/articles/Article-Table-viewer/table_viewer.html|TableViewer]]| Can display Data in a table. Is capable to sort columns| {{http://i.imgur.com/q5qHO.png?300}} |
|[[http://www.eclipse.org/articles/Article-TreeViewer/TreeViewerArticle.htm#update|TreeViewer]]| Can display hierarchical data. Is capable to have columns. Some columns can display hierarchical content, some can display non hierarchical content.| {{http://i.imgur.com/g5C9w.png?300}} |
== Tech ==
[[http://wiki.eclipse.org/FAQ_What_kinds_of_viewers_does_JFace_provide%3F|JFace Viewers]] use the Model-View-Controller metapher.
^MVC ^Class ^ Explanation ^
|View |**Viewer** \\ The Viewer list is available [[http://wiki.eclipse.org/FAQ_What_kinds_of_viewers_does_JFace_provide%3F|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 [[http://help.eclipse.org/helios/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjface%2Fviewers%2FContentViewer.html|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
|
|[[http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fcore%2Fruntime%2FIAdapterFactory.html|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.
*
*
*
* @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 tables = new ArrayList();
{
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 getTables() {
return tables;
}
public void setTables(List tables) {
firePropertyChange("tables", this.tables, this.tables = tables);
}
public static class Restauranttable{
int number;
boolean booked;
List neighbours = new ArrayList();
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 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.
{{http://i520.photobucket.com/albums/w327/schajtan/2014-07-07_15-12-27_zps713f6499.png}}
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.
{{https://eclipse.org/articles/Article-Forms/article.html}}
{{https://eclipse.org/articles/Article-Forms/images/plugin-overview.gif}}
{{https://eclipse.org/articles/Article-Forms/images/intro.gif}}
{{https://eclipse.org/articles/Article-Forms/images/master-details1.gif}}
== 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
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-13_16-21-13_zps7dc19e01.png}}
{{http://i520.photobucket.com/albums/w327/schajtan/2015-02-13_16-21-36_zps6a450bfb.png}}
====== 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==
|{{http://i520.photobucket.com/albums/w327/schajtan/2014-09-05_10-52-06_zps1f3c8b78.png}}| {{http://i520.photobucket.com/albums/w327/schajtan/2014-09-05_10-50-53_zps74b78939.png}}|
==GridLayout==
|{{http://i520.photobucket.com/albums/w327/schajtan/2014-09-05_10-53-53_zps9047b0fe.png}}| {{http://i520.photobucket.com/albums/w327/schajtan/2014-09-05_10-55-28_zps9d2171b2.png}}|
==FormLayout==
|{{http://i520.photobucket.com/albums/w327/schajtan/2014-09-05_10-56-45_zps2195b8af.png}}| {{http://i520.photobucket.com/albums/w327/schajtan/2014-09-05_10-57-28_zpsb8dfa9da.png}}|
====== Windowbuilder ======
The WindowBuilder should be used to create composites in SWT.
It has a preview function which is quite fragile.
{{http://i520.photobucket.com/albums/w327/schajtan/2014-10-14_12-29-48_zps1ecc5603.png?800}}
== 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