站内搜索: 请输入搜索关键词
当前页面: 图书首页 > Eclipse Rich Client Platform: Designing, Coding, and Packaging Java Applications

Section 16.2.  Views and Editors - Eclipse Rich Client Platform: Designing, Coding, and Packaging Java Applications

Previous Page
Next Page

16.2. Views and Editors

While 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 View

As 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 Views

A 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.

Sharing views and editors

When multiple perspectives are open in the same window, it's common to have the same views open in multiple perspectives. If two or more perspectives have the same view open, they share the same view instance. For example, open the Java and the Java Browsing perspectives in the same window. Then open the Package Explorer in the two perspectives. Expand some nodes in the Package Explorer in one perspective and notice that the other perspective for the Package Explorer has the exact same expansion state. Notice, however, that if you close the view in one perspective, it is not closed in the others.

Editors are very similar to views in terms of sharing the same instances, but in addition to being the same instance in all perspectives, closing an editor in one perspective closes it in all perspectives. For perspectives in different Workbench windows, neither editors nor views are shared.


16.2.3. Showing Contributed Views

It'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 Registry

Of 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 Together

There'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.

Using the selection The ISelectionService allows views and editors to register their selection with the Workbench, thus allowing other parts to listen to selection changes and respond appropriately. To publish selections, use the IWorkbenchSite method setSelectionProvider (ISelectionProvider), and to subscribe to selections, use the ISelectionService method addSelectionListener(ISelectionListener). This is a good technique because it allows parts to be decoupled. We've already used this technique to connect the Hyperbola actions to the Contacts view and ensure that when the selection changed, the actions are enabled correctly.

Part listeners You can also connect parts together by listening to the events that are fired when a part is closed, opened, and hidden. Use IPartService to register for part events. Again, this is a good technique because it keeps the parts decoupled. This is the technique that is used in the next snippet to implement the Hyperbola bolding of the contact example.

Direct communication Whereas the selection and part service allows any part to listen and react to changes, you can also use a direct connection by having specific views call back to or open other views or editors (e.g., using the methods on IWorkbenchPage to open or close parts). For example, the Hyperbola bolding feature can be implemented by allowing the ChatEditor to notify the ContactsView when it opens. This technique is not ideal because it places a tight coupling between the parts.

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.

  • Add a part listener to the Contacts view to track which chat editors are opened and closed.

  • Register and unregister the part listener.

  • Change the label decorator to set the font of an item and use the list of active chat editors to determine the font for a contact.

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.


Previous Page
Next Page