16.2. Views and EditorsWhile developing Hyperbola in Part II, you learned the basics of using views and editors and the differences between the two. Typically, an editor shows the main content of your application, whereas views support this with additional navigation or context-sensitive information related to the task being done with the editor. If you are familiar with the Eclipse IDE, then you already know that in Hyperbola, we have hidden a couple of the standard IDE mechanisms for managing views and editors. For example, in Hyperbola, the idea of a user opening an arbitrary view is not exposed, whereas in the Eclipse IDE, there's a Window > Show View menu entry that allows the user to open any view. As with many things related to perspectives, views, and editors, it's up to the individual RCP application to decide how to expose these to the end-users. The goal of this section is to review some of the advanced RCP-related view and editor features. 16.2.1. Multiple Instances of the Same ViewAs we have seen in Hyperbola, it's possible to open multiple instances of an editor each with its unique editor input. It's also possible to open multiple instances of a view. Imagine a more advanced Hyperbola that allows you to connect to multiple servers at once. Users can then manage different contact lists or different logins within the same application. To present this, however, Hyperbola would need a Contacts view for each connection. There are two techniques for opening multiple instances of the same view in the same perspective. Regardless of the technique used, the first step is to edit Hyperbola's plug-in definition and change the enableMultiple property of the Contacts view extension point to true. Having done that, multiple views can be opened directly in the perspective factory, as shown in the following. The view id argument of the addView() method can take a qualified view format as primary-id:secondary-id. The secondary id must be unique within the perspective. org.eclipsercp.hyperbola/Perspective
public void createInitialLayout(IPageLayout layout) {
layout.addStandaloneView(ContactsView.ID + ":1", false,
IPageLayout.LEFT, 0.33f, layout.getEditorArea());
layout.addView(ContactsView.ID + ":2",
IPageLayout.BOTTOM, 0.70f, layout.getEditorArea());
layout.addView(ContactsView.ID + ":3",
IPageLayout.BOTTOM, 0.70f, layout.getEditorArea());
}The second technique is to open the secondary views via an action. The OpenViewAction below is generically defined to open a new instance of a given view id, and the action is added to Hyperbola as Hyperbola > New Contacts View. org.eclipsercp.hyperbola/OpenViewAction
public class OpenViewAction extends Action {
private final IWorkbenchWindow window;
private int instanceNum;
private final String viewId;
public OpenViewAction(IWorkbenchWindow window, String viewId) {
this.window = window;
this.viewId = viewId;
}
public void run() {
try {
window.getActivePage().showView(viewId,
Integer.toString(instanceNum), IWorkbenchPage.VIEW_ACTIVATE);
instanceNum++;
} catch (PartInitException e) {
// handle exception
}
}
}Notice that the view is created with both a view id and a secondary unique instance id. The secondary id must be remembered if you need to refer to individual views. In this Hyperbola example, the secondary id does not need to be remembered. If you run Hyperbola and trigger the New Contact View action, a new Contacts view opens at a default location, at the bottom right of the editor area in the perspective. This can be improved. Just as you can define a perspective that contains multiple instances of the same view, you can define placeholders for these views in the layout. When the views are opened, they are placed in the pre-defined placeholder locations. The snippet below shows how the Free Moving perspective is set up to ensure that all Contacts views are opened together in the same stack. org.eclipsercp.hyperbola/Perspective public void createInitialLayout(IPageLayout layout) { IFolderLayout folder = layout.createFolder("contacts", IPageLayout.LEFT, 0.33f, layout.getEditorArea()); folder.addPlaceholder(ContactsView.ID + ":*"); folder.addView(ContactsView.ID); IViewLayout viewLayout = layout.getViewLayout(ContactsView.ID); viewLayout.setCloseable(false); layout.addPerspectiveShortcut(Perspective.ID); layout.addPerspectiveShortcut(PerspectiveFreeMoving.ID); } Notice that the Contacts view placeholder uses a multi-instance view format of primaryId:secondaryId and a * wildcard for the secondary id. This indicates that any Contacts view with a secondary id should be placed in the folder. Wildcards are also supported for the primary id. 16.2.2. Sticky ViewsA sticky view is a view that stays open across perspective switches. Once open in one perspective, the sticky view remains open in all perspectives hosted in that Workbench window. This is true even for perspectives that do not define that view as part of their layout. Sticky views were added to support instructional aids that can span perspectives such as Help, Intro, and Cheat Sheets. Sticky views are defined using the stickyView tag in an org.eclipse.ui.views extension, as shown in the snippet below: org.eclipsercp.hyperbola/plugin.xml <extension point="org.eclipse.ui.views"> <view class="org.eclipsercp.hyperbola.ContactsView" icon="icons/groups.gif" id="org.eclipsercp.hyperbola.views.contacts" name="Contacts"/> <stickyView closeable="false" id="org.eclipsercp.hyperbola.ContactsView" location="LEFT" moveable="false"/> </extension> The sticky view extension adds a placeholder for the view in the Workbench page, but does not show the view. The given id must be that of an existing view. When the existing view is opened, it becomes sticky. Think of this extension as adding the sticky attribute to an already defined view. 16.2.3. Showing Contributed ViewsIt's quite easy to open specific views if the application knows in advance about all the possible view ids. In applications that allow views to be contributed by other plug-ins, you may want to allow users to open views manually. The Workbench exposes a view shortlist mechanism that you can use to show users a list of available views. This is similar to the Window > Show View menu entry found in the Eclipse IDE. Set this up by creating a contribution item in the ActionBarAdvisor and adding it to any menu. In the example below, the item is added to the Hyperbola main menu. org.eclipsercp.hyperbola/ApplicationActionBarAdvisor
protected void makeActions(IWorkbenchWindow window) {
views =
ContributionItemFactory.VIEWS_SHORTLIST.create(window);
}
protected void fillMenuBar(IMenuManager menuBar) {
MenuManager viewsMenu = new MenuManager("Open View", "views");
viewsMenu.add(views);
menuBar.add(viewsMenu);
}The initial list of views shown in the menu consists of the view shortcuts defined by the active perspective. This list is configured by a perspective using IPageLayout.addShowViewShortcut(String). 16.2.4. View RegistryOf course, if you don't like the pre-defined view menus or features, you can consult the view registry and customize how views are managed in your application. The view registry is accessed using the IWorkbench method getViewRegistry() and has several useful methods for querying registered views. Each view is described using an IViewDescriptor that includes the view's name, icon, id, and other properties defined in the view's extension point. 16.2.5. Connecting Parts TogetherThere's a good chance that your UI partsthe views and editorswill need to communicate. For example, the Hyperbola Contacts view could show a contact in bold when a chat editor for that contact is active. In the Workbench, there are three techniques for communicating between parts. First, let's review the techniques, then we'll look at how to use one of these techniques to implement contact entry bolding.
To implement bolding of contacts when a chat is in progress, the Contacts view has to know when a chat editor is opened and closed and bold the appropriate contact accordingly. The best technique is for the Contacts view to register a part listener using the IPartService and remember which chat editors are open. The label provider is refreshed when a part is opened or closed, and the active chat list is consulted to decide whether or not a contact is bold. The code changes are simple, and are described below.
The part listener is added as a field of ContactsView. It remembers the set of open chats in openEditors and refreshes the labels when a chat editor is opened or closed. org.eclipsercp.hyperbola/ContactsView
private IPartListener partListener = new IPartListener() {
public void partOpened(IWorkbenchPart part) {
trackOpenChatEditors(part);
}
public void partClosed(IWorkbenchPart part) {
trackOpenChatEditors(part);
}
private void trackOpenChatEditors(IWorkbenchPart part) {
if (! (part instanceof ChatEditor))
return;
ChatEditor editor = (ChatEditor) part;
ChatEditorInput input = (ChatEditorInput) editor.getEditorInput();
String participant = input.getParticipant();
if (openEditors.contains(participant)) {
openEditors.remove(participant);
} else {
openEditors.add(participant);
}
treeViewer.refresh(true);
}
...
};The part listener is registered with the Workbench when the Contacts view is created and unregistered when it's closed. org.eclipsercp.hyperbola/ContactsView
public void createPartControl(Composite parent) {
...
getSite().getWorkbenchWindow().
getPartService().addPartListener(partListener);
}
public void dispose() {
getSite().getWorkbenchWindow().
getPartService().removePartListener(partListener);
}The existing label provider is augmented with a special decorating label provider that allows other label decorators to participate in label decorations. The new ContactsDecorator consults the list of opened chat editors and makes the contacts that have open editors show as bold. It also implements an IFontProvider as an indication that it can change the font in addition to providing text and images. The original HyperbolaLabelProvider is still used, but it is wrapped in the DecoratingLabelProvider instance. org.eclipsercp.hyperbola/ContactsView private class ContactsDecorator implements ILabelDecorator, IFontDecorator { ... public Font decorateFont(Object element) { if(element instanceof RosterEntry) { RosterEntry entry = (RosterEntry)element; if(ContactsView.this.openEditors.contains(entry.getUser())) return JFaceResources.getFontRegistry(). getBold(JFaceResources.DEFAULT_FONT); } return null; } } public void createPartControl(Composite parent) { treeViewer = new TreeViewer(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); getSite().setSelectionProvider(treeViewer); HyperbolaLabelProvider hyperbolaLabelProvider = new HyperbolaLabelProvider(); DecoratingLabelProvider decorator = new DecoratingLabelProvider(hyperbolaLabelProvider, new ContactsDecorator()); treeViewer.setLabelProvider(decorator); ... } } The result of these changes is shown in Figure 16-6. Figure 16-6. Contacts view with bold contacts
Approaches such as this are very powerful. One of the key points here is that the chat editor did not require modification. You can imagine adding other plug-ins to Hyperbola that also connect to existing parts without needing to modify those parts. That is, plug-ins have opportunities to integrate with your application without you needing to know about them. |