programming:java:swt
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revision | |||
| programming:java:swt [2023/11/01 07:31] – removed - external edit (Unknown date) 127.0.0.1 | programming:java:swt [2023/11/01 07:31] (current) – ↷ Page moved from camunda:programming:java:swt to programming:java:swt skipidar | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== SWT ====== | ||
| + | ==Literature== | ||
| + | |||
| + | ^Info ^Location ^ | ||
| + | |0. SWT Tutorial | [[http:// | ||
| + | |1. SWT is made up of widgets, which are listed | [[http:// | ||
| + | |2. Google Window builder is a tool for building SWT UI. | [[https:// | ||
| + | |3. Using SWT Layouts | ||
| + | |4. Creating own SWT Widgets | [[http:// | ||
| + | |5. JFace framework provides higher level Widgets | [[http:// | ||
| + | |6. RCP Widgets like analogue clock | [[http:// | ||
| + | |7. Forms API like sections and expandables | [[https:// | ||
| + | |||
| + | == Additonal SWT Widget libs == | ||
| + | ^What ^Location ^ | ||
| + | | The Eclipse **Nebula Project** provides custom SWT widgets | [[http:// | ||
| + | | The google code Opal Project provides many widget too | [[http:// | ||
| + | | JFace Widgets extens SWT, simplify the Widgets usage but do not hide the native SWT API (Git and CVS unfortunately offline :( ) | [[http:// | ||
| + | |||
| + | == Tools == | ||
| + | ^What ^Location ^ | ||
| + | | Window Builder is a WYSIWYG GUI Editor | [[https:// | ||
| + | |||
| + | == Snippters == | ||
| + | ^Describtion ^ Link^ | ||
| + | | The Snippet to insert Widgets into the table | [[http:// | ||
| + | |||
| + | |||
| + | ===== 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 | ||
| + | by excluding the **awt** package from autoimports. | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | |||
| + | ===== SWT Basics ===== | ||
| + | |||
| + | |||
| + | === Glossar === | ||
| + | * **[[http:// | ||
| + | * **[[http:// | ||
| + | * **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**, | ||
| + | * **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:// | ||
| + | * **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:// | ||
| + | - Widgets have to be manually disposed by calling < | ||
| + | - The SWT widgets change their appearance, depending on the stylebit, which is passed during the construction. The stylebits are documented in the JavaDoc. \\ <sxh java> | ||
| + | new Button(shell, | ||
| + | new Button(shell, | ||
| + | </ | ||
| + | |||
| + | ===== Widgets ===== | ||
| + | ==== Shell = Windows ==== | ||
| + | Shell can be created with different parameters: < | ||
| + | {{http:// | ||
| + | |||
| + | * 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:// | ||
| + | |CLOSE |Adds a close button. | {{http:// | ||
| + | |MIN |Adds a minimize button. When MIN is visible - CLOSE is visible too | {{http:// | ||
| + | |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:// | ||
| + | |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:// | ||
| + | |RESIZE |Adds a resizable border. You need TRIM to resize windows.| {{http:// | ||
| + | |TITLE |Adds a title bar.| {{http:// | ||
| + | |DIALOG_TRIM |Convenience style, equivalent to < | ||
| + | |SHELL_TRIM |Convenience style, equivalent to < | ||
| + | |APPLICATION_MODAL |Creates a Shell that's modal to the application. Note that you should specify only one of APPLICATION_MODAL, | ||
| + | |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:// | ||
| + | |||
| + | |||
| + | |||
| + | 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: | ||
| + | |new Shell(Shell parent, SWT.NONE);| Erstellt ein Fenster ohne Deko, siehe Bild oben| | ||
| + | |new Shell(Shell parent, SWT.MODAL_APPLICATION); | ||
| + | |||
| + | |||
| + | ==== Tables ==== | ||
| + | |||
| + | The Table in SWT | ||
| + | |Table to display data| {{http:// | ||
| + | |Table to make a user choice | {{http:// | ||
| + | |||
| + | 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 | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | </ | ||
| + | |||
| + | === 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:// | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | |||
| + | === Styling === | ||
| + | |||
| + | == Hiding vertical table lines == | ||
| + | |||
| + | | {{http:// | ||
| + | |||
| + | Is dine via a PaintListener, | ||
| + | |||
| + | < | ||
| + | table.addListener(SWT.EraseItem, | ||
| + | @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, | ||
| + | | ||
| + | // 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, | ||
| + | col1.setWidth(100); | ||
| + | col1.setText(" | ||
| + | |||
| + | TableColumn col2 = new TableColumn(table, | ||
| + | col2.setWidth(100); | ||
| + | col2.setText(" | ||
| + | |||
| + | |||
| + | TableItem tableItem = new TableItem(table, | ||
| + | tableItem.setText(0, | ||
| + | tableItem.setText(1, | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | == 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:// | ||
| + | |||
| + | <sxh java> | ||
| + | TreeItem trtmNewTreeitem_1 = new TreeItem(tree, | ||
| + | trtmNewTreeitem_1.setText(new String[] {"from TableItem1", | ||
| + | |||
| + | TreeItem trtmNewTreeitem = new TreeItem(tree, | ||
| + | trtmNewTreeitem.setText(new String[] {"from TableItem3", | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | == 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:// | ||
| + | |||
| + | <sxh java> | ||
| + | |||
| + | 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, | ||
| + | setLayout(new FillLayout(SWT.HORIZONTAL)); | ||
| + | |||
| + | Tree tree = new Tree(this, SWT.BORDER); | ||
| + | |||
| + | TreeColumn trclmnNewColumn = new TreeColumn(tree, | ||
| + | trclmnNewColumn.setWidth(200); | ||
| + | trclmnNewColumn.setText(" | ||
| + | |||
| + | TreeColumn trclmnNewColumn_1 = new TreeColumn(tree, | ||
| + | trclmnNewColumn_1.setWidth(100); | ||
| + | trclmnNewColumn_1.setText(" | ||
| + | |||
| + | TreeItem trtmNewTreeitem = new TreeItem(tree, | ||
| + | trtmNewTreeitem.setText(new String[] {"New TreeItem", | ||
| + | |||
| + | Link l = new Link(tree, SWT.NONE); | ||
| + | l.setText("< | ||
| + | |||
| + | |||
| + | /** | ||
| + | * 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, | ||
| + | |||
| + | } | ||
| + | |||
| + | |||
| + | @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' | ||
| + | < | ||
| + | IKeyFormatter formatter = SWTKeySupport.getKeyFormatterForPlatform(); | ||
| + | formatter.format(SWT.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, | ||
| + | sortMenuItemNo.setText(" | ||
| + | </ | ||
| + | |||
| + | More about Menus and mnemonics | ||
| + | http:// | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | For some reason acceleratos does not work on Windows7 | ||
| + | More about Menus ans accelerators: | ||
| + | http:// | ||
| + | |||
| + | |||
| + | |||
| + | ==== 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 | ||
| + | |||
| + | <sxh java> | ||
| + | |||
| + | 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, | ||
| + | CompositeCardContainer.this.getShell().redraw(); | ||
| + | } | ||
| + | }); | ||
| + | }; | ||
| + | }; | ||
| + | |||
| + | | ||
| + | | ||
| + | |||
| + | </ | ||
| + | ===== Widget Style ===== | ||
| + | |||
| + | == Background == | ||
| + | **SWT.NO_BACKGROUND** sets a single child to inherit the BG from the parent. | ||
| + | |||
| + | < | ||
| + | Composite composite_1 = new Composite(composite, | ||
| + | </ | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | |||
| + | **SWT.INHERIT_FORCE**, | ||
| + | |||
| + | < | ||
| + | Composite parent = new Composite(this, | ||
| + | parent.setBackgroundMode(SWT.INHERIT_FORCE); | ||
| + | parent.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE)); | ||
| + | |||
| + | Composite childWithBg = new Composite(parent, | ||
| + | // parent will override this chailds BG, because of INHERIT_FORCE | ||
| + | childWithBg1.setBackground(SWTResourceManager.getColor(SWT.COLOR_CYAN)); | ||
| + | |||
| + | Composite childWithoutBg = new Composite(parent, | ||
| + | </ | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | Children may override the BG even if the parent defined the **INHERIT_FORCE** mode. \\ | ||
| + | So **SWT.INHERIT_FORCE**, | ||
| + | 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:// | ||
| + | ===== Layouts ===== | ||
| + | The **FillLayout, | ||
| + | |||
| + | |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, | ||
| + | * **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, | ||
| + | |||
| + | Composite parent = new Composite(shell, | ||
| + | Composite child = new Composite(parent, | ||
| + | | ||
| + | Button button = new Button(child, | ||
| + | button.setText(" | ||
| + | | ||
| + | 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(); | ||
| + | </ | ||
| + | * **layout** - use **current configurations**, | ||
| + | |||
| + | |||
| + | === GridLayout === | ||
| + | |||
| + | Can make a row " | ||
| + | |||
| + | This Element then will disapear and will not take any space anymore. | ||
| + | |||
| + | |||
| + | {{http:// | ||
| + | {{http:// | ||
| + | |||
| + | |||
| + | |||
| + | **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:// | ||
| + | |||
| + | 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:// | ||
| + | {{http:// | ||
| + | ===== Drawing Cycle ===== | ||
| + | **Every Composite: | ||
| + | * **computeSize(int wHint, int hHint, <fc # | ||
| + | |||
| + | * **layout(boolean changed)** - updates the layout, according to the constraints. \\ <fc # | ||
| + | * **layout(boolean changed, boolean all)** - updates the whole hierarchy under the composite \\ <fc # | ||
| + | |||
| + | * **redraw()** - invalidates widget' | ||
| + | * **redraw(x, | ||
| + | |||
| + | * **update()** - forces all outstanding paint requests in the queue to be done immediately! | ||
| + | |||
| + | <sxh java> | ||
| + | //redraw whe whole Composite hierarchy immediately: | ||
| + | composite.getShell().layout(true, | ||
| + | composite.getShell().redraw(); | ||
| + | composite.getShell().update(); | ||
| + | </ | ||
| + | ===== Own Widgets ===== | ||
| + | Howt create own widgets is described [[http:// | ||
| + | |||
| + | - 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, | ||
| + | |||
| + | ===== Key Codes ===== | ||
| + | To capture Keypresses add a Filter to the Display. | ||
| + | The possible Color Codes are stored in the [[http:// | ||
| + | < | ||
| + | // 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(" | ||
| + | if (event.keyCode == SWT.CR || event.keyCode == SWT.KEYPAD_CR) { | ||
| + | pageManagerService.reloadActivePart(); | ||
| + | } | ||
| + | } | ||
| + | }; | ||
| + | composite.getDisplay().addFilter(SWT.Traverse, | ||
| + | </ | ||
| + | |||
| + | |||
| + | ===== Disposed Widgets ===== | ||
| + | Die Widgets werden entsorg, wenn deren Parent entsorgt wurde. \\ | ||
| + | Die Widgets sind im Zustand " | ||
| + | |||
| + | Sehr oft bleiben Listener übrig, welche versuchen die Widgets im Disposed Zustand zu nutzen. Das provoziert eine SWTException. | ||
| + | Da ein Widget jederzeit " | ||
| + | |||
| + | Dieser Exceptin ist ein gutes merkmal, um einen Listenr zu deregestrieren. | ||
| + | |||
| + | <sxh java> | ||
| + | final Listener enterListener = new Listener() { | ||
| + | @Override | ||
| + | public void handleEvent(Event event) { | ||
| + | try { | ||
| + | System.out.println(" | ||
| + | } catch (SWTException e) { | ||
| + | display.removeFilter(SWT.KeyDown, | ||
| + | } | ||
| + | } | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | ===== Animations ===== | ||
| + | There are animations available in SWT. | ||
| + | |Update-Site: | ||
| + | |Plugin: |< | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | </ | ||
| + | |Package: |org.eclipse.nebula.animation| | ||
| + | |||
| + | Details | ||
| + | |||
| + | {{youtube> | ||
| + | |||
| + | |||
| + | |||
| + | ===== Mouse position ===== | ||
| + | The mouse position is retrieved relative to the Shell (Window) | ||
| + | There are some Fallpits: | ||
| + | |||
| + | <sxh java> | ||
| + | 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()); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | <sxh> | ||
| + | /* The size and position | ||
| + | * So toDisplay() can be safely used here. | ||
| + | */ | ||
| + | Rectangle clientArea = parent.getClientArea(); | ||
| + | Point pos = parent.toDisplay(clientArea.x, | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | ====== 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:// | ||
| + | |||
| + | |||
| + | ==== Jface Basics ==== | ||
| + | |||
| + | All the bindings are executed within a context called Realm (Kingdom) as stated here: https:// | ||
| + | |||
| + | So the realm must have been initialized. This is done by eclipse automaticall or may be doneas following, when not using the Eclipse platform: | ||
| + | |||
| + | <sxh java> | ||
| + | public static void main(String[] args) { | ||
| + | Display display = new Display(); | ||
| + | |||
| + | Realm.runWithDefault(SWTObservables.getRealm(display), | ||
| + | @Override | ||
| + | public void run() { | ||
| + | // code that uses bindings | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== Jface Dialogs ==== | ||
| + | |||
| + | ^TopLevelContainers ^ | ||
| + | |**TopLevel classes:** < | ||
| + | They are described [[http:// | ||
| + | |< | ||
| + | |||
| + | | [[http:// | ||
| + | <sxh java> | ||
| + | boolean result = MessageDialog.openConfirm(parent, | ||
| + | MessageDialog.openError(parent, | ||
| + | MessageDialog.openWarning(parent, | ||
| + | </ | ||
| + | | [[http:// | ||
| + | | [[http:// | ||
| + | /* | ||
| + | * Tooltip example snippet: create a balloon tooltip for a tray item | ||
| + | * | ||
| + | * For a list of all SWT example snippets see | ||
| + | * http:// | ||
| + | | ||
| + | * @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, | ||
| + | tip.setMessage(" | ||
| + | |||
| + | Text tfTooltip = new Text(shell, SWT.BORDER); | ||
| + | tfTooltip.setText(" | ||
| + | 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(), | ||
| + | tip.setVisible(true); | ||
| + | } | ||
| + | }); | ||
| + | Text tfNext = new Text(shell, SWT.BORDER); | ||
| + | tfNext.setText(" | ||
| + | shell.setBounds(50, | ||
| + | shell.open(); | ||
| + | while (!shell.isDisposed()) { | ||
| + | if (!display.readAndDispatch()) | ||
| + | display.sleep(); | ||
| + | } | ||
| + | display.dispose(); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | | [[http:// | ||
| + | |||
| + | </ | ||
| + | |< | ||
| + | The containers below are lister here: [[http:// | ||
| + | |||
| + | </ | ||
| + | ==== 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**, | ||
| + | | ||
| + | |||
| + | |||
| + | ==List == | ||
| + | All viewers can: | ||
| + | * show and hide Table | ||
| + | * Add Images to columns | ||
| + | * Add Editors to cells | ||
| + | |||
| + | ^Viewer ^Describtion ^Pic^ | ||
| + | |[[http:// | ||
| + | |[[http:// | ||
| + | |||
| + | == Tech == | ||
| + | |||
| + | [[http:// | ||
| + | |||
| + | ^MVC ^Class ^ Explanation ^ | ||
| + | |View |**Viewer** \\ The Viewer list is available [[http:// | ||
| + | |Model | **Input** \\ Viewer# | ||
| + | |Controler | **ContentProvider** \\ Viewer# | ||
| + | |Controler2 | **LabelProvider** \\ Viewer# | ||
| + | |||
| + | |||
| + | ContentProvider is supported by the IAdapter Pattern in Eclipse RCP. | ||
| + | ^PatternConcept ^ Explanation ^ | ||
| + | |ContentProvider|< | ||
| + | |LabelProvider|< | ||
| + | * Viewer - this LabelViewer will be used when both are added. | ||
| + | * ViewerColumn | ||
| + | </ | ||
| + | |[[http:// | ||
| + | |||
| + | |||
| + | |||
| + | == How to use the JFace Viewers in RCP: == | ||
| + | **Example of instantiating a Viewer** | ||
| + | <sxh java> | ||
| + | // 1. create Viewer | ||
| + | viewer = new TableViewer(parent, | ||
| + | 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()); | ||
| + | |||
| + | // 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' | ||
| + | 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(), | ||
| + | | ||
| + | | ||
| + | | ||
| + | private void createColumns(final Composite parent) { | ||
| + | String[] titles = { " | ||
| + | int[] bounds = { 200, 100 }; | ||
| + | |||
| + | TreeViewerColumn col; | ||
| + | |||
| + | col = createViewerColumn(titles[0], | ||
| + | //Second way to add a LableProvider. The first is adding it to the viewer itself. When viewer has a LabelProvider - column' | ||
| + | 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], | ||
| + | //Second way to add a LableProvider. The first is adding it to the viewer itself. When viewer has a LabelProvider - column' | ||
| + | col.setLabelProvider(new ColumnLabelProvider() { | ||
| + | @Override | ||
| + | public String getText(Object element) { | ||
| + | return ((Module) element).getName(); | ||
| + | |||
| + | } | ||
| + | }); | ||
| + | |||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | **Example of creating a ContentProvider** | ||
| + | <sxh java> | ||
| + | /** | ||
| + | * 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 | ||
| + | | ||
| + | * @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** | ||
| + | <sxh java> | ||
| + | /** | ||
| + | * 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 adapter = (IWorkbenchAdapter) adapterObject; | ||
| + | return adapter.getImageDescriptor(element).createImage(); | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public String getText(Object element) { | ||
| + | Object adapterObject = Platform.getAdapterManager().getAdapter(element, | ||
| + | IWorkbenchAdapter adapter = (IWorkbenchAdapter) adapterObject; | ||
| + | return adapter.getLabel(element); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | **Example of creating an AdapterFactory** | ||
| + | <sxh java> | ||
| + | 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' | ||
| + | * ContentProviders are able to convert Objects to an Interface {@link IWorkbenchAdapter}, | ||
| + | * they can handle. | ||
| + | | ||
| + | */ | ||
| + | public class FactoryModule2IWorkbenchAdapter implements IAdapterFactory { | ||
| + | |||
| + | private static final String MODULE_TREE_NODE_ICON_PATH = " | ||
| + | |||
| + | 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, | ||
| + | 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**, | ||
| + | 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 | ||
| + | |||
| + | <sxh java> | ||
| + | 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.add(new Restauranttable(1, | ||
| + | tables.add(new Restauranttable(2, | ||
| + | tables.add(new Restauranttable(3, | ||
| + | tables.add(new Restauranttable(4, | ||
| + | tables.add(new Restauranttable(5, | ||
| + | |||
| + | tables.get(1).setNeighbour(tables.get(2)); | ||
| + | tables.get(3).setNeighbour(tables.get(4)); | ||
| + | } | ||
| + | |||
| + | |||
| + | public List< | ||
| + | return tables; | ||
| + | } | ||
| + | |||
| + | |||
| + | public void setTables(List< | ||
| + | firePropertyChange(" | ||
| + | } | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | public static class Restauranttable{ | ||
| + | int number; | ||
| + | boolean booked; | ||
| + | List< | ||
| + | Restauranttable parent; | ||
| + | |||
| + | @Override | ||
| + | public String toString() { | ||
| + | return "Table " | ||
| + | } | ||
| + | |||
| + | 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< | ||
| + | return neighbours; | ||
| + | } | ||
| + | |||
| + | public Restauranttable getParent() { | ||
| + | return parent; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | |||
| + | |||
| + | </ | ||
| + | |||
| + | == TableViewer == | ||
| + | The TableViewer displays the list of restauraunt tables in two columns: | ||
| + | |||
| + | |Table number| booked | | ||
| + | |||
| + | <sxh java> | ||
| + | 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, | ||
| + | setLayout(new FillLayout(SWT.HORIZONTAL)); | ||
| + | |||
| + | TreeViewer treeViewer = new TreeViewer(this, | ||
| + | Tree tree = treeViewer.getTree(); | ||
| + | |||
| + | TreeViewerColumn treeViewerColumn1 = new TreeViewerColumn(treeViewer, | ||
| + | TreeColumn treeColumn1 = treeViewerColumn1.getColumn(); | ||
| + | treeColumn1.setWidth(100); | ||
| + | treeColumn1.setText(" | ||
| + | |||
| + | TreeViewerColumn treeViewerColumn2 = new TreeViewerColumn(treeViewer, | ||
| + | TreeColumn treeColumn2 = treeViewerColumn2.getColumn(); | ||
| + | treeColumn2.setWidth(100); | ||
| + | treeColumn2.setText(" | ||
| + | createContents(); | ||
| + | |||
| + | setupMvc(treeViewer, | ||
| + | } | ||
| + | |||
| + | public void setupMvc(TreeViewer treeViewer, TreeViewerColumn treeViewerColumn1, | ||
| + | // 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()); | ||
| + | // | ||
| + | // | ||
| + | // | ||
| + | } | ||
| + | }); | ||
| + | // 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("" | ||
| + | // | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | // add the data to the viewer | ||
| + | treeViewer.setInput(model); | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Create contents of the shell. | ||
| + | */ | ||
| + | protected void createContents() { | ||
| + | setText(" | ||
| + | setSize(450, | ||
| + | |||
| + | } | ||
| + | |||
| + | @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, | ||
| + | buttonRadion.setSelection(true); | ||
| + | buttonRadio.notifyListeners(SWT.Selection, | ||
| + | </ | ||
| + | |||
| + | A good tutorial: http:// | ||
| + | Use WindowBuilder to establish bindings. | ||
| + | |||
| + | == Summary== | ||
| + | The Widgets are bound to the model: | ||
| + | <sxh java> | ||
| + | // A Model may be any POJO | ||
| + | public class MyPojoModel{ | ||
| + | private String 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 " | ||
| + | // Factory PojoProperties observes Pojos | ||
| + | IObservableValue enabledModelObserveValue = PojoProperties.value(" | ||
| + | |||
| + | // 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, | ||
| + | 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 " | ||
| + | <sxh java> | ||
| + | // Properties of Person " | ||
| + | IBeanValueProperty propName = BeanProperties.value(Person.class, | ||
| + | IBeanValueProperty propFirstname = BeanProperties.value( Person.class, | ||
| + | |||
| + | // IObservable Set of Persons | ||
| + | WritableSet writableSet = new WritableSet(this.viewModel.getPeople(), | ||
| + | |||
| + | /* 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, | ||
| + | |||
| + | // 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(), | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | ===API:=== | ||
| + | |||
| + | | IObservableList |< | ||
| + | | bindingContext|< | ||
| + | bindingContext.bindValue( | ||
| + | SWTObservables.observeText(selectedCommitterName), | ||
| + | BeansObservables.observeDetailValue(selection, | ||
| + | </ | ||
| + | | SWTObservables|< | ||
| + | SWTObservables.observeTooltipText(label) | ||
| + | |||
| + | observeDelayedValue(int, | ||
| + | observeEnabled(Widget) | ||
| + | observeEnabled(Control) | ||
| + | observeVisible(Control) | ||
| + | observeTooltipText(Widget) | ||
| + | observeTooltipText(Control) | ||
| + | observeSelection(Widget) | ||
| + | observeSelection(Control) | ||
| + | observeMin(Control) | ||
| + | observeMax(Control) | ||
| + | observeText(Control, | ||
| + | observeText(Control, | ||
| + | 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|< | ||
| + | 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|< | ||
| + | |||
| + | observeValue(Object, | ||
| + | observeValue(Realm, | ||
| + | observeMap(IObservableSet, | ||
| + | observeMap(IObservableSet, | ||
| + | observeMap(Realm, | ||
| + | observeMap(Realm, | ||
| + | observeMap(Object, | ||
| + | observeMap(Object, | ||
| + | observeMaps(IObservableSet, | ||
| + | observeMaps(IObservableSet, | ||
| + | observeList(Realm, | ||
| + | observeList(Object, | ||
| + | observeList(Realm, | ||
| + | observeList(Object, | ||
| + | observeSet(Realm, | ||
| + | observeSet(Object, | ||
| + | valueFactory(Realm, | ||
| + | valueFactory(String) | ||
| + | listFactory(Realm, | ||
| + | listFactory(String, | ||
| + | setFactory(Realm, | ||
| + | setFactory(String) | ||
| + | observeDetailValue(Realm, | ||
| + | observeDetailValue(IObservableValue, | ||
| + | observeDetailValue(Realm, | ||
| + | observeDetailValue(IObservableValue, | ||
| + | observeDetailList(Realm, | ||
| + | observeDetailList(IObservableValue, | ||
| + | observeDetailSet(Realm, | ||
| + | observeDetailSet(IObservableValue, | ||
| + | observeDetailMap(Realm, | ||
| + | observeDetailMap(IObservableValue, | ||
| + | observeSet(Realm, | ||
| + | observeSet(Object, | ||
| + | setFactory(Realm, | ||
| + | setFactory(String, | ||
| + | setToMapFactory(Class, | ||
| + | mapPropertyFactory(Realm, | ||
| + | mapPropertyFactory(String) | ||
| + | </ | ||
| + | | BeanProperties|< | ||
| + | |||
| + | IBeanValueProperty propName = BeanProperties.value(Person.class, | ||
| + | |||
| + | |||
| + | value(String) | ||
| + | value(String, | ||
| + | value(Class, | ||
| + | value(Class, | ||
| + | values(Class, | ||
| + | values(String[]) | ||
| + | set(String) | ||
| + | set(String, | ||
| + | set(Class, String) | ||
| + | set(Class, String, Class) | ||
| + | list(String) | ||
| + | list(String, | ||
| + | list(Class, | ||
| + | list(Class, | ||
| + | map(String) | ||
| + | map(String, | ||
| + | map(Class, String) | ||
| + | map(Class, String, Class, Class) | ||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | ====JFace ControlDecorators==== | ||
| + | It is possible to decorate the Controls like Forms or Labels using SWT. | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | Here is an example with **FieldDecorationRegistry.DEC_WARNING** | ||
| + | |||
| + | <sxh java> | ||
| + | ControlDecoration controlDecoration = new ControlDecoration(lblNewLabel, | ||
| + | controlDecoration.setDescriptionText(" | ||
| + | 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(), | ||
| + | <sxh java> | ||
| + | | ||
| + | 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: | ||
| + | | Feature-Name: | ||
| + | |< | ||
| + | | ||
| + | * org.eclipse.ui.forms | ||
| + | </ | ||
| + | |< | ||
| + | | ||
| + | * org.eclipse.ui.forms.* | ||
| + | * org.eclipse.ui.forms.widgets.* | ||
| + | </ | ||
| + | |||
| + | The Forms API is described here https:// | ||
| + | |||
| + | It brings some layouts and expandable secitons and forms. | ||
| + | Indeed it implements the forms, which are used within eclipse to display plugins, etc. | ||
| + | |||
| + | {{https:// | ||
| + | |||
| + | {{https:// | ||
| + | |||
| + | {{https:// | ||
| + | |||
| + | {{https:// | ||
| + | |||
| + | |||
| + | == 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:// | ||
| + | |||
| + | {{http:// | ||
| + | ====== Animation ====== | ||
| + | **Rules:** | ||
| + | - Use timer for animation: <sxh java> | ||
| + | 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 <sxh java> | ||
| + | Display.getDefault().asyncExec(new Runnable() { | ||
| + | public void run() { | ||
| + | someSwtLabel.setText(" | ||
| + | } | ||
| + | }); | ||
| + | </ | ||
| + | - Catch the errors, which will occur, when the widget is disposed by other threads, e.g. when the shell is closed <sxh java> | ||
| + | try{ | ||
| + | composite.width ++; | ||
| + | |||
| + | //layout of the composite doesn' | ||
| + | // | ||
| + | |||
| + | //layout of parent works | ||
| + | composite.getParent().layout(true, | ||
| + | |||
| + | composite.redraw(); | ||
| + | composite.update(); | ||
| + | |||
| + | }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: | ||
| + | <sxh java> | ||
| + | Display.getDefault().asyncExec(new Runnable() { | ||
| + | public void run() { | ||
| + | someSwtLabel.setText(" | ||
| + | } | ||
| + | }); | ||
| + | </ | ||
| + | ====== 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**. | ||
| + | < | ||
| + | |||
| + | |||
| + | |||
| + | ====== 2D Drawing ====== | ||
| + | |||
| + | <sxh java> | ||
| + | |||
| + | color_bgcolor | ||
| + | color_foreground | ||
| + | color_border | ||
| + | color_dark_shadow | ||
| + | color_highlight_shadow | ||
| + | color_light_shadow | ||
| + | color_normal_shadow | ||
| + | color_text | ||
| + | |||
| + | // | ||
| + | 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)/ | ||
| + | |||
| + | // | ||
| + | //0. default background | ||
| + | e.gc.setBackground(color_light_shadow); | ||
| + | e.gc.fillRectangle(0, | ||
| + | |||
| + | |||
| + | //1. draw a gradient shadow | ||
| + | e.gc.setForeground(color_light_shadow); | ||
| + | e.gc.setBackground(color_dark_shadow); | ||
| + | e.gc.fillGradientRectangle(shadow_margin_left, | ||
| + | |||
| + | //2. draw a light frame | ||
| + | if(accordion.styleItemBorder){ | ||
| + | e.gc.setForeground(color_highlight_shadow); | ||
| + | e.gc.drawRectangle(1, | ||
| + | } | ||
| + | |||
| + | //3. draw a dark frame | ||
| + | e.gc.setForeground(color_normal_shadow); | ||
| + | e.gc.drawRectangle(0, | ||
| + | |||
| + | //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, | ||
| + | |||
| + | //rotate back | ||
| + | tr.identity(); | ||
| + | e.gc.setTransform(tr); | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | //FOR DEBUGGING | ||
| + | if(HAccordion.DEBUG){ | ||
| + | |||
| + | //DEMO colors | ||
| + | e.gc.setBackground(color_bgcolor); | ||
| + | e.gc.fillRectangle(0, | ||
| + | |||
| + | e.gc.setBackground(color_dark_shadow); | ||
| + | e.gc.fillRectangle(10, | ||
| + | |||
| + | e.gc.setBackground(color_foreground); | ||
| + | e.gc.fillRectangle(10, | ||
| + | |||
| + | e.gc.setBackground(color_border); | ||
| + | e.gc.fillRectangle(10, | ||
| + | |||
| + | e.gc.setBackground(color_dark_shadow); | ||
| + | e.gc.fillRectangle(10, | ||
| + | |||
| + | e.gc.setBackground(color_highlight_shadow); | ||
| + | e.gc.fillRectangle(10, | ||
| + | |||
| + | e.gc.setBackground(color_light_shadow); | ||
| + | e.gc.fillRectangle(10, | ||
| + | |||
| + | e.gc.setBackground(color_normal_shadow); | ||
| + | e.gc.fillRectangle(10, | ||
| + | |||
| + | |||
| + | |||
| + | //write down the index in the top left corner | ||
| + | int index = accordionItem.getParent().getItemsIterator().getIndexOf(accordionItem); | ||
| + | e.gc.drawText(String.valueOf(index), | ||
| + | |||
| + | //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(), | ||
| + | |||
| + | //use colors random color for filling | ||
| + | e.gc.setBackground(random); | ||
| + | e.gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); | ||
| + | |||
| + | //draw | ||
| + | e.gc.fillOval(0, | ||
| + | e.gc.drawRectangle(0, | ||
| + | } | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | // 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, | ||
| + | ImageData sourceData = new ImageData(width, | ||
| + | |||
| + | // 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, | ||
| + | |||
| + | // | ||
| + | gc.setBackground(NUM_DECORATOR_textColor); | ||
| + | gc.drawText(" | ||
| + | | ||
| + | gc.dispose(); | ||
| + | |||
| + | return image; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Generating a colored rectangle in memory | ||
| + | |||
| + | <sxh java> | ||
| + | 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, | ||
| + | for (int x = 4; x < 8; x++) { | ||
| + | for (int y = 4; y < 8; y++) { | ||
| + | imageData.setPixel(x, | ||
| + | } | ||
| + | } | ||
| + | return new Image(Display.getCurrent(), | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ====== 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:// | ||
| + | |||
| + | ==GridLayout== | ||
| + | |{{http:// | ||
| + | |||
| + | ==FormLayout== | ||
| + | |{{http:// | ||
| + | |||
| + | |||
| + | |||
| + | ====== Windowbuilder ====== | ||
| + | The WindowBuilder should be used to create composites in SWT. | ||
| + | It has a preview function which is quite fragile. | ||
| + | |||
| + | {{http:// | ||
| + | |||
| + | == Rules == | ||
| + | Following should be considered when creating widgets with Windowbuilder: | ||
| + | * when using object' | ||
| + | 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 | ||
