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 |