Wednesday, November 4, 2009

Section 18.2.  Customizing a Workbench Window










18.2. Customizing a Workbench Window


The Workbench opens and configures Workbench windows based on the configuration information provided by a WorkbenchWindowAdvisor. The typical behavior when a Workbench window is created is to create the following controls: a menu bar, a toolbar, a trim that is used to place fast views and the perspective bar, a page content area, and a status line. Figure 18-1 shows a typical Workbench window's contents.



Figure 18-1. Typical Workbench window's contents

[View full size image]




The creation of these controls can be changed using the following techniques:


  • Change the visibility of the standard controls by calling the IWorkbenchWindowConfigurer methods setShow*() from the WorkbenchWindowAdvisor method preWindowOpen(). These methods control the initial visibility only and do not hide/show the controls once the window is opened.

  • Override the creation of the window's controls by implementing the WorkbenchWindowAdvisor method createWindowContents(). This moves the responsibility for creating a window's controls to the advisor.


For most applications, the setShow*() methods available on IWorkbenchWindowConfigurer are sufficient. To provide further customization, however, the WorkbenchWindowAdvisor needs to create a window's contents from scratch. To give you an idea of how this works, the following snippet shows a customized createWindowContents(Shell) method that creates a window in a manner very similar to the Workbench:


org.eclipsercp.hyperbola/ApplicationWorkbenchWindowAdvisor
public void createWindowContents(Shell shell) {
IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
Menu menu = configurer.createMenuBar();
shell.setMenuBar(menu);
FormLayout layout = new FormLayout();
layout.marginWidth = 0;
layout.marginHeight = 0;
shell.setLayout(layout);
toolbar = configurer.createCoolBarControl(shell);
page = configurer.createPageComposite(shell);
statusline = configurer.createStatusLine(shell);
}


Notice that most of the work is done by a set of helper methods on IWorkbenchWindowConfigurer (see Table 18-1 for a complete list). These methods create the typical Workbench controls and make it easier to compose a custom layout from standard parts. The controls supplied by the helpers are untyped and are not meant to be changed in any way. For example, do not downcast them and tweak the underlying control because the control type is not API. Just place them in the layout.


Table 18-1. IWorkbenchWindowConfigurer Control Creation Methods

Method

Description

createMenuBar (Composite)

Creates the top-level menu with style SWT.BAR. The created control is assigned as the menu bar for a shell using the Decorations method setMenuBar(menu). This menu is managed by the Workbench.

createCoolBarControl (Composite)

Creates the top-level toolbar that is managed by the Workbench. There are no parenting restrictions on this control, and it can be positioned anywhere.

createPageComposite (Composite)

Creates the area in which views and editors are shown. This method must be called from createWindowContents() or the Workbench will not run.

createStatusLine (Composite)

Creates the status line that is managed by the Workbench. There are no parenting restrictions on this control, and it can be positioned in areas other than under the top-level menu.



These methods are more than just conveniences. If the Workbench creates the controls, it can also add in declarative contributions. We saw this in Chapter 17, "Actions," where menus and toolbars needed to be managed by the Workbench if contributions were to be added.


If your application does not need action contributions in the menu, toolbar, or status line, you can leave these calls out completely or replace them. Of course, this may prevent some plug-ins from integrating into your application as they expect a menu bar and toolbar into which they can contribute actions.


In any event, createPageComposite(Composite) must be called for the Workbench to run properly. It can be called at any time within the createWindowContents() method.


Note



There are limitations as to when you can override the Workbench window. The Workbench's default implementation of createWindowContents() creates controls that are not available to clients, such as the job progress area, the trim that docks fast views, and the perspective bar. When you override createWindowContents(), you lose these areas.





18.2.1. Example: Hide and Show


To demonstrate the customization of a window's content, let's modify Hyperbola so that users can hide and show the toolbar and status lines. The Workbench does not directly support thisyou can add or remove the toolbar and status line when the window is created, but cannot change the visibility afterwards.


