Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Platform UI Command Design

Revision as of 15:13, 6 September 2006 by Pwebster.ca.ibm.com (Talk | contribs) (Menu Proposal 2)

Starting point for menu and toolbar placement of commands in 3.3. Please contribute comments and suggestions in the discussion area.


Command Architecture Overview

http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-ui-home/R3_1/contributions-proposal/requestForComments_html_m41374bdb.png

Figure 1: High Level Architecture

Current Framework

Commands

Commands are managed by the org.eclipse.ui.commands extension point and the ICommandService.

An example of using the extension point to create a command:

<extension
      point="org.eclipse.ui.commands">
   <category
         description="Actions take at lunch time."
         id="z.ex.view.keybindings.category"
         name="Lunch">
   </category>
   <command
         categoryId="z.ex.view.keybindings.category"
         description="Go for the taco."
         id="z.ex.view.keybindings.eatTaco"
         name="Eat That Taco">
   </command>
</extension>

You can programmatically create commands as well. From within a view:

ICommandService cmdService = (ICommandService) getSite().getService(
    ICommandService.class);
Category lunch = cmdService
    .getCategory("z.ex.view.keybindings.category");
if (!lunch.isDefined()) {
  lunch.define("Lunch", "Actions take at lunch time.");
}
Command eatTaco = cmdService
    .getCommand("z.ex.view.keybindings.eatTaco");
if (!eatTaco.isDefined()) {
  eatTaco.define("Eat That Taco", "Go for the taco.", lunch);
}

Note, however, that a plugin that programmatically defines commands is responsible for cleaning them up if the plugin is ever unloaded.

Also, like IAction you can execute a command directly ... but to get the proper environment it's better to execute it through the IHandlerService. See Platform_UI_Command_Design#Handlers.

Executing a command with parameters

When a Command specifies its parameters, it can also specify a parameter type and/or some valid values. For example, the showView command.

 <command
     name="%command.showView.name"
     description="%command.showView.description"
     categoryId="org.eclipse.ui.category.views"
     id="org.eclipse.ui.views.showView"
     defaultHandler="org.eclipse.ui.handlers.ShowViewHandler">
   <commandParameter
       id="org.eclipse.ui.views.showView.viewId"
       name="%command.showView.viewIdParameter"
       values="org.eclipse.ui.internal.registry.ViewParameterValues" />
 </command>

To execute this command, you need to create a ParameterizedCommand with a Parameterization (an instance of a parameter and its value).

		ICommandService commandService = ...;
		IHandlerService handlerService = ...;
		Command showView = commandService
				.getCommand("org.eclipse.ui.views.showView");
		IParameter viewIdParm = showView
				.getParameter("org.eclipse.ui.views.showView.viewId");

		// the viewId parameter provides a list of valid values ... if you
		// knew the id of the problem view, you could skip this step.
		// This method is supposed to be used in places like the keys
		// preference page, to allow the user to select values
		IParameterValues parmValues = viewIdParm.getValues();
		String viewId = null;
		Iterator i = parmValues.getParameterValues().values().iterator();
		while (i.hasNext()) {
			String id = (String) i.next();
			if (id.indexOf("ProblemView") != -1) {
				viewId = id;
				break;
			}
		}

		Parameterization parm = new Parameterization(viewIdParm, viewId);
		ParameterizedCommand parmCommand = new ParameterizedCommand(
				showView, new Parameterization[] { parm });

		handlerService.executeCommand(parmCommand, null);


This executes the showView command with the problem view id. This is done for us when declaratively specifying a keybinding.

 <key
     sequence="M2+M3+Q X"
     contextId="org.eclipse.ui.contexts.window"
     commandId="org.eclipse.ui.views.showView"
     schemeId="org.eclipse.ui.defaultAcceleratorConfiguration">
   <parameter 
       id="org.eclipse.ui.views.showView.viewId"
       value="org.eclipse.ui.views.ProblemView" />
 </key>

