Creating UI with the net.sf.okapi.common.ui.abstracteditor Package
Preface
Okapi provides a framework to allow you quickly and painlessly build rich user interfaces for various Okapi components.
The framework is pure SWT. You can use SWT Designer with the framework, or do things manually if you wish so.
Parts of the Framework
The framework is located in the net.sf.okapi.common.ui.abstracteditor package of the okapi-core-ui Maven project.
Examples of Use
okapi-filter-html-uiokapi-filter-plaintext-uiokapi-filter-table-ui
General Concepts
The framework introduces the container-page-control model.
- A container is a window (shell), hosting one or more pages.
- A page is a panel (composite), hosting one or more controls, groups, or composites.
- A control is an SWT widget, like a check-box or push-button.
The framework provides the means for controls’ interoperability. A control on one page can communicate with any other control either on the same page, or on a different page in the same or a different container.
Containers can have any SWT-supported style. In Okapi parameters editors there are normally multi-tab resizable dialogs and pop-up OK/Cancel dialogs. The framework provides base classes for those.
Every tab in the multi-tab dialog is a page. The client area of the OK/Cancel dialog is also a page.
Configuring SWT Designer
If to create the pages you will be using the SWT Designer, you should configure Eclipse in a special way:
In Window > Preferences > WindowBuilder > SWT > Code Generation:
- check Use the existing code generation settings...
- in Default code generation settings > Variable generation activate Field
- in Default code generation settings > Statement generation activate Flat (optional)
In Window > Preferences > WindowBuilder > SWT > Code Generation > Variables > Miscellaneous:
- check Remember variable name in component (Important!)
If You Do SWT Manually
When you create a control, don’t forget to put its name in the name property:
myButton = new Button(parent, SWT.NONE);
myButton.setData("name", "myButton");
The Framework in a Nutshell
- Every page is a subclass of the SWT composite plus an implementation of the
IDialogPageinterface. - The multi-tab parameters editors are subclasses of
AbstractParametersEditor, OK/Cancel dialogs are subclasses ofAbstractBaseDialog. For a query dialog there’s no need to create a new subclass, you can callSWTUtils.inputQuery(), passing it a custom dialog page class. - There are 2 kinds of communication in the framework: internal (in-page) and inter-page. Internal is the communication between controls of the same page. It’s provided by the
interop()method of the page. Inter-page communication is provided by the editor class, and is contained in itsinterop(). - To configure the internal interop, write a body to the auto-generated
interop()method, and call it from event listeners of all involved controls. - To assign rules for inter-page interop, make a body for the editor’s
interop()method, and calladdSpeaker(pageClass, controlName)fromcreatePages(). SWTUtilsprovide a range of helpers to easily configure inter-page interop. A few of the helpers are:enableIfSelected(target, source),disableIfNotSelected(target, source).
You can omit the remainder of this document unless you have a time to try an example hands on.
Let’s Give It a Try
Now we will create a parameters editor with two tab pages, and a dialog box for user input, run from a button on the second page.
The first page has a check-box controlling availability of the second page’s button.
Creating a New Parameters Editor Project
- Create a new Java Plug-in Project (File > New > Other > Plug-in Development > Plug-in Project)
- Give it a name, here
swt_test - Open the manifest
MANIFEST.MF - On the Dependencies tab add plug-ins:
org.eclipse.swtnet.sf.okapi.commonnet.sf.okapi.ui.common
- On the Runtime tab add
swt_testto have your editor visible from outside.
Creating the Main Editor Class
- Right-click on the
src/swt_testpackage, New > Class - In Name type in the standard name for an editor class:
Editor. For Superclass type in:net.sf.okapi.common.ui.abstracteditor.AbstractParametersEditor(Hint: You can click Browse and start typingAbstractParametersEditorin the Supeclass Selection dialog) - Make sure Inherited abstract methods is checked, click Finish.
- Eclipse will auto-generate 4 methods in the new class:
protected void createPages(TabFolder pageContainer)-- creates pages and provides references to the controls participating in inter-page interop.public IParameters createParameters()-- provides the parameters class (returnsnew net.sf.okapi.filters.table.Parameters();)protected String getCaption()-- provides the caption of your editor’s window (returnsMy Filter Parameters;)protected void interop(Widget speaker)-- provides interoperability between pages
Creating Tabs
- Select the
src/swt_testpackage - Create a new page.
- With SWT Designer: File > New > Other > WindowBuilder > SWT Designer > SWT > Composite.
- Manually: File > New > Class. In Superclass:
org.eclipse.swt.widgets.Composite.
- Give it a name:
FirstTab. - In the editor window in class declaration after
extends Compositetype inimplements IDialogPage. This will turn the composite into the framework’s client. You have to do it manually, because SWT Designer doesn’t handle descendants of abstract classes, and wants a direct subclass of a Composite. IDialogPagegets underlined, choose Import IDialogPage(net.sf.okapi.common.ui.abstracteditor)- Now
FirstTabis underlined, choose Add unimplemented methods. - Eclipse will insert four method stubs:
public boolean canClose(boolean isOK)-- returns true if the dialog can be closed at the moment; parameter indicates if the dialog is being closed with OK.public void interop(Widget speaker)-- provides interoperability between controls of this page, speaker is the control which fired the interop event.public boolean load(Object data)-- data contains the parameters being edited, controls should configure themselves according to the given parameters in this method.public boolean save(Object data)-- changes parameters referred by data according to the controls’ settings. Returns true if parameters were saved from GUI successfully.
- Note: For compound filters this method is called several times for every internal filter, so the type should be checked before typecasting to the actual parameters class). Returns true if parameters were loaded to GUI successfully.
- Important note: if the auto-generated
canClose(),load(),andSave()return false, change it toreturn true, otherwise the editor won’t close, and load/save page errors will be displayed.
- Place groups and controls on the page. (In SWT Designer click the Design tab, and drop controls from the palette.) Here we put one group named
group1and containing a check-boxcheck1and a buttonbutton1. The SWT Designer will create this code, which of course can be typed in manually:
private Group group1;
private Button check1;
private Button button1;
public FirstTab(Composite parent, int style) {
super(parent, style);
setLayout(new GridLayout(1, false));
group1 = new Group(this, SWT.NONE);
group1.setLayout(new GridLayout(2, false));
group1.setText("First group");
group1.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
group1.setData("name", "group1");
check1 = new Button(group1, SWT.CHECK);
check1.setText("Enable buttons");
check1.setSelection(true);
check1.setData("name", "check1");
button1 = new Button(group1, SWT.NONE);
button1.setData("name", "button1");
button1.setText("Don't click me!");
}
- Create the second page. Give it the name
SecondTab. Drop a groupgroup2and a buttonbutton2. The code is:
private Group group2;
private Button button2;
public SecondTab(Composite parent, int style) {
super(parent, style);
setLayout(new GridLayout(1, false));
group2 = new Group(this, SWT.NONE);
group2.setLayout(new GridLayout(1, false));
group2.setText("Second group");
group2.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
group2.setData("name", "group2");
button2 = new Button(group2, SWT.NONE);
button2.setData("name", "button2");
button2.setText("Click Me!");
}
- Add the created pages to the editor. Open
Editor.java, and add the following code tocreatePages():
addPage("General", FirstTab.class);
addPage("Options", SecondTab.class);
Testing
- In
okapi-ui-harnessopennet.sf.okapi.testutilities.uiharness.Okapi_GUI_Tester.java - right-click on
Okapi_GUI_Tester.java, Build Path > Configure Build Path... - Projects tab, Add, swt_test, OK
- Add the name of the new editor class to the
GUI_CLASSESlist (lines 66 on):swt_test.Editor.class.getName() - Click Ctrl+F11 to run the GUI Tester, and double-click on
swt_test.Editor. It will display a warning that parameters have not been created, and display the editor.
Creating a User Input Dialog
- Create a third page. Give it the name
LoginPage. - Type in
implements IInputQueryPagein the class declaration. - Add unimplemented methods.
- Change
return falsetoreturn true. Or better, find a place in Eclipse where to change the default. - Drop two labels and two text edits on it. The code is:
private Label lblName;
private Label lblPassword;
private Text name;
private Text password;
public LoginPage(Composite parent, int style) {
super(parent, style);
setLayout(new GridLayout(2, false));
lblName = new Label(this, SWT.NONE);
lblName.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
lblName.setData("name", "lblName");
lblName.setText("User Name:");
name = new Text(this, SWT.BORDER);
name.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
name.setData("name", "name");
lblPassword = new Label(this, SWT.NONE);
lblPassword.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
lblPassword.setData("name", "lblPassword");
lblPassword.setText("Password:");
password = new Text(this, SWT.BORDER);
password.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
password.setData("name", "password");
}
- Open the
SecondTab.java. Assign the following code to the select event ofbutton2:
button2.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (SWTUtils.inputQuery(LoginPage.class, getShell(), "Enter your user name and password", null, null)) {
}
}
});
- Check in GUI Tester.
In-Page Interop
Now we want the checkbox on the first page control the enabled state of the button on the same first page. We need to do the two things:
- Set an event listener to the check-box to call interop:
check1.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
interop(null);
}
});
- change our
interop()method inFirstTab.java:
public void interop(Widget speaker) {
button1.setEnabled(check1.getSelection());
}
Inter-Page Interop
And the final fun we want the check-box on the first page to control the enabled state of the button on the other (second) page.
Also we want the login dialog to display a user name and password.
And we do those thing without touching the pages’ code. We do two things in swt_test.Editor.java:
- Set references to the controls we want bound. Also bind them with
SWTUtilshelpers.
protected void interop(Widget speaker) {
Control check1 = findControl(FirstTab.class, "check1");
Control button2 = findControl(SecondTab.class, "button2");
Control name = findControl(LoginPage.class, "name");
Control password = findControl(LoginPage.class, "password");
SWTUtils.enableIfSelected(button2, check1);
SWTUtils.disableIfNotSelected(button2, check1);
SWTUtils.setText(name, "user");
SWTUtils.setText(password, "********");
}
- Set "speakers", i.e. controls initiating interop (we have only
check1speaker):
protected void createPages(TabFolder pageContainer) {
addPage("General", FirstTab.class);
addPage("Options", SecondTab.class);
addSpeaker(FirstTab.class, "check1");
}
Where to Now?
You can try the Table Filter parameters editor as it uses the framework to the upmost. The classes are located in okapi-filter-table-ui, also the Table Filter employs the Options tab of the Plain Text Filter (okapi-filter-plaintext-ui).