
This document outlines the state of the effort to unify the contributions mechanism in Eclipse under the new commands framework. Interested parties should review this document and verify that their use cases are reflected in the requirements and that the solution outline provided to date satisfies their needs.
Feedback is strongly encouraged and may be provided on the platform-ui-dev mailing list or in the bug report for this plan item.
This is the plan item:
“Improve action contributions. The current action contribution story is diverse and complex, and the interactions between key bindings and retargetable actions, unclear. Eclipse should unify the action contribution support, provide simplified support for key bindings, and clarify the interactions between key bindings and retargetable actions. Eclipse should also support new types of actions contributions, such as combo boxes in toolbars, and enable additional UI customization. [Platform UI] [Themes: User experience; Responsive UI]” (Bug 36968)
Author: Douglas Pollock (dpollock@acm.org)
Version: 0.4
Last Modified: Monday, January 31, 2005, 1:00 p.m. (Eastern European Time)
The Change History appears at the end of this document.
Thanks to Nick Edgar, Kai-Uwe Maetzel and Michael van Meekeren for their comments and assistance. Also, thanks to Kim Horne, Pascal Rapicault and Jean-Michel Lemiux for advice in areas I'm less familiar with.
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.
With the release of version 3.0 of Eclipse, we saw the start of a replacement for the old actions mechanism: ICommand. Commands were a distillation of the old RetargetAction concept, but further refined. A command is an abstract representation for some semantic behaviour. It is neither the actual implementation of this behaviour, nor the visual appearance of this behaviour in the user interface. It is a bridge between the two.
We would like to continue the replacement of the old actions mechanism – eventually deprecating all of the actions code and extensions points in JFace and the workbench. A new contributions mechanism will be built on top of the commands concept.
In summary, we want to:
Simplify the process of contributing to the workbench, both for new users and for advanced users.
Make the contributions mechanism easier to understand, maintain and extend.
Here is a list of requirements in the approximate order of their importance. Numbers are provided so that it easier to refer to these requirements later on. While the majority of these requirements are approached by the current state of this proposal, some of them are not addressed or not completely addressed.
Provide a single concept for contributing to the workbench. Right now, there are two distinct ontologies: actions and contribution items; and commands and handlers.
Support the addition and removal of plug-ins.
Separate model and behaviour from visual presentation. Adhere more closely to the Model-View-Controller pattern. Model and user interface separation.
Extensibility. Every group of items in the user interface (e.g., menu, tool bar, etc.) should be extensible – both in structure and content.
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.).
Separation of structure and content. The structure of the menus (e.g., groups) should be defined independently from the items.
No implicit declarations of structure or content. Everything should be explicit.
Fine-grained control over visibility.
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.
Improved control over menu definition and item ordering. This will affect the “Search” and “Run” menus.
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.
Address the difficulty in determining the keyboard shortcuts to show for context menu items.
Support dynamic entries in top-level menus. For example, the recently opened files in the “File” menu should be possible using only public API.
There should be an easy way to define the default behaviour in response to a user action (i.e., default handler for a command).
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.
Allow the same user interface element to be placed in multiple locations. Reduce duplication in the syntax, and try to reduce memory usage.
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.
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.
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.
Contributing arbitrary controls (e.g., combo boxes) to Eclipse, where appropriate.
Support arbitrary nesting of parts. For example, a page in a page book view should have the same API control as a part in a part tab folder. [requires: Component and Services framework]
Prevent some commands from appearing in the keys preference page. This is an issue of controlling the namespace of commands – mainly for RCP applications. [this problem is not addressed by this proposal]
Resolve problems with object contributions in editors. Early in the 3.1 development cycle, object contributions were added for the input of the active editor. This causes problems with multi-page editors. See Bug 75273. [this problem is not addressed by this proposal]
The general idea of the solution is to replace actions with commands – everywhere within Eclipse. Rather than trying to extend the existing extension points, we wish to deprecate those extension points and provide replacements based on the lessons we've learned so far.

