Every Sapphire UI part can expose operations that the user can invoke. These operations are called actions. While Sapphire does not prescribe how actions are surfaced to the user, a typical presentation would use buttons, menu items or links.
This documents covers the API for defining actions, contributing handlers and other related topics.
Table of Contents
An action is an abstract definition of an operation that a user may want to perform. It does not have a concrete implementation. That is provided by handlers that are attached separately. An action simply defines appearance, applicability and access.
An action that does not have at least one active handler will not be presented to the user. This makes it possible to define actions fairly widely and only provide handlers in situations where it makes sense. By defining the action once instead of on every use, we are guaranteed the consistency of presentation.
Example
In this example, a message box action is declared. Note that the definition includes appearance (label and image) and presentation (location hints and key binding), but no sign of the implementation. The handler would need to be contributed separately. The handler definition would reference action id.
<property-editor>
<action>
<id>MessageBoxAction</id>
<label>message</label>
<image>org/eclipse/sapphire/samples/gallery/BalloonYellow.png</image>
<location>after:Sapphire.Browse</location>
<location>before:Sapphire.Create</location>
<key-binding>SHIFT+CONTROL+ALT+m</key-binding>
</action>
</property-editor>
Details
##action-details##An action handler is what performs the actual work for an action. Handlers can be contributed by multiple parties and multiple handlers can be active at the same time. If multiple handlers are active, the user will typically be given a choice of which handler to invoke. Exactly how the choice is presented is dependent on the presentation layer, but a common approach is to use a cascading menu or a popup menu that is activated when user clicks on an action button.
Example
In this example, a handler is contributed for a browse action. The handler simply opens a dialog with a message.
<property-editor>
<action-handler>
<action>Sapphire.Browse</action>
<impl>MessageBoxActionHandler</impl>
</action-handler>
</property-editor>
public class MessageBoxActionHandler extends SapphireActionHandler
{
@Override
protected Object run( SapphireRenderingContext context )
{
MessageDialog.openInformation( context.getShell(), "Message", "Hello World!" );
return null;
}
}
Details
##action-handler-details##Sometimes it is necessary to be able to create handlers dynamically based on information that isn't known until runtime. When you contribute a handler, that handler is either activated or not depending on the provided condition. When you contribute a handler factory, the factory can decide at runtime how many handlers to contribute.
Example
In this example, a handler factory is contribute for a browse action. The factory in turn contributes a handler for every file in the specified folder. Activating one of these handlers sets the property value to the absolute path of the file.
<property-editor>
<action-handler-factory>
<action>Sapphire.Browse</action>
<impl>KeyFolderBrowseHandlerFactory</impl>
<param>
<name>folder</name>
<value>c:\samples</value>
</param>
</action-handler-factory>
</property-editor>
public class KeyFolderBrowseHandlerFactory extends SapphireActionHandlerFactory
{
private File folder;
@Override
public void init( SapphireAction action, ISapphireActionHandlerFactoryDef def )
{
super.init( action, def );
this.folder = new File( def.getParam( "folder" ) );
}
@Override
public List create()
{
List handlers = new ArrayList();
for( File file : this.folder.listFiles() )
{
if( file.isFile() )
{
handlers.add( new Handler( file ) );
}
}
return handlers;
}
private static class Handler extends SapphireBrowseActionHandler
{
private final File file;
public Handler( File file )
{
this.file = file;
setLabel( this.file.getName() );
}
@Override
protected String browse( Presentation context )
{
return this.file.getAbsolutePath();
}
}
}
Details
##action-handler-factory-details##In some scenarios it is useful to be able to suppress an action handler for a particular UI part that might otherwise be relevant. The action handler in question may have been contributed globally via Sapphire's extensions mechanism, but it just doesn't make sense in a given context. The mechanism for accomplishing this is action handler filters.
Filters are specified when defining a UI part. A filter implementation must extend SapphireActionHandlerFilter class. Note that a filter returns true for what should be kept, so false should be returned for handlers that should be filtered out.
Example
In this example, a very simple filter is applied that removes all browse action handlers. Since an action will not be surfaced unless it has active handlers, this is equivalent to removing the browse action.
<property-editor>
<action-handler-filter>
<impl>MyFilter</impl>
</property-editor>
</property-editor>
public class MyFilter extends SapphireActionHandlerFilter
{
@Override
public boolean check( SapphireActionHandler handler )
{
return ( ! handler.getAction().getId().equals( "Sapphire.Browse" ) );
}
}
Details
##action-handler-filter-details##Sometimes it is useful to present an adhoc link that is wired a specific action. This can be done with an action link, which is a UI part in its own right. An action link specifies the ID of the action that should be invoked when user clicks on the link. The action and its handler can be defined locally in the action link's definition. Alternatively, the part hierarchy above the action link is searched until the action is found.
Example
In this example from the contacts sample, an "Add a contact" link is placed in the section associated with the "Contacts" node in the content outline. The Contacts node has the add action due to the way its child nodes are defined. Since the Contacts node is the nearest ancestor part to the action link that defines the add action, that is the action that ends up bound to the link.
<action-link>
<action-id>Sapphire.Add</action-id>
<label>Add a contact</label>
</action-link>
Every UI part that supports actions must define one or more context. Actions are contributed to a context and the UI part displays all actions contributed to a context at the appropriate location.
The following context are currently available:
Context | Typical Presentation |
---|---|
Sapphire.Actuator | A stand-alone link or button. |
Sapphire.EditorPage | Buttons in the editor page header. |
Sapphire.EditorPage.Outline | Menu items in the content outline's context menu. |
Sapphire.EditorPage.Outline.Header | Buttons directly above the content outline. |
Sapphire.EditorPage.Outline.Node | Menu items in the content outline's context menu. |
Sapphire.ElementPropertyEditor | Buttons next to the property editor. |
Sapphire.ListPropertyEditor | Buttons next to the property editor. |
Sapphire.Section | Buttons in the section header. |
Sapphire.ValuePropertyEditor | Buttons next to the property editor. |
Sapphire.Diagram.Editor | Menu items in the diagram editor's context menu. |
Sapphire.Diagram.Node | Menu items in the diagram node's context menu. |
Sapphire.Diagram.Connection | Menu items in the diagram connection's context menu. |
Sapphire.WithDirective | Key binding in the with directive. |
While actions, handlers and handler factories can be contributed directly in a part's definition, sometimes it is more useful to contribute them in a system-wide fashion. This can be done using Sapphire's extension system.
To contribute an extension, create sapphire-extension.xml file in the META-INF folder. The extension must be located in the same classloader as Sapphire. On an OSGi system this is done by creating a fragment to the org.eclipse.sapphire.ui bundle.
The semantics and syntax of the contribution is identical to what is used in a part's definition. The biggest difference is that there is no implied scope, so it is very important to specify context and condition. If neither is specified, the contribution will be active for all UI parts, which is not very useful.
The listing of all extensions currently contributed can be found in the documentation.
Example
In this example, a message box action is contributed to all value property editors.
<extension xmlns="http://www.eclipse.org/sapphire/xmlns/extension">
<action>
<id>MessageBoxAction</id>
<label>message</label>
<image>org/eclipse/sapphire/samples/gallery/BalloonYellow.png</image>
<location>after:Sapphire.Browse</location>
<location>before:Sapphire.Create</location>
<key-binding>SHIFT+CONTROL+ALT+m</key-binding>
<context>Sapphire.ValuePropertyEditor</context>
</action>
</extension>