Handlers

Handlers are managed by the org.eclipse.ui.handlers extension point and the IHandlerService. Many Handlers can register for a command. At any give time, either 0 or 1 handlers will be active for the command. A handler's active state and enabled state can be controlled declaratively.

<extension
      point="org.eclipse.ui.handlers">
   <handler
         class="z.ex.view.keybindings.handlers.TacoHandler"
         commandId="z.ex.view.keybindings.eatTaco">
      <activeWhen>
         <with
               variable="activeContexts">
            <iterate
                  operator="or">
               <equals
                     value="z.ex.view.keybindings.contexts.taco"/>
            </iterate>
         </with>
      </activeWhen>
   </handler>
</extension>

Here the handler is checking the activeContexts variable (See org.eclipse.ui.ISources) and if the "taco" context is active, the handler is active.

The handler itself, TacoHandler, must implement IHandler but would usually be derived from the abstract base class org.eclipse.core.commands.AbstractHandler.

You can create and activate a handler programmatically:

IHandlerService handlerService = (IHandlerService) getSite()
    .getService(IHandlerService.class);
IHandler handler = new AbstractHandler() {
  public Object execute(ExecutionEvent event)
          throws ExecutionException {
    System.out.println("Eat that Taco");
    return null;
  }
};
handlerService
    .activateHandler("z.ex.view.keybindings.eatTaco", handler);


Apparently, to run commands we should be calling the IHandlerService, not the Command object execute method itself.

handlerService.executeCommand("z.ex.view.keybindings.eatTaco", null);

KeyBindings

KeyBindings are managed by the org.eclipse.ui.bindings extension point and the IBindingService. Keybindings cannot be updated programmatically.

<extension
      point="org.eclipse.ui.bindings">
   <key
         commandId="z.ex.view.keybindings.eatTaco"
         contextId="z.ex.view.keybindings.contexts.taco"
         schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
         sequence="CTRL+3">
   </key>
</extension>

A key binding is active when the context is active.

Contexts

Contexts are managed by the org.eclipse.ui.contexts extension point and the IContextService.

Most contexts are created by the extension point, and activated programmatically when appropriate. But you can create contexts programmatically as well. The active contexts usually form a tree, although in the case of keybindings this tree is narrowed down to a branch.

<extension
      point="org.eclipse.ui.contexts">
   <context
         description="To allow the consumption of Tacos"
         id="z.ex.view.keybindings.contexts.taco"
         name="Mexican Food"
         parentId="org.eclipse.ui.contexts.window">
   </context>
</extension>

For a context that was attached to a view, it would normally be activated in the view's createPartControl(*) method.

IContextService contextService = (IContextService) getSite()
  .getService(IContextService.class);
IContextActivation contextActivation = contextService.activateContext("z.ex.view.keybindings.contexts.taco");

You can only de-activate a context that you are responsible for activating.

Programmatically, you can create contexts:

Context tacos = contextService
    .getContext("z.ex.view.keybindings.contexts.taco");
if (!tacos.isDefined()) {
  tacos.define("Mexican Food", "To allow the consumption of Tacos",
      "org.eclipse.ui.contexts.window");
}

Note, however, that a plugin that programmatically defines contexts is responsible for cleaning them up if the plugin is ever unloaded.

Tracing Option

There are a couple of reasons why keybindings and commands might not work.

  1. Keybindings are in a context that is not active
  2. There is a keybinding conflict
  3. No handler is currently active for the command
  4. There is a handler conflict

To help track down the problem, you can run with debug tracing options. For example:

org.eclipse.ui/debug=true
org.eclipse.ui/trace/keyBindings=true
org.eclipse.ui/trace/keyBindings.verbose=true
org.eclipse.ui/trace/sources=true
org.eclipse.ui/trace/handlers=true
org.eclipse.ui/trace/handlers.verbose=true
#org.eclipse.ui/trace/handlers.verbose.commandId=org.eclipse.ui.edit.copy
org.eclipse.ui/trace/handlers.verbose.commandId=org.eclipse.jdt.ui.navigate.open.type
org.eclipse.ui/trace/contexts=true
org.eclipse.ui/trace/contexts.verbose=true


I put these options in a debug.options file and run eclipse using:

bash$ eclipse -debug debug.options -data /opt/local/pw_workspace >debug.log 2>&1

This logs the debug output to the debug.log file. This works on windows as well:

C:\development> eclipse32\eclipse.exe -debug debug.options -data workspaces\pw_workspace >debug.log 2>&1

handlers.verbose.commandId allows you to track the information about a specific command that isn't working. org.eclipse.jdt.ui.navigate.open.type is the open type dialog (normally CTRL+SHIFT+T) and org.eclipse.ui.edit.copy (commented out) is COPY (normally CTRL+C)


Menus and ToolBars

Menu and toolbar placement is managed by 4 extension points, and through programmatic contributions at a number of locations: IActionBars, IViewSite, IEditorSite, EditorActionBarContributor ... more to follow

I'm not sure of an appropriate way to wrap org.eclipse.ui.IActionDelegate. It is the base class and provides 2 methods to all of the I*ActionDelegates.

public void run(IAction action);
public void selectionChanged(IAction action, ISelection selection);

run(*) is the execution method, so that is pretty straight forward. The selectionChanged(*) method is called as the workbench selection changes, often times it updates the IAction enablement ... but moving forward there is no IAction enablement. However, an IHandler can be a selection listener and update its own enablement state directly.

The current action delegate proxy, ActionDelegateHandlerProxy, creates a bogus IAction. It allows the action delegates to continue working, but it is disconnected from any state.

Of course, there is also IActionDelegate2 :-)

Managing menus through the suggested org.eclipse.ui.menus extension maps one menu item to one org.eclipse.core.commands.ParameterizedCommand. This contains both the command and appropriate parameters needed to execute it.


org.eclipse.ui.actionSets

Action Sets are visible in the main menu and coolbar. Their visibility can be updated by the user using Customize Perspective. Here is a sample actionSet distributed with Eclipse SDK.

<extension
      point="org.eclipse.ui.actionSets">
   <actionSet
         id="z.ex.editor.actionSet"
         label="Sample Action Set"
         visible="true">
      <menu
            id="sampleMenu"
            label="Sample &amp;Menu">
         <separator
               name="sampleGroup">
         </separator>
      </menu>
      <action
            class="z.ex.editor.actions.SampleAction"
            icon="icons/sample.gif"
            id="z.ex.editor.actions.SampleAction"
            label="&amp;Sample Action"
            menubarPath="sampleMenu/sampleGroup"
            toolbarPath="sampleGroup"
            tooltip="Hello, Eclipse world">
      </action>
   </actionSet>
</extension>

The <actionSet/> element defines the group of elements that can be shown or hidden. The <menu/> elements create menus and groups. The <action/> elements define individual "actions" ... they contain rendering information (label, icon), menu placement (menubarPath, toolbarPath) and behaviour (the class attribute, in this case an IWorkbenchWindowActionDelegate).

To generate the same effect using the proposed changes, you would need:

<command id="z.ex.editor.commands.SampleAction"
    name="Sample Action"
    description="Sample Action command"/>
<handler commandId="z.ex.editor.commands.SampleAction"
    class="z.ex.editor.actions.SampleAction"/>
<!-- where SampleAction now implements IHandler -->
<extension
      point="org.eclipse.ui.commandImages">
   <image
         commandId="z.ex.editor.commands.SampleAction"
         icon="icons/sample.gif">
   </image>
</extension>

And here is a possible view of the menus extension point:

<extension
      point="org.eclipse.ui.menus">
   <menu
         id="z.ex.sampleMenu"
         label="Sample Menu">
      <location
            mnemonic="M">
      </location>
   </menu>
   <group
         id="z.ex.sampleGroup"
         separatorsVisible="false">
      <location>
         <bar
               path="z.ex.sampleMenu"
               type="menu">
         </bar>
      </location>
   </group>
   <item
         commandId="z.ex.editor.commands.SampleAction"
         id="z.ex.editor.items.SampleAction">
      <location
            mnemonic="S">
         <bar
               path="z.ex.sampleMenu/z.ex.sampleGroup"
               type="menu">
         </bar>
      </location>
   </item>
</extension>

I think ...

org.eclipse.ui.editorActions

Editor Actions appear in the main menu and coolbar as long as an editor of that type is the active editor. Using the org.eclipse.ui.actionSetPartAssociation extension with an org.eclipse.ui.actionSet works similarly, except all editor actions are IEditorActionDelegates instead of IWorkbenchWindowActionDelegates.

Here is an action example adapted as an editor action:

<extension
      point="org.eclipse.ui.editorActions">
   <editorContribution
         id="z.ex.editor.actions"
         targetID="z.ex.view.keybindings.editors.XMLEditor">
      <menu
            id="sampleMenu"
            label="Sample &amp;Menu">
         <separator
               name="sampleGroup">
         </separator>
      </menu>
      <action
            class="z.ex.view.keybindings.actions.SampleEditorAction"
            icon="icons/sample.gif"
            id="z.ex.view.keybindings.actions.SampleEditorAction"
            label="Sample &amp;Editor Action"
            menubarPath="sampleMenu/sampleGroup"
            toolbarPath="sampleGroup"
            tooltip="Hello, Eclipse world">
      </action>
   </editorContribution>
</extension>


The <editorContribution/> element ties the editor action to a specific editor type. Other than that, it is almost identical to org.eclipse.ui.actionSets.

Similarly, it can be adapted to commands and handlers.

<command id="z.ex.editor.commands.SampleEditorAction"
    name="Sample Editor Action"
    description="Sample Editor Action command"/>
<handler commandId="z.ex.editor.commands.SampleEditorAction"
    class="z.ex.view.keybindings.actions.SampleEditorAction"/>
<!-- where SampleEditorAction now extends AbstractHandler -->
<extension
      point="org.eclipse.ui.commandImages">
   <image
         commandId="z.ex.editor.commands.SampleEditorAction"
         icon="icons/sample.gif">
   </image>
</extension>

Assuming the actionSet had been contributed, you could place the editor action in the existing menu.

<extension
      point="org.eclipse.ui.menus">
   <item
         commandId="z.ex.editor.commands.SampleEditorAction"
         id="z.ex.editor.items.SampleEditorAction">
      <location
            mnemonic="E">
         <part
              id="z.ex.view.keybindings.editors.XMLEditor">
            <bar
                  path="z.ex.sampleMenu/z.ex.sampleGroup"
                  type="menu">
            </bar>
         </part>
      </location>
   </item>
</extension>



  • What happens in the actionSet is disabled?

org.eclipse.ui.viewActions

View actions are placed in the view menu or view toolbar, but the extension point looks almost identical to org.eclipse.ui.editorActions. The delegate for views is IViewActionDelegate.

<extension
      point="org.eclipse.ui.viewActions">
   <viewContribution
         id="z.ex.view.keybindings.viewAction"
         targetID="z.ex.view.keybindings.views.SampleView">
       <menu
            id="sampleMenu"
            label="Sample &amp;Menu">
         <separator
               name="sampleGroup">
         </separator>
      </menu>
      <action
            class="z.ex.view.keybindings.actions.SampleViewAction"
            icon="icons/sample.gif"
            id="z.ex.view.keybindings.actions.SampleViewAction"
            label="Sample &amp;View Action"
            menubarPath="sampleMenu/sampleGroup"
            toolbarPath="sampleGroup"
            tooltip="Hello, Eclipse world">
      </action>
   </viewContribution>
</extension>

Here, the Sample Menu will show up in the view menu, the dropdown from the top of the view's CTabFolder.

Similarly, it can be adapted to commands and handlers.

<command id="z.ex.editor.commands.SampleViewAction"
    name="Sample View Action"
    description="Sample View Action command"/>
<handler commandId="z.ex.editor.commands.SampleViewAction"
    class="z.ex.view.keybindings.actions.SampleViewAction"/>
<!-- where SampleViewAction now extends AbstractHandler -->
<extension
      point="org.eclipse.ui.commandImages">
   <image
         commandId="z.ex.editor.commands.SampleViewAction"
         icon="icons/sample.gif">
   </image>
</extension>

Placing the action (which is specifically a menu or button linked to a command) can be accomplished with the org.eclipse.ui.menus extension point and the <part/> element.

<extension
      point="org.eclipse.ui.menus">
   <menu
         id="z.ex.view.SampleViewMenu"
         label="Sample Menu">
      <location
            mnemonic="M">
         <part
               id="z.ex.view.keybindings.views.SampleView">
         </part>
      </location>
   </menu>
   <group
         id="z.ex.view.SampleViewGroup"
         separatorsVisible="false">
      <location>
         <bar
               path="z.ex.view.SampleViewMenu"
               type="menu">
         </bar>
      </location>
   </group>
   <item
         commandId="z.ex.editor.commands.SampleViewAction"
         id="z.ex.view.SampleViewAction">
      <location
            mnemonic="V">
         <bar
               path="z.ex.view.SampleViewMenu/z.ex.view.SampleViewGroup"
               type="menu">
         </bar>
      </location>
   </item>
</extension>


  • Is this really that complicated?

org.eclipse.ui.popupMenus

Popup menu contributions are actions contributed to the various popup menus in eclipse. They take 2 forms. A <viewerContribution/> contributes actions to a popup in a view or an editor. An <objectContribution/> contributes actions to any popup, as long as the selected object matches its enablement criteria.

Here is an example of each:

<extension
      point="org.eclipse.ui.popupMenus">
   <objectContribution
         id="z.ex.popup.objectContribution"
         objectClass="org.eclipse.ui.handlers.IHandlerActivation">
      <action
            class="z.ex.view.keybindings.actions.SampleContributionAction"
            icon="icons/sample.gif"
            id="z.ex.view.keybindings.actions.SampleContributionAction"
            label="Sample &Contribution Action"
            menubarPath="additions"
            tooltip="Hello, Eclipse world">
      </action>
   </objectContribution>
   <viewerContribution
         id="z.ex.popup.viewerContribution"
         targetID="z.ex.view.keybindings.editors.XMLEditor">
      <action
            class="z.ex.view.keybindings.actions.SampleViewerAction"
            icon="icons/sample.gif"
            id="z.ex.view.keybindings.actions.SampleViewerAction"
            label="Sample V&iewer Action"
            menubarPath="#Ruler"
            tooltip="Hello, Eclipse world">
      </action>
   </viewerContribution>
</extension>


Each of the actions can be turned into commands/handlers as was done for view actions or editor actions.

This would make use of the popup specifier. Visibility can be specified, or simply tied to handler enablement using <visibleWhen checkEnabled="true"/>.

<extension
      point="org.eclipse.ui.menus">
   <item
         commandId="z.ex.view.commands.SampleContributionAction"
         id="z.ex.objectContribution">
      <location
            mnemonic="C">
         <popup
               path="additions">
         </popup>
      </location>
      <visibleWhen>
          <with
               variable="selection">
            <adapt
                  type="org.eclipse.ui.handlers.IHandlerActivation">
            </adapt>
         </with>
      </visibleWhen>
   </item>
   <item
         commandId="z.ex.view.commands.SampleViewerAction"
         id="z.ex.viewerContribution">
      <location
            mnemonic="i">
         <part
               id="z.ex.view.keybindings.editors.XMLEditor">
            <popup
                  path="#Ruler">
            </popup>
         </part>
      </location>
   </item>
</extension>


While the <with/> expression is used to select a specific variable from the evaluation context, it is possible for a default variable to be supplied so that with is not necessary. In this case, the default variable would probably be "selection".

Programmatic Contributions and Delegates

Contributing menus through IActionBars, EditorActionBarContributor, IWorkbenchPartSite#registerContextMenu(*), etc


I*ActionDelegate

Each of the IActionDelegates has a slightly different initialization interface. With each execution the IHandler is provided an ExecutionEvent that contains the application context, which will allow the handler to retrieve the information of interest.

Creating an equivalent IHandler for IWorkbenchWindowActionDelegate that has access to the window is straightforward. ex:


public class SampleAction extends AbstractHandler {
	public Object execute(ExecutionEvent event) throws ExecutionException {
		IWorkbenchWindow window = null;
		ISelection selection = null;

		Object appContextObj = event.getApplicationContext();
		if (appContextObj instanceof IEvaluationContext) {
			IEvaluationContext appContext = (IEvaluationContext) appContextObj;
			window = (IWorkbenchWindow) appContext
					.getVariable(ISources.ACTIVE_WORKBENCH_WINDOW_NAME);
			selection = (ISelection) appContext
					.getVariable(ISources.ACTIVE_CONTEXT_NAME);
		}
		if (window != null) {
			MessageDialog.openInformation(window.getShell(), "Editor Plug-in",
					"Hello, Eclipse world");
		}
		if (selection instanceof IStructuredSelection) {
			if (((IStructuredSelection) selection).size() > 1) {
				setEnabled(false);
			} else {
				setEnabled(true);
			}
		}
		return null;
	}

	private boolean enabled = true;

	private void setEnabled(boolean b) {
		if (enabled != b) {
			enabled = b;
			HandlerEvent handlerEvent = new HandlerEvent(this, true, false);
			fireHandlerChanged(handlerEvent);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.core.commands.AbstractHandler#isEnabled()
	 */
	public boolean isEnabled() {
		return enabled;
	}
}

Also note that IHandlers are not handed an IAction, but the IHandler can return its own isEnabled() state directly.

At the moment, a wrapper for an existing I*ActionDelegate is the ActionDelegateHandlerProxy.

Similarly, an IEditorActionDelegate equivalent (same applies to IViewActionDelegate, except it would use the active part) can access the active editor:

	public Object execute(ExecutionEvent event) throws ExecutionException {
		IEditorPart activeEditor = null;
		ISelection selection = null;
		Object appContextObj = event.getApplicationContext();
		if (appContextObj instanceof IEvaluationContext) {
			IEvaluationContext appContext = (IEvaluationContext) appContextObj;
			activeEditor = (IEditorPart) appContext
					.getVariable(ISources.ACTIVE_EDITOR_NAME);
			selection = (ISelection) appContext
					.getVariable(ISources.ACTIVE_CONTEXT_NAME);
		}
		// ... execute the event.
		return null;
	}


IObjectActionDelegate

For the behaviour part of an IObjectActionDelegate, it can simply get the active part on execution similar to the IEditorActionDelegate with appContext.getVariable(ISources.ACTIVE_PART_NAME).

Handlers are the implementation of behaviour, and so there is no one to one equivalent of IObjectActionDelegate#setActivePart(IAction action, IWorkbenchPart targetPart). The menu item as displayed in the popup menu can declare visibleWhen in its XML to help control its visibility, but will not be notified just before it is about to show.

Framework Enhancements for 3.3

Quick list of code issues to be addressed in 3.3.

Issues to Address

Issue 101 - PluginActions disconnected from Handlers

Menus and toolbars point to PluginActions, but the keybindings have an ActionDelegateHandlerProxy that is disconnected from the original action. See Bug 151612 -KeyBindings- keybinding not enabled even though actions appear enabled in menus

Does this become an issue if we replace our menuing abstraction?

Issue 102 - Commands implementation of label changing

Commands are an abstraction of behaviour, an have a lot in common with RetargetableActions. Certain kinds of RetargetableActions provide the ability to switch the labels, like switching Undo to Redo. This can be accomodated by adding text state to the command:

<extension
      point="org.eclipse.ui.commands">
   <command
         description="A test case for a command with state"
         id="org.eclipse.ui.tests.commandWithState"
         name="Command Wtih State">
      <state
            class="org.eclipse.jface.menus.TextState"
            id="NAME"/>
   </command>
</extension>

Currently, command states that have meaning are in org.eclipse.jface.menus.IMenuStateIds and org.eclipse.core.commands.INamedHandleStateIds.

The NAME state can be updated, and also supports state change notification. Our menu and/or toolbar rendering would have to listen for changes to properties and states associated with commands?

States are also the proposed way to handle information like toggled state (for check boxes or toggle buttons) and radio state (for a group of radio buttons).


Issue 103 - Menu items map to ParameterizedCommands

Just like a keybinding, each menu item or toolbar button would map to a ParameterizedCommand. If there was no parameters involved, then just containing the command id would be acceptable. For example, if you were creating menu items for Show Console View and Show Problems View each menu item would map to the ParameterizedCommand.

IHandlerService#executeCommand(String commandId, Event event) can execute a normal command with a selection event, and IHandlerSerivce#executeCommand(ParameterizedCommand command, Event event) does the same with a parameterized command.

Issue 104 - Dynamic menus need a good story

Dynamic menus need to work correctly for menus and menu items. They also need to create the drop-downs for toolbar items.

If the top part of a dynamic menu is declarative, then it can be provided without loading the plugin. Then a callback could be specified.

Menu Proposal 1

Replace the entire thing with something really simple (haha). This should most closely resemble the design described in the original RFC in Historical Information Section.

Menu placements would be controlled by a tree of data.

  • File (menu)
    • New (menu)
      • (group: new.wizard)
      • New Wizard (item)
    • Open File... (item)
    • (group: file.close)
    • Close (item)
    • Close All (item)
  • Edit (menu)
    • Undo (item)
    • Redo (item)

All nodes in the tree would have visibility and enablement state. Enablement for the menu node would be tied to its Command.

The each menu item would be able to specify some order constraints.

Dynamic menus would have a simpler form. They would also be included in the declarative extension point (as much as possible) See Issue 104.

There would be a product level visibility (similar to the way Customize Perspective works for ActionSets). Would we also provide product level ability to customize other prospective menu item attributes? For example, a product level way to override menu order?


This would mean an out-and-out replacement of most of the internal classes to do with PluginActions and the ActionSetRegistry. For RCP support, we would continue to allow MenuManager and contribution items to be added, but those structures would need to be used to populate the tree structures. This is hard :-)


The new system would have to manage the interaction between 4 properties: Enabled, Active, Visible, Showing. Enabled is the state of the command (from the active handler), Active is the state of the handler, Visible is the state of the menu item, and Showing (and internal state) describes if the menu item can currently be seen by the user. Showing is a new concept designed to help with lazy loading and lazy creation of sub-menus. It's not in the current menu behaviour.


From an menu visibility/enablement point of view, Enabled is the state that matters. If no handler is active, Enabled will be false. If a handler is active, the Enabled will be the handler Enabled state.

Menu Proposal 2

We need a relatively straight forward model to represent our menu and toolbar structures. But JFace already provides decent coverage for that using MenuManager, ToolBarManager, and IContributionItems.

We would still have to deal with Issue 104 and improve dynamic menu creation.

Menu Proposal 3

The stuff we have is fine, we just need to hook commands at the bottom. All of those PluginActions will be CommandActions.


Historical Information

The Contribution proposal started a couple of releases ago with the original RFC Contribution RFC

There are discussions in a number of places:


Original Requirements

  1. Provide a single concept for contributing to the workbench. Right now, there are two distinct ontologies: actions and contribution items; and commands and handlers.
  2. Support the addition and removal of plug-ins.
  3. Separate model and behaviour from visual presentation. Adhere more closely to the Model-View-Controller pattern. Model and user interface separation.
  4. Extensibility. Every group of items in the user interface (e.g., menu, tool bar, etc.) should be extensible – both in structure and content.
  5. Universal keyboard shortcuts. A user should be able to add a keyboard shortcut to any item that appears in the user interface (e.g., menu item, tool item, menu, etc.).
  6. Separation of structure and content. The structure of the menus (e.g., groups) should be defined independently from the items.
  7. No implicit declarations of structure or content. Everything should be explicit.
  8. Fine-grained control over visibility.
  9. More intelligent updating of elements within the user interface. Support for lazy updating for elements that are not showing within the user interface. This lazy updating should be handled automatically – without the elements needing to understand whether they are showing.
  10. Improved control over menu definition and item ordering. This will affect the “Search” and “Run” menus.
  11. The selection should be capable of overriding the behaviour of a user action. For example, if a Java element is selected in the Resource Navigator, a rename should be a refactoring rename.
  12. Address the difficulty in determining the keyboard shortcuts to show for context menu items.
  13. Support dynamic entries in top-level menus. For example, the recently opened files in the “File” menu should be possible using only public API.
  14. There should be an easy way to define the default behaviour in response to a user action (i.e., default handler for a command).
  15. Provide localized control of the model, view and controller elements talked about in this proposal. This includes such concepts as automatic addition/removal as parts are become active/inactive, and automatic removal as parts are destroyed.
  16. Allow the same user interface element to be placed in multiple locations. Reduce duplication in the syntax, and try to reduce memory usage.
  17. Provide facilities for finding and triggering elements within the user interface. This is intended to provide better support for the welcome facilities, cheat sheets, macros and scripting.
  18. JFace must not lose functionality. Everything that can be accomplished with JFace must still be possible in JFace, even if the API changes radically. Similarly, everything that can be accomplished with the workbench must still be possible in the workbench.
  19. Contribute all of the workbench and IDE model, view and controller elements using the API from this proposal. Everything that the workbench and IDE can do should be possible for third-party plug-ins as well.
  20. Contributing arbitrary controls (e.g., combo boxes) to Eclipse, where appropriate.

Rational

The Eclipse Platform has always provided a mechanism for contributing items to the menus and tool bars in Eclipse. This mechanism has – up until now – been based on instances of IAction.

Actions suffered from a few key deficiencies. First of all, the interaction with the application model (e.g., the handling of the run method) was tightly coupled with its presentation elements (e.g., icon, label, etc.). Also, there was no easy way to provide user-configurable keyboard shortcuts. Actions were not initially designed with a way to identify two actions as sharing the same semantic behaviour. To further confuse matters, there were action delegates. Action delegates were not actions, but could handle action behaviour in some circumstances.

Actions were defined in XML using several extension points. This XML syntax had several problems. First of all, there were too many extension points, which made the syntax hard to learn and caused maintenance problems. Features added to one extension point, would have to be copied into other extension points. Ultimately, what ended up happening is that for any given feature, it was possible that only a subset of the extension points would actually support it (e.g., dynamic menus). Partly due to this and partly due to the tight coupling mentioned above, this lead to an overly verbose syntax containing duplicate XML elements. If an action was required in a view menu and in a context menu, then the XML would need to be copied and contributed to two different extension points. This also led to multiple instances of the action in memory.

Aside from these main points, there are handful of other significant problems we hope to address – either directly or indirectly. These include dynamic menus, ordering of contribution items, performance problems, and better macro and instrumentation support.

Back to the top