While we're at it, let's include a quick search panel that appears below the page content area when Ctrl+F is pressed and hidden when the Escape key or Close button is pressed. The quick search panel is not a view or editor, but a custom area that is completely controlled by Hyperbola.


Figure 18-2 shows the result of this customization. The toolbar and status line are hidden and the quick search panel is visible at the bottom of the window. There are actions in the main menu that toggle the visibility of each.



Figure 18-2. Example of new panel with toolbar and status line hidden









18.2.2. FormLayout


The key to getting the flexibility we need in this scenario is the use of SWT's FormLayout. A FormLayout can be used to describe complex layouts and make it easy to add or remove controls. A FormLayout is based on FormAttachments. There is an attachment for each side of a control. Each attachment specifies how its side of the control is attached to other controls or the control's parent composite.


Window creation in the WorkbenchWindowAdvisor method createWindowContents() is simple. The FormLayout is added to the shell and then the toolbar, page, and status line are created using the appropriate IWorkbenchWindowConfigurer methods. Notice that each control is stored as a field on the WorkbenchWindowAdvisorthis is needed to allow changing the layout of the controls as they are hidden and shown.


org.eclipsercp.hyperbola/ApplicationWorkbenchWindowAdvisor
public void createWindowContents(Shell shell) {
IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
Menu menu = configurer.createMenuBar();
shell.setMenuBar(menu);
FormLayout layout = new FormLayout();
layout.marginWidth = 0;
layout.marginHeight = 0;
shell.setLayout(layout);
toolbar = configurer.createCoolBarControl(shell);
page = configurer.createPageComposite(shell);
statusline = configurer.createStatusLineControl(shell);

// The layout method does the work of connecting the
// controls together.
layoutNormal();
}


After the controls are created, they are connected together in the layoutNormal() method. Here, the attachments are specified either as a percentage of the space available in the parent or as a direct connection to a neighboring control. Once the layout is configured, the shell must be told to lay out its controls.


