Random thoughts and constraints
Some thoughts on executing handlers in E4:
- Commands should be easy to execute
- When a command executes it should have access to the parts of the model that it cares about
- A command should always be able to determine if it can execute
- A command in a local context (like within a view) should always be presented with its local state
- Should a handler be responsible for state? Keeping track of its enabled state, or the last radio button selected, for example?
- Should a handler simply have access to some model state that it can publish information about?
- Should a handler be request driven? It can answer questions and execute behaviour, but it will never offer events. For example, the handler instance would not have an enabled state that could change, but could always answer if asked.
- If the handler doesn't publish enabled state changes, who would request updates?
Handlers and the ISelectionProviders/ISelectionService/ISources.ACTIVE_CURRENT_SELECTION_NAME variable. Eclipse currently offers a selection at a number of levels. You can go to your workbench window and get the selection, which is often used track a common selection between multiple parts. You can track down an individual part and get the selection if it provides one. Context menus can have even more localized or specialized selection providers.
Handlers have access to the "selection" in the IEvaluationContext. Currently this selection tracks the ISelectionService (workbench window) selection. The context menu's localized selection provider was introduced so that it wouldn't have to be published to the more global ISelectionService. To avoid bleeding localized state to all of the handlers, the variable activeMenuSelection was introduced to expose the localized selection provider. The cost was more complexity when writing core expressions and code that has to work in regular menus/keybindings and in context menus.
Thoughts on menus in E4:
- The application or product should have final say on which menus are visible or not.
- Should menus be request driven: MenuItems check their visibility and enabled state before they appear, in the desktop case on an SWT.Show event
- Should menus be event driven: property change events update the MenuItems, creating, disposing, and enabling them asynchronously
- How does a menu item know what handler it is talking to? Is it even important?
- If a menu item only knows its command, then it will reflect the state of the current handler for that command, no matter where it was chosen from. This is the state of commands in 3.x.
- If a menu item was talking to a local handler, then it reflect its local state more accurately. But that would mean that the handler class would need to be instantiated in every relevant local context (every text editor, for example).
How do keybindings fit into the context hierarchy?
- In 3.x the keybinding system is given first crack at any incoming key event. If it can look up a command for the key, it eats the key event and executes the command (against the current global application state).
- Should the global application state track a "local" context that the key event can execute against?
- Should the appropriate context always be tied to the UI element that the key event is issued against?
- How does the context effect macro replay?
Thoughts on toolbars in E4:
- they share a lot of code with menus in the Eclipse framework: this can make the code for both complicated (perhaps by necessity)
- Should toolbars be request driven: ToolBars are update by a request into the framework, potentially a layout or perhaps timer driven
- Should toolbars be event driven: property change events can update the tool items and add/remove them from toolbars where necessary
This is available in the incubator now as 'org.eclipse.e4.locator'. It has been added to the ui-e4Set.psf Project Set Files.
This prototype is written to be technically accurate. It contains no optimizations (that's why the algorithms are simple, but involve a lot of swapping information).
The idea of commands being backed by one or more handlers provides flexibility for many of the scenarios we have in Eclipse. One aspect of the command framework that does not handle edge conditions easily is the global application context.
It works well for commands that don't care about state, or are always enabled. Actions that must simply be executed, like About and Exit. Also, commands that are contributed by specific parts work well, as they tend to be instantiated by the part and have access to source of "local contexts": the part and its member variables.
In 3.2 the
org.eclipse.ui.services.IServiceLocator was introduced, to try and break the workbench dependency on "going global" to get information and services. Most of the implementation to date has focused on allowing "more local" implementations of a service to answer questions asked by the part. This work is important to help provide the ability to nest one component within another. But it's possible that it can be extended to help with the problem of having access to the correct information that a handler needs to execute or a menu item needs to determine its visibility.
The Locator provides 2 interfaces (bear with me, the separation of implementation and use is not in the prototype :-):
- ILocator - allow a component to look up variables (perhaps some relevant part of the model) and services.
- IUpdateableLocator - an interface for use by service provides and variable provides:
- UpdateStrategy - how variables are set in the locator hierarchy. An UpdateStrategy can use the IUpdateableLocator to walk the ILocator hierarchy, requesting and setting information. The UpdateStrategy also determines how events are fired when a variable changes.
- LookupStrategy - how a request to get a variable is answered. A LookupStrategy uses the IUpdateableLocator to walk the ILocator hierarchy and retrieve the variable value that is appropriate for where the lookup was initiated.
The locator hierarchy knows its parent, children, and has the notion of an activeChild. A locator responds to activate() events by activating itself and its parent chain (all the way to the root locator). A locator anywhere in the hierachy has the notion of an active chain, since it can always see its parent locator, and it may (or may not) have an active child. This allows a locator to be queried for a variable whether or not it is in the global "active" chain. This is important to support the notion of local context information, for both things like commands that are local to a view and for nested components.
The 2 strategies that provide the most value are
Consider this example hierarchy. Workbench represents the global locator. WorkbenchWindow1 represents the open window. ProblemView represents one part, and OutlineView represents another part.
Lookups search the "current active branch" from the locator that initiated the lookup. This usually involves traveling down the chain of activeChildren until a "leaf" is reached, then walking back up the parent hierarchy until the first locator can answer the lookup. That value is then returned.
If the OutlineView locator has the selection set and is active, then asking for the selection at the Workbench or WorkbenchWindow1 level will walk down to the OutlineView locator and return that value.
If the OutlineView is active and the ProblemView locator has the selection set as well, the Workbench and WorkbenchWindow1 will still see the OutlineView selection.
If the ProblemView becomes active, then the Workbench and WorkbenchWindow1 will walk down to the ProblemView locator and return that selection value.
If the WorkbenchWindow1 was to set a selection, Workbench and WorkbenchWindow1 would still return the ProblemView selection as it is the first locator that can answer the lookup request.
Updating or setting a variable (like 'selection') works on a "visibility" algorithm.
If the OutlineView is active and sets its selection, that change will be visible up the parent chain to WorkbenchWindow1 and Workbench. Any listeners for this property change on the OutlineView, WorkbenchWindow1, and Workbench would need to be notified.
If the OutlineView is active and the ProblemView sets its selection, that change will only be visible within the ProblemView and only ProblemView listeners need to be notified.
If the ProblemView then becomes active, that selection is now visible to the WorkbenchWindow1 and Workbench. Any of their property change listeners would need to be notified. OutlineView and ProblemView do not see each others selection changes, so the OutlineView listeners would not need to be notified. The ProblemView listeners don't need to be notified either, since they were already.
Setting a variable on the WorkbenchWindow1 also effects all locators that can see it.
If the WorkbenchWindow1 sets an activeEditorId, this is visible to its parent, the Workbench, and also visible to all of its children (regardless of which is the current active child). So all listeners (Workench, WorkbenchWindow1, OutlineView, and ProblemView) would need to have their listeners notified.
Thoughts on this Prototype's Design
This prototype's main goal is "Correctly answer questions based on a local context". It needs:
- to have a "location", something that identifies its place w.r.t. other components/model/information within the application space.
- a strategy that knows how to look up a particular piece of information or state, based on the location the question is asked.
- a strategy that knows how to set a piece of state, based on the location
- a strategy to notify listeners about state that changes (that would be visible to those listeners)
Macro Recording & Playback
- From Architecture Council/Minutes May 15 2008#Macro Recording and Playback:
- Commands plus Undo Stack together are good but unsure how far they will get us -- at least also need Non-UI Scripting
- Cannot force all plugins to use Commands -- EMF introduced ChangeListener
- Thinking about Macro Recording in all architectural decisions may help coming up with a better architecture (that's more cleanly separated) - but we may not be able to make 100% of all operations recordable
- Need to make the discussion more concrete, need experts to look at how Macro Recording is done in other apps
- Shoot for a "90%" solution where editable Scripts are recorded and users can fine-tune them by editing
- Strongly Related to e4/Eclipse Application Model (Service / Command Bus)
Thoughts on commands:
- they can be recorded and played back
- parameterizations of commands are static in nature. Information that can effect the way a command executes, like showing a specific IDed view or telling the handler if this invocation should move forward or backwards.
- parameterizations don't deal with the model (which in the current world is proprietary to the handlers).
- Undo-able operations is one way to interact with the undo stack. This is currently available in core.commands.
- LTK uses another pattern, where operations generate one or more Change objects. When executed, they update the "proprietary model" and generate a reverse Change object that can reset the model and generate the original Change object.
The granularity of your macro recording will push how complex the system needs to be:
- you can issue commands for what you consider recordable items. i.e. the TextEditor has commands defined like org.eclipse.ui.edit.text.goto.lineEnd
- for more UI driven macros, you can listen for all Key events and Selection events. This is very challenging on a replay, since you need to apply some events to the equivalent widget in the new starting context (i.e. replay in the new editor), and replaying after a restart will have all new widgets that need to react to the events.
Should E4 include a more standardized way that all contributors expose their model? That might simplify scripting by restricting modelling decisions contributors can make.
... more to come
Mylyn Monitor API
Mylyn provides a standalone Monitor API that provides sophisticated workbench monitoring facilities with support for capturing commands, actions, menu selections, selections, edits and preference changes:
- Overview: Mylyn Integrator Reference#Monitor API (see resources links)
- Relation to other Eclipse monitoring frameworks: Mylyn FAQ#Which usage monitoring framework should I use.3F
- Example of use for playback: http://www.cs.ubc.ca/grads/resources/thesis/May07/Safer_Izzet.pdf (Master's thesis, see screenshots)