The following Java code will become deprecated: org.eclipse.jface.action.*, action bars, action bar contributors, org.eclipse.ui.actions.*, org.eclipse.ui.commands.*, org.eclipse.ui.contexts.*, and org.eclipse.ui.keys.*.
The following extension points will become deprecated: actionSets, commonAction, editorActions, popupMenus, and viewActions.
Since actions will be removed from JFace, a replacement must be provided based on commands. To do this, a new plug-in will be created, called org.eclipse.core.commands, which will contain a port of the commands and contexts mechanism from org.eclipse.ui.workbench. This plug-in will have no dependencies1. This plug-in will house all of the commands and contexts code that is not dependent on SWT (i.e., model-only). In this way, the core of the commands code will be usable within headless applications.
The majority of the code supporting keyboard shortcuts will be moved from the workbench to JFace. Some workbench-specific pieces of commands, contexts and keys will remain in the workbench. Old public API will be supported via the wrapper pattern. That is, wrappers will be created around the new code that satisfy the old APIs.
Functionally, the code will still support all of the old features it used to. In that respect, not much will change. However, it will be necessary to separate the user interface elements (e.g., keyboard shortcuts, menus, etc.) from the model elements (e.g., commands, contexts, etc.). It will also be necessary to make all of the various managers mutable by default. (In the old API, all of the managers were only exposed as immutable objects.)
New functionality will be added to commands.
One of the goals is to try to make actions conform to the Model-View-Controller pattern more closely. To do this, we will try to separate the model, view and controller components into separate extension points. The extension points for contributing to the workbench will be as follows:
Model. org.eclipse.ui.commands, org.eclipse.ui.contexts, org.eclipse.ui.handlers
View. org.eclipse.ui.commands.images, org.eclipse.ui.menus
Controller. org.eclipse.ui.bindings
These extension points will still be defined in org.eclipse.ui, and only read by the workbench. A complete example of the extension point syntax can be viewed on-line. There is also an examples plug-in available with an end-to-end example (i.e., including code).
The Java code backing the model extension points will all be defined in org.eclipse.core.commands.
A lot of the commands extension point will look the same, but there will be some signficiant additions, such as parameters, state and default handlers.
The following insignficant changes will also be made.
keyBinding and keyConfiguration will be moved to the bindings extension point.
activeKeyConfiguration will be deprecated. Its replacement is simply to use the product configuration preference (i.e., plugin_customization.ini): org.eclipse.ui/KEY_CONFIGURATION_ID
The experimental element handlerSubmission will be removed.
It will be possible to define a default handler for a command. This is just syntactic sugar for a common case. A default handler is simply a handler which has no conditions that need to be satisfied to become active.
<extension
point="org.eclipse.ui.commands">
<command
name="%name"
description="%description”
id="org.eclipse.debug.ui.commands.commandId”
defaultHandler="org.eclipse.debug.Command" />
</extension>
This proposal will add the ability to define parameters for commands. The reason for parameters is explained in more detail further on (see Parameterized Commands in the Java API and Behaviour section). A parameter declaration allows the plug-in developer to notify the command architecture that the handlers for a particular command need more information before they can execute. This declaration is done via an extension point, so that the keys preference page can ask questions about what kind of parameters the command expects.
<extension
point="org.eclipse.ui.commands">
<command
name="%parameterizedName"
description="%parameterizedDescription”
id="parameterizedCommandId”
defaultHandler="ParameterAwareHandler">
<parameter
id=”myFirstParameter”
name=”%humanReadableName1”
values=”org.eclipse.MyParameter1”
optional=”false” />
<parameter
id=”mySecondParameter”
name=”%humanReadableName2”
values=”org.eclipse.MyParameter2” />
</command>
</extension>
Note that the parameters must be declared at the same time as the command. Otherwise, handler developers would not know whether the command will have parameters, or what those parameters might be. It is not possible for third-party plug-ins to define additional parameters for a command.
It is assumed that a parameter is optional, unless otherwise noted in the declaration. If a parameter is optional, it means that it is either not needed or that the handler should be capable of prompting the user for its value.
It is also possible to define a help context identifier for a command. This will be the default help context, and can be overridden by the active handler. This help context is a pointer into some application-specific help. In the case of the workbench, this is typically an info pop-up triggered by pressing Ctrl+F1. This is an optional attribute.
<command
name="%name"
description="%description”
id="org.eclipse.debug.ui.commands.commandId”
helpContextId="my_help_context_id" />
Sometimes, a plug-in developer wants to define a command that represents the toggling of some boolean property (i.e., graphically represented by a check box). Or, perhaps, it is a group of commands which share some state (i.e., graphically represented by a radio button). This state is declared on the command, and passed to its handler as the handler becomes active. This is discussed in more detail further on (see Handling State in the Java API and Behaviour section). For now, we'll just look at the XML syntax.
<command
name="Check"
description="Single boolean”
id="checkCommandId”>
<state type=”toggle” default=”true” />
</command>
<command
name="Radio 1"
description="Shared boolean 1”
id="radioCommandId1”>
<state type=”radio” default=”true” id=”radio” />
</command>
<command
name="Radio 2"
description="Shared boolean 2”
id="radioCommandId2”>
<state type=”radio” default=”false” id=”radio” />
</command>
<command
name="Unpersisted Check"
description="Single, unpersisted boolean”
id="checkUnpersistedCommandId”>
<state
type=”toggle”
default=”true”
persisted=”false”/>
</command>
In the case of a conflict between the default attributes on a radio state, one of the commands is chosen arbitrarily. No guarantees are made on how this choice is made.
If the plug-in developer does not want the state to be automatically persisted, then this can be specified. Otherwise, it will. This is explained further on.
The handlers extension point is an elaboration of the experimental handlerSubmission element defined in Eclipse 3.0. A handler is the behaviour of a command at a particular point in time. A command can have zero or more handlers associated with it. At any one point in time, however, a command will either have no active handler or one active handler. The active handler is the one which is currently responsible for carrying out the behaviour of the command. This is very similar to the concept of an action handler and a retargettable action.
The handlers extension point allows a plug-in developer to specify a handler that should become active and/or enabled under certain conditions. If a handler is inactive, then no command will delegate its behaviour to the handler. If a handler is disabled, then the handler will not be asked to execute2; execution of the handler is blocked. The conditions are defined using the expression language facility added during 3.0. They are expressed using activeWhen and enabledWhen clauses.
The workbench provides some variables that these expressions can rely on. The variables supported are: the active contexts, the active editor, the active part and the current selection. While not supported in this initial design, it is easy to see how it would be possible to add other variables or even allow plug-in developers to contribute other variables.
A handler that specifies no conditions is a default handler. A default handler is only active if no other handler has all of its conditions satisfied. If two handlers still have conditions that are satisfied, then the conditions are compared. The idea is to select a handler whose condition is more specific or more local. To do this, the variables referred to by the condition are looked at. The condition that refers to the most specific variable “wins”. The order of specificity (from least specific to most specific) is:
Active contexts (least specific)
Active editor
Active part
Current selection (most specific)
If this still doesn't resolve the conflict, then no handler is active. If a particular tracing option is turned on, then this leads to a message in the log. A conflict can also occur if there are two default handlers. It is the responsibility of the plug-in developers and integration testers to ensure that this does not happen.
These conditions are used to avoid unnecessary plug-in loading. These handler definitions are wrapped in a proxy. For a proxy to load its underlying handler, two things must happen: the conditions for the proxy must be met so that it becomes active, and the command must be asked to do something which it must delegate (e.g., execute()).
<extension
point="org.eclipse.ui.handlers">
<handler
commandId="commandId"
class="org.eclipse.compare.Command">
<activeWhen>
<with variable="selection">
<count value="1" />
<iterate operator="and">
<adapt type="IResource" />
</iterate>
</with>
</activeWhen>
</handler>
</extension>
To further avoid plug-in loading, it is possible to specify when the handler is enabled. If the proxy has not yet loaded the handler, then only the expressions syntax is used to decide if the handler is enabled. If the proxy has loaded the handler, then the expressions syntax is consulted first. If the expressions syntax evaluates to true, then the handler is asked if it is enabled. (This is a short-circuit Boolean “and” operation between the expressions syntax and the handler's enabled state.)
<extension
point="org.eclipse.ui.handlers">
<handler
commandId="commandId"
class="org.eclipse.Handler">
<enabledWhen>
<with variable="context">
<property
id="id"
value="debugging" />
</with>
</enabledWhen>
</handler>
</extension>
It is also possible to specify a help context identifier for a handler, in much the same way as for a command. This allows a particularly handler to provide more detailed or more specific help, if necessary. This is an optional attribute.
<handler
commandId="commandId"
class="org.eclipse.Handler"
helpContextId="my_more_detailed_help">
</handler>
The contexts extension point will look exactly the same as it did in 3.0.
<extension
point="org.eclipse.ui.contexts">
<context
name="%Context.console.name"
description="%Context.console.description"
id="org.eclipse.debug.ui.console"
parentId="org.eclipse.ui.contexts.window" />
</extension>
The Java code backing the view extension points will exist mostly in JFace, with some workbench-specific components located within org.eclipse.ui.workbench.
The images extension point provides a way of linking different types of images and icons to a particular command. These images can be used, as appropriate, wherever the command appears in the user interface. To accommodate some boundary cases (e.g., tool bar drop-down items), it is possible to group the images for a particular commands into “styles”. These styles can then be specifically requested when a command is to be displayed.
<extension
point="org.eclipse.ui.commands.images">
<image
commandId="ProfileLast"
hoverIcon="icons/full/etool16/profile.gif"
disabledIcon="icons/full/dtool16/profile.gif"
icon="icons/full/etool16/profile.gif" />
<image
commandId="ProfileLast"
hoverIcon="icons/full/etool16/history.gif"
disabledIcon="icons/full/dtool16/history.gif"
icon="icons/full/etool16/history.gif"
imageStyle="toolbar" />
</extension>
The most complex of the extension points to be added as part of this proposal is the menus extension point. It allows the plug-in developer to define menus, separators, logical groups and menu items – appearing anywhere within the application, from status lines to context menus. It also allows sets of such contributions to be defined (i.e., action sets3); these action sets can be turned on or off by the end user. In brief, the menus extension point contains all of the presentation elements (except the icons) from the extension points we wish to deprecate.
Every element within this extension point is given a unique identifier. This is so that these elements can be referred to elsewhere without having to restate the element. For example, the identifier might be required for ordering or for defining an action set. Also, this allows third-party plug-in developers to place these elements in new locations within the interface, as appropriate.
The plug-in developer can define menus. A menu can appear either attached to a tool item, or somewhere within a view menu, context menu or the top-level menu bar. For free, the plug-in developer can assume that there is a menu and tool bar for every view, and that the top-level menu bar exists.4 Context menus must be registered programmatically before they can be used.
A menu definition looks something like this:
<menu
id="projection"
label="%Folding.label">
<location mnemonic="%Folding.label.mnemonic">
<part id=”AntEditor”>
<popup id=”#RulerContext”>
<groupLoc id=”rest” />
</popup>
</part>
</location>
</menu>
The location element is common to most of the elements within the menus extension point. It allows the plug-in developer to specify a location-specific mnemonic, and a path. The mnemonic exists with the location, as the appropriate mnemonic is usually selected in the context of the other elements at the same location. The mnemonic is externalizable, as the command name is also externalizable.
In this small example, we see that the path is specified with the combination of three elements: part, popup and group. This means that the item should be placed in a context menu. In this case, it is a context menu in the AntEditor called “#RulerContext”. In this menu, the submenu is appended to the “rest” group. The look-up is done in a strongly typed way. Each element along the path must specify its type. There are seven different types of elements: part, toolbar, menubar, popup, statusline, groupLoc and menuLoc.
The only restrictions are that:
The only elements that can appear at the top-level are part, toolbar, menubar, popup and statusline.
part elements can only contain popup, toolbar, menubar and statusline elements.
menubar elements can only contain menu elements.
toolbar elements can only contain menu or group elements.
popup elements can only contain menu or group elements.
statusline elements can only contain menu or group elements.
menu elements can only contain group elements.
group elements can contain menu or group elements.
Items can only be placed within groups.
The location elements are strongly typed for a few reasons. First of all, if the location was given as some kind of absolute path, this opens the possibility for confusion. For example, a menu or a group might share the same identifier. Also, we would need to be able to distinguish whether a path element refers to a menu or a toolbar. This opens the way for naming conflicts between our choice of distinguishing symbol and legacy code. Finally, we open the way for future extension to particular types of location elements.
<menu
id="textEditorMenu"
label="Text Commands">
<location mnemonic="X">
<part class=”AbstractTextEditor”>
<popup id=”#RulerContext”>
<groupLoc id=”rest” />
</popup>
</part>
</location>
</menu>
In this example, the plug-in developer is contributing to all parts who subclass or implement the given type. This allows, for example, for some contributions to be added to all text editors.
To define a dynamic menu, the dynamic element is used.
<menu
id="DynamicMenu"
label="%DynamicMenu.label">
<dynamic class="DynamicMenu" />
<location mnemonic="%mnemonic">
<menubar>
<menuLoc id=”org.eclipse.ui.run”>
<groupLoc id=”runGroup" />
</menuLoc>
</menunbar>
</location>
</menu>
The only real difference here is that the menu defines a call-back class. This class is notified when the menu is about to change from not showing to showing (see SWT Renderer and Dynamic Menus). At this point, the class is given a chance to modify the contents of its group – in this case, the menu whose id is “RunWithConfigurationAction”.
And, the most complex menu example is the tool item drop-down. Its definition looks like this.
<menu
id="DynamicMenuWithCommand"
label="%DynamicMenuWithCommand.label"
commandId="ClickCommand">
<dynamic class="DynamicMenu" />
<location imageStyle=”toolbar”>
<toolbar>
<groupLoc id="launchActionSet”>
<groupLoc id=”debug" />
</groupLoc>
</toolbar>
</location>
</menu>
In this example, the menu has a command associated with it. This simply means that if the menu is somehow selected rather than shown (e.g., clicking on the left part of a tool item drop-down), that this is the command that should be executed. It also means that the menu will inherit all the other properties of the command (e.g., name, icon, etc.). It is always possible to override these properties. The label attribute on the menu overrides the name of the command. By specifying an image style attribute on the location, it is possible to select a different set of images to associate with the menu.5
If you wish to bind a key to a menu, then you will need to specify a keyBindingCommandId. The command must be defined like a regular command. However, by specifying this attribute, you avoid needing to define a handler to open the menu. One will be created automatically for you, and will be managed by the workbench. It is strongly recommended that plug-in developers avoid creating handlers for commands that are used in this way.6
<menu
id="Occurrences"
label="Occurrences in File"
keyBindingCommandId="ShowOccurrencesMenu">
<location>
<part class=”AbstractTextEditor”>
<popup id=”#EditorContext”>
<groupLoc id=”additions” />
</popup>
</part>
</location>
</menu>
It is also possible to attach a help context identifier to a menu itself. This is required if there is no command to provide the help context.
<menu
id="RunWithConfigurationAction"
label="Run With Configuration"
helpContextId="run_with_configuration_context">
<location>
<menubar />
</location>
</menu>
Within a menu, it is possible to define logical groups. These logical groups can either be visible (e.g., separators are drawn before and after, as appropriate) or invisible. By default, logicial groups are visible.7
<group id="stepGroup">
<location>
<menubar>
<menuLoc id="org.eclipse.ui.run" />
</menubar>
</location>
</group>
<group
id="stepIntoGroup"
separatorsVisible="false" >
<location>
<menubar>
<menuLoc id="org.eclipse.ui.run" />
</menubar>
</location>
</group>
Groups can be declared to be dynamic, in the same way as menus.
<group
id="runHistory">
<dynamic class="DynamicRunHistoryGroup" />
<location>
<menubar>
<menuLoc id=”org.eclipse.ui.run”>
<groupLoc id=”runGroup” />
</menuLoc>
</menubar>
</location>
</group>
Within a group, the plug-in developer can place items. An item could be a menu item or a tool item, depending on where it is placed. It could also be something in the status line. Here is a fairly complicated item definition:
<item
id="ToggleStepFilters"
commandId="ToggleStepFilters">
<location mnemonic="%mnemonic">
<menubar>
<menuLoc id=”org.eclipse.ui.run”>
<groupLoc id=”emptyStepGroup” />
</menuLoc>
</menubar>
</location>
<location>
<part id="org.eclipse.debug.ui.DebugView">
<toolbar>
<groupLoc id="renderGroup" />
</toolbar>
</part>
</location>
<location mnemonic="%mnemonic">
<part id="org.eclipse.debug.ui.DebugView">
<popup>
<groupLoc id="renderGroup" />
</popup>
</part>
</location>
</item>
This declaration places an item in three different locations within the user interface. It appears in the top-level menu bar, in the debug view's tool bar and in the debug view's default context menu. Notice also that the popup element does not specify an id. If no identifier is specified, then it is assumed that the id is the same as the part id.8
If the popup element is specified with no id and no parent part element, then it applies to any context menu registered with the workbench. This is similar to the behaviour of the old object contributions. Similarly, a top-level popup element with an id will affect any context menu registered with the given name.
<item
id="ObjectContribution"
commandId="ObjectContribution">
<location>
<popup>
<groupLoc id="additions" />
</popup>
</location>
</item>
Sometimes, you might want to control the visibility of an item. While normally it is preferrable to maintain stability in the layout of menus and tool bars9, it is sometimes desirable to hide items that aren't immediately relevent. This is particularly true on context menus, where space is limited. In this case, you would define a visibleWhen element. This element is almost identical to the activeWhen and enabledWhen elements defined in the handlers extension point.
<item
id="ConvertToWatchExpression"
commandId="ConvertToWatchExpression">
<location mnemonic="%mnemonic">
<part id="org.eclipse.debug.ui.DebugView">
<popup>
<groupLoc id="additions" />
</popup>
</part>
</location>
<visibleWhen>
<with variable="selection">
<iterate operator="and">
<not>
<instanceof value="IWatchEx"/>
</not>
<instanceof value="IExpression" />
</iterate>
</with>
</visibleWhen>
</item>
The most common case is simply to make something visible when its handler is enabled. This is handled with some syntactic sugar. There is a visibleWhenEnabled element.
<item
id="compareWithPatch"
commandId="compareWithPatch">
<location mnemonic="%mnemonic">
<part id=”MyPart”>
<popup>
<groupLoc id="additions" />
</popup>
</part>
</location>
<visibleWhenEnabled />
</item>
Any item associated with a command can include parameter values. If the parameter of the given identifier is not defined, this is an error. If the item does not have a command, then this is also an error.
<item
id="RunHistory"
commandId="RunHistory">
<location>
<menubar>
<menuLoc id="org.eclipse.ui.run" />
</menubar>
</location>
<parameter name=”index” value=”1” />
</item>
It also possible to specify relative ordering. This is done using the order attribute on the location element. The order attribute accepts the following values: start (put the element at the beginning of the container); end (put the element at the end of its container); after (put the element after the sibling element whose id matches ref); and, before (put the element before the sibling element whose id matches ref). Relative ordering can be applied to any type of menu element.
<item
id="MyFirstItem"
commandId="MyFirstCommand">
<location order=”start”>
<menubar>
<menuLoc id="org.eclipse.ui.run" />
</menubar>
</location>
</item>
<item
id="MySecondItem"
commandId="MySecondCommand">
<location order=”after” ref=”MyFirstItem”>
<menubar>
<menuLoc id="org.eclipse.ui.run" />
</menubar>
</location>
</item>
In the event of conflicts, Eclipse will chose an arbitrary order. The only guarantee is that, in the event of a conflict, the order will remain the same as long as the following holds:
The Eclipse version is the same.
The list of elements is the same (i.e., no dynamic elements, no plug-ins added or removed).
None of the menu elements mentioned so far have direct access to the widgets. Their access is managed by an SWT Renderer. If you require direct access to the widgets (e.g., for rendering a combo box), then you can use a widget element. Unfortunately, this means that if a widget element becomes visible in the user interface, this will lead to plug-in loading.
<widget
id="MyComboBoxSimple”
class=”MyComboBox” />
<location>
<toolbar>
<groupLoc id="myGroup">
</toolbar>
</location>
</widget>
<widget
id="MyComboBoxParameterized1”
class=”MyComboBox:a,b,c”>
<location>
<toolbar>
<groupLoc id="myGroup" />
</toolbar>
</location>
</widget>
<widget
id="MyComboBoxParameterized2”>
<class class=”MyComboBox”>
<parameter name=”list” value=”a,b,c” />
<parameter name=”selected” value=”c” />
<parameter name=”editable” value=”false” />
</class>
<location>
<toolbar>
<groupLoc id="myGroup" />
</toolbar>
</location>
</widget>
Widgets are characterized by the lack of a command identifier. The behaviour of a widget is undefined, and could be quite complex (e.g., the location bar in a web browser). All of this behaviour must be encoded by the plug-in developer. That being said, JFace will strive to provide useful common widgets – partly to avoid plug-in loading issues.
The widget class specified must implement IWidget (see Custom Widgets). This means that they must also implement IExecutableExtension. Parameters can be passed into the instance during initialization. By doing this, it is possible to create reusable implementations of IWidget (possibly to avoid plug-in loading).
It is possible to add an existing menu element of any kind (i.e., menu, group, item) to a new location. This allows third-party plug-in developers to include an existing definition in some new location – perhaps in a new view or editor – without needing to worry about things like externalized strings. A reference inherits all of its properties from the element to which it refers. Any attributes or sub-elements added to the reference will override the inherited values.
<menuRef id="SomeoneElse'sMenu">
<location>
<menubar>
<menuLoc id="org.eclipse.ui.run" />
</menubar>
</location>
</menuRef>
Finally, it is possible to define action sets, or groups of elements with a name. The workbench allows the user to view acton sets, and disable or enable them as desired. It is also possible for certain action sets to be associated with particular parts or perspectives.
<actionSet
label="%ProfileActionSet.label"
visible="false"
id="org.eclipse.debug.ui.profileActionSet">
<menuSetRef id="ProfileDropDownAction" />
<menuSetRef id="ProfileWithConfigurationAction" />
<menuSetRef id="ProfileHistoryMenuAction" />
<itemSetRef id="OpenProfileConfigurations" />
<itemSetRef id="ProfileLast" />
</actionSet>
An action set has a name and a unique identifier. It is also possible to specify whether the action set should be visible by default. Within the action set is simply a list of references to menus, groups and items that have been declared elsewhere.
The Java code backing the controller extension points will exist in mostly in JFace, with some workbench-specific components located within org.eclipse.ui.workbench.
The bindings extension point provides a way of defining responses to different types of user input. In the scope of this proposal, bindings are essentially the keyBinding and keyConfiguration elements from the commands extension point. The code and XML elements have been made a little more generic, to support the future addition of gestures and mouse button shortcuts. However, most of these changes are simply in nomenclature.
The most notable changes are:
keyConfiguration has been renamed to scheme
keyBinding is now simply key
The bindings extension point looks roughly like this:
<extension
point=”org.eclipse.ui.bindings”>
<key
sequence="M2+F5"
commandId="commandId"
schemeId=”default”
contextId=”windows” />
<scheme
name="Default"
description="Default shortcuts for Eclipse"
id="default" />
</extension>
As mentioned above, these more generic naming is adopted to make way for different types of bindings in the future. For example, it might be possibly to make a binding to “Shift+Hover” or “Ctrl+LeftMouseButton”. However, these types of bindings are outside of the scope of this proposal.
It is also possible to make a binding to a particular parameter for a command. The following example binds “showView:Problems” to “Alt+Shift+Q X”.
<key
sequence="M2+M3+Q X"
commandId="showView"
schemeId=”default”
contextId=”windows”>
<parameter name=”view” value=”Problems” />
</key>
Most of the extension point elements are simply converted into data objects of the same name in Java10. The more interesting changes here deal with how these elements are integrated with the rest of the workbench components.
There is also an examples plug-in available with an end-to-end example (i.e., including code).
The design of the Java classes is intended to allow an abstraction of the widgets that will appear in the user interface. This abstraction is then read by a renderer (in the case of Eclipse, an SWT renderer), and converted into actual widgets. There are two main things that this renderer is attempting to support: dynamic menus and lazy updating.
The widget abstraction provides a global picture of how the menu structure should look – regardless of whether certain elements are currently displayed to the user or not. So, for example, a menu may appear in a menu bar, but we do not need to have created all of its menu items until the menu is shown. In the context of the renderer, this is the difference between an element that is showing versus an element that is not showing.
So, for every element that could appear in the user interface, there are three boolean values that matter to the renderer. It matters whether the element is enabled, whether the element is visible and whether the element is showing.
When a change occurs in one of the variables which affects the “when” conditions, the list of all showing elements is notified11. So, if a menu item is visible and enabled, but its parent menu is not yet shown, then the menu item is not showing. This means it will not be notified of any changes until its parent menu is shown. However, a top-level tool item which is not visible is still considered to be showing. This means that the tool item will be notified of changes. If this notification phase causes any of the visual characteristics of an element to change, then it is flagged as dirty. In a similar fashion to contribution items, this dirty state is inherited by elements containining dirty elements.
If a variable that is not provided by the workbench changes (e.g., a system property), then the renderer must be notified by the plug-in developer who caused the change to occur. If the variable might change without the plug-in developer knowing, then the plug-in developer must implement a polling routine.
public interface IRenderer {
/**
* Notifies the renderer that the variable of the
* given name has changed. This will trigger
* re-evaluations of all the showing interface
* elements.
*
* @param name The name of the variable that
* changed. If this value is null, then re-
* evaluate all expressions.
*/
public void variableChanged(String name);
}
When the notification phase is complete, all of the showing elements are checked to see if they are dirty. If they are dirty, then their state is updated in the interface.
This idea also helps better support dynamic menus. When a menu containing dynamic elements is about to show, it is possible for the renderer to ask the abstraction layer to update its contents. So, when a dynamic menu is shown, it can add or remove elements from itself. If changes do occur, then the renderer can create and destroy widgets in the menu before it actually becomes visible to the user.
The enabled property controls the execution of the handler. A handler that is disabled cannot execute, while a handler that is enabled can execute. It is common for a command whose handler is disabled to alter its appearance when shown in the user interface. For example, a menu item for a command whose handler is disabled might also choose to appear as disabled.
The active property controls which handler is currently associated with a command. For each command, there can be zero or more handlers associated with it. The workbench must be able to decide which handler should currently be responsible for a command's behaviour. A command can have zero or one active handlers at any moment in time.
The visible property controls whether a menu element's widget should be created. If a menu element is not visible, then the renderer (see SWT Renderer) may not create the underlying widget. If the widget supports it, then it might also use a setVisible method to hide the widget. Either way, if a menu element is not visible, then the user will not be able to see it.
The showing property is an internal property used by the renderer. It is a flag indicating whether a menu element has the possibility of being seen by the user. For example, a widget might exist in the menus, but the user will not see it until the menu is shown. This property is used to try to defer work until it is absolutely necessary.
In the case of a menu element, the renderer will first test if the element is showing. If it is, then it will test whether the element is visisble. If it is, then it will create the widget. The renderer then checks to see if the command is enabled. To answer this question, the command checks to see which handler is active. The handler is then asked if it is enabled. If it is disabled, then the widget is drawn disabled.
In the case of a keyboard shortcut, the binding architecture will ask the command is it is enabled. To answer this question, the command checks to see which handler is active. The handler is then asked if it is enabled. If it is enabled, then the command is executed.
When the renderer is intended to block access to the underlying widgets, this is not always possible. Sometimes it is necessary for objects to create a custom widget representation. This should be avoided as much as possible, as it is very easy to severely affect start-up performance.
To get access to the underlying widgets, the plug-in developer defines a widget element. This element mentions a class, which must implement IWidget.
public interface IWidget extends IExecutableExtension {
/**
* Disposes of the underlying widgets. This is
* called when the widget is becoming hidden.
*/
public void dispose();
/**
* Fills the given composite control with controls
* representing this widget.
* @param parent the parent control
*/
public void fill(Composite parent);
/**
* Fills the given menu with controls representing
* this widget.
* @param parent the parent menu
* @param index the index where the controls are
* inserted, or <code>-1</code> to insert at the
* end
*/
public void fill(Menu parent, int index);
/**
* Fills the given tool bar with controls
* representing this contribution item.
* @param parent the parent tool bar
* @param index the index where the controls are
* inserted, or <code>-1</code> to insert at the
* end
*/
public void fill(ToolBar parent, int index);
/**
* Fills the given cool bar with controls
* representing this contribution item.
* @param parent the parent cool bar
* @param index the index where the controls are
* inserted, or <code>-1</code> to insert at the
* end
*/
public void fill(CoolBar parent, int index);
}
Once one of these objects is created, it is its responsibility to manage the enabled state of the controls. The visibility will be managed by the renderer. When the widget becomes visible (and showing), it will get a fill. When it becomes invisible (or not showing), it will get a dispose.
Dynamics menus are declared via the menus extension point. A class is provided in the declaration, which must implement the IDynamicMenu interface.
public interface IDynamicMenu {
/**
* Called just before the given menu is about to
* show. This allows the implementor of this
* interface to modify the list of menu elements
* before the menu is actually shown.
*
* @param menu The menu that is about to show.
* This value is never null.
*/
public void aboutToShow(IMenuCollection menu);
}
Both menus (SMenu) and groups (SGroup) contain an instance of IMenuCollection. This instance is only (publicly) reachable through the dynamic menus callback. The interface is intended to provide a modifiable list of menu elements. With the list provided, it is possible to add or remove elements at any position.
public interface IMenuCollection {
/**
* Appends a menu element to the end of the
* collection.
* @param element The element to append. Must not
* be null, and must be of the appropriate type
* for the type of collection.
*/
public void add(MenuElement element);
/**
* Adds a menu element at the given index.
* @param index The index at which to insert.
* @param element The element to append. Must not
* be null, and must be of the appropriate type
* for the type of collection.
*/
public void add(int index, MenuElement element);
/**
* Removes all elements from the collection.
*/
public void clear();
/**
* Gets the element at a given index.
* @param index The index at which to retrieve
* the element.
* @return The element at the index.
*/
public MenuElement get(int index);
/**
* Removes the element at a given index.
* @param index The index at which to remove
* the element.
* @return The element that has been removed.
*/
public MenuElement remove(int index);
/*
* Removes the given menu element, if it exists.
* @param element The element to remove.
* @return true if the object was removed; false
* if it could not be found.
*/
public boolean remove(MenuElement element);
/**
* Returns the number of elements in the
* collection.
* @return The size of the collection.
*/
public int size();
}
An element must match the type of the collection. If the collection is created on a group, then the contents can be menus, groups or items. If the collection is created on a menu, then the contents can be only be groups.
The concept of state is used to handle objects like check boxes and radio buttons in the interface. The state is defined on a command, but it is the handler that manipulates this state. The renderer observes changes to this state, and updates the user interface (if appropriate). Any handler for a command with state must subclass HandlerWithState.
public abstract class HandlerWithState extends AbstractHandler implements IHandlerStateListener {
/**
* Returns the state.
*
* @returns The state for the command, if this
* handler is active. If this handler is
* inactive, then this returns null.
*/
public IHandlerState getState() {
return state;
}
/**
* Notifies this handler that the state has been
* changed by some third-party. This method is
* not called if the execute() method is called.
*/
public abstract handleStateChange();
/**
* Sets the state for the handler.
*
* @param state The new state for this handler.
* If the handler has just been activated, this
* will be the state for command. If the handler
* has just been deactivated, then this will be
* null.
*/
public void setState(IHandlerState state) {
if (this.state != null)
this.state.removeListener(this);
this.state = state;
if (this.state != null)
this.state.addListener(this);
}
}
public interface IHandlerState {
/**
* Adds a listener to changes for this state.
* @param l The listener to add.
*/
public void addListener(IHandlerStateListener l);
/**
* Returns whether this state is toggle.
* @return true iff toggled; false otherwise.
*/
public boolean isToggled();
/**
* Removes a listener to changes from this state.
* @param l The listener to remove.
*/
public void removeListener(
IHandlerStateListener l);
/**
* Toggles the state. This will notify any
* associated states, if necessary.
*/
public void toggle();
}
In this way, the state is passed to the currently active handler, and no other handler can manipulate it. This also provides a mechanism by which the handler can be notified of the state changing. Such notification only affects shared states (e.g., radio buttons).
The workbench also takes care of persisting this state. When the workbench shuts down, the value of the state for each command will be persisted. On start-up, the values from the persisted states will be restored, rather than the default values declared in the extension point. To override this behaviour, the plug-in developer must specify so in the state declaration.
The core support provided in org.eclipse.jface will be based around the concept of elements (e.g., bindings, menus, commands, etc.) and managers. A manager is a mutable collection of one or more types of objects. When a manager is changed in some meaningful way, then events will be emitted. Applications can listen to this events, and updates themselves appropriately.
In the case of a JFace/SWT application, it is the responsibility of the developer to create these managers, as appropriate. It is also the responsibility of the developer to define the relationship between changes to these managers and changes in their application.
In the case of the workbench, these managers are private. Access to the managers is provided through services. These services provide support for nesting. They also block access to some or all of the methods available on the managers. In this way, the workbench can maintain control over how and when changes are made.
Strictly speaking, these classes are not needed, as all of their responsibilities can now be handled purely in XML. However, as these interfaces will need to be supported for backward compatibility reasons, it is reasonable to provide replacements. This also makes it possible to contribute user interface elements at run-time12.
SMenu menu = new SMenu(“id”, “externalizedLabel”);
LocationElement groupLocation = new GroupLocation(“group”);
LocationElement popupLocation = new PopupLocationElement(“contextMenu”, groupLocation);
LocationElement partLocation = new PartLocation(“part”, popupLocation);
MenuLocation location = new Location(partLocation);
// Assume that part is a view or editor.
IMenuService service = (IMenuService) part.getService(IWorkbenchServices.MENU);
service.contributeMenu(menu, location);
As parts become active or inactive, the service abstraction ensures that the appropriate contributions appear. If the part to which a contribution is made becomes inactive, then the contribution is temporarily removed. If that part becomes active again, then the contribution is added back. This all happens without the intervention of the plug-in developer.
Editors are treated specially in Eclipse. If the editor becomes inactive, then nothing changes. If the editor becomes hidden, then the contributions become disabled.
When the part is destroyed, these contributions will automatically be removed as well. It is also possible to remove the contributions programmatically, by holding on to value returned by the contributeMenu method.
IMenuContribution request = service.contributeMenu(menu, location);
...
service.removeMenu(request);
The IMenuContribution is used to ensure that two third parties cannot interfere with each other's requests. So, if plug-in developer A contributes a menu, then plug-in developer B cannot retract that request.
All of the data objects related to menus are immutable. Once constructed, it is not possible to modify their values. This ensures that data passed into the menus service cannot be changed – only removed.
It will not be publicly possible to contribute image bindings for commands in the workbench. There will be private (i.e., internal) support for this, but it is only intended to support the old contributions mechanism. All new code should find the images extension point sufficient. (Note that the JFace layer does not have a concept of services or parts, and so these comments do not apply to pure JFace applications.)
In the case of the workbench, we want the list of keyboard shortcuts to be a static experience for the user – changing in a predictable way. We also want changes to the keyboard shortcuts made by the user to be respected. To ensure that this happens, it is necessary to make sure that keyboard shortcuts can only be defined via the bindings extension point – rather than programmatically. The binding service will not allow a plug-in developer to define bindings. If a plug-in developer wishes to provide keyboard shortcuts for a particular task (e.g., in a particular editor), then contexts can be used.
Commands and contexts are similar to bindings (see Defining Bindings). In JFace/SWT, it will be possible to add and remove commands and contexts. However, in the workbench, it will only be possible to define commands and contexts using the appropriate extension points.
If commands and contexts could be contributed dynamically to the workbench, then this would affect dynamic unloading of plug-ins. If a command or context is contributed through an extension point, then it is possible to clean up properly when the corresponding plug-in is unloaded. If they are contributed through Java code, then it places a burden on the plug-in developer to clean up properly when the plug-in is unloaded.
Since contexts and commands are global in nature, this is a more significant problem than with other types of contributions. A menu would typically be contributed for a particular part; when this part closes (possibly due to plug-in unloading), the contribution will be removed automatically. The life cycle for commands and contexts in the workbench is intended to be longer – over the life of an application.
The life cycle needs to be longer. One of the requirements is that it should be possible to add a keyboard shortcut to anything that appears in the user interface. If commands and contexts could appear and disappear quickly, then the keys preference page would be undermined.
For these reasons, commands and contexts are only statically defined in the workbench – using the appropriate extension points. In the case of dynamically generated menu items, please read the subsection called Parameterized Commands.
There are three conditions which lead to a contradiction. Menu items must be linked to command identifiers. It is possible to generate menu items dynamically – based on changes in the application model. Commands are purely static.
To workaround this contradiction – while maintaining stability for the user and bearing in mind dynamic plug-ins – we introduce the concept of parameters for commands. The plug-in developer defines – via the commands extension point13 – whether the command expects parameters, and how many parameters it expects. For each parameter, it is possible to declare whether it is optional, and which class should be called to generate the list of possible parameter values. This class must implement ICommandParameter.
public interface ICommandParameter {
/**
* @returns A map of the externalizable name of
* the parameter value (e.g., as appropriate for
* display on the keys preference page) [String]
* to the actual value of the parameter (i.e., as
* should be passed to the handler) [String].
*/
public Map getParameterValues();
}
As an example, an instance of ICommandParameter might define a map that looks similar to the following.
public class ShowViewParameters {
public Map getParameterValues() {
Map map = new HashMap();
IViewDescriptor[] views=getViewDescriptors();
for (int i = 0; i < views.length; i++) {
IViewDescriptor view = views[i];
map.put(view.getLabel(), view.getID());
}
return map;
}
}
This class will be loaded if the full list of parameters is ever needed14 (e.g., keys preference page). Otherwise, the parameter values can be retrieved from the menu element -- either provided in the extension point or when the element was dynamically generated). Or, in the case of keyboard shortcuts, they are retrieved from the persisted keyboard shortcut. In all cases, the parameter value must be an instance of String, as they can be read from and persisted to XML.
When a command with a parameter is executed, the parameter is passed through to the handler. The handler then decides what to do. So, for example, let's consider a “run history” command. This command will execute a particular item in the history of launches. The code of the handler's execute method might look as follows.
public Object execute(ExecutionEvent event) {
int index;
Map parameters = event.getParameters();
String value = (String) parameters.get(“index”);
try {
index = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ExecutionException(e);
}
... // do the actual work
}
In most cases, the handler should try to make its parameters optional. This means that the handler is capable of prompting the user for more information, if required. This leads to more robust, flexible ... and complex code.
public Object execute(ExecutionEvent event) {
int index = -1;
Map parameters = event.getParameters();
String value = (String) parameters.get(“index”);
while (index == -1) {
if (value == null) {
value = promptUserForValue();
}
try {
index = Integer.parseInt(value);
} catch (NumberFormatException e) {
warnUser();
value = null;
}
}
... // do the actual work
}
The keys preference page will be modified to handle parameterized commands. The user will be capable of adding a keyboard shortcut to a command with certain parameter values. So, in the example above, it would be possible to bind “runHistory(1)” to “Ctrl+F11”.
Handlers should only be activated at the level of a view or editor (or something nested within such a part). If a plug-in developer wishes to register a handler at a higher level – globally, for example – then she should use the handlers extension point15.
Here is an example of what it might take to activate a handler.
// Assume that part is a view or editor.
IHandlerService service = (IHandlerService) part.getService(IWorkbenchServices.HANDLER);
service.activateHandler(commandId, handler);
When the part is destroyed, this handler will automatically be deactivated. It is also possible to deactivate the handler programmatically, by holding on to value returned by the activate handler method.
IHandlerActivation request = service.activateHandler(commandId, handler);
...
service.deactivateHandler(request);
If two activations are made for the same command identifier on the same service object, then a conflict occurs. The command will have no active handler. If the requests happen on sibling services (e.g., two Java editors), then the active service controls which handler is active. If the requests happen on parent-child services (e.g., a multi-page editor and a page within the multi-page editor), then the child service controls which handler is active.
Contexts are activated in a similar fashion to handlers. As with handlers, the context will automatically be deactivated if the part providing the service is destroyed. Also, the same conflict resolution rules apply (see above).
// Part can be a view or editor.
IContextService service = (IContextService) part.getService(IWorkbenchServices.CONTEXT);
service.activateContext(contextId);
Contexts can be activated at any point in time. This leads to a tricky situation with respect to dynamic plug-in loading. While the most common case is to activate a context for a particular editor, it is also possible to activate a context in response to some global event (e.g., starting a debugging session).
If a more global context is activated, then it is the responsibility of the plug-in to manage the possibility of the plug-in being unloaded. The context might be registered as follows.
// Part can be a view or editor, or even the workbench.
IContextService service = (IContextService) workbench.getService(IWorkbenchServices.CONTEXT);
IContextActivation request = service.activateContext(contextId);
MyPlugin.getDefault().activatedContext(request);
Then, the plug-in should override the stop method to unload the context.
public void stop(BundleContext context) {
IContextService service = (IContextService
workbench.getService(IWorkbenchServices.CONTEXT);
Iterator contextItr = contexts.iterator();
while (contextItr.hasNext()) {
IContextActivation request =
(IContextActivation) contextItr.next();
service.deactivateContext(request);
contextItr.remove();
}
}
If the context should be deactivated before the plug-in is unloaded, then the plug-in should be notified as well.
// Part can be a view or editor, or even the workbench.
IContextService service = (IContextService) workbench.getService(IWorkbenchServices.CONTEXT);
service.deactivateContext(request);
MyPlugin.getDefault().deactivatedContext(request);
It will be possible to find practically any element that has been contributed to the workbench. This can be accomplished through the various services provided.
// Assume that part is a view or editor.
IHandlerService service = (IHandlerService) part.getService(IWorkbenchServices.HANDLER);
IHandler handler = service.findHandlerByVariable(ICommandExpressionVariables.EDITOR, “myEditor”);
It will be possible to monitor the execution of commands. This facility is provided to support such things as macros and instrumentation. Plug-in developers will be capable of attaching a listener which will be notified when a command is about to be executed, or has finished execution. The listener can be attached either to the command manager or the command itself.
public interface IExecutionListener {
/**
* Called when a command is about to execute.
*
* @param commandId The id of the command that is
* about to execute. Never null.
* @param e The execution event that will be
* passed to the command. Never null.
*/
public void aboutToExecute(String commandId,
ExecutionEvent e);
/**
* Called when a command has completed executing
* successfully.
*
* @param commandId The id of the command that is
* about to execute. Never null.
* @param returnValue The return value from the
* command. May be null.
*/
public void executionSucceeded(String commandId,
Object returnValue);
/**
* Called when a command has failed to execute.
*
* @param commandId The id of the command that is
* about to execute. Never null.
* @param e The exception thrown by the command.
*/
public void executionFailed(String commandId,
ExecutionException e);
}
It will not be possible to prevent the command from executing.
The most complex piece of this proposal will likely be the support of the old action-based contributions mechanism. It is difficult this early in the process to determine exactly what issues we will encounter. As these issues are discovered, they will be discussed here.
What happens if an action is created without an action definition id (i.e., with no associated command)? An action is an aggregation of a command, handler, and an image. When an action is created, one or more of these elements will be created automatically. If there is no action definition id, then a command identifier will be generated automatically. For this purpose, the workbench reserves the command identifier name space starting with org.eclipse.ui.compatibility. For example, org.eclipse.ui.compatibility.1 would be the first command automatically generated by the workbench. These types of commands will not be accessible to plug-in developers; they will be filtered out automatically.
How will contribution items get direct access to the SWT controls? There will be a wrapper created that implements IWidget and takes an IContributionItem as an argument.
How do you intend to support IAction.run(Event)? This will be supported using the ExecutionEvent. The execution event will carry the SWT event that triggered the execution, if any.
The plan can be divided into 3 rough phases: moving code down to JFace and below, adding support for the new contributions mechanism in terms of the old internal code, and rewriting the internals in terms of the new mechanism.
In the first phase, the goal is simply to move large sections of code introduced during the 3.0 development cycle. The commands, contexts and keyboard shortcuts support should be moved down to JFace or below. This will involve some reworking of the existing concepts, as well as creating wrappers for the new code that satisfies the 3.0 API. This will be done during the 3.1 M5 development cycle.
After this is done, the next step is to add support for the new extension points. This involves adding the extension points to org.eclipse.ui, as well as defining a Java API for the menus. Wrappers will need to be created around this new API so that it can be integrated with the old code. This work will be done during the latter half of the 3.1 M5 development cycle, and release during 3.1 M6.
Once the new code is working alongside the old code, the goal will be to rewrite the internals based on the new design elements. The wrappers for the new API will be removed, and wrappers for the old API will be created. This work – completing the ideas laid out in this proposal – will be completed during the first two milestones of the next major release cycle (i.e., 3.2 or 4.0, depending on what the PMC decides).
During the 3.1 development cycle, this work will require changes to the org.eclipse.core.expressions plug-in. This plug-in will need to be extended to support introspection on a constructed expression. This will be needed for at least two reasons. First of all, handler conflict resolution will need to decide which expression is “most precise”. To do this, the workbench will need to know which variables are referenced. Secondly, it will be needed for performance reasons. If a single variable changes, we would like to avoid re-evaluating all expressions. Instead, we would only like to re-evaluate those expressions whose variables have changed.
We will also require changes to how the org.eclipse.core.expressions plug-in handles the adaptable expression. It will need performance enhancements. Mostly likely, this will involve passing pre-computed adapter and hierarchy information via the evaluation context.
Also, this proposal interacts with – either directly or indirectly – the following 3.1 plan items: macros, undo/redo and the services/component framework.
This project represents a significant amount of change in the contributions API. After the changes are complete, it will be expected that there will be a significant period of time during which other Eclipse SDK teams will be migrating to new API. Eclipse SDK teams should expect to spend time during the next major release (e.g., 3.2 or 4.0) migrating to this work.
Large downstream clients will likely never migrate, unless they require functionality provided only by the new API. The people who will benefit most from this work will be smaller downstream applications based on Eclipse (particularly RCP applications) and Eclipse users (i.e., some of the new functionality provided by these changes will allow enhancements to the Eclipse IDE user experience).
As well, this should ease the maintenance effort of the Eclipse team, as significant changes will be restricted to the new contributions mechanism. Once this project is complete, it is expected that all but the most minor changes (bug fixes or enhancements) will only be applied to the new code base. Plug-in developers requiring significant improvements or fixes will be expected to migrate to the new code base.
Fixed the hyperlink to the full extension point example.
Added parameters to the bindings extension point.
Added the widget element for supporting arbitrary controls in menus.
Fix a typo in the class diagram (KeyBinding should have been Key).
Added a link to an example.
Added more explanations around active, enabled, visible and showing.
Renamed showMenuCommandId to keyBindingCommandId
Removed the concept of a set.
Changed the IMenuCollection interface.
Added a note about action delegates not being the same as actions in the old contributions mechanism.
Added a UML class diagram showing the general relationship of the major elements in this proposal.
Moved the help context id from the view to the model.
Moved state to the command and handler, rather than having it as part of the menu item.
Made the conflict resolution on handlers more specific.
Completely reworked the location element. Made it strongly typed.
Moved the imageStyle attribute from the widget element to the location element.
Added the showMenuCommandId attribute to menu.
Renamed the visible attribute on groups to separatorsVisible.
Added a note about the visibleWhenEnabled element.
Changed the ordering syntax slightly.
Added the ability to add new locations for existing element declarations.
Added more information about future bindings.
Distinguished between editors and other parts in how menu contributions are handled.
Explained how JFace/SWT applications can add bindings, commands and contexts.
Added a sample implementation of ICommandParameter.
Changed the execute method's signature to take an ExecutionEvent instead of a Map.
Added a small section for finding contributed elements.
Added a mechanism for listening to command execution.
Added a section discussing backward compatibility.
Added several unaddressed issues.
Minor wording changes.
Spelling and grammar corrections.
Initial draft.
1This plug-in will be used to house the undo-redo operations code. The undo-redo packages will introduce a dependency on org.eclipse.core.runtime. Previously, org.eclipse.jface.action had a dependency on this package to support status lines. So, adding this dependency to org.eclipse.core.commands does not seem unreasonable. The code talked about in this proposal will be free of dependencies.
2Also, if the handler for a command is disabled, then anywhere the command appears, it will appear disabled. For example, if the command were placed in a menu, then the menu item will appear disable when the active handler for the command is disabled.
3The name “action set” will continue to be used, as this avoids having nomenclature conflicts with the actionSetPartAssociations extension point. The reason for why it is called an “action set” (and not a “menu set” or some other name) will be left as a historical oddity.
4Plug-in developers can refer to these locations, but they might not actually exist. Particularly in RCP applications, the presentation of the workbench window or of individual views might be changed. For example, you can refer to the tool bar of an editor, but this tool bar is never actually constructed in the default presentation.
5Image styles are only designed to cover the use case of drop-down tool items. It assumed that the vast majority of commands will only have one type of image associated with them.
6If you wish to interact with the menu in a more complex manner, then create your own handler. Menus can be retrieved programmatically by path and id.
7It is the responsibility of the renderer (see SWT Renderer in the Java API and Behaviour section) to ensure that a visible group has a separator both between it and any other elements.
8See IWorkbenchPartSite.registerContextMenu(MenuManager,ISelectionProvider) to understand why.
9The reason for this is two-fold. If the item is showing when it is made visible or invisible, then this causes “flicker” and possibly a significant layout change in the application (e.g., an extra row in the cool bar). It is also important to bear in my mind that experienced users will frequently find items using positional memory. An experienced user stops reading each item carefully, and simply moves to a remembered location on the screen.
10With the exception of those elements whose name conflicts with SWT class names. In these cases, the name will prepend an “S”. So, an XML menu definition becomes an instance of SMenu in Java.
11The performance costs are further amortized by sharing the EvaluationContext across all elements. So, for example, this avoids the need to make multiple calls to getSelection().
12This opens a hole in the dynamic plug-in story. If a plug-in contributes its user interface elements through extension points, then they can be cleaned up when the plug-in is unloaded. If there are contributed programmatically, then there is no way to know that these elements should be removed.
13Please see the section on Extension Points (subsection Model) for more information about declaring parameters in the commands extension point.
14There is no mechanism for notification if the map of potential parameters changes. The list will only be asked for in situations where the list if not expected to change (e.g., keys preference page). If the workbench were ever to use the map of potential parameters in a more dynamic setting, then polling would be used.
15Technically, it will be possible to activate handlers on a workbench window or on the workbench itself. This service is provided for legacy purposes only, and is not intended for developers writing new code. If new developers do use this service, then they are advised to read the section about activating contexts.