org.eclipsercp.hyperbola/ApplicationWorkbenchWindowAdvisor
private void layoutNormal() {
// Toolbar
FormData data = new FormData();
data.top = new FormAttachment(0, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
toolbar.setLayoutData(data);
// Status line
data = new FormData();
data.bottom = new FormAttachment(100, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
statusline.setLayoutData(data);
// page contents
data = new FormData();
data.top = new FormAttachment(toolbar);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
data.bottom = new FormAttachment(statusline);
page.setLayoutData(data);
getWindowConfigurer().getWindow().getShell().layout(true);
}




18.2.3. Hiding the Toolbar


With this setup, hiding a control is a matter of making it invisible and then rewiring the controls to which it is attached. For example, the code snippet below, from Hyperbola's WorkbenchWindowAdvisor, shows how to hide and show the toolbar from Figure 18-1. To hide the toolbar, it is made invisible and its bottom neighbor, the page area, has its top wired to the top of the parent composite. The toolbar disappears and the page area snaps up to take its place. Showing the toolbar is the reverse. This same pattern is used to control the layout of the status line.


org.eclipseFrcp.hyperbola/ApplicationWorkbenchWindowAdvisor
public void setShowToolbar(boolean visible) {
if (visible) {
if (toolbar.isVisible())
return;
FormData data = (FormData) page.getLayoutData();
data.top = new FormAttachment(toolbar, 0);
page.setLayoutData(data);
toolbar.setVisible(true);
} else {
if (!toolbar.isVisible())
return;
FormData data = (FormData) page.getLayoutData();
data.top = new FormAttachment(0, 0);
page.setLayoutData(data);
toolbar.setVisible(false);
}
getWindowConfigurer().getWindow().getShell().layout(true);
}




18.2.4. Adding the Toggle Actions


Now that the window and its controls have been created, we need actions that toggle the visibility of these controls. The setShow*() methods on the WorkbenchWindowAdvisor can be used to toggle the controlsall that is left is to create a set of actions that call these new methods. Hyperbola's ActionBarAdvisor's constructor is modified to take an extra argument of type WorkbenchWindowAdvisor. This allows it to call back and control the visibility, as shown in the snippet below. As usual, actions are created and managed by the ActionBarAdvisor.


org.eclipsercp.hyperbola/ApplicationActionBarAdvisor
protected void makeActions(IWorkbenchWindow window) {
...
toggleToolbar = new Action("Toolbar", IAction.AS_CHECK_BOX) {
public void run() {
windowAdvisor.setShowToolbar(!windowAdvisor.getShowToolbar());
}
};
}


There is a subtle implementation issue to consider: As you can see in Figure 18-3, the actions are created before the window. Actions whose enablement depends on the state of a control in the window must be initialized after the window is created rather than when the action bars are created.



Figure 18-3. Example method sequence

[View full size image]




Figure 18-3 also shows an easy solution to this problemcode your WorkbenchWindowAdvisor and ActionBarAdvisor to coordinate when the window's contents have been created. See the call to updateEnablements() below.


The code for updateEnablements(), shown below, is straightforward. It sets the state of the actions according to the current visibility of the associated controls.


org.eclipsercp.hyperbola/ApplicationActionBarAdvisor
private void updateEnablements() {
toggleToolbar.setChecked(windowAdvisor.getShowToolbar());
toggleStatusLine.setChecked(windowAdvisor.getShowStatusline());
toggleQuickSearch.setChecked(windowAdvisor.getShowSearchPanel());
}


In addition to using this method when the window is created, it is handy to call the toggling actions to update the action, as shown in the following snippet from the ActionBarAdvisor method makeActions():


org.eclipsercp.hyperbola/ApplicationActionBarAdvisor
toggleQuickSearch = new Action("Search Panel", IAction.AS_CHECK_BOX) {
public void run() {
windowAdvisor.setShowSearchPanel(!windowAdvisor.getShowSearchPanel());
updateEnablements();
}
};




18.2.5. Quick Search Panel


So far, you've seen how to toggle controls that the Workbench creates. Adding controls to a Workbench window is handled the same way. Most of the work required is in designing the controls and deciding on an appropriate SWT layout. The snippet below sketches the quick search panel:


org.eclipsercp.hyperbola/QuickSearchPanel
public class QuickSearchPanel extends Composite {
private Image nextImage;
private Image previousImage;
private Image closeImage;

public QuickSearchPanel(Composite parent, final ICloseable closeable) {
super(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginHeight = 3;
layout.marginWidth = 5;
layout.numColumns = 4;
setLayout(layout);
Label l = new Label(this, SWT.NONE);
l.setText("Find Contact: ");
...
}


To add the quick search panel, create the control and wire it into the window's layout. In our example, the quick search panel does not show at startup, but is instead shown when explicitly toggled via the keyboard or menu item. The snippet below shows how the layout must consider the visibility of the status line in determining where to attach the bottom of the search panel:


org.eclipsercp.hyperbola/ApplicationWorkbenchWindowAdvisor
public void setShowSearchPanel(boolean visible) {
if (visible) {
if (searchPanel != null)
return;
searchPanel = new QuickSearchPanel(
getWindowConfigurer().getWindow().getShell(), null);
FormData data = (FormData) page.getLayoutData();
data.bottom = new FormAttachment(searchPanel, 0);
page.setLayoutData(data);
data = new FormData();
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
if (statusline.isVisible()) {
data.bottom = new FormAttachment(statusline, 0);
} else {
data.bottom = new FormAttachment(100, 0);
}
searchPanel.setLayoutData(data);
} else {
...
}
}




18.2.6. Checkpoint


Overriding the Workbench window's content creation is possible, but not free. If you rely on things such as the perspective bar, fast views, and the background progress area, then you should think twice about this approach. In fact, it's really only fast views that are lost when replacing the window's contents because in Chapter 16, "Perspectives, Views, and Editors," you saw how to implement your own perspective bar and in Chapter 17, "Actions," how to implement your own background progress indicator.


The reason for showing the quick search panel was to emphasize that once you control the creation of the window's content area, anything is possible. The next section shows another example of this when we override createWindowContents() to create a completely new window shape.













No comments: